微服务之网关

API网关除了提供对外部http接口统一管理,还需要实现动态路由、限流管理等能力。

网关能力

服务路由

  • 静态路由策略配置
  • 后端服务的软负载均衡
  • 后端服务的心跳检查
  • 参数分流
  • 流量的镜像复制

服务治理

  • 后端服务的故障隔离
  • 网关、服务、API 级别的限流和熔断
  • 固定时段和周期时段的 API 维护开关

服务治理框架层的服务治理和流量管理:

服务治理

  • 服务限流,支持 QPS、Thread 等多种限流方式
  • 降级与熔断,支持基于RT、错误率的熔断策略以及手动降级策略
  • 服务容错,支持 failover、failfast、failback等多种容错机制

流量管理

  • 路由管理,支持基于黑白名单的路由规则
  • 负载均衡,支持多种负载均衡规则,兼容 Spring Cloud Ribbon
  • 参数分流,支持参数取模、名单分流、权重分流等

技术选型

image-20221016221436014

这里使用Kong作为演示

Kong

Kong是一个云原生,快速,可扩展和分布式微服务抽象层(也称为API网关,API中间件或某些情况下的Service Mesh)。作为2015年的开源项目,其核心价值在于高性能和可扩展性。

安装

这里使用postgres作为后端存储,为了后续网络通信方便,使用新创建的网桥网络

1
docker network create kong-net

如果使用默认网络,使容器可以相互互通即可(官方提供自定义网桥,可以实现通过容器直接通信,如果使用默认bridge,则需要通过--link name:name通信)

1
2
3
4
5
6
7
docker run -d --name kong-database \
--network=kong-net \
-p 5432:5432 \
-e "POSTGRES_USER=kong" \
-e "POSTGRES_DB=kong" \
-e "POSTGRES_PASSWORD=kongpass" \
postgres:9.6

初始化数据库

1
2
3
4
5
6
docker run --rm --network=kong-net \
-e "KONG_DATABASE=postgres" \
-e "KONG_PG_HOST=kong-database" \
-e "KONG_PG_PASSWORD=kongpass" \
-e "KONG_PASSWORD=test" \
kong/kong-gateway:3.0.0.0 kong migrations bootstrap

