1。背景本文主要写的是最近深入调查一个客户访问业务前端域名,报504和超时错误。该客户是私有化部署。部署到客户的服务有跨大陆呼叫,没有专线。如果澳大利亚呼叫欧洲的服务,可能会有比较大的网络延迟。需要排查504的具体原因,然后通过优化参数暂时解决。2、故障处理步骤及思路2.1故障现象沟通对于toB客户,通常在使用我们的产品时,报错时只会反馈截图。我们需要向客户传达或关键信息,有利于排除故障。比如:打开了什么页面,在哪个界面方便复现具体报错?如果有大概报错时间的x-request-id,通过requestid获取具体报错的url2.2。我们需要了解整个访问请求的链接和浏览器上的请求链接,才能更好的排查问题,比如我遇到的问题,请求链接是这样的。客户端机器访问浏览器域名->私有域名cdn(1)->私有域SLB(2)->私有域nginx(3)->saas服务域名cdn(4)->saas域SLB(5))->saas端nginx(6)->saas端业务后端服务。每个公司的业务情况不同,根据自己的实际情况来梳理。2.3第一次排错查看日志。通过第一步故障现象的沟通,获取内容,然后查看链接上的nginx(3),即私有nginx的日志,想确认请求是否到达服务器.根据x-request-id查找日志,时间点和路径也可以匹配到。状态码504,请求时间30s,多次刷新页面,超时30s。然后查看nginx上的配置,发现接口位置的后端服务器的响应时间,proxy_read_timeout时间设置为30s,也就是说nginx会等待30s得到请求的响应。如果30s内收不到响应,就会报504超时。因此,我修改proxy_read_timeout时间为300s,然后重新加载nginx。第二次排查,客户反馈访问页面依赖错误504和超时,于是继续看nginx日志,想是不是没有生效,但是查看日志后发现错误状态code改为499,request_time为60s。相当于客户端向Nginx发起请求,Nginx将请求转发给服务器A。由于nginx的proxy_connect_timeout超时时间默认为60s,所以当客户端的请求转发给服务器A时,会导致Nginx尝试连接60s,而客户端响应时间设置为30s,所以客户端导致大量超时,Nginx报大量499。。然后查了一下,发现参数proxy_ignore_client_abort需要改成on。想看看真实情况,所以在报错的位置下添加,然后重新加载nginx。继续观察日志,发现日志又变了,报504180s。这时候我开始怀疑是nginx之后saas端的nginx有问题,然后根据x-request-id查找日志,发现请求确实到达了saas端,但是很明显,日志打印200,请求时长60s。所以根据上面的链接情况,我怀疑是saas端和private端的slb(5)在saas端。经客户验证,他们使用的阿里云的slb默认最大连接请求超时为180s,与私端基本一致。nginx中大量日志可以对应180s超时。于是给阿里云客服提交了工单,询问是否可以加价。结论是不可能。listenerhttp和https协议的最大限制只能是180s(其实都是合理的,这完全是因为我们私有端在澳洲,saas端在欧洲,跨大陆访问的结果),但是客服说可以用tcp协议,可以支持900s,于是新建一个tcp协议监听器,连接超时也设置为350s(为了和nginx上的proxy_read_timeout区别),然后将私端上游转发的地址和端口改成新的测试,客户端回复访问正常。第三次排查是我太天真了,以为彻底解决了,结果第二天客户反映还是会随机出现504超时,影响客户体验,所以有第三种排查。还是先去查看私有nginx的日志,没有异常,状态码200,但是请求响应时间超过60s。查看saas端的nginx日志也是正常的。然后就没明白,是哪里的问题,然后让客户出示报错接口的copyurl,如果再出现,然后在服务器上手动请求url,504可以重现,返回通过nginx。于是在私有端,在手动请求的同时,tcpdump抓包,发现也是正常的tcp三次握手连接,http正常请求无异常返回。但是在请求返回的数据中,我发现了端倪。服务器不是nginx。我们的nginx已经更名为sws了,所以刚才请求的时候,nginx504超时我们业务方没有返回,然后怀疑请求链接上的私有端SLB(2),问了客户确认访问的域名虽然经过了CDN加速,但是会在这个slb上回源,然后listener的连接超时时间确实设置为60s,然后客户修改为180s,以及后面两天没有超时问题。3、调查过程中的知识点3.1nginx中499状态码的定义及处理方法查看Nginx源码当客户端主动关闭链接时,没有HTTP状态码可以指示该状态,但需要记录在nginx中,所以自定义一个499状态来表示。**HTTP没有为客户端关闭*连接的情况定义代码,而我们正在处理它的请求,因此我们引入*自己的代码来记录客户端关闭连接时的这种情况*,甚至在我们尝试将HTTP标头发送到之前it**/#defineNGX_HTTP_CLIENT_CLOSED_REQUEST499所以很明显,当客户端主动关闭请求或者客户端网络断开时,nginx会记录499状态,并断开与后续服务器的连接(这可能会导致服务器返回数据,报错被报告是因为连接丢失了)。解决499问题,查看服务器响应为什么这么慢,是否需要优化,或者增加客户端的连接超时时间,断开proxy_ignore_client_abort参数调整不要那么快。该参数表示忽略客户端的终止,默认关闭。当客户端网络中断请求时,nginx服务器中断其向后端服务器的请求,并立即记录一条499日志。如果设置为on,nginx会忽略客户端中断,等待代理服务返回,并记录后端返回的请求状态。location=/api{proxy_ignore_client_aborton;proxy_passhttp://service_backends;}该参数表示:客户端主动关闭连接后,nginx与分发服务器的连接是否保持连接状态。如果参数设置为on,如果客户端断开连接,nginx不会断开与后端服务器的连接。Nginx会等待后端处理完成(或超时),然后将“后端返回信息”记录到日志中。因此,如果后端返回200,则记录200;如果后端放回5XX,则记录5XX。如果超时(默认60s,可以使用proxy_read_timeout设置),Nginx会主动断开并记录504。注意:启用后,nginx只会在读取超时时关闭连接。默认值为60秒。可能有连接挤压的请求,所以默认关闭。如果启用,则必须设置proxy_read_timeout超时时间,nginx最好不要做反向代理以外的事情。该方案只解决了两个问题:(1)nginx499错误(2)服务器报Brokenpipe错误,因为连接断开。所以最好的办法就是优化服务器。3.2nginx中的时间是否这个时间的解释取决于nginx的日志格式中是否配置了request_timelog_format:指的是从接收到用户请求的第一个字节到发送响应数据的时间,即$request_time包括了接收客户端请求数据的时间,后端程序响应的时间,响应数据发送给客户端的时间。(请求处理时间以秒为单位,毫秒分辨率;从客户端读取第一个字节到最后一个字节发送到客户端后写入日志之间经过的时间。)up_resp_time/upstream_response_time:指nginx从中获取结果的处理backend时间,从nginx与后端建立连接到关闭连接,连接的后端地址就是upstream_addr的值。(保留从上游服务器获得的响应时间;时间以秒为单位,分辨率为毫秒。几个响应时间由逗号和冒号分隔,如$upstream_addr变量中的地址)。up_addr/upstream_addr:后端服务地址。request_time必须大于up_resp_time。3.3nginx中proxy相关参数说明proxy_connect_timeout:后端服务器connection_initiate握手等待响应的超时时间(proxyconnectiontimeout)默认60sproxy_read_timeout:决定了nginx等待多久得到请求的响应(proxyreceivetimeout)默认值为60sproxy_send_timeout:后端服务器数据返回时间_表示后端服务器必须在指定时间内发送完所有数据(代理发送超时)默认值为60s4。总结一下目前修改配置参数其实是一种不规范的操作,本文只是提供一个调查过程中的思考方向。每个问题的情况和背景都不同,需要根据实际情况进行调整。这个问题主要是跨大陆访问,没有专线,网络不稳定会导致请求时出现超时问题,然后根据具体问题,调整配置暂时解决这个问题,这样客户可以正常使用。客户就是上帝。不要怕出问题,所有的问题总能找到原因,我们不能简单的归结为网络问题,重启大法解决,其实我们可以更详细的定位,要知道为什么。
