在日常工作中,不同的场合下,我们可能听说过很多次网关这个名称,这里说的网关特指API网关(API Gataway)。字面意思是指将所有API的调用统一接入API网关层,由网关层负责接入和输出。
那么在什么情况下需要一个API网关呢?下面从单体应用到微服务演变的过程去阐述,回顾单体应用时代,在业务简单、团队组织规模很小的时候,我们常常把功能都几种与一个应用中,统一部署,统一测试,如下图:
随着业务的迅速发展,组织成员日益增多。将所有的功能几种在一个Tomcat中的时候,没更新一个功能模块,势必要更新所有的程序。牵一发而动全身,系统将很难维护。
单体应用满足不了日趋增长的需求之后,微服务出现了。我们利用微服务的思想,将原来的单体应用进行微服务化。将原来集中于一体的功能(如商品、订单服务)进行拆分,每个功能模块又各自的自成体系的发布、运维等功能。这样就解决了单体应用的弊端,如下:
这时,我们还没有看到API Gateway。举例来说,原先IOS、Android、PC客户端调用服务的地方,需要多个URL地址,有订单的、商品的、用户的。微服务化后就必须有统一的出入口,这种情况下,API Gateway就出现了。API Gateway很好的解决了微服务下调用、统一接入等问题,如下图所示:
有了API网关之后,各个API服务提供团队可以专注于自己的业务逻辑处理,而API罔顾赞更专注于安全、流量、路由等问题。
看到上面的图示与描述,我们可能会想到另外一个与网关类似的东西——代理。网关与代理的区别:代理是纯粹的数据透传,协议不会发生变化;网关在数据透传的背景下,还会设计协议的转换,比如上图中用户请求传输到网关的协议是HTTP,通过网关透传到下游则可能已经转换成企业内部的RPC了(比如JSF、Dubbo等企业自研的RPC框架)。
API网关涵盖的基本功能
一个API网关的基本功能包含了统一接入、协议适配、流量管理与容错、以及安全防护,这四大基本功能构成了网关的核心功能。网关首要的功能是负责统一接入,然后将请求的协议转换成内部的接口协议,在调用的过程中还要有限流、降级、熔断等容错的方式来保护网关的整体稳定,同时网关还要做到基本的安全防护(防刷控制),以及黑白名单(比如IP白名单)等基本安全措施,如下图所示:
API网关的架构示例
除了基本的四大功能,网关运行良好的环境还包括注册中心(比如:ZK读取已发布的API接口的动态配置)。为了实现高性能,将数据全部异构到缓存(如:Redis)中,同时还可以配合本地缓存来进一步提高网关系统的性能。为了提高网关的吞吐率,可以使用NIO+Servlet 3 异步的方式,还可以利用Servlet 3 的异步特性将请求线程与业务线程分开,为后续的线程池隔离做好基本的支撑。访问日志的存储我们可以放到Hbase中,如果要作为开放网关使用,那么需要一个支持OAuth2.0的授权中心。还可以引入Nginx + lua的方式将一些基本的校验判断放到应用系统之上,这样可以更轻量化的处理接入的问题,整体的网关架构示例如下所示:
案例
API网关是系统的单一入口,用于将所有的客户端请求路由到后端的服务上。以下是设计一个简单API网关的步骤和示例代码:
选择编程语言和框架。
实现路由逻辑。
集成服务发现机制。
处理认证和授权。
记录和监控。
以下是使用Node.js和Express框架的示例代码:
const express = require('express');
const request = require('request'); // 用于发送请求到后端服务
// 创建API网关
const app = express();
const port = 3000;
// 模拟服务发现,将服务的URL存储在一个对象中
const services = {
'service1': 'http://service1-host:port',
'service2': 'http://service2-host:port'
};
// 路由请求到后端服务
app.use('/api/service1', (req, res) => {
// 从请求中获取服务的URL
const serviceUrl = services['service1'];
// 转发请求到后端服务
req.pipe(request({
uri: serviceUrl + req.url,
method: req.method,
headers: req.headers
})).pipe(res);
});
// 启动API网关
app.listen(port, () => {
console.log(`API Gateway listening at http://localhost:${port}`);
});
这个示例只是用于演示目的,实际的API网关需要更多的功能,如负载均衡、服务健康检查、负载均衡、缓存、API限流、负载均衡、负载均衡等。在生产环境中,你可能需要使用像Nginx、Kong、Traefik或者自研的API网关软件。
------------------------------------------------------最快的gateway代理方式:nginx-proxy-----------------------------------------------------------
经常会有业务场景中,需要启动一个代理去代理一个额外的服务,我们可以最快速的方式,使用nginx来代理此服务,
#原地址
curl -i -X POST 'http://xxx/api/predict/qwen2_72b_instruct_ai4bi/stream/predict' \
-H 'Content-Type: application/json' \
-H 'Authorization: xxx' \
-d '{
"input": "讲个段子",
"history": []
}'
# 代理访问地址
curl -i -X POST '127.0.0.1/api/predict' \
-H 'Content-Type: application/json' \
-d '{
"input": "讲个段子",
"history": []
}'
以下是相关服务代码
1.Dockerfile
FROM m.daocloud.io/docker.io/library/ubuntu:22.04
RUN apt update && \
apt install -y vim curl wget net-tools && \
apt install -y nginx && \
apt clean && rm -rf /var/lib/apt/lists/*
COPY nginx.conf /etc/nginx/nginx.conf
COPY proxy.conf /etc/nginx/conf.d/proxy.conf
COPY start.sh /opt/start.sh
RUN chmod +x /opt/start.sh
2.nginx.conf
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 768;
# multi_accept on;
}
http {
##
# Basic Settings
##
sendfile on;
tcp_nopush on;
types_hash_max_size 2048;
# server_tokens off;
# server_names_hash_bucket_size 64;
# server_name_in_redirect off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
##
# SSL Settings
##
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
##
# Logging Settings
##
log_format detailed '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /dev/stdout detailed;
error_log /dev/stderr;
##
# Gzip Settings
##
gzip on;
##
# Virtual Host Configs
##
include /etc/nginx/conf.d/*.conf;
# include /etc/nginx/sites-enabled/*;
}
3.proxy.conf
server {
#listen ${PORT}; # 监听端口
listen 9080; # 监听端口
server_name 0.0.0.0; # 你的域名
location / {
#proxy_pass http://${MASTER_HOST}:${MASTER_PORT}/api/predict/qwen2_72b_instruct_ai4bi;
proxy_pass http://www.baidu.com;
}
location /api/predict {
#proxy_pass http://${MASTER_HOST}:${MASTER_PORT}/api/predict/qwen2_72b_instruct_ai4bi;
proxy_pass http://xxx/api/predict/qwen2_72b_instruct_ai4bi/api/predict;
# 设置代理请求头
proxy_set_header Content-Type "application/json";
proxy_set_header Authorization "xxx";
}
location /stream/predict {
#proxy_pass http://${MASTER_HOST}:${MASTER_PORT}/api/predict/qwen2_72b_instruct_ai4bi;
proxy_pass http://xxx/api/predict/qwen2_72b_instruct_ai4bi/stream/predict;
# 设置代理请求头
proxy_set_header Content-Type "application/json";
proxy_set_header Authorization "xxx";
}
}
start.sh
#!/bin/bash
set -e
set -x
PORT=${PORT:-80}
MASTER_HOST=${MASTER_HOST:-"host.docker.internal"}
MASTER_PORT=${MASTER_PORT:-8080}
sed -i "s/\${PORT}/${PORT}/g" /etc/nginx/conf.d/proxy.conf
sed -i "s/\${MASTER_HOST}/${MASTER_HOST}/g" /etc/nginx/conf.d/proxy.conf
sed -i "s/\${MASTER_PORT}/${MASTER_PORT}/g" /etc/nginx/conf.d/proxy.conf
nginx -g 'daemon off;'
# 运行命令:PORT=9080 MASTER_HOST=http://xxx/api/predict/qwen2_72b_instruct_ai4bi MASTER_PORT=80 bash /opt/start.sh
4.打包运行
docker buildx build --platform linux/amd64 -t nginx-proxy:1.1 -f Dockerfile .
docker rm -f nginx-proxy && docker run -itd --privileged=true -p 80:80 --restart=always --name nginx-proxy nginx-proxy:1.0
docker exec -ti nginx-proxy sh && bash /opt/start.sh