中国保险万事通与2018-07-08日正式上线,历经三年精心组织、持续优化,为消费者打造了一个贯穿保险全链条的公益性服务平台。涵盖六类功能、十个模块,贯穿保险需求诊断、产品筛选、产品咨询、保单管理、理赔咨询、投诉咨询、知识教育七大环节。

该系统具有五大特色功能:

高并发下系统功能的优化_第1张图片

    我作为其中的一家保险公司,参与了第一个功能的对接工作:消费者保单统一查询通道。万事通首次在全国范围内为消费者提供集中的线上保单查询通道。该通道通过采用金融行业最为严格的四要素(姓名、×××、手机、人脸识别)认证,在确保用户隐私的情况下,实现保单一键查询。


高并发下系统功能的优化_第2张图片

其实这个需求非常简单,保险公司把每日产生的新的客户的证件号同步给保协,保协根据用户的身份信息判断是那个保险公司的保单,然后调用保险公司的接口查询保单信息。

高并发下系统功能的优化_第3张图片

 

    在接收到需求后我们2个月左右已经开发测试完成,并且和保协进行了联调测试。从测试的结果看单个请求都可以在3秒内返回。我们初始的系统架构如下,用户查询保单后,先经过我们的前置(前置什么事情都不干,只做转发和报文存储,只有前置有外网的访问权限),然后进入我们的后置服务器(进行报文解析,业务规则处理),后置服务器接收到请求后请求核心,返回返回相应的查询结果,在返回给保协。

高并发下系统功能的优化_第4张图片

在我们觉得这个项目就要完成的时候,保协为了程序的稳定性需要进行压力测试,测试的指标如下:

 

标准是

  1. 并发数100,请求数:240000(总请求数)

  2. 允许失败的请求数:最多允许2400

  3. 业务解析正确的请求数:至少237600

  4. 平均响应时长/ms:小于3000(所有的请求响应均应小于3000ms)

并发100的情况下,坚持两小时,则100*60*60*2/3(最大响应时长)=24W

在接收到这个压测标准后,我们使用了LoadRunner进行了压力测试,压力测试的结果如下:

先跑了一个小时:

高并发下系统功能的优化_第5张图片

我们测试了一个小时,平均的响应时间在2.9秒,而且还是内网的。这离标准还差的很远,然后我们进一步分析了到底是网络原因还是服务的原因

 高并发下系统功能的优化_第6张图片


    我们发现并发量小的时候主要消耗在网络上,并发量大时主要消耗在后台服务上。我们首先猜测是不是服务器配置的最大线程数量不够,并发量大的时候线程一直处于等待的状态,导致响应速度比较慢。我们对服务器的参数做了如下调整:

(1).把jboss等待线程上调为550,执行线程上调为150.

 (2). 调整了nginx的 worker_connections

 

调整后,重新测试了下,这一次压测在100并发的情况下跑了两个半小时,平均速度要3.247。还是没有达到我们预期的结果

高并发下系统功能的优化_第7张图片


现有的程序已经无法满足压测需求,为了达到压力测试的标准,我们提出了如下几种解决方案:

1)升级网络带宽,升级服务器硬件(提高CPU和内存)

    压力测试100并发,响应时间3秒推荐配置:

  • 一台nginx转发机,配置:内存3G,硬盘:37G   4核CPU CPU:Intel(R) Xeon(R) CPU E7- 4830  @ 2.13GHz

  •  五台负载机,配置:内存4G,硬盘:37G 4核CPU CPU:Intel(R) Xeon(R) CPU E7- 4830  @ 2.13GHz

  •    10M带宽

 

2)升级DB服务器,数据落到SSD磁盘上

 

3)请把每天需要的数据提前计算好,并单独落到DB中存储,不要通过各种 join 查询,从其他表实时获取数据。

 

     根据项目现有的情况对上面的三种方案的可行性进行了分析。第一,二种解决方案虽然可以减少开发量,但是需要单独为这个需求去申请硬件资源,增加了相应的成本,所以就直接pass掉了。这里我们采用了第三种解决方案,这种解决方案的思路就是异步化。

我们对现有系统的架构做了如下调整:

1.  核心系统T日同步T-1日的保单数据,保单数据按照保协要求的格式封装好。然后通过批处理的方式把这些数据保存到我们中间平台。用户查询保单信息的时候,我们直接返回,不在实时请求我们的核心系统。每日凌晨同步的数据如下:

高并发下系统功能的优化_第8张图片


2.  由于我们原有的系统架构要求保存每个交易的请求,返回报文及保存相应的操作记录,方便后续调查相应的问题。这里面涉及到IO的操作及数据库的插入操作。针对这种情况我们引入了Redis,先把这些存入到Redis中,在服务器空闲的时候比如凌晨从Redis获取出来,然后在保存到数据库中。请求及返回报文通过每日凌晨批出里从Redis中获取然后存入到相应的目录中,减少了实时请求的IO操作

