我今天就在和大家探讨一下 API Gateway。在微服务的架构下,API 网关是一个常见的架构设计模式。

目前创新互联已为近1000家的企业提供了网站建设、域名、虚拟主机、网站托管运营、企业网站设计、关岭网站维护等服务,公司将坚持客户导向、应用为本的策略,正道将秉承"和谐、参与、激情"的文化,与客户和合作伙伴齐心协力一起成长,共同发展。
以下是微服务中常见的问题,需要引入 API 网关来协助解决:
常见的API网关主要提供以下的功能:
有很多的开源软件可以提供 API 网关的支持,下面我们就看看他们各自的架构和功能。
为了对这些开源网关进行基本功能的验证,我创建了一些代码,使用 OpenAPI 生成了四个基本的 API 服务,包含 Golang,Nodejs,Python Flask 和 Java Spring。
API 使用了常见的宠物商店的样例,声明如下:
- openapi: "3.0.0"
 - info:
 - version: 1.0.0
 - title: Swagger Petstore
 - license:
 - name: MIT
 - servers:
 - - url: http://petstore.swagger.io/v1
 - paths:
 - /pets:
 - get:
 - summary: List all pets
 - operationId: listPets
 - tags:
 - - pets
 - parameters:
 - - name: limit
 - in: query
 - description: How many items to return at one time (max 100)
 - required: false
 - schema:
 - type: integer
 - format: int32
 - responses:
 - '200':
 - description: A paged array of pets
 - headers:
 - x-next:
 - description: A link to the next page of responses
 - schema:
 - type: string
 - content:
 - application/json:
 - schema:
 - $ref: "#/components/schemas/Pets"
 - default:
 - description: unexpected error
 - content:
 - application/json:
 - schema:
 - $ref: "#/components/schemas/Error"
 - post:
 - summary: Create a pet
 - operationId: createPets
 - tags:
 - - pets
 - responses:
 - '201':
 - description: Null response
 - default:
 - description: unexpected error
 - content:
 - application/json:
 - schema:
 - $ref: "#/components/schemas/Error"
 - /pets/{petId}:
 - get:
 - summary: Info for a specific pet
 - operationId: showPetById
 - tags:
 - - pets
 - parameters:
 - - name: petId
 - in: path
 - required: true
 - description: The id of the pet to retrieve
 - schema:
 - type: string
 - responses:
 - '200':
 - description: Expected response to a valid request
 - content:
 - application/json:
 - schema:
 - $ref: "#/components/schemas/Pet"
 - default:
 - description: unexpected error
 - content:
 - application/json:
 - schema:
 - $ref: "#/components/schemas/Error"
 - components:
 - schemas:
 - Pet:
 - type: object
 - required:
 - - id
 - - name
 - properties:
 - id:
 - type: integer
 - format: int64
 - name:
 - type: string
 - tag:
 - type: string
 - Pets:
 - type: array
 - items:
 - $ref: "#/components/schemas/Pet"
 - Error:
 - type: object
 - required:
 - - code
 - - message
 - properties:
 - code:
 - type: integer
 - format: int32
 - message:
 - type: string
 
构建好的 Web 服务通过 Docker Compose 来进行容器化的部署。
- version: "3.7"
 - services:
 - goapi:
 - container_name: goapi
 - image: naughtytao/goapi:0.1
 - ports:
 - - "18000:8080"
 - deploy:
 - resources:
 - limits:
 - cpus: '1'
 - memory: 256M
 - reservations:
 - memory: 256M
 - nodeapi:
 - container_name: nodeapi
 - image: naughtytao/nodeapi:0.1
 - ports:
 - - "18001:8080"
 - deploy:
 - resources:
 - limits:
 - cpus: '1'
 - memory: 256M
 - reservations:
 - memory: 256M
 - flaskapi:
 - container_name: flaskapi
 - image: naughtytao/flaskapi:0.1
 - ports:
 - - "18002:8080"
 - deploy:
 - resources:
 - limits:
 - cpus: '1'
 - memory: 256M
 - reservations:
 - memory: 256M
 - springapi:
 - container_name: springapi
 - image: naughtytao/springapi:0.1
 - ports:
 - - "18003:8080"
 - deploy:
 - resources:
 - limits:
 - cpus: '1'
 - memory: 256M
 - reservations:
 - memory: 256M
 
