本文共 5827 字,大约阅读时间需要 19 分钟。
随着应用越来越多,tomcat容器也越来越重,随之而来的是tomcat宕机越来越频繁。最终选择jar包部署测试反向代理的效果。虽然jar包部署之后项目肯定会稳定很多。但是nginx宕机检测还是要做的。在本地调试发现并没有出现什么问题。就将nginx部署到了灰度环境。在项目跑起来之后我们直接关闭了其中一个项目。按照nginx反向代理的被动检测机制应该还是可以返回正常的数据。但是我们发现灰度的nginx宕机检测花费了21秒。这个时间有点太大了,用户估计要爆炸。想了好多也没找到问题。下面记录一下自己的狗血经历。
怎么发现21秒的问题?
在我两台服务停掉一台之后,再去访问项目页面。发现项目太慢,但是通过几次刷新就可以迅速快起来。但是过一段时间项目的又开始缓慢了,而且具有规律性。很快我就知道问题应该是fail_timeout的问题。
upstream kmdiscussj { server kk.kkk.kk.165:8091 weight=1 max_fails=2 fail_timeout=30s; server jj.jjj.jj.166:8091 weight=1 max_fails=2 fail_timeout=30s;}location ^~ /discuss/ { 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_pass http://kmdiscussj/discuss/;}
我将fial_timeout改成了4,然后max_fails改成了1。然后写了一个简单的python脚本每隔一秒请求一次接口,然后打印请求时间。试图确定真实的宕机检测时间。
import requestsimport timeheaders = { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 ' '(KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36','ignore-identity':'true'}def spiderForKm(url): while 1: start=time.time() html=requests.get(url, headers=headers).content end=time.time() print(end-start) time.sleep(1) def main(): spiderForKm("https://kkkk.kkkk.com/discuss/mytest/v1/get") if __name__ == "__main__": main()
但是这21秒到底去哪里了。按照weight=1 max_fails=2 fail_timeout=30s的官方解释和python控制台的打印结果也没问题。为啥需要那么长时间?是nginx版本不对还是项目的问题还是网络问题?这让人很难,所以咋都得测试一遍。
当时用的nginx版本是1.12,有同事说是不是版本的问题。其实我感觉不太可能,但活马当死马医吧。就更新到了1.18版本。反正是灰度环境还没上线。但是通过测试还是发现神奇的21秒并没有消失。所以只能继续向后边排查。如果不是版本问题那是不是网络问题?但是如果是网络问题那21秒之后为啥又很快,难道是初次连接的时候会不识别?我觉得有必要看一下日志。找了半天发现日志的时间怎么都是一样的,zzz。这是什么鬼。想了好久,突然灵机一动这是不是响应时间?最后查了相关资料发现nginx默认认知是没有响应时间的,于是我们又开始配置日志了。
log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for" ' '"$request_time"';
注意这个log_format要配置在http中。日志打印要配置到server中
access_log logs/host.access.log main;#其中的main是日志的定义。和上边的保持一致。
配置好日志之后,开启我们的python脚本,然后去看nginx的请求日志。发现响应时间确实是21秒。。。
想想看这21秒肯定是包括宕机检测的时间加正常请求的时间。所以宕机检测的时间至少也有20秒。如果要甩锅给运维的话时机还不成熟,可能还会被反杀。算了还是再定位一下吧。既然在灰度环境是21秒,那么我本地是多少秒。为了方便期间,我本地创建了两个测试项目其中就只有一个方法。然后在本地部署了nginx,然后代理到不同的端口,然后故意关闭一个服务。发现2秒就可以检测到。但为什么会灰度环境需要那么久?不应该是服务器性能更好吗?难道是灰度的应用服务太大,宕机检测需要的时间更长???我觉得这不可能吧,于是我将其中一个包放到灰度环境上。然后测试,发现还是花费了21秒时间,这就奇怪了。同样的项目,我本地2秒,线上21秒?而且所有的配置都一样,唯一的不同就是线上的nginx代理的应用比较多,大概有20个,每个部署2台,总计也超过20了,其中还有websocket。所以我怀疑是项目太多导致的。为此把nginx上的其他代理全部取消了。然后再看python控制台打印的结果。发现居然还是21秒。。。。。我奔溃了。
我都已经准备好说服领导接受这个现实,因为宕机的概率已经降低很多了,而且宕机检测虽然慢一点,但是项目接口还是会正常运行的。我们只需要将fail_timeout设置的大一点,这样就可以避免过多的超时检测。这样用户请求被转发到宕机的机器的可能性就会降低很多,当然我还在怀疑是不是网络的问题。我记得有个read_timeout和send_timeout。但是我本地都没配置这个,2秒就可以检测成功。为啥线上需要那么久?显然应该也不是这两个参数的问题。
但是怀着学习的心态还是仔细学习了这个参数的意义
proxy_send_timeout=60s;表示请求发送给上游服务的超时时间,默认为60秒
proxy_read_timeout=60s;表示nginx从上游服务读取返回的超时时间,也就是上游服务处理请求时间加网络通信时间的最大时间。默认为60秒
无意中发现了nginx中的proxy_connect_timeout的配置。
proxy_connect_timeout表示nginx与上游服务的连接超时时间。默认为60秒。想想看如果没有连接上是不是会在60秒的时间范围内进行尝试,然后等待。问题是不是在这里?我马上将这个配置添加到nginx的location中,并将值设置为4,然后观察python控制台
proxy_connect_timeout 4s; proxy_send_timeout 4s; proxy_read_timeout 4s;
整个人都愉快了好多。想着能不能再降低一下,我将上边的配置中4全部设置成了1,控制台的时间也变成了1点多,但是访问页面的时候发现接口开始没有响应了。一直没有返回值。然后也没开始报告说让查看nginx日志。怀疑是send_timeout和read_timeout太小导致的死循环。所有我将 proxy_send_timeout 和 proxy_read_timeout 都设置为8,问题解决。但是想着 proxy_connect_timeout 1s; 有点太短,所以还是将其设置为2并重启reload了nginx的配置。果然即便宕机了,nginx的最大延迟时间也就2秒。总体上还是可以接受的。
问题解决完毕之后,感觉整个人都轻松了好多。
在此记录了一下其他的问题。
1.具有websocket的项目,由于项目websocket可以采用http1.1来维持单机的唯一链路,但是接口可能会被路由到不同的机器,从而导致websocket不能及时将消息广播到用户。使用ip_hash也不能解决问题。最好的方法是将websocket和接口分离。或者如果分离不开的话就需要对接mq的广播机制了。
2.如果采用threadlocal缓存前端某个标志,可能需要采用ip_hash,否则会被路由到不同机器,然后导致业务出现错误 。
在此贴一下nginx的配置
events { worker_connections 1024;}http { include mime.types; default_type application/octet-stream; client_max_body_size 500M; keepalive_timeout 65; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for" ' '"$request_time"'; map $http_upgrade $connection_upgrade { default upgrade; '' close; } upstream kmdiscussj { server kk.kkk.kk.165:8091 weight=1 max_fails=2 fail_timeout=30s; server jj.jjj.jj.166:8091 weight=1 max_fails=2 fail_timeout=30s; } upstream kmsocket{ ip_hash; server kk.kkk.kk.165:8091; server jj.jjj.jj.166:8091; keepalive 1000; } server { listen 80; server_name xxx.xx.com; access_log logs/host.access.log main; location ^~ /socket.io/ { proxy_connect_timeout 4s; proxy_send_timeout 12s; proxy_http_version 1.1; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_read_timeout 3600s; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Connection $connection_upgrade; proxy_pass http://kmsocket/socket.io/; } location ^~ /discuss/ { proxy_connect_timeout 2s; proxy_send_timeout 8s; proxy_read_timeout 8s; 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_pass http://kmdiscussj/discuss/; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } }}
其他:
windows环境的关闭nginx,taskkill /im nginx.exe /f
转载地址:http://vqkmi.baihongyu.com/