假设我们需要为超高业务量的保险代理企业设计一个“互联网+”保险平台。假设这家保险代理企业网上保险注册用户规模为2千万,门店及加盟商销售人员2万,年保单量2亿单(中国平安总用户规模达1.67亿,拥有超过79.8万名寿险销售人员和约24.6万名正式雇员。截至2015年6月30日,集团总资产达4.63万亿元,归属母公司股东权益为3,311.90亿元。而目前互联网保险领头羊众安保险,经营以小额贷款为主,由于背靠阿里巴巴,日保单销售量可达1亿,不过别人很难复制众安保险的模式)。因此我们取大型互联网企业和众安保险的折衷来考虑这个保险O2O平台。
分类
功能
质量
约束
电子商务(B2C)
产品展示(搜索、详情展示等)
及时响应、安全性、健壮性、易用性
多种险种,处理方式可能不同
产品购买(提交订单、支付)
用户中心(我的保单、我的理赔等)
代理人管理(加盟商管理)
车险投保(询价、录单、缴费)
及时响应、健壮性、可扩展性
非车险投保(询价、录单、缴费)
保单查询
单证管理
我的账户(我的保单、佣金结算等)
及时响应、安全性、可靠性、易用性
案卷管理
案卷录入
索赔资料收取
案卷交接
案卷跟踪
客户管理
客户信息维护
上传大量文件
客户活动管理
商机管理
我的工作台(消息、活动、商机)
保险公估
车险定损过程跟踪协助
人伤出险协助
法律援助服务
上面的需求是按照用户角度提出的,虽然使用“系统”一词,但这里的系统是一个抽象概念,可能包括软件系统以及人事、制度等在内。上面的需求可以分为三大类,一类是针对终端用户纯线上服务:电子商务网站(B2C);一类是专门针对代理人服务的:代理人系统;剩下的是一些公共服务,有可能电子商务网站和代理人系统都会用到的一些服务。另外保险业务最大的一个问题是多个险种,不同险种处理方式有很大不同,这跟普通电子商务网站区别很大,比如天猫,所有商品都是同样的下单方式,同样售后服务(主要就是快递这块),而保险产品即使是线上B2C网站下单操作,短险、寿险、车险等也是不同的,更何况保险有一大堆售后服务,这些售后服务更加不相同。传统保险公司在处理这方面时,一般会做多个系统,比如寿险系统,车险系统等等。
安装之前提到的业务规模我们分析(假设)出一些比较重要的性能需求:
a)产品方面:继续上线当前没有的保险产品
b)B2C网站日访问量:5000万PV
c)B2C产品购买并发高峰:2000TPS
d)运维系统同时在线:1万(共有2万销售员或代理人)
e)运维系统并发高峰:2000TPS
f)短险订单:每年1.85亿单
g)长险订单:每年500万
h)车险订单:每年1000万
i)案卷信息:每年新增100万单
日访问量5000万和产品并发2000TPS是我们假设的,客户信息和案卷信息是随订单数据量变化而变化,在前面我们虽然假设了总共每年产生2亿个订单,但是根据保险种类,短险(旅游险、伤残险)明显产生了90%的订单量,这一点需要特殊处理。除此之外车险和长险(主要指寿险等)无论是投保还是售后服务都有明显不同,所以也需要特殊处理。
那么我们按照上面的需求,进行系统分析,首先按大的职责将职责相同的划分为一个服务。并且有了上面这个性能需求,所有功能需求都需要增加一项“质量”特性,那就是“高并发”,高并发会影响到所有设计。另外如果要将互联网保险平台质量特性排个序,最重要的是可扩展性、安全性,因为保险的种类多而且处理方式不同,除此之外,高并发和可靠性也会直接影响功能的实现,但并没有可扩展性影响大。深入分析职责后把每一种功能的实现关键技术列出,如下:
需求分类
实现需求
实现子系统及服务
软硬件实现技术
客户端
B2C电子商务网站
B2CWeb客户端
集群部署、高速缓存、分布式缓存、搜索引擎技术、静态化
B2C电子商务网站手机客户端
B2CApp客户端
代理人管理
代理人Web客户端
代理人管理手机客户端
代理人App客户端
案卷处理管理
案卷处理Web客户端
集群部署、分布式缓存
客户管理管理
客户管理Web客户端
保险公估管理
保险公估Web客户端
运维产品管理
产品管理Web客户端
报表及财务统计
报表及财务统计Web客户端
公共服务
运维产品管理、Web前端产品访问
产品服务
电子商务或代理人订单管理
订单服务
电子商务或代理人等涉及财务操作
总账服务
报表服务
业务服务
B2C电子商务网站及手机客户端个人账户
B2C个人账户服务
代理人管理服务
案卷处理管理服务
客户管理服务
保险公估管理服务
短险开放式接入
开放式接入平台服务
工具性服务
保险公司产品对接
产品对接服务
集群部署、消息队列
在线支付
第三方支付服务
集群部署
短信邮件通知
通知服务
性能监控
日志采集服务
文件服务器
文件服务
分布式事务管理
分布式事务管理服务
定时任务管理
定时任务服务
各个子系统及模块的关系如下图。
其中订单服务、产品服务、财务服务、工具服务为基础服务,其它各个业务模块的服务会调用这些基础服务。各个业务模块的服务都是根据业务领域进行划分的,同一业务领域下实现技术不同会被划分为两个服务,比如产品展示和订单原本属于同一个大的领域,但其因为实现技术和质量要求不同需要划分为两个服务。因为短险接入量大,而且大部分是跟第三方合作接入,因此设计短险接入公共接口服务平台处理大量短险订单。
对于大型的高并发系统来讲,最重要的当属数据的架构。我们在前面也提到过,web系统业务处理模块本身就可以集群部署,当用户出现高并发时最先遇到的瓶颈就是数据库访问的瓶颈。这也是我们说数据架构最为重要的原因。其实web系统是典型的“计算机信息系统”,也就是说一切以数据(信息)为基础,所有的功能都是围绕着数据来的。这也是我们在这里说数据是web系统最重要的,很多公司在做少用户量web系统时直接设计好数据库就可以开发了。
按照上面划分的业务领域我们设计多个数据库,技术选项包括“是否读写分离”、“是否水平切分”及“路由键”。其中路由键是指在进行水平切分后,我们使用那个“标识”去查询数据库,一般来说会使用该业务领域聚合根对象的主键作为路由键。
数据库
是否读写分离
是否水平切分
水平切分路由键
产品数据库
是
订单数据库
客户ID
公共数据库(元数据、公共数据)
客户管理数据库
案卷管理数据库
代理人服务数据库
代理人ID
B2C电子商务个人账户数据库
保险公估数据库
工具数据库(短信、支付、文件)
报表数据库
日志采集服务数据库
日志ID
另外在数据架构中我们也可以看到一个规律,就是使用了水平分库的存储结构就不能再使用读写分离,原因是防止存储单元过度泛滥,因为使用了水平分库之后,本身也要为数据库建立多个备份库,这个时候如果再用读写分离需要建立一套只读库,数据库的数量将增加一倍。使用了分库后我们可以把热点数据存储在分布式缓存中以起到读写分离类似的作用。
另外缓存也是存储技术的一种,而且非常重要。从日常生活中我们也可以看到这一点。比如你要去购买一台电脑,你会发现二级缓存和内存大的价格高出很多,如果你的显卡显存巨大,那将是顶级配置,发烧级配置。手机也是一样,内存大的手机速度明显快,价格也高,如果内存和持久化存储都高,那也是顶级配置。对于web系统也是一样,有些大型web系统只要缓存处理的好,数据库不需要分库就可以承载亿级的用户,比如维基百科、新浪微博等。也因此,不管是电子商务网站还是互联网+大型应用,都会在它们的架构中看到缓存大量使用的情况。
从整个web系统的架构来看,缓存在两个层面大量使用,分别是展示层和逻辑层,展示层通常使用高速的页面缓存,逻辑层通常使用高并发的分布式缓存。当然有些分布式缓存工具既可以在逻辑层使用也可以在显示层使用。
系统
是否使用CDN
是否使用高速缓存服务器(varnish)
是否使用分布式缓存(redis)
报表及财务统计Web端
短险公共平台服务
图中的服务组件就是指各个业务服务,这些服务可以看成是组件的概念。通常前端界面一个界面中需要的数据通常不仅仅来自于同一个服务,即使是来自于同一个服务,那也来自于很多不同的接口,servlet或api网关作为控制层,所起到的作用就是组合接口、组合数据,并处理接口间的事务性。另外服务组件也是分层的,图中并没有展现,一般可以分为3层,从低到高依次是工具性服务组件、基础业务层服务组件、业务层服务组件。前端界面的请求按照从高到底向下传递和处理请求。
按照职责、通用性、技术特性综合考虑和计量,逻辑架构设计如下图:
十几个子系统分别分布在服务层、控制层、表现层(典型的三层架构)。实体层和接口访问层虽然属于“层”,但它们并不单独发布,而是使用Jar包类库的方式提供给其它服务调用,是逻辑上的层。服务组件的构成大都是按照业务领域划分的,只有一个除外,就是“B2C网站”,B2C网站由于是面向终端用户的高并发电子商务网站,为了处理高并发,我们将其拆分为两个业务领域(也可以拆分成多个业务领域,看实际并发量),分别是用户账户和产品。用户浏览产品并购买,这是电子商务网站最基本的两个领域。其中产品浏览等功能由更底层的产品服务提供,用户账户功能由会员服务提供。还有一些功能,比如推荐商品可能是由其它服务提供,这些可以在控制层直接组合这些服务。
服务框架采用典型的“服务注册表”模式,注册表使用redis。服务组件在启动时将自己注册进服务注册表,web服务器或api网关在访问服务时查询服务注册表得到服务的uri,然后调用某服务接口。
淘宝的dubbo使用的是zookeeper作为服务注册表,之所以使用zookeeper,主要是使用它的负载均衡的功能。笔者认为restful接口没有必要使用zookeeper做负载均衡,可以使用nginx(负载均衡服务器都可以),所以没必要选用动态的zookeeper作为注册表,而是使用“redis+心跳监测”机制(redis也可以换为LDAP等)来完成服务注册和监控失效服务的功能。这个方案至少比dubbo简单几个数量级,简单就是硬道理。
在注册服务时只需要注册nginx服务器的IP以及服务描述信息即可。反观dubbo还要注册接口进注册表,笔者认为这个没必要,因为调用一个服务接口的充分必要条件就是知道服务器的IP即可。至于调用什么服务接口,肯定在代码里已经写死,目标服务器必定存在这个服务接口。将服务接口注册进服务注册表如果是为了监控审计服务的使用情况,那这个功能使用访问日志来实现能做的更好。
总体的分布式拓补结构如下图:
对于服务集群来讲,看上去像是一个大哥(nginx)带有众多小弟的结构。访问某服务群组只需要访问其nginx服务器即可,这里nginx均采用高可用方案,防止单台nginx出现问题。至于高层服务调用底层服务也是直接访问其服务器组的nginx。这个执行的流程其实和日常生活中的概念很像,如公司内部的执行流也是如此:老板分配任务给部门经理,部门经理分配任务给主管,主管分配任务给具体的个人(某单台服务器)。领导们起到的作用也是负载均衡和监控,负载均衡就是把任务可以分配给多个人同时执行(并且某个执行失败自动切换),监控就不必说了就是监控任务完成情况。在我们的方案里,会专门有个服务去监控所有服务器的执行情况,很简单的服务就可以做到。
如果研究一下EJB就会发现,EJB和微服务的架构基本相同,甚至所有面向服务(SCA、SOA等等)的架构都相差不大。很多人反对EJB,并不是EJB不够强大,而是它不够简单,它给项目带来的复杂性甚至超过了项目本身(这也是笔者不建议使用dubbo框架的原因)。曾有人说过:你要么把事情做的尽可能简单,让人挑不出毛病;要么把事情做的尽可能复杂,让人找不出毛病,EJB就是后者。EJB分布式事务机制实现的很好,可惜的是这种“一刀切”的事务机制,大大降低了web系统的性能,所以几乎所有面向互联网的应用都极少使用分布式事务,也就不会采用EJB。互联网应用本身事务性操作并不多,一些新闻、博客之类的网站甚至都不需要事务。另外一个方面,即使出现一个或两个分布式事务应用场景,也可以通过其它手段解决,比如事件机制等等。
1)将分布式事务性操作封装在一个服务中,这个服务使用XA或链式分布式事务管理。
2)可以使用事件机制协调事务管理(具体做法就是事务失败后发失败事件到可持久化的消息队列,然后需回滚操作的接口监控此事件并执行回滚)。