启动 Kong Gateway服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
docker run -d --name kong-gateway \
--network=kong-net \
-e "KONG_DATABASE=postgres" \
-e "KONG_PG_HOST=kong-database" \
-e "KONG_PG_USER=kong" \
-e "KONG_PG_PASSWORD=kongpass" \
-e "KONG_PROXY_ACCESS_LOG=/dev/stdout" \
-e "KONG_ADMIN_ACCESS_LOG=/dev/stdout" \
-e "KONG_PROXY_ERROR_LOG=/dev/stderr" \
-e "KONG_ADMIN_ERROR_LOG=/dev/stderr" \
-e "KONG_ADMIN_LISTEN=0.0.0.0:8001" \
-e "KONG_ADMIN_GUI_URL=http://localhost:8002" \
-e KONG_LICENSE_DATA \
-p 8000:8000 \
-p 8443:8443 \
-p 8001:8001 \
-p 8444:8444 \
-p 8002:8002 \
-p 8445:8445 \
-p 8003:8003 \
-p 8004:8004 \
kong/kong-gateway:3.0.0.0
  • 8001 管理端口
  • 8002 管理员访问页面端口(功能较少,后续使用konga
  • 8000 用户访问的端口

通过8081端口查看服务是否正常

1
curl -i -X GET --url http://localhost:8001/services
1
2
3
4
5
6
7
8
9
10
11
HTTP/1.1 200 OK
Date: Sun, 16 Oct 2022 15:19:49 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
Access-Control-Allow-Origin: http://localhost:8002
X-Kong-Admin-Request-ID: IU4hDZVM8va4pHfxb3USILri1TmiOmn4
vary: Origin
Access-Control-Allow-Credentials: true
Content-Length: 23
X-Kong-Admin-Latency: 9
Server: kong/3.0.0.0-enterprise-edition

安装konga

kongga 是一个操作kong的图形化界面,也支持通过docker安装

1
2
3
4
5
6
docker run -d -p 1337:1337 \
--network kong-net \
--name konga \
-e "NODE_ENV=production" \
-e "TOKEN_SECRET={{somerandomstring}}" \
pantsel/konga

通过 http://ip地址:1337/register打开首次登录注册页面,注册之后,登录

通过 8001端口配置

image-20221016234427472

定义

  • Service 服务

    顾名思义,服务实体是每个上游服务的抽象。举个例子,services 可以是一个数据转换微服务,一个计费api等等。

    Service 的主要属性是其URL(Kong应该将流量代理到的地方),可以设置为单个字符串,也可以单独指定其protocol, host, portpath

    Service 与 router 相关联(一个 Service 可以有许多与之关联的 router)。router 是Kong的入口点,并定义匹配客户端请求的规则。一旦 router 匹配,Kong就会将请求代理到其关联的服务。有关Kong代理流量的详细说明,请参阅代理参考

  • Route 路由

    路由实体定义规则以匹配客户端请求。每个Route与一个服务相关联,一个服务可能有多个与之关联的路由。匹配给定路由的每个请求都将代理到其关联的服务。
    Routes 和 Services 的组合(以及它们之间的关注点分离)提供了一种强大的路由机制,通过它可以在 Kong 中定义细粒度的入口点,从而导致基础架构的不同上游服务。

  • Upstream 上游

    上游对象表示虚拟主机名,可用于通过多个服务(目标)对传入请求进行负载均衡。例如,对于主机为service.v1.xyz的 Service 对象,上游名为 service.v1.xyz。对此服务的请求将代理到上游定义的目标。

    上游还包括健康检查程序,该检查程序能够基于其能力或无法提供请求来启用和禁用目标。运行状况检查程序的配置存储在上游对象中,并应用于其所有目标。

  • plugin 插件

    插件实体表示将在HTTP请求/响应生命周期期间执行的插件配置。它是如何为在Kong后面运行的服务添加功能的,例如 Authentication 或 Rate Limiting 。您可以访问Kong Hub,获取有关如何安装以及每个插件所需值的更多信息。

    将插件配置添加到服务时,客户端向该服务发出的每个请求都将运行所述插件。如果某个特定消费者需要将插件调整为不同的值,您可以通过创建一个单独的插件实例来实现,该实例通过serviceconsumer这两个字段指定服务和消费者。

  • consumer 消费者

    Consumer对象表示Service服务的使用者或用户。您可以依靠Kong作为主数据存储,也可以将使用者列表与数据库映射,以保持Kong与现有主数据存储之间的一致性。

基础操作

新增service

点击 service-add service

image-20221017153931587

Url:http://192.168.41.68:8080代表转发到的Url

upstream可进行健康检查和负载均衡,在结合consul后,可以更好的实时变更后端服务状态。

设置dns_resolverconsul的地址,将Url改为consul的域名。

新增route

在新增的service中,点击名称-routes-add route即可

image-20221017154050865

需要注意,点击path后,需要通过回车,将该路径加入到Paths

通过Kong8000端口,以及对应服务的path,访问测试接口

1
curl http://192.168.41.68:8000/api/v1/version
1
{"code":200, "result":{"go_version":"go1.16.7"}}%

配置jwt的插件

JWT 插件

Consumers中创建一个consumer

image-20221017160415996

为这个consumer添加jwt

点击credentials,点击JWT,点击Create JWT

image-20221017160806361

添加全局JWT,点击plugins-add global plugins-jwt

image-20221017162104367

设置header,与后端服务保持一致,通过回车添加。

在jwt.io中生成jwt

image-20221017161300242

需要注意,在payload中添加iss,与创建全局jwt时默认的 key claim name 保持一致

image-20221017161411417

拷贝consumer中创建jwt时生成的secret作为生成jwt的secret

image-20221017161539422

当没有配置jwt时发送请求

image-20221017161803314

header中配置x-token: Bearer 配置之后

image-20221017162146227

推荐阅读:

API 网关能做什么?](https://www.redhat.com/zh/topics/api/what-does-an-api-gateway-do)

Goku

Apinto

微服务 API 网关 Kong 中文文档