第四章:API网关设计——微服务的“守门人”
聊到微服务架构,有个角色你绕不开——API网关。说白了,它就是整个系统的“前门”。所有外部请求,都得先经过它,再由它分发到后端的各个服务。
我记得刚接触微服务那会儿,团队直接把服务地址暴露给客户端。结果呢?每次重构接口,客户端就得跟着改。后来加了网关,整个世界清净了。嗯,这就是网关的第一个价值——解耦。
4.1 网关的作用与模式
网关到底干了什么?我总结下来,核心就三件事:
- 请求路由:把/user/**转到用户服务,把/order/**转到订单服务
- 横切关注点:认证、限流、日志、监控——这些每个服务都要做的事,统一在网关层搞定
- 协议转换:外部可能是HTTP,内部可能是gRPC,网关帮你做翻译
你想想看,如果没有网关,每个服务都得自己写认证逻辑。那代码得多冗余?维护起来得多痛苦?
网关的常见模式:
- 边缘网关:面向客户端,处理外部请求。这是最常见的模式。
- 内部网关:服务之间的通信也走网关,方便做灰度发布、流量复制。
- BFF模式:为每种客户端(Web、iOS、Android)各建一个网关。我在项目中用过这个模式,效果不错——每个端可以定制自己的聚合接口。
我的经验:别把网关当成“万能胶”。有些团队喜欢在网关里写业务逻辑,比如数据聚合、字段转换。我个人建议,网关只做路由和横切关注点。业务逻辑还是放到后端服务里,否则网关会变得臃肿不堪。
4.2 基于Kong的网关搭建
Kong是我用得最多的网关之一。它基于OpenResty(Nginx + Lua),性能没得说。而且插件生态丰富,开箱即用。
搭建Kong其实很简单。我习惯用Docker Compose来部署:
# docker-compose.yml
version: '3.8'
services:
kong-database:
image: postgres:13
environment:
POSTGRES_DB: kong
POSTGRES_USER: kong
POSTGRES_PASSWORD: kong_pass
networks:
- kong-net
kong-migrations:
image: kong:3.5
command: "kong migrations bootstrap"
environment:
KONG_DATABASE: postgres
KONG_PG_HOST: kong-database
KONG_PG_PASSWORD: kong_pass
networks:
- kong-net
depends_on:
- kong-database
kong:
image: kong:3.5
environment:
KONG_DATABASE: postgres
KONG_PG_HOST: kong-database
KONG_PG_PASSWORD: kong_pass
KONG_PROXY_ACCESS_LOG: /dev/stdout
KONG_ADMIN_ACCESS_LOG: /dev/stdout
KONG_PROXY_ERROR_LOG: /dev/stderr
KONG_ADMIN_ERROR_LOG: /dev/stderr
KONG_ADMIN_LISTEN: 0.0.0.0:8001
ports:
- "8000:8000" # 代理端口
- "8001:8001" # 管理端口
networks:
- kong-net
depends_on:
- kong-migrations
networks:
kong-net:
driver: bridge
启动后,通过Admin API配置路由。举个例子,把/user/**路由到用户服务:
# 添加服务
curl -i -X POST http://localhost:8001/services \
--data "name=user-service" \
--data "url=http://user-service:8080"
# 添加路由
curl -i -X POST http://localhost:8001/services/user-service/routes \
--data "paths[]=/user" \
--data "strip_path=false"
为什么会这样配置?strip_path=false的意思是保留路径前缀。比如请求/user/info,转发到后端时还是/user/info。如果设为true,就会变成/info。这个细节,我在项目里踩过坑——有一次忘了设置,后端接口全404了。
4.3 基于Ocelot的网关搭建
如果你的技术栈是.NET,Ocelot是个不错的选择。它是轻量级的,配置驱动,适合中小型项目。
Ocelot的配置核心是一个JSON文件。我直接贴一个典型配置:
{
"Routes": [
{
"DownstreamPathTemplate": "/api/user/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "user-service",
"Port": 8080
}
],
"UpstreamPathTemplate": "/user/{everything}",
"UpstreamHttpMethod": [ "GET", "POST" ]
},
{
"DownstreamPathTemplate": "/api/order/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "order-service",
"Port": 8081
}
],
"UpstreamPathTemplate": "/order/{everything}",
"UpstreamHttpMethod": [ "GET", "POST", "PUT" ]
}
],
"GlobalConfiguration": {
"BaseUrl": "http://localhost:5000"
}
}
你看,配置很直观。UpstreamPathTemplate是外部路径,DownstreamPathTemplate是内部路径。Ocelot会自动做路径映射。
注意:Ocelot默认不支持热更新。修改配置后需要重启应用。我曾经在生产环境吃过这个亏——改了路由配置,忘了重启,结果新接口一直404。后来我用了Consul做动态配置,才解决了这个问题。
4.4 请求路由与限流
路由是网关的基本功。但光有路由还不够,你还得考虑限流。为什么?因为微服务架构下,一个服务挂了,可能引发雪崩效应。
限流的常见算法有四种:
| 算法 | 原理 | 优缺点 |
|---|---|---|
| 令牌桶 | 以固定速率往桶里放令牌,请求消耗令牌 | 允许突发流量,实现简单 |
| 漏桶 | 请求先进桶,以固定速率流出 | 流量平滑,但无法应对突发 |
| 固定窗口 | 统计单位时间内的请求数 | 实现简单,但窗口边界有毛刺 |
| 滑动窗口 | 将时间窗口细分为多个小格 | 比固定窗口更平滑,但内存占用高 |
我个人习惯用令牌桶算法。它允许一定的突发流量,又不会让系统过载。在Kong里,开启限流插件很简单:
# 启用限流插件
curl -i -X POST http://localhost:8001/services/user-service/plugins \
--data "name=rate-limiting" \
--data "config.minute=100" \
--data "config.hour=5000" \
--data "config.policy=local"
这里配置了每分钟最多100次请求,每小时最多5000次。policy=local表示使用本地计数器。如果是集群部署,建议用redis模式,保证数据一致性。
避坑指南:我曾经在限流阈值上吃过亏。一开始设得太宽松,结果某个客户端疯狂调用,把后端服务打垮了。后来我学乖了——先设一个保守的阈值,然后根据监控数据逐步调优。记住,限流不是目的,保护系统才是。
说到路由,还有一个细节:灰度发布。你可以通过网关的权重路由,把一部分流量引到新版本服务上。比如:
# Kong的权重路由(通过upstream配置)
curl -i -X POST http://localhost:8001/upstreams \
--data "name=user-upstream"
curl -i -X POST http://localhost:8001/upstreams/user-upstream/targets \
--data "target=user-service-v1:8080" \
--data "weight=90"
curl -i -X POST http://localhost:8001/upstreams/user-upstream/targets \
--data "target=user-service-v2:8080" \
--data "weight=10"
这样,90%的流量走v1,10%的流量走v2。观察一段时间,如果v2没问题,再逐步提高权重。嗯,这个技巧我在生产环境用了很多次,效果很好。
4.5 小结
API网关是微服务架构的“守门人”。它帮你统一处理路由、限流、认证等横切关注点。Kong和Ocelot是两个不错的选择——前者功能强大,后者轻量灵活。
最后提醒一句:网关虽好,但别滥用。不要把业务逻辑塞进网关,保持它的“薄”和“快”。毕竟,网关是流量的入口,它的性能直接影响整个系统的响应速度。