高并发下系统功能的优化_第9张图片

    

交易记录也通过批处理的方式插入到数据库中:

高并发下系统功能的优化_第10张图片


3.  批处理的配置如下:

image020.png

系统通过改造后,数据库的插入及IO相关的操作基本上都通过异步的方式进行处理了,当前只剩下查询的操作。我们对查询操作进一步进行了优化,在表中添加了相应的索引。一切准备就绪后,我们内部先进行了压力测试,压力测试的结果是:1.068秒,效果还是很明显的。


高并发下系统功能的优化_第11张图片

然后我们提交到保协压力测试,测试的结果如下。平均响应时长883 [ms]。这个时间之所以比我们压测的时间还要快,是因为这个直接在生产做的压力测试,生产环境的服务器配置比测试环高

返回信息

======================================================
保单验真二期验真接口压力测试报告

报文长度 :  495 bytes
并发量 :  100
测试总时长:  2134
成功的请求数 :  240000
失败的请求数 :  0
业务解析正确的请求数:  240000
累计传输数据量/byte :  471556000 bytes
累计传输的业务数据量/byte :  437264000 bytes
平均每秒请求数 :  112 [#/sec] [mean]
平均响应时长/ms :  883 [ms] [mean]
传输速率 KB/s :  215 [KB/s] [mean]
请求响应时长分布 (ms):
 1%  395
 2%  491
 3%  518
 4%  540
 5%  562
 6%  585
 7%  607
 8%  626
 9%  640
 10%  651
 11%  659
 12%  666
 13%  672
 14%  677
 15%  683
 16%  688
 17%  692
 18%  697
 19%  702
 20%  708
 21%  713
 22%  718
 23%  724
 24%  731
 25%  737
 26%  744
 27%  751
 28%  758
 29%  765
 30%  771
 31%  778
 32%  784
 33%  790
 34%  796
 35%  801
 36%  807
 37%  812
 38%  816
 39%  820
 40%  824
 41%  828
 42%  832
 43%  836
 44%  839
 45%  843
 46%  846
 47%  850
 48%  853
 49%  857
 50%  860
 51%  864
 52%  867
 53%  871
 54%  875
 55%  879
 56%  884
 57%  889
 58%  893
 59%  899
 60%  904
 61%  910
 62%  916
 63%  922
 64%  928
 65%  934
 66%  940
 67%  946
 68%  952
 69%  958
 70%  963
 71%  969
 72%  974
 73%  979
 74%  984
 75%  989
 76%  994
 77%  998
 78%  1003
 79%  1008
 80%  1014
 81%  1019
 82%  1025
 83%  1031
 84%  1039
 85%  1048
 86%  1058
 87%  1070
 88%  1084
 89%  1099
 90%  1116
 91%  1131
 92%  1148
 93%  1165
 94%  1184
 95%  1212
 96%  1294
 97%  1366
 98%  1544
 99%  1909
======================================================

压测结果:通过;

    从接收到需求到压测通过,历时大半年的时间。由于之前没有这方面的经验,在选用第三种解决方案之前大量测尝试,才能保证该方案可以一次性测试通过。

尝试如下:

 (1).我们系统的外网是电信的,保协是联通。我们让网络的同事给了联通的IP进行过外网的测试,但是没有达压力测试的标准。

 (2). 把我们的程序发布到北京腾讯云服务器,让保协直接同城访问,这样虽然达到了测试的要求,但是不符合我们部署的规则,所以无法采用该方案。该方案说明网络在压力测试中的影响还是蛮大的。

(3).在不改变网络和硬件服务器的基础上,把程序中与数据库及IO的操作先删除,采用固定变量写死的方式,查询的时候直接返回一个常量字符串。该方案测试后符合压力测试的标准,这也是我们为什么会采用第三种方案的原因。

在压力测的过程中我们也遇到过如下问题:

 (1). 程序中使用了全局变量,由于是高并发的,所以有些刚刚加密完成就被下一个请求给替换了,所以返回了明文.

 (2).程序中使用了springmvc,struts这些mvc框架的,压测可能接收不到中保协加密后的报文体。修改为 注解@RequestBody 可以获取request中数据,具体可参考如下两篇文章

    http://blog.csdn.net/tianhongqiang/article/details/51636413

    http://www.it610.com/article/966404.htm

  (3). 压力测试以后,SFTP连接的时候保错,错误信息为:too many open files。我们排查了一下程序,是我们程序有个公共模块的输入流及输出流没有关闭,由于之前没有测试过这么大的并发量,所以之前没有发现。修改程序,重启服务器后恢复正常。

     相应的问题可参考这篇文章:http://www.51testing.com/html/71/410671-819286.html

 

这个需求经历的大半年的时间,从中也学习到很多。最后总结一句话就是:好的架构不是设计出来的而是不断的演进而来的。解决大并发的思路为:异步!异步!异步!重要的事情说三遍。