Nginx实现负载均衡

138 阅读3分钟

前言:那些年我们绕不开的架构痛点

在日常系统迭代中,你是否经常面临这样的困境:

发布即服务中断:每次更新都要停服,用户投诉如潮水般涌来

单体架构之殇:流量激增时只能纵向扩容,服务器成本指数级增长

技术选型两难:既想保持架构简单,又需要具备弹性扩展能力

本文将揭秘如何通过 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、上线发布

  1. 81,82 端口启动原服务,为了防止端口冲突,可以通过命令行的方式修改启动占用端口 --server.port = 端口号
  2. 重载 nginx 配置文件
  3. 下线原服务

这样流量就可以转发到 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:根据权重占比分配流量