nginx回源时bind ip的一些优化
字数 1120 2025-08-15 21:33:32

Nginx回源时bind IP的优化与实现

1. 背景与问题描述

在Nginx作为反向代理服务器时,默认情况下proxy_bind指令只支持绑定单个IP地址进行回源连接。这会导致以下问题:

  1. 单IP回源连接数受限,可能触发源站连接数限制
  2. 缺乏IP轮换机制,无法充分利用多IP资源
  3. 健康检查与业务回源使用相同IP,缺乏隔离

本文介绍如何通过修改Nginx源码实现多IP回源绑定和健康检查IP隔离的优化方案。

2. 核心优化点

2.1 proxy_bind多IP支持优化

proxy_bind配置:

{ 
    ngx_string("proxy_bind"), 
    NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12, 
    ngx_http_upstream_bind_set_slot, 
    NGX_HTTP_LOC_CONF_OFFSET, 
    offsetof(ngx_http_proxy_loc_conf_t, upstream.local), 
    NULL 
}

优化后配置:

{ 
    ngx_string("proxy_bind"), 
    NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, 
    ngx_http_upstream_bind_set_slot_array, 
    NGX_HTTP_LOC_CONF_OFFSET, 
    offsetof(ngx_http_proxy_loc_conf_t, upstream.local_array), 
    NULL 
}

关键修改:

  • NGX_CONF_TAKE12改为NGX_CONF_1MORE,支持多个参数
  • 使用ngx_http_upstream_bind_set_slot_array替代原函数
  • 存储结构改为local_array数组

2.2 多IP绑定实现代码

char *ngx_http_upstream_bind_set_slot_array(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {
    ngx_http_upstream_local_array_t **plocal, *local;
    ngx_str_t *value;
    ngx_addr_t *addr;
    ngx_int_t rc;
    ngx_uint_t i;

    plocal = (ngx_http_upstream_local_array_t **) (p + cmd->offset);
    if (*plocal != NGX_CONF_UNSET_PTR) {
        return "bind is duplicate";
    }

    value = cf->args->elts;
    
    // 建立local array
    local = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_local_array_t));
    *plocal = local;
    
    // 建立local peer addr
    local->addr = ngx_pcalloc(cf->pool, sizeof(ngx_peer_addrs_t));
    
    // 建立addr array
    local->addr->addrs = ngx_array_create(cf->pool, 1, sizeof(ngx_addr_t));
    
    // 遍历所有的local ip,放进array中
    for (i = 1; i < cf->args->nelts; i++) {
        addr = ngx_array_push(local->addr->addrs);
        if (addr == NULL) {
            return NGX_CONF_ERROR;
        }
        
        rc = ngx_parse_addr(cf->pool, addr, value[i].data, value[i].len);
        switch (rc) {
            case NGX_OK:
                addr->name = value[i];
                break;
            case NGX_DECLINED:
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid address \"%V\"", &value[i]);
                /* fall through */
            default:
                return NGX_CONF_ERROR;
        }
    }
    // ...
}

2.3 初始化请求时绑定IP数组

static void ngx_http_upstream_init_request(ngx_http_request_t *r) {
    // ...
    u = r->upstream;
    u->peer.local_array = ngx_http_upstream_get_local_array(r, u->conf->local_array);
    // ...
}

2.4 获取配置的流程

  1. 通过proxy_pass获取配置:
static ngx_int_t ngx_http_proxy_handler(ngx_http_request_t *r) {
    // ...
    u = r->upstream;
    plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);
    u->conf = &plcf->upstream;
    // ...
}
  1. proxy_pass指令设置upstream:
static char *ngx_http_proxy_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {
    // ...
    plcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);
    // ...
}

3. 健康检查IP隔离(check_bind)

3.1 check_bind配置

