接上篇《使用篇》,本文主要记录 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 实例, 模拟后台两台服务器, 控制器层在调用接口时,将返回端口信息,并打印到控制台

打印端口.png

整个链路调用分发逻辑:

链路.png

传统的 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 等等, 但这里没有用到,暂且不表

组件名称说明
serviceservice 对应服务,可以直接指向一个 API 服务节点(host 参数设置为 ip + port),也可以指定一个 upstream 实现负载均衡。简单来说,服务用于映射被转发的后端 API 的节点集合
routeroute 对应路由,它负责匹配实际的请求,映射到 service 中
upstreamupstream 对应一组 API 节点,实现负载均衡
targettarget 对应一个 API 节点

组件调用关系:

调用逻辑.png

逻辑说明完整,下面开始执行 Admin Api

1 调用 Kong Admin API /upstreams, 创建 名为 demo-upstream 的 upstream:
$ curl -X POST http://127.0.0.1:8001/upstreams --data "name=demo-upstream"

upstream.png

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"

targets.png

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 的名字

service.png

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"

routes.png

paths.png

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 项目,结果如下:

负载均衡结果.png

符合预期!

限流限速

这个是 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 基础维度的限流

ratelimiting.png

开启ratelimiting.png

结果测试

快速使用 curl http://127.0.0.1:8000/demo-api/printPort 命令 2 次,会被 Kong 限流,返回结果如下:

{"message":"API rate limit exceeded"}

限制结果.png

符合预期!

访问权限控制(身份校验)

同样的,我们通过使用 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"

jwt.png

验证

使用 curl http://127.0.0.1:8000/demo-api/printPort 命令,会被 Kong 安全拦截。返回结果如下:

{"message":"Unauthorized"}

jwt受限.png

2 创建用户
  • 调用 Kong Admin API consumers,创建一个 Consumer 消费者(用户):
curl -i -X POST http://localhost:8001/consumers/ \
    --data "username=gotkx"

consumer.png

  • 调用 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/ 用到,进行解密

jwt Token收工.png

结果验证:

重新访问 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"

验证jwt Token 生效.png

符合预期!

以上就是 Kong 常用 功能分享, 后续将继续学习和总结