API网关介绍及选型(kong)

3,981 阅读8分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路

API网关是一个服务器,是系统的唯一入口。从面向对象设计的角度看,它与外观模式类似。API网关封装了系统内部架构,为每个客户端提供一个定制的API。它可能还具有其它职责,如身份验证、监控、负载均衡、缓存、请求分片与管理、静态响应处理。 API网关方式的核心要点是,所有的客户端和消费端都通过统一的网关接入微服务,在网关层处理所有的非业务功能。通常,网关也是提供REST/HTTP的访问API。服务端通过API-GW注册和管理服务。

为什么需要 API 网关

在微服务架构之下,服务被拆的非常零散,降低了耦合度的同时也给服务的统一管理增加了难度。在旧的服务治理体系之下,鉴权,限流,日志,监控等通用功能需要在每个服务中单独实现,这使得系统维护者没有一个全局的视图来统一管理这些功能。

不需要网关的情况,是由客户端直接访问服务提供方,由注册中心向客户端返回服务方的地址

xdtsdgj

API网关的功能

  • 路由:根据上下文或消息内容将请求发送到不同的目标。
  • 转换:负责转换或屏蔽数据的组件。
  • 入站和出站流量监控。
  • 为API添加身份验证,授权和加密的安全策略。
  • 使用策略:它们可以创建消耗,性能和失败策略以保护SLA。

在这里插入图片描述

单节点网关 在这里插入图片描述 针对不同的客户端的多节点网关 在这里插入图片描述

API网关选型

  • 私有云开源解决方案: Netflix Zuul,zuul是spring cloud的一个推荐组件,github.com/Netflix/zuu… Kong kong是基于Nginx+Lua进行二次开发的方案, konghq.com/ Tyk是2014年创建的开源API网关,甚至比AWS的API网关即服务功能还要早。Tyk用Golang编写并使用Golang自己的HTTP服务器。

  • 公有云解决方案: Amazon API Gateway,aws.amazon.com/cn/api-gate… 阿里云API网关,www.aliyun.com/product/api… 腾讯云API网关, cloud.tencent.com/product/api…

  • 自开发解决方案 基于Nginx+Lua+ OpenResty的方案,可以看到Kong,orange都是基于这个方案 基于Netty、非阻塞IO模型。 通过网上搜索可以看到国内的宜人贷等一些公司是基于这种方案,是一种成熟的方案。 基于Node.js的方案。 这种方案是应用了Node.js天生的非阻塞的特性。 基于java Servlet的方案。 zuul基于的就是这种方案,这种方案的效率不高,这也是zuul总是被诟病的原因。

如果没有开源项目的支撑前提下,自己来做这样一套东西,是非常大的一个工作量,而且还要做 API 网关本身的高可用等,如果一旦做不好,有可能最先挂掉的不是你的其他服务,而就是这个API网关。

国外目前比较成型的是kong和tyk

kong是基于ngnix+lua的,它借助于Nginx的事件驱动模型和非阻塞IO,性能方面是非常棒的,但是从公司的角度比较难于找到能去维护这种架构产品的人。 需求评估当前公司是否有这个能力去维护这个产品。

SpringCloud-Zuul 社区活跃,基于 SrpingCloud 完整生态,但因为架构的原因(zull基于 Servlet 框架构建,采用的是阻塞和多线程方式,即一个线程处理一次连接请求,当出现问题时,如后端延迟或设备错误重试,活跃的连接和线程数量会增加,这会加大服务器负载并可能使集群无法承受)在高并发的情况下性能不高,同时需要去基于研究整合开源的适配zuul的监控和管理系统。较新的Spring cloud Gateway和zuul2倒是不错

Tyk用Golang编写,并发性能较好,但一切均导向收费版本,免费版本第一次申请有一年的使用授权.没找到明确表示是否可以免费继续使用的说明.

扩展Tyk需要会Go语言,扩展Kong需要会写lua脚本,使用 zuul 还得会Java

API 网关实现对比

20190709140836

kong vs tyk

test1 禁用基本身份验证的结果。纵轴表示每秒请求数(Req / Sec),水平轴表示运行的微服务实例数。

test2 启用基本身份验证的结果。纵轴表示每秒请求数(Req / Sec),水平轴表示运行的微服务实例数。

kong

ref:www.pocketdigi.com/book/kong/

Kong(github.com/Kong/kong) 是一个云原生,高效,可扩展的分布式 API 网关。 自 2015 年在 github 开源后,广泛受到关注,最新版本1.2.1,目前已收获 2.23w+ 的 star,其核心价值在于高性能和可扩展性。

准确地说,Kong是一个使用了lua-nginx-module运行在Nginx之上的Lua应用。Kong没有作为一个module和Nginx一起编译,而是与OpenResty(一个已经包含lua-nginx-module的应用)一起分发。OpenResty不是Nginx的分支,而是一些继承它能力的module的合集。 在这里插入图片描述

