分布式、服务化的ERP系统架构设计

ERP之痛java

       曾几什么时候,我混迹于电商、珠宝行业4年多,为这两个行业开发过两套大型业务系统(ERP)。做为一个ERP系统,系统主要功能模块无非是订单管理、商品管理、生产采购、仓库管理、物流管理、财务管理等等。做为一个管理系统,你们的通常开发习惯就是使用.Net或Java技术,创建一个单块(单进程)架构的应用,只有一个SQLServer或MySql数据库。而后在项目文件中分一下各个模块,三层结构方式组织代码编写开发。最后测试,交付上线。web

        起初,由于数据量不大,系统性能还不错,各类列表查询,报表查询,Excel数据导出功能等用的都很流畅。可是随着公司业务发展,订单量日积月累,后期各类业务部门的报表查询、数据导出需求不断增多,咱们渐渐就感受系统运行愈来愈慢。因而咱们可能最早想到的解决方案就是,优化系统瓶颈数据库这个大头。咱们可能的一种尝试就是将数据库单独放置到一个服务器,实现数据库和应用程序分离,或者是创建各类数据库表索引,优化程序代码等方法。通过这样一番研究优化,系统某些功能可能性能的确大大提升,可是咱们仍是发现某些功能列表的数据查询导出依然很慢,或者随着数据量继续积累,原来较快的列表导出功能,也越来越变得缓慢了。咱们用尽各类办法,最后也达不到理想的系统性能速度。数据库

        为了提升系统性能,咱们也许会主动学习一些互联网公司的技术经验,什么高并发、高性能、大数据、读写分离等方案,发现本身根本无从下手。咱们会以为由于系统业务特色不同。ERP系统并发量不高,主要是业务复杂,各类业务耦合度远高于那些互联网应用,很差作拆分,数据查询逻辑要远比互联网系统复杂,一个列表页查询出来的数据,每每须要关联四、5张表才能获得结果。有些报表类的甚至更多。加上各类业务操做事务性、数据一致性要求很高,不少时候致使咱们措不及手,没法进一步优化系统。缓存

        曾几什么时候,我也被这样或那样的理由所挫败,认为ERP系统很是特殊,无药可救,但是后来。。。服务器

        我如今已经不这么认为了,彷佛有了新的解决方案O(∩_∩)O哈哈~restful

曙光乍现网络

       在叙述具体方案前,先说下本身的想法。我首先以为咱们作ERP系统前,就得有当今互联网思惟。咱们不要再去作一个大一统的系统了。咱们要分拆一个大系统,作成一个个小系统。而后经过系统接口让这些小系统相互通讯。这样来组成一个大系统,具体来讲就是“分布式”、“服务化”的互联网思惟。让系统在架构设计上就是一个先天支持高度可扩展的系统。架构

       怎么作呢?具体来讲就是要将订单管理、商品管理、生产采购、仓库管理、物流管理、财务管理拆分红一个个子系统。这些子系统能够单独设计开发,对外暴露出各类其余子系统需求的数据接口便可。每一个子系统都有单独的数据库。甚至这些子系统能够交由不一样的团队去开发和维护,使用不一样的技术体系,使用不一样的数据库。而不是再像之前那样,都集成在同一个大而全的系统中,一个大而全的数据库。并发

       对于新架构的系统他有什么优势呢?负载均衡

       首先,也是最重要的就是解决系统的性能问题。以往数据库实例只有一个,无法扩展出多个实例,以便在性能受限的状况下依靠增长数据库实例来达到负载均衡。也许有人会说可使用读写分离方案,可是由于ERP系统的特色,这个方案不少时候不现实。好比说操做库存的时候,你不能从读库里读库存,而后在写库里写入库存。由于主从复制会有时效性,写入的库存并不能立刻写入从库。这样的场景在ERP中也有多处。况且写库不能扩展,只能有一个。而新设计方案是写库是分离的,每一个子系统有本身的数据库。

       其次,就是更新很是方便,各个子系统之后台微服务的方式存在。前台一个单独的web项目,这个web项目调用后台这些子系统的服务接口。这样的设计,在某个业务子系统须要更新的时候,能够单独更新。不用像之前那种单进程架构时,一个小更新须要整个系统重启,致使用户会话也丢失,用户须要新登陆。而如今的这种设计就不会有这个问题。