ngx_string("check_bind"), 
NGX_HTTP_MAIN_CONF|NGX_CONF_1MORE, 
ngx_http_upstream_bind_set_slot_array, 
NGX_HTTP_MAIN_CONF_OFFSET, 
offsetof(ngx_http_upstream_check_main_conf_t, global_local), 
NULL

3.2 配置结构体

typedef struct {
    ngx_uint_t check_shm_size;
    ngx_http_upstream_check_peers_t *peers;
    ngx_http_upstream_local_array_t *global_local;
} ngx_http_upstream_check_main_conf_t;

3.3 初始化主配置

static char * ngx_http_upstream_check_init_main_conf(ngx_conf_t *cf, void *conf) {
    // ...
    // 拿到upstream module的main conf
    umcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_upstream_module);
    
    // 拿到后端数组的指针
    uscfp = umcf->upstreams.elts;
    
    for (i = 0; i < umcf->upstreams.nelts; i++) {
        // 循环赋值
        if (ngx_http_upstream_check_init_srv_conf(cf, uscfp[i], ucmcf->global_local) != NGX_OK) {
            return NGX_CONF_ERROR;
        }
    }
    // ...
}

3.4 初始化服务器配置

static char * ngx_http_upstream_check_init_srv_conf(ngx_conf_t *cf, void *conf, ngx_http_upstream_local_array_t *global_local) {
    // ...
    ngx_http_upstream_srv_conf_t *us = conf;
    
    // 拿到这个upstream srv conf下的check module conf
    ucscf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_check_module);
    
    // 进行赋值
    if (ucscf->global_local == NGX_CONF_UNSET_PTR) {
        if (global_local != NGX_CONF_UNSET_PTR && global_local != NULL) {
            ucscf->global_local = global_local;
        }
    }
    // ...
}

3.5 添加peer时的处理

ngx_uint_t ngx_http_upstream_check_add_peer(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us, ngx_addr_t *peer_addr) {
    // ...
    // add check_bind support for dyups modules.
    if (ucscf->global_local == NGX_CONF_UNSET_PTR) {
        if (ucmcf->global_local != NGX_CONF_UNSET_PTR && ucmcf->global_local != NULL) {
            ucscf->global_local = ucmcf->global_local;
        }
    }
    
    // 添加peer
    peers = ucmcf->peers;
    peer = ngx_array_push(&peers->peers);
    peer->index = peers->peers.nelts - 1;
    
    // 这部分很关键,上面刚刚赋值好global_local的ucscf被赋值为peer->conf
    peer->conf = ucscf;
    peer->upstream_name = &us->host;
    peer->peer_addr = peer_addr;
    // ...
}

3.6 健康检查连接处理

static void ngx_http_upstream_check_connect_handler(ngx_event_t *event) {
    // ...
    peer = event->data;
    // peer的conf就是ucscf
    ucscf = peer->conf;
    
    // 赋值
    if (peer->conf->global_local != NGX_CONF_UNSET_PTR && peer->conf->global_local != NULL) {
        peer->pc.local_array = peer->conf->global_local->addr;
    } else {
        peer->pc.local_array = NULL;
    }
    
    rc = ngx_event_connect_peer(&peer->pc);
    // ...
}

4. 实现效果

  1. 多IP回源:通过proxy_bind可以指定多个IP地址,Nginx会轮询使用这些IP与后端建立连接
  2. 连接数扩展:突破单IP连接数限制,提高并发能力
  3. 健康检查隔离:通过check_bind指定专门用于健康检查的IP组,与业务流量隔离
  4. 配置灵活性:支持在main、server和location级别配置不同的绑定IP策略

5. 使用示例

5.1 多IP回源配置

http {
    upstream backend {
        server 192.168.1.100:8080;
    }
    
    server {
        location / {
            proxy_pass http://backend;
            proxy_bind 10.0.0.1 10.0.0.2 10.0.0.3;
        }
    }
}

5.2 健康检查IP隔离配置

