原文:http://www.p2pquan.com/article-740-1.html
一、简介
随着互联网金融的持续火热,越来越多的银行纷纷发布了各自的互联网金融产品。但是互联网产品“高并发、大数据量”的特点却对于银行传统的核心系统架构带来了新的挑战。
1、互联网的核心技术特征
当前互联网的核心技术特征主要可以概括为:分布式,易扩展,大量低端设备,底层开源软件。分布式结构可以通过平行扩展来支撑互联网上蜂拥而至的访问客户。同时,基于客户行为分析的大数据平台也需要分布式系统来完成,其中最典型的就是Hadoop集群。
2、传统银行系统的技术特征
传统银行系统的技术特征主要可以概括为:专用设备、底层闭源软件,成本昂贵,系统稳定。由于金融业对核心系统的稳定性、可靠性和安全性要求极高,目前大部分银行都是采用IBM的整体解决方案,并且几乎没有可替代性,IBM仍然保持着市场垄断地位。
3、挑战主要来自客户体验的升级
随着互联网客户规模的增长,客户行为和系统响应次数也呈现爆发式增长。
传统电子银行提供的是被动服务,产品只提供相应的功能,需要客户自己去查询和操作;而互联网产品更多的是提供主动服务,形象直观的展现客户相关数据信息。这就要求单个客户发起的或者产品自动发起的一次请求响应需要同时与核心系统的多个功能接口进行交互,再考虑互联网客户规模效应,其对系统的压力是非常大的,由此也会带来巨大的风险。
4、产品设计应具备合理性
大多数情况下,核心系统的架构是无法改变的。这就需要我们从产品设计和应用系统上尽量规避高并发交互响应的发生,但这绝不是以牺牲客户体验为代价来完成的。
如果核心系统因为高并发量而出现瓶颈时,首先应当保证应用系统对产品的支撑,至少产品不能出现崩溃和给客户带来超出预期的失落感;其次,产品的异常提示内容也要足够友好,可以减少客户的负面情绪。最后,对发起请求的总量进行控制,可以采用临时输入验证码等方式来降低客户请求的频率。
下面以“宜人贷系统架构”为例,宜人贷架构师孙军着重地分享介绍了宜人贷系统发展过程中遇到的实际问题和解决的办法,并重点介绍宜人贷出借系统和借款系统的高并发解决方案。
二、宜人贷系统版本的迭代
1.0 版本——简单的烦恼
迭代之前宜人贷的系统,其实就是一个前台,一个后台,一个DB,前台采用的是多级部署的方式。
软件也是跟最传统的软件一样分三层,第一层是Controller,第二个是Service,第三层是DB。显然这个系统并不适合互联网,有一些难以避免的问题。首先当用户过万,在线用户上线的时候,这样的部署方式会产生一些瓶颈,包括服务器和数据。第二个就是团队变大,所有开发人员集中针对同一个系统,冲突严重。
1.5版本——“吃大补”试试!
为了上面的问题他们做了一些修改,孙军把它定义成“吃大补”。吃大补通常有一个很明显的特点,就是立马见效,但是副作用也很大。
首先,他们在宜人贷的页面层更加关注性能,比如说浏览器,压缩传输,页面都经过了Yslow的优化,浏览层增加了CDN,做了静态化甚至反向代理,这样可以抵挡80%的流量。
页面层中间加了一个集群,这个集群基本上可以挡掉80%流量,最后系统把它业务垂直拆分成多个系统,比如说APP的后台,Web、信审、抓取、活动、报表等等。数据库也有一些变化,开始只是一台主机,一台数据库,现在变成了主从,一主多从。
用户可以撑到过户百万,除此之外他们的制约在数据库,两套系统分别挡掉了4/5的流量,整体挡掉后其流量变成了以前的1/25,其实就是数据库并发能力来说扩大了25倍。但是他们的业务发展远远不止25倍,所以数据库依然是一个很大的瓶颈。
再者、第二个问题就是团队划分,其实每个团队都做自己的系统,但是宜人贷仍然使用同一个数据库,这个时候比如说设计和修改数据库的时候,都非常麻烦。每次都要问一下其他团队,我这么改行不行,对你有什么影响等等。
另外、第三个问题也非常棘手,大量使用了缓存,数据的时效性和一致性的问题越来越严重。我记得4月份的时候上了一个理财产品,它的库存多退了一倍,10点钟上线非常准时,但是发现问题之后把它下线,下了半天之后下不了,原因就是有缓存,非常难下。
2.0版本——“开小灶”精细化
为了解决1.5T,孙军他们需要做精细化的优化,他把它定义成开小灶。
首先,合理划分数据归属、优化查询效率、缩短数据库事物时间;其次,分系统,每个系统用固定的表。从每天都在做的事中,让运维找出线上最慢的有哪一些,从而对它们做优化。第三,做去事物,或者尽可能的提升缩短事物的时间。
然后、开始关注代码质量,提高执行效率,并且开始关注并发问题;用户达到这个量的时候,就会有有用户帮他们测试。打一个比方同一个用户用同一个帐户登录了两个客户端,他同时点进去,这个时候如果程序处理的不好,很有可能让他提两次。 最后,要区分强一致与最终一致,合理使用缓存与读写分离来解决这些问题。
2.0性能问题解决很多了,还会带来新的问题——系统越来越多,系统间依赖关系变得复杂;这个时候很容易出现A调B,B调C的循环调用。第二个是系统间互相调用增多,上游系统压垮下游系统;第三个,也是非常头疼的问题,系统很多,查找线上问题变得越来越困难;试想一下当系统部署到很多机器上,想找一个线上的问题,通过查日志的形式非常难查。所以在这个基础上他们做了几件事,一是关于限流,限流通常基于两点:最大活动线程数(高消耗任务),每秒运行次数(低消耗任务);
活动最大线程数适合于高消耗的任务,然后每秒运行次数适合于低消耗的任务(这里应该是针对请求接口次数做的限制吧)。 二是一个建议,他建议尽可能统一内部系统间返回值,返回值中一定要记录返回状态(业务正常、业务异常、程序异常)和错误说明;第三个,可配合RPC框架完成限流工作。
再说一下关于查找问题,宜人贷日志系统部署框架,最左侧的是他们的业务系统,在业务系统上把日志搜集到卡夫卡队列,最终采用Kibana和他们自己研发的系统去查看日志,日志到了一个集中点不容易找,而这样就更好找了。
关于软件方面,宜人贷统一使用slf4j+logback输出日志,然后日期系统做到日志串联,所有服务端和客户端之间都隐藏的一些参数,这些参数会随着调用链一步一步往下传,通过AOP来实现,日志串联需要传递哪一些参数,或者日志中到底要打哪一些参数呢?第一个是时间,这个时间应该到毫秒级,第二个是流水号,流水号就是每次请求升华到唯一的一个P值。然后是设备号:时间(到毫秒)、流水号(uuid,每次请求生成唯一值)、用户session、设备号、调用者时间(APP使用手机本地时间)、本机IP、客户端IP、用户真实IP、跨越系统次数——有了这些找问题就非常容易。
做到2.0之后,宜人贷的网站基本上到了中大型的网站,短时间内不会有太多的性能问题了,但是他们肯定还得继续往下走。
3.0版本——拆分做服务化
3.0总结下来就是要做服务化,通俗一点说就是拆分,包括垂直拆分,充值拆分基础上系统之上的水平,那么服务化要怎么做呢。
首先,做业务拆分的时候,可以按照基础服务和业务服务先做一个大的服务的拆分,然后基础服务又包括无业务型的基础服务和有业务型的基础服务,这些系统非常明显的跟其他的系统没有太大的关系。业务型基础服务的特点就是跟业务之间的关系很小,就是这些系统跟业务系统之间的关联关系只是主键和外键的关联关系。
宜人贷可以天然地拆卸分成两大系统,一个是贷款业务,一个是理财业务,贷款业务可以拆卸成后台、Web、合作渠道,这个系统之下会有一个基础服务,就是提供一些基础服务和接口的一个系统。
基础服务拆成了两部分,一个是基础服务的进件,一个是服务售后。然后拆分过程中他们又发现一个问题,理财和贷款有两个业务怎么拆都拆不开,就是撮合业务和债券关系,这种拆不开的可以单独再提升一个功能服务来提供服务就可以了。
拆分的系统看起来好像很容易,拆分的办法孙军总结了如下几个:
第一,适当冗余,冗余确保数据库依然可以关联查询;大部分时间并不是做一个全新的系统,而是在原来系统之上做修改,这个时候可以做一些冗余,保证他们可以不修改。
第二,数据复制,但必须保证数据归属系统有修改和发起复制的权限;这个比较适合于刚才说的全局配置,比如说宜人贷的所有公司都会有这么一个集成表,记录了全国的区线,这些在每个系统中都会用,不一定每个系统都以接口的形式调用他。可以在每个系统里面都冗余。
第三,就是如何验证数据库——并不一定非把它拆分成两个验证它,可以一个数据库上建两个帐号,这两个帐号分别的权限指向拆分之后的表,可以通过帐号直接验证拆分效果。
第四,提前规划服务,拆卸之前确定一下区分读多、写多服务,区分快请求、慢请求服务,不同服务需要分开部署。
最后,同一数据不能由超过一个以上的系统控制,同一系统不能由超过一个以上的负责人负责。
4.0版本——云的展望
做到以上几点, 3.0版本已经做的差不多了,但是后面宜人贷依然还有很多要做的,4.0版本是不是要做云平台,异地部署的方案,表很大的时候是不是要做垂直拆分,去IOE或者使用Docker快速部署等等这些,这些其实都是我们做4.0或者5.0将来要考虑的事情。
三、宜人贷理财系统的优化
合理预估流量——强一致与最终一致
图中这三个界面分别为首页、列页,详情页。
首先要合理预估流量,区分出什么是强一致性的流量,什么是非强制性的引流。
评估方法一:平日PV*(24/热度时间);
评估方法二:热度时间内在线用户数*平均每人操作次数/热度时间。以宜人贷理财端为例,他们在高时期有2万人,然后平均做20次操作,在2分钟左右基本上就把所有的债券抢光了,翻番出来大概是3000多次每秒。
然后要区分什么是强一致,什么是最终一致,这两个流量分别是多少。强一致这个数据必须是最准确的数据,这个数字不能用读写分流的形式保留,必须是正确的数据。最终的数据就是时效性没有那么高,只要最后的结果是一致的就可以。
20次操作包含:注册、注册验证码、登陆、解锁手势密码、首页、浏览产品列表等等这些操作,这里面其中有一些比如说产品余额、生成订单、支付短信、付款,这些都是非常强一致的要求,这个大概占每个操作数的占1/7左右,翻算出来是约500次/S。
针对最终一致的方案非常简单,增加机器就可以解决,时效性较高的可以直接使用数据库的读写分离,增加应用服务器,处理更多的并发; 实时性较高使用读写分离方案、缩短catched时间;实时性较低的可以使用较长时间catched。
强一致性的流量处理方案,总的来说就是加速,可以使用数据库的锁,也可以使用ZK,或者直接使用排队的机制,这里面数据库的锁,基本上变化在2000次每秒以下。在这个情况之下,完全可以使数据库的锁来防止并发,第一个方法就是有事务的防止并发的方法。
然后再更新共享资源,最后再查询一次共享资源,然后判断一下结果。假如说这个结果是成立的,就直接运行,假如说这个结果是不成立的,那么再做。第二个就是无事物下的方法,加一个条件去判断他们的资源成不成立,当没有更新成功的时候,就返回。
如果流量依然承受不住该怎么办?
做到这些其实已经能够承受非常大的流量,但是业务可能继续发展,还承受不住怎么办呢?
首先的一个原则就是没有任何分布式的操作,最好的方法就是单点、排队处理。
第二,单点并发过大,使用合适的方式拆分锁的粒度;比如说产品12月期的,增加一个9月期的,6月期的等等。比如说增加这个还不够,能不能按省卖他们的产品,每个省有一个自己的库存,粒度会很多。
第三,增加降级需求,不影响用户正常使用情况下可以适当降低服务质量。适当修改需求、适当增加用户等待结果时间;如果让用户等2秒,是不是能撑到4400每两秒,答案是肯定的,可以多让用户等一段时间,这个交互上让用户有更好的体验。 最后适当调整运营策略,分散用户集中活跃时间。