<pre class="wp-block-code"><code>业务同事反馈新增consistent hash url的时候有报错, "load balancing method redefined"
看了下配置, 大致是这样子
<p>upstream 2165 {
keepalive 4096;
check interval=10000 rise=2 fall=3 timeout=3000 type=http default_down=false;
check_http_send 'GET /check_alive HTTP/1.0\r\nHost: check.sohuitc.cn\r\n\r\n';
check_http_expect_alive http_2xx;
server 1.1.1.1 max_fails=0;
server 1.1.1.2 max_fails=0;
hash $request_uri consistent;
}</p>
简单搜索了下, 有这么个答案:
https://ma.ttias.be/nginx-nginx-warn-load-balancing-method-redefined/
答案是: "You can mix keepalive and least_conn, but you should define least_conn before keepalive.", 就是把LB的method 放到Keepalive 指令的后边, 考虑到least_conn 和 hash其实都是LB的method
那么, 这是为什么呢?
参考nginx官方的文档, https://nginx.org/en/docs/http/ngx_http_upstream_module.html#keepalive
<p><code>When using load balancing methods other than the default round-robin method, it is necessary to activate them before the keepalive directive.</code></p>
官方文档要求把keepalive指令放到其他非RR的LB method的后边
我还想知道为什么呢?
翻开nginx的代码, 可以看到我这个hash 函数有个判断
http/modules/ngx_http_upstream_hash_module.c
<p>
if (uscf->peer.init_upstream) {
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"load balancing method redefined");
}</code>而Keepalive指令, 会尝试去初始化这个 <span style="background-color: initial; font-family: inherit; font-size: 0.857143rem;">uscf->peer.init_upstream</p>
http/modules/ngx_http_upstream_keepalive_module.c
<p>
kcf->original_init_upstream = uscf->peer.init_upstream
? uscf->peer.init_upstream
: ngx_http_upstream_init_round_robin;
uscf->peer.init_upstream = ngx_http_upstream_init_keepalive;</p>
所以如果Keepalive指令在前边, 准备加载LB method的时候, 会发现这个变量已经被初始化了, 会认为是有其他LB method被加载? 就会有个<span style="background-color: initial; font-family: inherit; font-size: 0.857143rem;">load balancing method redefined</span> 告警了
另外, 这个问题其实是由于另一个小的配置问题引出来的, 这里一并记录下
我们的配置有个报错, "enable hash balancing method support parameter backup", upstream中有backup配置也需要新增hash method, 触发了这个问题
参考文档https://forum.nginx.org/read.php?29,281365,281369#msg-281369
<p>Generally, hash methods don't support the "backup" parameter,
but for those who need backup when falling back to round robin,
there's a work around: put the "hash" directive after the
"server" directives in the "upstream" block.</p>
因此需要把hash 挪到后边, 这就刚好跟keepalive指令有了前后的冲突</code></pre>
从1.12版本测试结果看, hash 和backup一起用, 会导致backup无法起作用, 需要特别留意, 而1.25系列则正常, 估计是做了修复
标签归档:nginx
nginx 升级后访问400 BAD REQUEST增多的问题
一些旧设备访问nginx的时候可能会出现400 bad request, 这个跟2020.2月nginx移除了一个兼容特性有关
Disabled duplicate “Host” headers (ticket #1724). Duplicate “Host” headers were allowed in nginx 0.7.0 (revision b9de93d804ea) as a workaround for some broken Motorola phones which used to generate requests with two “Host” headers[1]. It is believed that this workaround is no longer relevant.
新增的这块代码如下: 会判断是否有重复的host 头, 而在之前的版本是认为可以容忍的
![](https://www.4os.org/wp-content/uploads/2021/03/image.png)
这个兼容特性被移除后, 会导致一些旧版本的移动设备响应异常
而我们线上测试机器主要是兼容了spdy协议, 也出现了400 BAD REQUEST, 这个跟spdy代码里边本身进行了一遍header处理有关:
![](https://www.4os.org/wp-content/uploads/2021/03/image-1.png)
可以参考:
https://hg.nginx.org/nginx/rev/4f18393a1d51
http://mailman.nginx.org/pipermail/nginx-devel/2020-February/012999.html
nginx 生成coredump的办法
网上很多说nginx生成coredump的办法在RHEL7 RHEL8都无法正确的生成core 文件, 这里给一下正确的步骤
#新建一个文件夹, 并确认nginx可以读写 $ mkdir /opt/itc/fssnginx/logs/cores/ $ sudo chown root:root /opt/itc/fssnginx/logs/cores/ $ sudo chmod 1777 /opt/itc/fssnginx/logs/cores/ #设置unlimited core file dump $ ulimit -c unlimited #也可以在系统中彻底修改 $vim /etc/security/limits.conf * soft core unlimited #设置系统级别的core file $ echo "/opt/itc/fssnginx/logs/cores/core.%e.%p" | sudo tee /proc/sys/kernel/core_pattern #允许suid dumpable $ sudo sysctl -w fs.suid_dumpable=2 $ sysctl -p #重启nginx systemctl restart nginx
#编译nginx.conf,然后重启nginx 服务 working_directory /opt/itc/fssnginx/logs/cores/; worker_rlimit_core 500M;
测试,找到nginx workprocess的进程号,发送 SIGSEGV 给该work process进程
$ps aux|grep nginx $kill -11 `$work_process_pid` #检查core 文件 $ll /opt/itc/fssnginx/logs/cores/ -rw-------. 1 nobody nobody 65753088 Jun 22 17:54 core.nginx.5662 -rw-------. 1 nobody nobody 70475776 Jun 22 18:03 core.nginx.5702
释疑: 看起来跟内核变量有关
#旧版本RHEL6是这个设置:
kernel.core_pattern = core
#新版本OS 是这个: 会被通过管道发给这个可执行命令
kernel.core_pattern = |/usr/lib/systemd/systemd-coredump %P %u %g %s %t %c %h %e
参考文档:
https://docs.nginx.com/nginx/admin-guide/monitoring/debugging/
nginx spdy patch for 1.14.0 1.13.12
spdy 协议由于安卓碎片化的存在 暂时还是需要保留一段时间的兼容性
准备升级到nginx1.14的时候发现 work process 会自动 退出, 同时系统日志有 nginx segfault的信息
修改配置,抓取coredump信息,需要做以下内容
nginx 增加
worker_rlimit_core 5000M;
working_directory /path/to/cores/;
$> ulimit -c unlimited
$> mkdir /opt/coredump/ && chown nobody.nobody /opt/coredump/ # 先建目录,还要确认nginx用户可以写此目录
$> echo “/opt/coredump/core-%e-%p-%h-%t” > /proc/sys/kernel/core_pattern
拿到coredump文件后使用gdb分析
gdb /path/to/nginx /path/to/cores/nginx.core
backtrace full
发现问题指向了
src/http/ngx_http_spdy.c:ngx_http_spdy_state_read_data 的
buf->last = ngx_cpymem(buf->last, pos, size);
简单调试发现buf->last是个0, ngx_cpymem会因为内存越界导致coredump
而分析代码 + gdb 断点调试 看到初始化r->request_body->buf的部分: ngx_http_spdy_init_request_body(r) 并未执行
打印r->request_body 内容发现这块被初始化了,对比nginx1.12.2和1.10.3版本发现旧版本则是未做初始化
翻了下调用的部分:ngx_http_request_body: ngx_http_read_client_request_body 可以看到在nginx 1.13.12版本开始会对r->request_body 做了初始化操作,这部分直接导致了SPDY 补丁 的不兼容
新补丁放在了:https://github.com/favortel/nginx_patch/blob/master/nginx-1.14.0_spdy_h2.patch
参考文档:
https://toontong.github.io/blog/nginx-gdb-coredump-segfault.html
https://www.nginx.com/resources/wiki/start/topics/tutorials/debugging/
http://lxr.nginx.org/source/src/http/ngx_http_request_body.c
http://lxr.nginx.org/source/src/http/ngx_http_request_body.c?v=nginx-1.12.2