系统总体设计

      系统物理部署视图

详细设计

   拆分应用层

        拆分应用层,是践行“微服务”架构的理念。将原来大而全的单进程架构按照业务模块拆分红可独立部署的应用程序,以此来达到平滑系统更新、升级、方便负载扩展的目的。具体来讲,技术上可使用restfull风格的接口,也可使用像java中dubbo框架方式来简化开发复杂度。ERPWeb端或其余移动端也是一个单独的应用充当表现层。很是薄,只是简单的接受参数,调取后台其余各类微服务程序的接口获取所需展现的数据。微服务充当业务逻辑层,每一个微服务都是可独立部署上线的程序,对外提供数据访问接口。

        微服务可使用流行的各类RPC框架,好比dubbo,能够支持多种调用协议Http、TCP等,这些框架使得编码比较容易,框架封装底层数据通讯细节,使得客户端执行远程方法如同执行本地方法同样简单。

        dubbo微服务架构,还支持服务治理,负载均衡等功能。这样不只能够提升系统的可用性,还能动态提高系统应用层的性能。好比仓库管理中入库业务很是繁忙,占用很是多的CPU和内存资源,咱们能够另外加一台机器,单独再部署一个仓库管理服务上去。这样使得整个系统,有两个仓库管理服务在同时工做,平衡负载。而这一切都是在服务注册中心,好比Zookeeper下自动完成的。

        微服务结构,天生很好的支持系统更新升级操做。好比财务模块有个新需求须要上线,咱们只须要替换财务模块的服务重启便可。这对已经登陆系统的用户来讲,没有多少影响,不用从新登录系统,其余模块服务使用也不受影响。

    拆分数据层

        数据库瓶颈是ERP系统的永久之伤。大量复杂的数据查询表链接逻辑充斥着整个系统。数据库垂直拆分红功的关键就是如何从新设计系统数据层各个模块相互耦合的问题。能解决这个问题,永久之伤即可以解决了。

咱们先来看一个典型数据层模块耦合问题。需求是展现物料库存,列表字段:物料编号、物料名称、品类、仓库、数量

物料表:

物料ID

名称

品类ID

Z0001

Iphone6红色手机壳

Z

Z0002

iPhone6黑色手机壳

Z

库存表:

物料ID

仓库ID

数量

Z0001

W1

10

Z0002

W1

20

       品类和仓库表省略。。。

        很显然,传统一个数据库中,咱们只须要简单的join操做,便可关联这两张表,外加关联品类和仓库表便可查询出咱们所要的数据。可是如今咱们的架构中,物料表和商品表不在同一个数据库实例中,咱们不能使用join操做了,那咱们该怎么实现需求呢?

        新的架构,只容许咱们经过对方的服务接口来获取数据,不能直接关联对方服务的私有数据库。至少从架构上,服务化角度来讲不能直接访问对方服务的数据库。这种状况下,假设web模块子系统调用仓库子系统来获取数据,则咱们须要在仓库模块中建立一个service方法来装配这些数据。而后返回给web子系统。以下图所示,仓库管理方法首先获取本地库存表的物料编码、和仓库表的仓库名称字段信息,而且分页完后最终准备返回20条数据到Web模块前,将这20条数据中的物料ID做为参数请求商品模块子系统,商品子系统返回这20个物料ID相关的商品信息给到仓库管理模块,而后仓库管理模块从新组装上列表所需的物料名称和品类两个字段数据,实现最终要返回给Web子系统的数据。

        也许你会说,这太麻烦了,这种方法的性能确定没有直接join来的高,解决不了性能问题。咋看起来好像是这么回事,可是仔细考虑看看,在系统并发量低、数据量小、业务不算繁忙的环境下,的确性能还不如传统一个数据中join方式来的快速。但咱们想一想之后吧!咱们如今的架构设计是将一个数据库拆成多个数据库,每一个数据库能够运行在单独的服务器上去,这样之后就能负载数据库的压力了。总体来讲这样才能不会让数据库成为将来业务繁忙时候的性能瓶颈了。想一想都以为让人兴奋不已,是否是?

       这时候有人又会问,那之后系统数据量、业务更大了,连你这个拆分红几个数据库还不够用怎么办呢?个人方法是,能够基于拆分的数据库,单独每一个库能够作读写分离、使用缓存等。甚至能够继续拆分下去,将子系统再次拆分红多个孙子系统。视业务模块繁忙程度而定。