我们在学习这些开源网关架构的同时,也会对其最基本的路由转发功能作出验证。
这里用户发送的请求 server/service_name/v1/ 会发送给 API 网关,网关通过 service name 来路由到不同的后端服务。
我们使用 K6 用 100 个并发跑 1000 次测试的结果如上图,我们看到直连的综合响应,每秒可以处理的请求数量大概是 1100+。
Nginx 是异步框架的网页服务器,也可以用作反向代理、负载平衡器和 HTTP 缓存。
该软件由伊戈尔·赛索耶夫创建并于 2004 年首次公开发布。2011 年成立同名公司以提供支持。2019 年 3 月 11 日,Nginx 公司被 F5 Networks 以 6.7 亿美元收购。
Nginx 有以下的特点:
如上图所示,Nginx 主要由 Master,Worker 和 Proxy Cache 三个部分组成。
Master 主控:NGINX 遵循主从架构。它将根据客户的要求为 Worker 分配工作。
将工作分配给 Worker 后,Master 将寻找客户的下一个请求,因为它不会等待 Worker 的响应。一旦响应来自 Worker,Master 就会将响应发送给客户端。
Worker 工作单元:Worker 是 NGINX 架构中的 Slave。每个工作单元可以单线程方式一次处理 1000 个以上的请求。
一旦处理完成,响应将被发送到主服务器。单线程将通过在相同的内存空间而不是不同的内存空间上工作来节省 RAM 和 ROM 的大小。多线程将在不同的内存空间上工作。
Cache 缓存:Nginx 缓存用于通过从缓存而不是从服务器获取来非常快速地呈现页面。在第一个页面请求时,页面将被存储在高速缓存中。
为了实现 API 的路由转发,需要只需要对 Nginx 作出如下的配置:
- server {
 - listen 80 default_server;
 - location /goapi {
 - rewrite ^/goapi(.*) $1 break;
 - proxy_pass http://goapi:8080;
 - }
 - location /nodeapi {
 - rewrite ^/nodeapi(.*) $1 break;
 - proxy_pass http://nodeapi:8080;
 - }
 - location /flaskapi {
 - rewrite ^/flaskapi(.*) $1 break;
 - proxy_pass http://flaskapi:8080;
 - }
 - location /springapi {
 - rewrite ^/springapi(.*) $1 break;
 - proxy_pass http://springapi:8080;
 - }
 - }
 
我们基于不同的服务 goapi,nodeapi,flaskapi 和 springapi,分别配置一条路由,在转发之前,需要利用 rewrite 来去掉服务名,并发送给对应的服务。
使用容器把 Nginx 和后端的四个服务部署在同一个网络下,通过网关连接路由转发的。
Nginx 的部署如下:
- version: "3.7"
 - services:
 - web:
 - container_name: nginx
 - image: nginx
 - volumes:
 - - ./templates:/etc/nginx/templates
 - - ./conf/default.conf:/etc/nginx/conf.d/default.conf
 - ports:
 - - "8080:80"
 - environment:
 - - NGINX_HOST=localhost
 - - NGINX_PORT=80
 - deploy:
 - resources:
 - limits:
 - cpus: '1'
 - memory: 256M
 - reservations:
 - memory: 256M
 
