《京东话费充值系统架构演进实践》阅读笔记

一、应用层面

引入缓存nginx

  在应用层和数据库层增长缓存层,热点数据放入缓存。如系统中经常使用的开关、白名单等数据,读取频率高写入频率低,针对这部分数据就能够在JimDB(Redis)中存储一份,JimDB (Redis)会把高频数据存储在内存中,读写性能很高。数据写入缓存时设置一个有效期,更新数据库成功后,异步更新缓存数据。若是实时性要求不高,也能够等缓存失效后,主动更新缓存。引入缓存层,下降数据库压力,提高系统响应速度。数据库

编写并发处理程序后端

  多任务并发处理,充分利用CPU资源。无依赖关系的多条任务能够并行处理,提升系统处理能力。如结算任务,每笔订单之间的结算操做没有依赖关系,能够同时执行多条结算任务缓存

系统结构优化网络

  核心生成流程异步处理,接收用户订单和给用户充值两个流程异步化处理,提升系统处理能力。对用户来讲,用户付款成功,等待充值便可。系统可经过worker触发充值动做,设置合理的重试次数,间隔必定的时间进行重试。在到达最终状态前,给用户显示中间状态。架构

  拆解大应用,使其微服务化。如话费充值应用,核心功能有PC端接单、移动端接单、MQ消息处理端、后台管理端、worker端等五大块。拆解以前,话费应用只有三个系统,系统之间共用service\manager\dao模块代码,经过Maven构建管理。实现一个后台的小需求,都须要考虑PC端、移动端、后台管理端代码是否会受到影响,是否须要所有回归测试。更恐怖的是上线,充值应用有将近100个实例,修改一行代码都须要所有上线,即便没有受到影响的系统也要发布新版本由于要保证线上和代码库中的代码一致。并发

  从功能上进行微服务化之后,应用拆解为PC端、Server端、MQ消息处理端、后台管理端、worker端等五个应用,应用之间功能独立,依赖公司的RPC框架(JSF)和消息框架(JMQ)进行通讯。单个应用的内聚性更高,应用之间的耦合度更低。再实现一个后台的需求时,开发、测试、部署都只须要关注后台管理端系统便可,无需再关注其余四个系统。框架

  系统微服务化设计,关键点是如何寻找限界。除了能够从功能上进行切分还能够根据关注点上进行拆解为生产端和支撑端,业务场景不一样,寻找限界的方法也不相同,关键是微服务后单个应用的内聚性更高,应用之间的耦合度更低。使大系统微服务化的方案有不少种,重点是制定好目标,逐步向目标靠拢。在服务粒度方面,如话费充值应用的MQ消息处理端,在功能上保持职责单一,只负责接收、解析MQ消息内容,具体业务逻辑处理交由相应的Server端处理。在技术选择上,公司有成熟的技术框架,如RPC框架JSF,消息框架JMQ等,这些框架都有对应的服务治理和监控等相关服务和团队。异步

  不要为了微服务化而实施对大系统微服务,要确保微服务化以后,系统运行更稳定,应对变化更快速,开发更敏捷。微服务

读写分离

  实时性要求不高的数据读取从库,下降主库压力。如对帐功能,读取的是前一天的订单数据,这些数据就不必从主库中读取。关于技术实现上,Spring框架自己有提供,实现其抽象类AbstractRoutingDataSource便可。

变化频率低的页面静态化

  充值应用中有不少卡片页,如QQ页卡等,页面上的数据变化的只有广告位。这种类型的页面就能够静态化,定时更新页面,推送到存储介质上,nginx配置location,直接读取页面,下降后端服务的压力。

 

