1.摘要
前一阵子发明处事会有偶发的处事不行用的环境,记录一下这个问题的排查进程。
现象是这样的:天天到了某个时间点,就会呈现处事不不变的环境,偶发接口调不通。
线上业务利用了lvs-nginx-tomcat三层布局,首先查察tomcat监控,没有什么出格异常的环境,响应时间和错误码没发明有什么异常,软件开发,CPU、IO等等指标也都正常。
再查察nginx上的监控,发此刻某个时刻这个处事的5xx报错突增,软件开发,或许7、8秒之后又规复了。
继承在nginx处事器上找线索,发明Nginx在谁人时间点会呈现报错:
2015/12/24 10:30:38 [error] 13433#0: check time out with peer: 10.79.40.1xx:80
线上nginx会每秒探测后端所有处事器的某个uri,假如返回的http状态码是200则认为正常,持续3次探测失败则摘除探测失败的处事器,直到探测乐成再规复。
从日志中可以发明nginx在出问题的时间点对付后端所有tomcat的探测请求都呈现了问题,导致摘除了所有后端处事器,在这段时间里请求会报502异常。
从nginx上的日志可以看到探测请求没有返回,那么请求实际发到tomcat了没有?线上业务中的探测频率是1s/次,于是到tomcat的会见日志里查找线索,过滤一个nginx对tomcat的所有探测请求:
可用看出从7:00:10-7:00:40阁下的探测请求是有丢失的。
前端机的负载并不高,于是我们第一时间认为这大概是nginx到tomcat处事器的网络有问题。统计了一下线上日志,出问题的呆板会合在某个网段,劳务派遣管理系统,而且会合在一天之内的某几个时间点,这好像也进一步印证了我们的揣摩。
但到此为止仅仅是猜疑,为了证明我们的揣摩,我们实验去复现问题。我们在nginx上陈设了一个简朴的剧本,用curl呼吁对同样的tomcat提倡每秒一次的请求,但功效较量诡异:
监测方法 监测地点 http版本 频率 地址处事器 目标处事器 问题
nginx / 1.0 1s nginx tomcat 有 curl / 1.0 1s nginx tomcat 无
这跟我们之前的揣摩纷歧致,没步伐,实验在两头抓包查察网络状况,
tomcat抓包:
nginx抓包:
tomcat处事器在7:00:10已经吸收了请求而且回覆了ACK,7:00:13 nginx超时主动断开毗连,7:00:15时tomcat才返回数据,网络的问题被解除了。
那么接下来的重点就是tomcat自己,在吸收问题请求的时候,tomcat处事毕竟做了什么?
照旧通过简朴的剧本,在容易出问题的时间段持续利用jstack打印线程栈,查找出问题时处于RUNNABLE状态的catalina线程,发明这里有一句很可疑:
这个处事用的照旧较量陈腐的tomcat6.0.32,查察源码,可以发此刻tomcat对请求header做完理会之后会挪用这个函数:
MessageBytes valueMB = headers.getValue("host"); // Check host header if (http11 && (valueMB == null)) { error = true; // 400 - Bad request if (log.isDebugEnabled()) { log.debug(sm.getString("http11processor.request.prepare")+ " host header missing"); } response.setStatus(400); adapter.log(request, response, 0); } parseHost(valueMB); .... /** * Parse host. */ public void parseHost(MessageBytes valueMB) { if (valueMB == null || valueMB.isNull()) { // HTTP/1.0 // Default is what the socket tells us. Overriden if a host is // found/parsed request.setServerPort(socket.getLocalPort()); InetAddress localAddress = socket.getLocalAddress(); // Setting the socket-related fields. The adapter doesn't know // about socket. request.serverName().setString(localAddress.getHostName()); return; }
也就是说,假如request请求的header里没有配置host,那么tomcat会利用本身处事器的hostname作为request工具的host属性。
再比拟线上nginx探测的请求和curl发出的请求,可以看出nginx的探测请求确实没有带任何header,而curl请求默认是带了3个header的:
curl:
GET / HTTP/1.0 Host: localhost:8080 User-Agent: curl/7.43.0 Accept: */*
nginx:
GET / HTTP/1.0
到这里可以确认,假如请求的header里没有带Host的话就有大概呈现问题。找到了hang住的位置,那么接下来的问题就是,为什么这里会hang住?