K6 通过 Nginx 网关的测试结果如下:
每秒处理的请求数量是 1093,和不通过网关转发相比非常接近。
从功能上看,Nginx 可以满足用户对于 API 网关的大部分需求,可以通过配置和插件的方式来支持不同的功能,性能非常优秀。
缺点是没有管理的 UI 和管理 API,大部分的工作都需要手工配置 config 文件的方式来进行。商业版本的功能会更加完善。
Kong 是基于 NGINX 和 OpenResty 的开源 API 网关。
Kong 的总体基础结构由三个主要部分组成:NGINX 提供协议实现和工作进程管理,OpenResty 提供 Lua 集成并挂钩到 NGINX 的请求处理阶段。
而 Kong 本身利用这些挂钩来路由和转换请求。数据库支持 Cassandra 或 Postgres 存储所有配置。
Kong 附带各种插件,提供访问控制,安全性,缓存和文档等功能。它还允许使用 Lua 语言编写和使用自定义插件。
Kong 也可以部署为 Kubernetes Ingress 并支持 GRPC 和 WebSockets 代理。
NGINX 提供了强大的 HTTP 服务器基础结构。它处理 HTTP 请求处理,TLS 加密,请求日志记录和操作系统资源分配(例如,侦听和管理客户端连接以及产生新进程)。
NGINX 具有一个声明性配置文件,该文件位于其主机操作系统的文件系统中。
虽然仅通过 NGINX 配置就可以实现某些 Kong 功能(例如,基于请求的 URL 确定上游请求路由),但修改该配置需要一定级别的操作系统访问权限,以编辑配置文件并要求 NGINX 重新加载它们。
而 Kong 允许用户执行以下操作:通过 RESTful HTTP API 更新配置。Kong 的 NGINX 配置是相当基本的:除了配置标准标头,侦听端口和日志路径外,大多数配置都委托给 OpenResty。
在某些情况下,在 Kong 的旁边添加自己的 NGINX 配置非常有用,例如在 API 网关旁边提供静态网站。在这种情况下,您可以修改 Kong 使用的配置模板。
NGINX 处理的请求经过一系列阶段。NGINX 的许多功能(例如,使用 C 语言编写的模块)都提供了进入这些阶段的功能(例如,使用 gzip 压缩的功能)。
虽然可以编写自己的模块,但是每次添加或更新模块时都必须重新编译 NGINX。为了简化添加新功能的过程,Kong 使用了 OpenResty。
OpenResty 是一个软件套件,捆绑了 NGINX,一组模块,LuaJIT 和一组 Lua 库。
其中最主要的是 ngx_http_lua_module一个NGINX 模块,该模块嵌入 Lua 并为大多数 NGINX 请求阶段提供 Lua 等效项。
这有效地允许在 Lua 中开发 NGINX 模块,同时保持高性能(LuaJIT 相当快),并且 Kong 用它来提供其核心配置管理和插件管理基础结构。
Kong 通过其插件体系结构提供了一个框架,可以挂接到上述请求阶段。从上面的示例开始,Key Auth 和 ACL 插件都控制客户端(也称为使用者)是否应该能够发出请求。
每个插件都在其处理程序中定义了自己的访问函数,并且该函数针对通过给定路由或服务启用的每个插件执行 kong.access()。
执行顺序由优先级值决定,如果 Key Auth 的优先级为 1003,ACL 的优先级为 950,则 Kong 将首先执行 Key Auth 的访问功能,如果它不放弃请求,则将执行 ACL,然后再通过将该 ACL 传递给上游 proxy_pass。
由于 Kong 的请求路由和处理配置是通过其 admin API 控制的,因此可以在不编辑底层 NGINX 配置的情况下即时添加和删除插件配置。
因为 Kong 本质上提供了一种在 API 中注入位置块(通过 API 定义)和配置的方法。它们通过将插件,证书等分配给这些 API。
我们使用以下的配置部署 Kong 到容器中(省略四个微服务的部署):
- version: '3.7'
 - volumes:
 - kong_data: {}
 - networks:
 - kong-net:
 - external: false
 - services:
 - kong:
 - image: "${KONG_DOCKER_TAG:-kong:latest}"
 - user: "${KONG_USER:-kong}"
 - depends_on:
 - - db
 - environment:
 - KONG_ADMIN_ACCESS_LOG: /dev/stdout
 - KONG_ADMIN_ERROR_LOG: /dev/stderr
 - KONG_ADMIN_LISTEN: '0.0.0.0:8001'
 - KONG_CASSANDRA_CONTACT_POINTS: db
 - KONG_DATABASE: postgres
 - KONG_PG_DATABASE: ${KONG_PG_DATABASE:-kong}
 - KONG_PG_HOST: db
 - KONG_PG_USER: ${KONG_PG_USER:-kong}
 - KONG_PROXY_ACCESS_LOG: /dev/stdout
 - KONG_PROXY_ERROR_LOG: /dev/stderr
 - KONG_PG_PASSWORD_FILE: /run/secrets/kong_postgres_password
 - secrets:
 - - kong_postgres_password
 - networks:
 - - kong-net
 - ports:
 - - "8080:8000/tcp"
 - - "127.0.0.1:8001:8001/tcp"
 - - "8443:8443/tcp"
 - - "127.0.0.1:8444:8444/tcp"
 - healthcheck:
 - test: ["CMD", "kong", "health"]
 - interval: 10s
 - timeout: 10s
 - retries: 10
 - restart: on-failure
 - deploy:
 - restart_policy:
 - condition: on-failure
 - db:
 - image: postgres:9.5
 - environment:
 - POSTGRES_DB: ${KONG_PG_DATABASE:-kong}
 - POSTGRES_USER: ${KONG_PG_USER:-kong}
 - POSTGRES_PASSWORD_FILE: /run/secrets/kong_postgres_password
 - secrets:
 - - kong_postgres_password
 - healthcheck:
 - test: ["CMD", "pg_isready", "-U", "${KONG_PG_USER:-kong}"]
 - interval: 30s
 - timeout: 30s
 - retries: 3
 - restart: on-failure
 - deploy:
 - restart_policy:
 - condition: on-failure
 - stdin_open: true
 - tty: true
 - networks:
 - - kong-net
 - volumes:
 - - kong_data:/var/lib/postgresql/data
 - secrets:
 - kong_postgres_password:
 - file: ./POSTGRES_PASSWORD
 