http {
    upstream backend {
        server 192.168.1.100:8080;
        check interval=3000 rise=2 fall=5 timeout=1000 type=http;
        check_http_send "GET /health HTTP/1.0\r\n\r\n";
        check_http_expect_alive http_2xx http_3xx;
    }
    
    check_bind 172.16.0.1 172.16.0.2;
    
    server {
        location / {
            proxy_pass http://backend;
            proxy_bind 10.0.0.1 10.0.0.2 10.0.0.3;
        }
    }
}

6. 注意事项

  1. 确保所有绑定的IP地址在服务器上已配置且可用
  2. 修改源码后需要重新编译Nginx
  3. 对于动态upstream(dyups)需要特殊处理,确保添加peer时正确继承配置
  4. 健康检查IP组和业务IP组最好使用不同的IP段,便于监控和管理
  5. 多IP轮询是简单的顺序轮询,没有复杂的负载均衡算法

7. 总结

通过本文介绍的Nginx源码修改方案,可以有效解决单IP回源连接数限制问题,并实现健康检查与业务流量的IP隔离。这种优化特别适用于以下场景:

  1. 需要大量回源连接的高并发应用
  2. 源站对单个IP有连接数限制的环境
  3. 需要严格隔离健康检查流量的场景
  4. 多线路接入需要指定不同出口IP的情况

该方案已经在生产环境中得到验证,能够显著提高Nginx作为反向代理时的连接能力和稳定性。

Nginx回源时bind IP的优化与实现 1. 背景与问题描述 在Nginx作为反向代理服务器时,默认情况下 proxy_bind 指令只支持绑定单个IP地址进行回源连接。这会导致以下问题: 单IP回源连接数受限,可能触发源站连接数限制 缺乏IP轮换机制,无法充分利用多IP资源 健康检查与业务回源使用相同IP,缺乏隔离 本文介绍如何通过修改Nginx源码实现多IP回源绑定和健康检查IP隔离的优化方案。 2. 核心优化点 2.1 proxy_ bind多IP支持优化 原 proxy_bind 配置: 优化后配置: 关键修改: 将 NGX_CONF_TAKE12 改为 NGX_CONF_1MORE ,支持多个参数 使用 ngx_http_upstream_bind_set_slot_array 替代原函数 存储结构改为 local_array 数组 2.2 多IP绑定实现代码 2.3 初始化请求时绑定IP数组 2.4 获取配置的流程 通过 proxy_pass 获取配置: proxy_pass 指令设置upstream: 3. 健康检查IP隔离(check_ bind) 3.1 check_ bind配置 3.2 配置结构体 3.3 初始化主配置 3.4 初始化服务器配置 3.5 添加peer时的处理 3.6 健康检查连接处理 4. 实现效果 多IP回源 :通过 proxy_bind 可以指定多个IP地址,Nginx会轮询使用这些IP与后端建立连接 连接数扩展 :突破单IP连接数限制,提高并发能力 健康检查隔离 :通过 check_bind 指定专门用于健康检查的IP组,与业务流量隔离 配置灵活性 :支持在main、server和location级别配置不同的绑定IP策略 5. 使用示例 5.1 多IP回源配置 5.2 健康检查IP隔离配置 6. 注意事项 确保所有绑定的IP地址在服务器上已配置且可用 修改源码后需要重新编译Nginx 对于动态upstream(dyups)需要特殊处理,确保添加peer时正确继承配置 健康检查IP组和业务IP组最好使用不同的IP段,便于监控和管理 多IP轮询是简单的顺序轮询,没有复杂的负载均衡算法 7. 总结 通过本文介绍的Nginx源码修改方案,可以有效解决单IP回源连接数限制问题,并实现健康检查与业务流量的IP隔离。这种优化特别适用于以下场景: 需要大量回源连接的高并发应用 源站对单个IP有连接数限制的环境 需要严格隔离健康检查流量的场景 多线路接入需要指定不同出口IP的情况 该方案已经在生产环境中得到验证,能够显著提高Nginx作为反向代理时的连接能力和稳定性。