Kong 支持功能

  • 云原生: 与平台无关,Kong可以从裸机运行到Kubernetes

  • 动态路由:Kong 的背后是 OpenResty+Lua,所以从 OpenResty 继承了动态路由的特性

  • 熔断: 追踪不健康的upstream services,当某服务出现不可用或响应超时的情况时熔断

  • 健康检查: 对upstream services进行主动或者被动地监控

  • 日志: 可以记录通过 Kong 的 HTTP,TCP,UDP 请求和响应。

  • 鉴权: 权限控制,IP 黑白名单

  • SSL: Setup a Specific SSL Certificate for an underlying service or API.

  • 监控: Kong 提供了实时监控插件

  • 认证: 如数支持 HMAC, JWT, Basic, OAuth2.0 等常用协议

  • 限流: 基于多变量对请求进行阻塞或者限制

  • REST API: 通过 Rest API 进行配置管理,从繁琐的配置文件中解放

  • 可用性: 天然支持分布式

  • 高性能: 背靠非阻塞通信的 nginx,性能自不用说

  • 插件机制: 提供众多开箱即用的插件,且有易于扩展的自定义插件接口,用户可以使用 Lua 自行开发插件

使用 Kong 之后,Nginx 可以完全摒弃,Kong 的功能是 Nginx 的父集

Kong 的管理方式

所有的管理操作都是基于 HTTP Restful API 来进行的,使用Postgres 或者 Cassandra 来存储路由配置,服务配置,upstream 配置等信息。

kong端点

其中 8000/8443 分别是 Http 和 Https 的转发端口,等价于 Nginx 默认的 80 端口,而 8001 端口便是默认的管理端口,我们可以通过 HTTP Restful API 来动态管理 Kong 的配置。

在这里插入图片描述

kong策略模式

为了便于理解,将Kong目前能实现模式简单分为两种,以便于理解:Request host 和 Request path。

Request host

- www.domain1.com/api1  → s1.domain1.com/api1
- www.domain1.com/api2  → s1.domain1.com/api2
- ...
- www.domain1.com/*     → s1.domain1.com/*
- www.domain2.com/api1  → s2.domain1.com/api1
- www.domain2.com/api2  → s2.domain1.com/api2
- ...
- www.domain2.com/*     → s2.domain1.com/*

要在Kong服务器上绑定多个域名。(提供api服务的服务器可以没有域名绑定,只要有IP即可)

Request path

- www.domain1.com/s1/api1   → s1.domain1.com/s1/api1
- www.domain1.com/s1/api2   → s1.domain1.com/s1/api2
- ...
- www.domain1.com/s1/*      → s1.domain1.com/s1/*
 
- www.domain1.com/s2/api1   → s2.domain1.com/s2/api1
- www.domain1.com/s2/api2   → s2.domain1.com/s2/api2
- ...
- www.domain1.com/s2/* → s2.domain1.com/s2/*

Kong服务器上只需要绑定一个域名,不同的API集合依靠不同的请求路径实现。(同上,提供api服务的服务器可以没有域名绑定,只要有IP即可)

Request host 和 Request path 可组合使用,但比较复杂,不建议使用。

docker启动kong

# 启动kong使用的数据库postgres/cassandra
docker run -d --name kong-database \
        -p 5432:5432 \
        -e "POSTGRES_USER=kong" \
        -e "POSTGRES_DB=kong" \
        postgres:9.6

# kong配置数据初始化
docker run --rm \
  --link kong-database:kong-database \
  -e "KONG_DATABASE=postgres" \
  -e "KONG_PG_HOST=kong-database" \
  kong:0.14.0 kong migrations up

# 启动kong容器
docker run -d --name kong \
  --link kong-database:kong-database \
  -e "KONG_DATABASE=postgres" \
  -e "KONG_PG_HOST=kong-database" \
  -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, 0.0.0.0:8444 ssl" \
  -p 8000:8000 \
  -p 8443:8443 \
  -p 8001:8001 \
  -p 8444:8444 \
  kong:0.14.0

基于kong + oauth2 + acl的用户访问权限管理

ref:docs.konghq.com/hub/kong-in…

ref:docs.konghq.com/hub/kong-in…

OAuth2密码模式: 用户认证 z3dfq4736j

acl白名单: 用户分组权限

在网关层做访问权限管控,而非后端应用的业务权限

整体流程

在这里插入图片描述

用户认证服务器在Kong的内侧,我们需要通过Kong进行访问

  • 用户发送账号密码到 Kong服务器的用户验证API;
  • Kong服务器将请求1转发给用户验证服务器;
  • 用户认证服务器验证用户账号密码是否正确,如果不正确直接返回错误结果并完成用户认证;如果正确则将:用户账号密码、client_id和client_secret、scope、provision_key、用户ID 发送给 Kong 的OAuth2 接口
  • Kong生成access_token并返回给用户认证服务器,并缓存与过期时间相同的 scope,用户ID
  • 认证服务器将4中收到的access_token返回请求2给Kong,Kong再返回请求1给用户。
  • 用户得到access_token之后,就可以访问有OAuth2验证的接口了。access_token中已隐含了用户ID和scope。

需求

对用户进行分组管理,不同的用户有访问不同api(服务)的权限,类似django admin的用户组功能

由于认证系统是完全可信的内部系统,简单起见使用密码授权方式

实现

服务和权限一对一关系,每一个服务(或者api)拥有唯一的权限名,不同的服务不同的权限, 通过对用户赋予不同的权限,从而限制用户访问指定的api或服务

具体参见: 基于kong + oauth2 + acl的用户访问权限管理