数据库选择了 PostgreSQL,开源版本没有 Dashboard,我们使用 RestAPI 创建所有的网关路由:
- curl -i -X POST http://localhost:8001/services \
 - --data name=goapi \
 - --data url='http://goapi:8080'
 - curl -i -X POST http://localhost:8001/services/goapi/routes \
 - --data 'paths[]=/goapi' \
 - --data name=goapi
 
需要先创建一个 service,然后在该 service 下创建一条路由。
使用 K6 压力测试的结果如下:
每秒请求数 705 要明显弱于 Nginx,所以所有的功能都是有成本的。
Apache APISIX 是一个动态、实时、高性能的 API 网关, 提供负载均衡、动态上游、灰度发布、服务熔断、身份认证、可观测性等丰富的流量管理功能。
APISIX 于 2019 年 4 月由中国的支流科技创建,于 6 月开源,并于同年 10 月进入 Apache 孵化器。
支流科技对应的商业化产品的名字叫 API7 。APISIX 旨在处理大量请求,并具有较低的二次开发门槛。
APISIX 的主要功能和特点有:
从这个角度来看,API 网关可以替代 Nginx 来处理南北流量,也可以扮演 Istio 控制平面和 Envoy 数据平面的角色来处理东西向流量。
APISIX 的架构如下图所示:
APISIX 包含一个数据平面,用于动态控制请求流量;一个用于存储和同步网关数据配置的控制平面,一个用于协调插件的 AI 平面,以及对请求流量的实时分析和处理。
它构建在 Nginx 反向代理服务器和键值存储 etcd 的之上,以提供轻量级的网关。
它主要用 Lua 编写,Lua 是类似于 Python 的编程语言。它使用 Radix 树进行路由,并使用前缀树进行 IP 匹配。
使用 etcd 而不是关系数据库来存储配置可以使它更接近云原生,但是即使在任何服务器宕机的情况下,也可以确保整个网关系统的可用性。
所有组件都是作为插件编写的,因此其模块化设计意味着功能开发人员只需要关心自己的项目即可。
内置的插件包括流控和速度限制,身份认证,请求重写,URI 重定向,开放式跟踪和无服务器。
APISIX 支持 OpenResty 和 Tengine 运行环境,并且可以在 Kubernetes 的裸机上运行。它同时支持 X86 和 ARM64。
我们同样使用 Docker Compose 来部署 APISIX:
- version: "3.7"
 - services:
 - apisix-dashboard:
 - image: apache/apisix-dashboard:2.4
 - restart: always
 - volumes:
 - - ./dashboard_conf/conf.yaml:/usr/local/apisix-dashboard/conf/conf.yaml
 - ports:
 - - "9000:9000"
 - networks:
 - apisix:
 - ipv4_address: 172.18.5.18
 - apisix:
 - image: apache/apisix:2.3-alpine
 - restart: always
 - volumes:
 - - ./apisix_log:/usr/local/apisix/logs
 - - ./apisix_conf/config.yaml:/usr/local/apisix/conf/config.yaml:ro
 - depends_on:
 - - etcd
 - ##network_mode: host
 - ports:
 - - "8080:9080/tcp"
 - - "9443:9443/tcp"
 - networks:
 - apisix:
 - ipv4_address: 172.18.5.11
 - deploy:
 - resources:
 - limits:
 - cpus: '1'
 - memory: 256M
 - reservations:
 - memory: 256M
 - etcd:
 - image: bitnami/etcd:3.4.9
 - user: root
 - restart: always
 - volumes:
 - - ./etcd_data:/etcd_data
 - environment:
 - ETCD_DATA_DIR: /etcd_data
 - ETCD_ENABLE_V2: "true"
 - ALLOW_NONE_AUTHENTICATION: "yes"
 - ETCD_ADVERTISE_CLIENT_URLS: "http://0.0.0.0:2379"
 - ETCD_LISTEN_CLIENT_URLS: "http://0.0.0.0:2379"
 - ports:
 - - "2379:2379/tcp"
 - networks:
 - apisix:
 - ipv4_address: 172.18.5.10
 - networks:
 - apisix:
 - driver: bridge
 - ipam:
 - config:
 - - subnet: 172.18.0.0/16
 