二、数据库层面

  当业务量发展到必定程度后,数据库就会成为系统的瓶颈。话费充值应用包含企业订单业务和普通用户订单业务,正是因为其业务的特殊性,采用了垂直+水平分库方案。根据业务类型进行垂直切分,不一样业务类型订单数据独立存储,同一种业务类型在水平上由多个库保存。垂直+水平的分库方案可以最大限度的下降不一样业务类型订单数据之间的相互影响,提升数据读写并发量。普通用户订单业务,根据帐户PIN进行hash打散能够均匀的分布到每一个库中,sharding规则就是hash(pin)值,同时这个hash(pin)值还作为本地订单号的前缀,这样就能够经过帐户PIN和本地订单号两个维度中任一维度均可以路由到数据库。建立ERP订单成功后,把本地订单号和ERP订单的映射关系保存到JmiDB中,对于只有ERP订单号的业务流程,能够经过映射关系找到本地订单号,有了本地订单号也就能够路由到数据库了。而企业订单业务,每一个企业帐户的订单量不均,差异能达到三个数量级,若是再根据帐户PIN进行hash打散分布到每一个书库中的订单就会不均匀,不能使用这种sharding规则。根据本地订单号进行hash,而后再做为本地订单号的前缀。建立ERP订单成功后,一样须要保存本地订单号和ERP订单号的映射关系到JmiDB中,以保证在后续的业务流程中,可以根据ERP订单号路由到数据库。

  拆分完成后,有的业务场景须要聚合查询数据,如订单管理。若是没有聚合数据,就须要在应用中,开发人员自行考虑聚合。通用的聚合方案是从每一个库中查询一页数据,在内存中根据条件排序,返回一页数据,若是须要翻页的话,逻辑更为复杂。话费充值应用采用了第三方存储,把每一个分库中的订单数据聚合到ElasticSearch中,查询聚合数据的场景读取ElasticSearch。模拟MySQL slave的交互协议,解析数据库的增量BinLog,同步分库的数据到ElasticSearch中。因为数据库主从同步存在延迟的风险,须要准备一个降级方案。在话费充值应用中,数据库写订单成功后,插入一条任务记录,经过任务模型当即同步数据到ElasticSearch中。保证数据同步的实时性。

 

三、应用部署

  计算机的CPU、线程、IO等资源都是宝贵且有上限的,当某一个资源耗尽时,那这台计算机上全部的服务都将中止服务。例如某一个服务依赖的第三方服务性能低,响应缓慢,这时若是客户端的继续请求,会致使该服务持续建立线程等资源,最终致使服务宕机。此时,计算机资源的隔离显得尤其重要。

  在JVM内部隔离分为信号量隔离和线程池隔离,Netflix Hystrix插件提供了完美的支持。JD-Peer(多机房公网出口路由系统)中使用了Hystrix对每个商家进行了隔离。话费充值应用对接了几十个商家,经过JD-Peer系统跟商家进行交互。因为某些网络缘由致使其中一个商家A响应慢,持续的调用,全部资源都会被这一个商家占用,致使其余商家服务也不可用,最终宕掉。

独立部署,物理隔离。每一个应用分配独立容器,从硬件层面进行资源隔离。

多机房部署,从入口处分摊流量,提升系统总体的吞吐量。

版本发布

  灰度发布,平滑过渡,异常状况下的版本回滚,要确保回滚前的数据在老版本中可用。如京东话费充值系统在发布数据库架构升级版本时,设置了数据流向开关,并对订单打标,同时缓存标识位。 开关打开时,数据进入新的数据库,开关关闭,数据进入老的数据库。线上验证阶段,一旦发现问题,可当即关闭开关。确保系统版本发布,对用户无感知。

  在保证系统服务正常可用的状况下,进行上述一系列的升级,犹如给空中的战斗机更换引擎,稍不留神就会坠机,因此除了充分的理论储备,还须要综合业务场景,从自身业务场景出发,合理设置引擎更换方案。

  通过上面一系列的改造升级,话费充值应用的吞吐量、运行稳定性都达到了最优的状态,历经数次的618和双11冲击,各项运行指标保持稳定,面对流量洪峰,岿然不动。

相关文章
相关标签/搜索