17-API-Gateway微服务网关
一 API网关基础
1.1 什么是API网关
API网关是一个服务器,是系统的唯一入口。 从面向对象设计的角度看,它与外观模式类似。
API网关封装了系统内部架构,为每个客户端提供一个定制的API。它可能还具有其它职责,如身份验证、监控、负载均衡、缓存、协议转换、限流熔断、静态响应处理。
API网关方式的核心要点是,所有的客户端和消费端都通过统一的网关接入微服务,在网关层处理所有的非业务功能。通常,网关也是提供REST/HTTP的访问API。
1.2 网关的主要功能
微服务网关作为微服务后端服务的统一入口,它可以统筹管理后端服务,主要分为数据平面和控制平面:
-
数据平面主要功能是接入用户的HTTP请求和微服务被拆分后的聚合。使用微服务网关统一对外暴露后端服务的API和契约,路由和过滤功能正是网关的核心能力模块。另外,微服务网关可以实现拦截机制和专注跨横切面的功能,包括协议转换、安全认证、熔断限流、灰度发布、日志管理、流量监控等。
-
控制平面主要功能是对后端服务做统一的管控和配置管理。例如,可以控制网关的弹性伸缩;可以统一下发配置;可以对网关服务添加标签;可以在微服务网关上通过配置Swagger功能统一将后端服务的API契约暴露给使用方,完成文档服务,提高工作效率和降低沟通成本
-
路由功能:路由是微服务网关的核心能力。通过路由功能微服务网关可以将请求转发到目标微服务。在微服务架构中,网关可以结合注册中心的动态服务发现,实现对后端服务的发现,调用方只需要知道网关对外暴露的服务API就可以透明地访问后端微服务。
-
负载均衡:API网关结合负载均衡技术,利用Eureka或者Consul等服务发现工具,通过轮询、指定权重、IP地址哈希等机制实现下游服务的负载均衡。
-
统一鉴权:一般而言,无论对内网还是外网的接口都需要做用户身份认证,而用户认证在一些规模较大的系统中都会采用统一的单点登录(Single Sign On)系统,如果每个微服务都要对接单点登录系统,那么显然比较浪费资源且开发效率低。API网关是统一管理安全性的绝佳场所,可以将认证的部分抽取到网关层,微服务系统无须关注认证的逻辑,只关注自身业务即可。
-
协议转换:API网关的一大作用在于构建异构系统,API网关作为单一入口,通过协议转换整合后台基于REST、AMQP、Dubbo等不同风格和实现技术的微服务,面向Web Mobile、开放平台等特定客户端提供统一服务。
-
指标监控:网关可以统计后端服务的请求次数,并且可以实时地更新当前的流量健康状态,可以对URL粒度的服务进行延迟统计,也可以使用Hystrix Dashboard查看后端服务的流量状态及是否有熔断发生。
-
限流熔断:在某些场景下需要控制客户端的访问次数和访问频率,一些高并发系统有时还会有限流的需求。在网关上可以配置一个阈值,当请求数超过阈值时就直接返回错误而不继续访问后台服务。当出现流量洪峰或者后端服务出现延迟或故障时,网关能够主动进行熔断,保护后端服务,并保持前端用户体验良好。
-
黑白名单:微服务网关可以使用系统黑名单,过滤HTTP请求特征,拦截异常客户端的请求,例如DDoS攻击等侵蚀带宽或资源迫使服务中断等行为,可以在网关层面进行拦截过滤。比较常见的拦截策略是根据IP地址增加黑名单。在存在鉴权管理的路由服务中可以通过设置白名单跳过鉴权管理而直接访问后端服务资源。
-
灰度发布:微服务网关可以根据HTTP请求中的特殊标记和后端服务列表元数据标识进行流量控制,实现在用户无感知的情况下完成灰度发布。
-
流量染色:和灰度发布的原理相似,网关可以根据HTTP请求的Host、Head、Agent等标识对请求进行染色,有了网关的流量染色功能,我们可以对服务后续的调用链路进行跟踪,对服务延迟及服务运行状况进行进一步的链路分析。
-
文档中心:网关结合Swagger,可以将后端的微服务暴露给网关,网关作为统一的入口给接口的使用方提供查看后端服务的API规范,不需要知道每一个后端微服务的Swagger地址,这样网关起到了对后端API聚合的效果。
-
日志审计:微服务网关可以作为统一的日志记录和收集器,对服务URL粒度的日志请求信息和响应信息进行拦截
1.3 常见API网关
Kong | Traefik | Ambassador | Tyk | Zuul | |
---|---|---|---|---|---|
基本 | |||||
主要用途 | 企业级API管理 | 微服务网关 | 微服务网关 | 微服务网关 | 微服务网关 |
学习曲线 | 适中 | simple | simple | 适中 | simple |
成本 | 开源/企业版 | 开源 | 开源/pro | 开源/企业版 | 开源 |
社区star | 20742 | 21194 | 1719 | 4299 | 7186 |
配置 | |||||
配置语言 | Admin Rest api, Text file(nginx.conf 等) | TOML | YAML(kubernetes annotation) | Tyk REST API | REST API,YAML静态配置 |
配置端点类型 | 命令式 | 声明式 | 声明式 | 命令式 | 命令式 |
拖拽配置 | yes | no | no | no | no |
管理模式 | configurable | decentralised, self-service | decentralised, self-service | decentralised, self-service | decentralised, self-service |
部署 | |||||
kubernetes | 适中(k8s yaml,helm chart) | easy | easy | 适中(k8s yaml,helm chart) | 适中(k8s yaml,helm chart) |
Cloud IAAS | high | easy | N/A | easy | easy |
Private Data Center | high | easy | N/A | easy | easy |
部署模式 | 金丝雀(企业版) | 金丝雀 | 金丝雀,shadow | 金丝雀 | 金丝雀 |
state | postgres,cassandra | kubernetes | kubernetes | redis | 内存文件 |
可扩展性 | |||||
扩展功能 | 插件 | 自己实现 | 插件 | 插件 | 自己实现 |
扩展方法 | 水平 | 水平 | 水平 | 水平 | 水平 |
功能 | |||||
服务发现 | 动态 | 动态 | 动态 | 动态 | 动态 |
协议 | http,https,websocket | http,https,grpc,websocket | http,https,grpc,websocket | http,https,grpc,websocket | http,https |
基于 | kong+nginx | traefik | envoy | tyk | zuul |
ssl 终止 | yes | yes | yes | yes | no |
websocket | yes | yes | yes | yes | no |
routing | host,path,method | host,path | host,path,header | host,path | |
限流 | yes | no | yes | yes | 需要开发 |
熔断 | yes | yes | no | yes | 需要其他组件 |
重试 | yes | yes | no | yes | yes |
健康检查 | yes | no | no | yes | yes |
负载均衡算法 | 轮询,哈希 | 轮询,加权轮询 | 加权轮询 | 轮询 | 轮询,随机,加权轮询,自定义 |
权限 | Basic Auth, HMAC, JWT, Key, LDAP, OAuth 2.0, PASETO, plus paid Kong Enterprise options like OpenID Connect | basic | yes | HMAC,JWT,Mutual TLS,OpenID Connect,基本身份验证,LDAP,社交OAuth(例如GPlus,Twitter,Github)和传统基本身份验证提供程序 | 开发实现 |
tracing | yes | yes | yes | yes | 需要其他组件 |
istio集成 | no | no | yes | no | no |
dashboard | yes | yes | grafana,Prometheus | yes |
总结
由上述对比表格中可以看出:
从开源社区活跃度来看,无疑是Kong和Traefik较好;
从成熟度来看,较好的是Kong、Tyk、Traefik;
从性能角度来看,Kong要比其他几个领先一些;
从架构优势的扩展性来看,Kong、Tyk有丰富的插件,Ambassador也有插件但不多,而Zuul是完全需要自研,但Zuul由于与Spring Cloud深度集成,使用度也很高,近年来Istio服务网格的流行,Ambassador因为能够和Istio无缝集成也是相当大的优势
二 Kong的使用
ong是一款基于OpenResty(Nginx + Lua模块)编写的高可用、易扩展的,由Mashape公司开源的API Gateway项目。Kong是基于NGINX和Apache Cassandra或PostgreSQL构建的,能提供易于使用的RESTful API来操作和配置API管理系统,所以它可以水平扩展多个Kong服务器,通过前置的负载均衡配置把请求均匀地分发到各个Server,来应对大批量的网络请求
github地址:https://github.com/Kong/kong
中文文档地址:https://github.com/qianyugang/kong-docs-cn
2.1 安装配置
// docker安装文档
https://docs.konghq.com/gateway/latest/install-and-run/docker/?install=oss&_ga=2.159504765.1957380028.1654955981-1961332975.1654955981
// 第一步:使用docker安装postgresql
// ntpdate ntp3.aliyun.com
docker run -d --name kong-database \
-p 5432:5432 \
-e "POSTGRES_USER=kong" \
-e "POSTGRES_DB=kong" \
-e "POSTGRES_PASSWORD=kong" \
postgres:12
// 可以使用Navicat连接测试一下
// 第二步:迁移数据库
docker run --rm \
-e "KONG_DATABASE=postgres" \
-e "KONG_PG_HOST=10.0.0.102" \
-e "KONG_PG_PASSWORD=kong" \
-e "POSTGRES_USER=kong" \
-e "KONG_CASSANDRA_CONTACT_POINTS=kong-database" \
kong kong migrations bootstrap
// 可以看到表迁移完成
// 第三步:安装kong
// 3.1 rpm安装
curl -Lo kong-enterprise-edition-2.8.1.1.rpm $(rpm --eval "https://download.konghq.com/gateway-2.x-centos-%{centos_ver}/Packages/k/kong-enterprise-edition-2.8.1.1.el%{centos_ver}.noarch.rpm")
//或者直接从网站下载:https://download.konghq.com/gateway-2.x-centos-7/Packages/k/
// yum install kong-2.1.0.el7.amd64.rpm
sudo yum install kong-enterprise-edition-2.8.1.1.rpm
// 3.2 yum安装
curl $(rpm --eval "https://download.konghq.com/gateway-2.x-centos-%{centos_ver}/config.repo") | sudo tee /etc/yum.repos.d/kong.repo
sudo yum install kong-enterprise-edition-2.8.1.1
// 第四步:配置
cp /etc/kong/kong.conf.default /etc/kong/kong.conf
vim /etc/kong/kong.conf
database = postgres
pg_host = 10.0.0.102
pg_port = 5432
pg_timeout= 5000
pg_user = kong
pg_password = kong
pg_database = kong
dns_resolver = 10.0.0.102:8600 #如果需要使用服务发现组件,这里可以配置dns,比如consul的dns端口,默认8600
admin_listen = 0.0.0.0:8001 reuseport backlog=16384, 0.0.0.0:8444 http2 ssl reuseport backlog=16384
proxy_listen = 0.0.0.0:8000 reuseport backlog=16384, 0.0.0.0:8443 http2 ssl reuseport backlog=16384
// 第五步:启动kong
kong start -c /etc/kong/kong.conf
// 第六步:访问,可以看到显示,表示成功启动
10.0.0.102:8001
// 第七步:安装konga,图形化界面
docker run -d -p 1337:1337 --name konga pantsel/konga
// 第八步:访问konga
10.0.0.102:1337
// 第九步:汉化 https://github.com/jsonljd/konga-lang-plugin
mkdir dockertmp //创建一个临时目录
cd dockertmp
docker ps -a //查找konga的容器id
docker stop {konga容器id} //停止正在运行的容器
docker cp {konga容器id}:/app/assets ./ //将容器的文件复制到本地
docker pull jsonljd/konga-lang-plugin:latest //拉取语言插件镜像
docker run -d --name konga-lang-plugin -v /root/dockertmp/assets:/app/assets jsonljd/konga-lang-plugin //运行镜像,需要设置逻辑目录
docker cp ./assets {konga容器id}:/app //覆盖成功后即可
docker start {konga容器id} //重启容器
创建用户
配置kong地址
2.2 Kong配置
2.2.1 名词解析
kong作为网关基本功能当然是路由转发了,konga提供图形化界面来配置kong。首先如果需要配置kong第一步需要理解konga的几个术语。
- route
route是所有请求的入口,也就是kong会根据route的不同来转发到不同的service,一个route只能对应一个service,但是一个service可以有多个route,并且route不能单独存在,必须是先建立service后才能建立route。 - service
service其实可以理解为一个微服务,konga通过service桥接route和upstream,一个service只能有一个upstream。 - upstream
upstream其实就是一个上游地址,upstream可以有多个target,target就是真实的目标地址,target可以是一个ip地址或者是一个域名
2.2.2 kong的内部流程
2.2.3 配置service
首先配置第一步需要创建一个service,因为上也说了service作为route和upstream的桥梁,所以我们第一步先创建一个service。打开konga的admin端界面。如下
Name:可以随便填,但是建议填某个有意义的名称比如为某个微服务的名称。
Description:就是一个描述信息,可以不填,建议填微服务的中文描述。
Tags:标签,可以填多个,填一个需要按一个回车确定,这个可以不填。
Url:这个需要注意一下,因为这个比较重要,这个其实是下面四个的合体,填了这个,下面的四个不需要填,也就是Protocol,Host,Port,Path这四个的合体,这个值不会保存,提交后konga会自动拆成下面四个,其实就是一个访问的完整URL,对应的目标地址。
Protocol:请求协议,可选值:http,https。
Host:目标域名,这里可以是一个内部域名或者直接可以访问的地址,如果是内部域名,则需要匹配有相关的upstream,若是外部可以访问的地址,则不需要配置upstream。
Port:端口
Path:访问路径,也就是若匹配upstream后自动添加的路径
2.2.4 配置route
Name:这个可以不填,若不填,konga会默认生成一个随机字符串。
重点关注Hosts,Paths,Methods这三个属性,这三个是主要的属性。这三个属性必须三选一,也就是路由匹配规则。
Hosts这个是匹配访问的域名,这一般是配置为二级域名,比如配置为 blog.github.com ,若在以这个域名访问,则匹配成功,可以填多个,这里注意填完一个后需要按回车键确定。
Paths属性,这个用的最多,就是根据路径匹配,比如配置为/konga,则访问如 www.xxxx.com/konga 则匹配成功。
Methods换个就是http请求方法,比如get,post这些。
Strip Path:这个属性也需要注意一下,就是当path匹配时,是否去掉,如果打开,则去掉,比如上面所说的/konga,访问后面的host时,则不会带上/konga。
Preserve Host:这个选项的意思是真是请求host时是否保留,如果保留,则请求的的header里面的来源为原始来源,否则为service中定义的host
配置好,访问kong的地址:http://10.0.0.102:8000/ http://10.0.0.102:8000/index
2.2.5 配置从consul中读取并负载均衡
启动三次
package main
import (
"fmt"
"github.com/gin-gonic/gin"
consulapi "github.com/hashicorp/consul/api"
uuid "github.com/satori/go.uuid"
"net"
)
func RegisterConsul(localIP string, localPort int, name string,id string, tags []string) error {
// 创建连接consul服务配置
config := consulapi.DefaultConfig()
config.Address = "10.0.0.102:8500"
client, err := consulapi.NewClient(config)
if err != nil {
fmt.Println("consul client error : ", err)
}
// 创建注册到consul的服务到
registration := new(consulapi.AgentServiceRegistration)
registration.ID = id
registration.Name = name //根据这个名称来找这个服务
registration.Port = localPort
//registration.Tags = []string{"lqz", "web"} //这个就是一个标签,可以根据这个来找这个服务,相当于V1.1这种
registration.Tags = tags //这个就是一个标签,可以根据这个来找这个服务,相当于V1.1这种
registration.Address = localIP
// 增加consul健康检查回调函数
check := new(consulapi.AgentServiceCheck)
check.HTTP = fmt.Sprintf("http://%s:%d/health", registration.Address, registration.Port)
check.Timeout = "5s" //超时
check.Interval = "5s" //健康检查频率
check.DeregisterCriticalServiceAfter = "30s" // 故障检查失败30s后 consul自动将注册服务删除
registration.Check = check
// 注册服务到consul
err = client.Agent().ServiceRegister(registration)
if err != nil {
return err
}
return nil
}
func GetCanUsePort() (int, error) {
// 解析地址
addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
if err != nil {
return 0, nil
}
// 利用 ListenTCP 方法的如下特性
// 如果 addr 的端口字段为0,函数将选择一个当前可用的端口
listen, err := net.ListenTCP("tcp", addr)
if err != nil {
return 0, nil
}
// 关闭资源
defer listen.Close()
// 为了拿到具体的端口值,我们转换成 *net.TCPAddr类型获取其Port
return listen.Addr().(*net.TCPAddr).Port, nil
}
func GetUUId() string {
// 创建
u1 := uuid.NewV4()
//fmt.Println(u1.String())
return u1.String()
}
func main() {
r:=gin.Default()
port,_:=GetCanUsePort()
GetUUId()
RegisterConsul("192.168.31.226",port,"lqz_web",GetUUId(),[]string{"lqz","web"})
r.GET("/index", func(c *gin.Context) {
c.JSON(200,"hello")
})
r.GET("/health", func(c *gin.Context) {
c.JSON(200,"hello")
})
r.Run(fmt.Sprintf("192.168.31.226:%d",port))
}
访问:http://10.0.0.102:8000/index,自动从consul中发现并实现负载均衡
2.2.6 设置反扒和黑名单
添加插件