网站nginx负载下因程序错误导致多节点重复处理请求的解决过程
目录
前言:
这是我上周工作过程中的一次解决问题的过程。解决的是nginx负载环境下,因为应用程序异常导致某一请求被多节点站点重复处理的问题。 我整理这个复盘的过程时,在给这个记叙文命名时思考了一段时间。 从业务角度说,应该是“一次短信重复发送问题的解决过程”,如果这样命名,过于土气;从技术角度说,应该是“nginx负载下当程序错误时会导致请求被多次重复处理”,如果这样命名,可能没人会注意了;最后,我懒得再费脑汁了,从人类智慧的角度我把它命名为“复杂的问题简单的解,小不慎则乱大谋”。
问题来了
3月16日,有客服、销售、运营人员不断反映,客户在saas预定机票完成后,会连续收到3条重复的支付提醒短信。 很多客户都投诉了这样的问题,一直以来的正常情况下这样的支付提醒短信是仅发1条的。
以下是短信平台上的截图:
前一天的3月15日周二是每周的上线日,当晚saas上过线。
迫于压力,当天下午不得不回滚3月15日的上线。回滚之后,问题不再重现。
3月15日saas的上线包括锦如和刘涛对saas所做的代码改动。刘涛涉及到的代码是审批接口,称可能不会导致这样的问题,锦如也肯定地表示不涉及到这块。 我让刘涛来排查原因,他在测试环境做了几次测试,并未发现这样的问题。我呢,由于最近忙于另一个项目,一直没来得及亲自和他一起查找原因。
问题又来了
3月17日是周四,上线日。要发布中新融创的对接程序。 晚上上线后,立即在线上订票,发现订单支付提醒短信还是会重复发3遍。
当时已经是晚上21点,我决定放下手头的活儿,来跟刘涛一起排查原因。
问题分析
客户在saas站点里预定机票,涉及到审批。审批是独立部署的一个站点。
订单的审批逻辑是这样的:客户在saas订票成功后,会跳转到统一审批页,提交审批后,统一审批系统会把审批状态通知给saas,然后重定向页面到订单流的下一个页面(由saas提供)。
Saas提供了一个由一般处理程序(文件是HandlerAirOrderExamineStatus.ashx)实现的接口,用来接收审批状态。内部大致逻辑是,修改订单的审批状态,然后会触发相应的对客通知短信。 客户重复收到的通知短信是支付提醒短信,意味着订单不需要审批。不需要审批在系统里表现在这些企业没有配置审批流。 根据现有代码逻辑,从填单页跳转到统一审批页,统一审批页初始化时没有获取到新审批,则会自动执行通知saas和页面重定向。
要补充说明一下的是,线上的审批站点和saas站点均是通过nginx做的3节点服务器的负载。
通过查看线上日志,审批系统正常,即只会在其中一个节点服务器来请求saas接口进行审批状态的通知,并未发现异常日志。而saas呢,却发现3个节点服务器都记录了同样的日志。
为什么会出现这样的情况呢? 审批在通知saas时,nginx接收到请求后应该分发给其中一个节点服务器来处理请求才对呀。
困惑
因为之前系统运行都是正常的,并未出现这种短信重发的情况,所以我们暂先不怀疑代码逻辑。
马上想到,记得运维说过,如果一个服务器处理时间超长,会自动分发给另一个节点服务器。 历史原因,获取订单详情和触发短信通知这两段代码执行比较慢,那么,我在这两段代码外面加了个Stopwatch来诊断其执行时长。
public void ProcessRequest(HttpContext context)
{
Stopwatch sw = new Stopwatch();
sw.Start(); if (context.Request.Params.Count == )
{
context.Response.Write("{'returnCode':'2','returnMsg':'no parameters','notifiedSMSContent':'no parameters'}");
return;
} string parametersStr = context.Request.Params[].AsToString();
SysLogToFile.WriteAuditMessage("dat修改订单状态传入参数_" + parametersStr); // 下面的是200行逻辑代码,包括获取订单详情和触发短信通知
...... ...... ...... ...... ...... ...... ...... ......
...... ...... ...... ...... ...... ...... ...... ......
...... ...... ...... ...... ...... ...... ...... ...... sw.Stop();
SysLogToFile.WriteAuditMessage("审批接口响应时间" + (sw.ElapsedMilliseconds / )); string returncode, returnmsg;
if (flag)
{ returncode = ""; returnmsg = "success"; }
else
{
returncode = ""; returnmsg = "fail";
} context.Response.Write("{'returnCode':'" + returncode + "','returnMsg':'" + returnmsg + "','notifiedSMSContent':'" + returnmsg + "'}");
}
发布后经测试,发现3个节点的执行时长都长达7~8秒。
那么,我们接下来就优化程序吧,以期把duration降到最小。20分钟后,我们把优化的代码再发布到服务器。发现处理时间已经下降到了1~2秒。
然而,响应时间已经都优化到2秒了,依然是3个服务器节点上都做了处理。这带给我们的是一个更大的问号。
转机
为什么会有一个更大的问号呢? 因为我们线上的系统都是分布式部署的,一个流程逻辑会涉及到多个系统之间的交互访问。 每个系统都是通过nginx做的3个节点的负载,并没有出现过这种一个请求被3个服务器节点同时响应的情况。
在这种情况,我的一贯做法是釜底抽薪。当然,这次我依然坚持釜底抽薪。
Q:怎么釜底抽薪呢?
A:把接口里的代码都注释掉。响应时间应该接近于0毫秒,难道还会出现3个节点都响应的情况?
于是,我们注释掉所有逻辑代码,只保留了最后的输出(context.Response.Write)语句,发布到服务器。
由于我们每次的测试步骤是:登陆saas,选择航班,然后订票下单,再看日志,这一系列的操作很耗时间,同时给线上系统带来了很多无效订单(垃圾数据)。这次呢,我让刘涛直接在ie里访问那个通知接口地址来测试性能。
转机来了,在ie里直接访问那个接口时,页面直接抛出了大黄页,报“System.FormatException:输入字符串的格式不正确”,是由主方法里最后的这条语句产生的:
context.Response.Write(string.Format("{'returnCode':'{0}','returnMsg':'{1}','notifiedSMSContent':'{2}'}", returncode, returnmsg, returnmsg));
即在string.Format方法里,你是不能乱用‘{’的~。
于是,修复后(改成字符串拼接了)再次上线。
再次访问那个接口,返回正常了。最大的惊喜是,SaaS那边只有一个服务器节点受理了请求。到这里呢,短信重发3次的问题终于得到了根治!
这时,我才想起来,nginx在接收到请求后,会分发给多个服务器节点中的一个节点来处理请求,但是,当出现错误响应时,nginx会自动分开给另一个节点来处理,直到没有节点可供分发,即直到所有可用节点都被分发为止。(当然,这可能是我们运维对nginx的配置策略)
后续
我们解决完这个问题,已经午夜23:40,如释重负的打车回家了。
在回去的路上,我的脑子像过电影似的过了一遍我们这一晚的处理过程。
l 复杂的问题,在你用心解决时,往往产生自很简单的一行代码。很戏剧性的是,这行代码可能是你不经意的疏忽写错了,也可能是技能受限写错的。如果,有做简单的测试,这样的问题完全是可以在开发时被发现的,而不至于花费那么长的时间来。
l 另外,我又想到,审批系统调用接口时难道没有判断响应结果就做下一步的重定向了?这个疑问在第二天咨询刘涛时得到了肯定。这样的实现不够严谨,于是,我让他加上对结果的判断。 如果事先有这个判断,那么,这样的问题在审批请求时就会被发现的,何至于经历那么漫长的排查过程呢。
网站nginx负载下因程序错误导致多节点重复处理请求的解决过程的更多相关文章
- nginx负载下站点错误响应会导致其他节点重复响应问题的解决过程
目录 前言 问题来了 问题又来了 问题分析 困惑 转机 后续 前言: 这是我上周工作过程中的一次解决问题的过程.解决的是nginx负载下站点错误响应导致其他节点重复响应. 我在整理这个记叙文时,在给这 ...
- eclipse下修改项目名导致tomcat内发布名不一致的解决方法 .
eclipse下修改项目名导致tomcat内发布名不一致的解决方法 . ------------------------------------------------------- 解决方案: 直接 ...
- 2种方式解决nginx负载下的Web API站点里swagger无法使用
Web API接口站点,引入了swagger来实时生成在线的api文档,也便于api接口的在线测试.swagger:The World's Most Popular Framework for API ...
- nginx 转发 由于php语法错误 导致的 50x
server { listen 8008; root /root/php-test; index index.php index.html index.htm ...
- Dynamics CRM 依赖组件类型为应用程序功能区导致的无法删除实体问题的解决方法
看到有人问到这个问题,这边就简单描述下解决方法,主要是针对第一次碰到这个问题云里雾里的朋友,错误如下 在我们建lookup关联的时候有下图中的这么个设置,对于很多新手默认就是下图这样不会去做改动,因为 ...
- centos7下安装elasticSearch错误总结(单节点模式)
1.首先确定你安装了jdk,版本需要1.8以上 2.上传elasticsearchjar包,只需配置一个文件即可 修改配置文件config/elasticsearch.yml network.h ...
- nginx负载均衡和tomcat热部署简单了解
简单说下几个名词 nginx 它是一个反向代理,实际上就是一台负责转发的代理服务器,貌似充当了真正服务器的功能,但实际上并不是,代理服务器只是充当了转发的作用,并且从真正的服务器那里取得返回的 ...
- 非常诡异的IIS下由配置文件加上svg的mime头导致整个网站的静态文件访问报错误
调试了两天遇到一个非常诡异的问题 一个系统稳定运行了很多年,是用mvc5+WIN2008R2 + .NET 4.5 +IIS环境下运行,非常稳定,最近想迁移到一台新的服务器,为了少麻烦在阿里云上买了 ...
- Linux(7)- Nginx.conf主配置文件、Nginx虚拟主机/访问日志/限制访问IP/错误页面优化、Nginx反向代理、Nginx负载均衡
一.Nginx.conf主配置文件 Nginx主配置文件conf/nginx.conf是一个纯文本类型的文件,整个配置文件是以区块的形式组织的.一般,每个区块以一对大括号{}来表示开始与结束. 核心模 ...
随机推荐
- Eclipse 创建和读取yaml文件
工具和用法: 1. eclipse插件包:org.dadacoalition.yedit_1.0.20.201509041456-RELEASE.jar 用法:将此jar包复制到eclipse-jee ...
- shell 进制转换
包括: i.任意进制转化为十进制((num=base#number)) [base和number必须一致,是同一种进制] ii.十进制转化为任意进制`echo "obase=进制;值&quo ...
- ThinkPHP-5.0.23新的RCE漏洞测试和POC
TP5新RCE漏洞 昨天又是周五,讨厌周五曝漏洞,还得又得加班,算了,还是先验证一波.新的TP5RCE,据说发现者因为上次的RCE,于是又审计了代码,结果发现的.TP5也成了万人轮啊. 测试 环境搭建 ...
- Struts2之命名空间与Action的三种创建方式
看到上面的标题,相信大家已经知道我们接下来要探讨的知识了,一共两点:1.package命名空间设置:2.三种Action的创建方式.下面我们开始本篇的内容: 首先我们聊一聊命名空间的知识,namesp ...
- Unity3D protobuf-net使用方式
1.下载protobuf-net 2.创建Unity工程,创建一个Plugins文件夹,将protobuf-net解压把里面得protobuf-net放到Plugins 3.创建一个名为mcs的文本文 ...
- TypeScript中处理大数字(会丢失后面部分数字)
为啥要弄这玩意? 最近做数值游戏,需要用到很大的数字,在前端大数字会自动变成e的科学计数法. 有啥问题? 问题: 1. 在传递给服务端时,服务端因为不能处理大数字(怎么就处理不了?!),就想要我传字符 ...
- FPGA时序约束的几种方法 (转)
FPGA时序约束的几种方法 对自己的设计的实现方式越了解,对自己的设计的时序要求越了解,对目标器件的资源分布和结构越了解,对EDA工具执行约束的效果越了解,那么对设计的时序约束目标就会越清晰,相应地, ...
- JAVAORM框架之Mybatis (Ibatis) 详解
目录 Mybatis基础概念 Mybatis开放方式演进 Mybatis框架核心要点 关联查询 延迟加载(懒加载) 动态SQL Mybatis缓存 Mybatis逆向工程 PageHelper分页插件 ...
- sftp本地上传和远程下载
1. 打开SecureCRT 连接相应的主机 2. 打开会话后,使用快捷键 alt + p,进入 sftp> 界面 3. 查看 sftp 相应的命令 help 4. 常用命令 (1)查看 ...
- ZOJ 3777 - Problem Arrangement - [状压DP][第11届浙江省赛B题]
题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3777 Time Limit: 2 Seconds Me ...