接上篇《使用篇》,本文主要记录 Kong 的常用用法
负载均衡
这里示例的负载均衡,会狭义一些,因为在复杂业务环境里面,通常会对请求做一致性哈希 + Lua ,去固定请求某组服务器,而当流量异常紊乱、服务器集群通信异常、或弹性伸缩,会先切换成 负载均衡模式,待稳定后,再切换为 一致性哈希,相对来说,一致性哈希计算,比负载均衡使用场景更广。
但我们依然可以对负载场景,借助 kong 进行讨论和实现
实现负载均衡,手段是很多的,如果是 Java 代码实现,通过 Eureka 内置 Ribbon 相关功能, 添加方法级别注解 @LoadBalance 并编辑策略满足需求
策略类 | 命名 | 描述 |
---|---|---|
RandomRule | 随机策略 | 随机选择 Server |
RoundRobinRule | 轮询策略 | 按照顺序循环选择 Server |
RetryRule | 重试策略 | 在一个配置时间段内,当选择的 Server 不成功,则一直尝试选择一个可用的 Server |
BestAvailableRule | 最低并发策略 | 逐个考察 Server,如果 Server 的断路器被打开,则忽略,在不被忽略的 Server 中选择并发连接最低的 Server |
AvailabilityFilteringRule | 可用过滤测试 | 过滤掉一直连接失败,并被标记未 circuit tripped(即不可用) 的 Server,过滤掉高并发的 Server |
ResponseTimeWeightedRule | 响应时间加权策略 | 根据 Server 的响应时间分配权重,响应时间越长,权重越低,被选择到的几率就越低 |
ZoneAvoidanceRule | 区域权衡策略 | 综合判断 Server 所在区域的性能和 Server 的可用性轮询选择 Server,并判定区域 Zone 性能是否可用,剔除不可用的 Zone 中的所有 Server |
但通过 Java 实现负载均衡, 效率相对于 Nginx 来说是很低的, 因为 Nginx 配合 Lua 语法, 偏向底层的语言,机器执行效率比较高. 另外用 Java 实现负载均衡, 这就是请求已经到达服务器了, 再判断是否执行. 这已经占用了服务器的 QPS , 整体业务吞吐量处理能力下降
应该在外围,建立判断准入机制. 而 Kong 是基于 Nginx 的, 继承了相关优势
实操举例: 后端两台服务器 A 和 B,提供相同的接口功能,要求前台访问该接口时,配置的权重相等 (也就是说, 两次请求,就会有一次访问到 A 或者 B ).
后端服务代码, 见 https://github.com/hkdijia/Kong-Access.git
通过启动两个 SpringBoot 实例, 模拟后台两台服务器, 控制器层在调用接口时,将返回端口信息,并打印到控制台
整个链路调用分发逻辑:
传统的 Nginx 实现上述效果, 需要如下配置:
upstream demo-upstream {
server localhost:9001 weight=100;
server localhost:9002 weight=100;
}
server {
listen 80;
location /demo-api/ {
proxy_pass http://demo-upstream;
}
}
Kong 提供了非常优雅的一套 Admin API, 可以进行上述的负载均衡的配置, 不用像之前那样, 改 Nginx 配置文件, 还生怕出错啦.
Kong 主要四对象, 当前还有其他的 Consumer、Plugin、Tag、Certificate、Target 等等, 但这里没有用到,暂且不表
组件名称 | 说明 |
---|---|
service | service 对应服务,可以直接指向一个 API 服务节点(host 参数设置为 ip + port),也可以指定一个 upstream 实现负载均衡。简单来说,服务用于映射被转发的后端 API 的节点集合 |
route | route 对应路由,它负责匹配实际的请求,映射到 service 中 |
upstream | upstream 对应一组 API 节点,实现负载均衡 |
target | target 对应一个 API 节点 |
组件调用关系:
逻辑说明完整,下面开始执行 Admin Api
1 调用 Kong Admin API /upstreams, 创建 名为 demo-upstream 的 upstream:
$ curl -X POST http://127.0.0.1:8001/upstreams --data "name=demo-upstream"
2 调用 Kong Admin API /upstreams//targets,创建 Spring Boot 项目对应的 2 个 target
路径参数,为 upstream 的名字
# 端口 9001 对应的 target
curl -X POST http://127.0.0.1:8001/upstreams/demo-upstream/targets --data "target=192.168.50.88:9001" --data "weight=100"
# 端口 9002 对应的 target
curl -X POST http://127.0.0.1:8001/upstreams/demo-upstream/targets --data "target=192.168.50.88:9002" --data "weight=100"
Tips:
如上的配置,效果等同于如下 Nginx 的配置:
upstream demo-upstream {
server localhost:9001 weight=100;
server localhost:9002 weight=100;
}
3 调用 Kong Admin API /services,创建名字为 demo-service 的 service:
curl -X POST http://127.0.0.1:8001/services --data "name=demo-service" --data "host=demo-upstream"
host 参数,用于设置对应的 upstream 的名字
4 调用 Kong Admin API services/$/routes,创建一个请求路径为 path 的 route:
curl -X POST http://127.0.0.1:8001/services/demo-service/routes --data "name=demo-route" --data "paths[]=/demo-api"
Tips:
如上的配置,效果等同于如下 Nginx 的配置:
server {
listen 80;
location /demo-api/ {
proxy_pass http://demo-upstream;
}
}
结果测试
不断执行 curl http://127.0.0.1:8000/demo-api/printPort 命令,请求 Kong 网关来负载均衡转发到后端的 Spring Boot 项目,结果如下:
符合预期!
限流限速
这个是 Kong 提供的优秀插件功能之一. Kong 提供了 Rate Limiting 插件,实现对请求的限流功能,通过量化请求,精准化管理.
同样的 Java 内部也可以用代码实现,但是不太优雅啦
Rate Limiting 支持秒/分/小时/日/月/年多种时间维度的限流,并且可以组合使用。例如说:限制每秒最多 100 次请求,并且每分钟最多 1000 次请求
Rate Limiting 支持 consumer、credential、ip 三种基础维度的限流,默认为 consumer。例如说:设置每个 IP 允许每秒请求的次数。计数的存储,支持使用 local、cluster、redis 三种方式进行存储,默认为 cluster:
存储方式 | 说明 |
---|---|
local | 存储在 Nginx 本地,实现单实例限流 |
cluster | 存储在 Cassandra 或 PostgreSQL 数据库,实现集群限流 |
redis | 存储在 Redis 数据库,实现集群限流 |
Rate Limiting 采用的限流算法是计数器的方式,所以无法提供类似令牌桶算法的平滑限流能力
创建 Rate Limiting 插件
调用 Kong Admin API services/$/plugins,创建 Rate Limiting 插件的配置:
curl -X POST http://127.0.0.1:8001/services/demo-service/plugins \
--data "name=rate-limiting" \
--data "config.second=1" \
--data "config.limit_by=ip"
- name 参数,设置为 rate-limiting 表示使用 Rate Limiting 插件
- config.second 参数,设置为 1 表示每秒允许 1 次请求
- config.limit_by 参数,设置为 ip 表示使用 IP 基础维度的限流
结果测试
快速使用 curl http://127.0.0.1:8000/demo-api/printPort 命令 2 次,会被 Kong 限流,返回结果如下:
{"message":"API rate limit exceeded"}
符合预期!
访问权限控制(身份校验)
同样的,我们通过使用 Kong 提供的JWT 插件,实现使用 JWT 进行认证,保护后端服务的安全性。
我们依然在负载均衡的基础上,对名字为 demo-service 的 service 进行 JWT 身份认证
1 创建 JWT 插件
调用 Kong Admin API services/$/plugins,创建 JWT 插件的配置:
curl -X POST http://127.0.0.1:8001/services/demo-service/plugins \
--data "name=jwt"
验证
使用 curl http://127.0.0.1:8000/demo-api/printPort 命令,会被 Kong 安全拦截。返回结果如下:
{"message":"Unauthorized"}
2 创建用户
- 调用 Kong Admin API consumers,创建一个 Consumer 消费者(用户):
curl -i -X POST http://localhost:8001/consumers/ \
--data "username=gotkx"
- 调用 Kong Admin API consumers//,生成该消费者的 JWT 信息:
curl -i -X POST http://localhost:8001/consumers/gotkx/jwt/
响应结果:
{
"created_at": 1602197335,
"id": "c9a3c493-f866-4aad-b7d0-b5236c6e4c68",
"tags": null,
"secret": "CcbZgmcUx2NuhOG9lOatmZ3wX62KgGuZ",
"rsa_public_key": null,
"consumer": {
"id": "3d86f527-5e64-4b7b-b05b-a61d4ddb183a"
},
"key": "7OPCg97d3Y0mT74vs6z3W0PjilZZeYNQ",
"algorithm": "HS256"
}
Tips:注意 key 和 secret 的结果,在 JSON Web Tokens - jwt.io
https://jwt.io/ 用到,进行解密
结果验证:
重新访问 http://127.0.0.1:8000/demo-api/printPort 地址,带上刚生成的 JWT Token。操作命令如下:
curl http://127.0.0.1:8000/demo-api/printPort \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI3T1BDZzk3ZDNZMG1UNzR2czZ6M1cwUGppbFpaZVlOUSJ9.m4-s-8r9yOfAEVWiyIduLZoYN10xaue79y4L_hRexJY"
符合预期!