报表系统

        有人又会问,有些列表查询逻辑很是复杂,关联十多张表,若是按上述方法拆分数据,那简直是灾难啊!是的,你说的没有错。这种状况下个人方案是将这种更加复杂的报表级别的数据查询展现需求,能够单独作个报表系统。报表数据库设计采用数据仓库方式。为了更高的读取性能,咱们能够将数据库表设计成不少冗余字段方式也就是反范式设计,以及创建很是多的组合索引。

       这种系统成功的关键就是数据和主ERP系统业务库的同步问题了。通常能够写一个定时同步程序,将ERP主业务系统的数据通过帅选、转化等方式直接生成报表视图所需的最终或中间数据,简化关联查询。报表系统也能够采用微服务架构设计。以下图所示:

        若是报表所需的数据要求实时的,咱们可让ERP系统业务操做时,触发同步数据的请求,实时同步至报表库。

分布式事务

       也许有人又又问了,ERP系统不少操做都要求事务性,你拆分系统后怎么实现事务性,保障数据一致性呢?

这个问题很好,也是我决定写这篇文章前思考的最后一个问题。在微服务架构中,实现夸服务的事务并不容易,至少不像本地应用使用本地数据库事务那样方便,性能高效,数据一致性好。

        也许你听过度布式事务这个概念。有两种情景,一种是一个应用中使用多个数据库,为保障数据一致性,须要使用分布式事务。还有一种状况就是针对咱们这个架构而言的。微服务环境下的分布式事务,具体来讲打个比方。采购入库这个操做设计在仓库管理服务中。入库后,须要更新采购子系统中的采购单中的入库数量。这个过程要求数据一致性,也就是采购单入库成功后写入了库存表中的数量,同时要更新采购单表中的入库数量。咱们不能直接在仓库服务中去访问采购服务中的数据库,必须经过采购服务提供的服务接口才行。若是这样,咱们怎么能保证数据一致性呢?由于颇有可能库存表写入成功,但调取采购服务写入采购单数据时失败了。多是网络问题缘由致使的,这样数据就不一致了。

       在分布式事务技术中,有实现最终一致性这么一说,意思就是只要我能保证两边数据最终实现了一致性就行,不必定要使用事务。这样说来就有方案了。如仓库子系统在处理采购入库时须要增长入库单数据和更新库存数据等多个表。这多个表都在仓库子系统中,咱们可使用一个本地事务来保证仓库子系统中的表数据一致性。而后调用采购子系统更新采购单里的入库数量。为了防止这个过程忽然中断致使调用失败,咱们考虑增长一个消息队列中间件如ActiveMQ。若是接口返回失败咱们就往MQ里写入这个处理请求,等到采购子系统恢复正常后,MQ通知采购子系统处理这个更新操做。因为消息消费掉之后不会再有通知了,采购子系统处理过程当中发生异常致使更新失败,须要将问题写入本地的日志库,以便通知管理员作后续补偿处理。就这样经过各类办法来达到数据的最终一致性便可。虽然听上去有点坑,但这就是解决方案。没有其余更好的了。或者更新失败后从新调用仓库子系统回滚入库单和库存数据,达到最终一致性!如图所示:

       很是有幸能和你们一块儿分享知识和经验,正是因为你们的无私分享,才让咱们得以成长和进步,我最近几年来都不多分享东西,有时候是由于工做很忙没有时间写点东西,有时候也是由于本身懒或是没有什么新东西能够分享给你们的。最后也但愿你们对个人分享不足之处给予批评指正,一块儿进步!

相关文章
相关标签/搜索