开源的 APISIX 支持 Dashboard 的方式来管理路由,而不是像 KONG 把仪表盘功能限制在商业版本中。
但是 APISIX 的仪表盘不支持对路由 URI 进行改写,所以我们只好使用 RestAPI 来创建路由。
创建一个服务的路由的命令如下:
- curl --location --request PUT 'http://127.0.0.1:8080/apisix/admin/routes/1' \
 - --header 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' \
 - --header 'Content-Type: text/plain' \
 - --data-raw '{
 - "uri": "/goapi/*",
 - "plugins": {
 - "proxy-rewrite": {
 - "regex_uri": ["^/goapi(.*)$","$1"]
 - }
 - },
 - "upstream": {
 - "type": "roundrobin",
 - "nodes": {
 - "goapi:8080": 1
 - }
 - }
 - }'
 
使用 K6 压力测试的结果如下:
APISix 取得了 1155 的好成绩,表现出接近不经过网关的性能,可能缓存起到了很好的效果。
Tyk 是一款基于 Golang 和 Redis 构建的开源 API 网关。它于 2014 年创建,比 AWS 的 API 网关即服务功能早。Tyk 用 Golang 编写,并使用 Golang 自己的 HTTP 服务器。
Tyk 支持不同的运行方式:云,混合(在自己的基础架构中为 GW)和本地。
Tyk 由 3 个组件组成:
我们同样使用 Docker Compose 来创建 Tyk 网关来进行功能验证。
- version: '3.7'
 - services:
 - tyk-gateway:
 - image: tykio/tyk-gateway:v3.1.1
 - ports:
 - - 8080:8080
 - volumes:
 - - ./tyk.standalone.conf:/opt/tyk-gateway/tyk.conf
 - - ./apps:/opt/tyk-gateway/apps
 - - ./middleware:/opt/tyk-gateway/middleware
 - - ./certs:/opt/tyk-gateway/certs
 - environment:
 - - TYK_GW_SECRET=foo
 - depends_on:
 - - tyk-redis
 - tyk-redis:
 - image: redis:5.0-alpine
 - ports:
 - - 6379:6379
 
