前言:那些年我们绕不开的架构痛点
在日常系统迭代中,你是否经常面临这样的困境:
发布即服务中断:每次更新都要停服,用户投诉如潮水般涌来
单体架构之殇:流量激增时只能纵向扩容,服务器成本指数级增长
技术选型两难:既想保持架构简单,又需要具备弹性扩展能力
本文将揭秘如何通过 Nginx 反向代理,在不改造架构的前提下,实现以下三大能力:
1. 无损滚动发布 - 告别服务停机窗口
2. 智能流量分发 - 单机 QPS 提升的实战方案
3. 动态节点管理 - 自动剔除异常实例的故障熔断机制(本文未实现)
1、背景
频繁发布(日均多次)与 99.999% 高可用要求形成冲突,传统停机发布模式风险极高 。需要实现无损流量切换,确保任意单节点下线时业务零中断。
2、修改nginx配置
# 指定一个服务组
upstream loadServer{
server localhost:8081;
server localhost:8082;
}
server{
listen 8080; # 监听8080端口
server_name localhost;
location / { # 将所有请求转发到proxy_pass 下
proxy_pass http://loadServer; # 指定转发的服务组
# 下面的配置将请求头带过去
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
3、验证
本文使用 Java的SpringBoot+slf4j 框架,为了方便演示,只对日志做修改,当请求进入后日志框架会将端口打印出来。以下是获取端口及打印的代码
@Slf4j
@Component
public class AuthInterceptor implements HandlerInterceptor {
private static final String REQUEST_ID_KEY = "requestId";
private String port="-1";
@Autowired
private Environment env;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
MDC.put("port",getPort());
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
MDC.clear();
}
public String getPort(){
if (port.equals("-1")){
port = env.getProperty("server.port");
}
return port;
}
}
logback.xml 配置
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} port:[%X{port}] [%thread] %-5level %logger{50}:%L - %msg%n</pattern>
调用接口验证
成功轮询 8081,8082 端口
4、上线发布
- 81,82 端口启动原服务,为了防止端口冲突,可以通过命令行的方式修改启动占用端口 --server.port = 端口号
- 重载 nginx 配置文件
- 下线原服务
这样流量就可以转发到 nginx 从而实现负载均衡了
5、其它配置
有几个配置在网上看见的,目前只用了一个 down 配置。
5.1 服务下线
为了实现服务下线后,不会有流量转发到该端口,可以修改服务组的配置例:
upstream loadServer{
server localhost:8081;
server localhost:8082 down;
}
修改后重载配置,会视为 82 端口服务下线,流量便不会转发到此了。等服务维护好后,重新上线,去掉 down 即可。
还有其他的动态剔除的下线机制
max_fails=2 fail_timeout=30s;
# max_fails最大超时次数,fail_timeout服务器代理监听超时时间
5.2 轮询
- least_conn:将请求分配给当前活动连接数最少的服务器,更适合长连接的场景。
- ip_hash:根据客户端的 IP 地址分配请求,保证同一 IP 的请求总是被分配给同一台服务器,适合需要会话保持的场景。
- weight:根据权重占比分配流量