Tyk 的 Dashboard 也是属于商业版本的范畴,所我们又一次需要借助 API 来创建路由,Tyk 是通过 app 的概念来创建和管理路由的,你也可以直接写 app 文件。
- curl --location --request POST 'http://localhost:8080/tyk/apis/' \
 - --header 'x-tyk-authorization: foo' \
 - --header 'Content-Type: application/json' \
 - --data-raw '{
 - "name": "GO API",
 - "slug": "go-api",
 - "api_id": "goapi",
 - "org_id": "goapi",
 - "use_keyless": true,
 - "auth": {
 - "auth_header_name": "Authorization"
 - },
 - "definition": {
 - "location": "header",
 - "key": "x-api-version"
 - },
 - "version_data": {
 - "not_versioned": true,
 - "versions": {
 - "Default": {
 - "name": "Default",
 - "use_extended_paths": true
 - }
 - }
 - },
 - "proxy": {
 - "listen_path": "/goapi/",
 - "target_url": "http://host.docker.internal:18000/",
 - "strip_listen_path": true
 - },
 - "active": true
 - }'
 
使用 K6 压力测试的结果如下:
Tyk 的结果在 400-600 左右,性能上和 KONG 接近。
Zuul 是 Netflix 开源的基于 Java 的 API 网关组件。
Zuul 包含多个组件:
Zuul 提供了灵活性和弹性,部分是通过利用其他 Netflix OSS 组件进行的:
Zuul 的核心是一系列过滤器,它们能够在路由 HTTP 请求和响应期间执行一系列操作。
以下是 Zuul 过滤器的主要特征:
- class DeviceDelayFilter extends ZuulFilter {
 - def static Random rand = new Random()
 - @Override
 - String filterType() {
 - return 'pre'
 - }
 - @Override
 - int filterOrder() {
 - return 5
 - }
 - @Override
 - boolean shouldFilter() {
 - return RequestContext.getRequest().
 - getParameter("deviceType")?equals("BrokenDevice"):false
 - }
 - @Override
 - Object run() {
 - sleep(rand.nextInt(20000)) // Sleep for a random number of
 - // seconds between [0-20]
 - }
 - }
 
Zuul 提供了一个框架来动态读取,编译和运行这些过滤器。过滤器不直接相互通信。
而是通过每个请求唯一的 RequestContext 共享状态。过滤器使用 Groovy 编写。
有几种与请求的典型生命周期相对应的标准过滤器类型:
Spring Cloud 创建了一个嵌入式 Zuul 代理,以简化一个非常常见的用例的开发,在该用例中,UI 应用程序希望代理对一个或多个后端服务的调用。
此功能对于用户界面代理所需的后端服务很有用,从而避免了为所有后端独立管理 CORS 和身份验证问题的需求 。
要启用它,请使用 @EnableZuulProxy 注解一个 Spring Boot 主类,这会将本地调用转发到适当的服务。
Zuul 是 Java 的一个库,他并不是一款开箱即用的 API 网关,所以需要用 Zuul 开发一个应用来对其功能进行测试。
对应的 Java 的 POM 如下:
- xmlns="http://maven.apache.org/POM/4.0.0"
 - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 4.0.0 naughtytao.apigateway - &n
 
网站名称:开源API网关,到底哪个强?
当前网址:http://cdxtjz.cn/article/cdpgdhi.html