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

news2024/9/22 8:32:55

ERP之痛

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

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

        为了提高系统性能,我们也许会主动学习一些互联网公司的技术经验,什么高并发、高性能、大数据、读写分离等方案,发现自己根本无从下手。我们会觉得因为系统业务特点不一样。ERP系统并发量不高,主要是业务复杂,各种业务耦合度远高于那些互联网应用,不好做拆分,数据查询逻辑要远比互联网系统复杂,一个列表页查询出来的数据,往往需要关联4、5张表才能得到结果。有些报表类的甚至更多。加上各种业务操作事务性、数据一致性要求很高,很多时候导致我们措不及手,无法进一步优化系统。

        曾几何时,我也被这样或那样的理由所挫败,认为ERP系统非常特殊,无药可救,可是后来。。。

        我现在已经不这么认为了,似乎有了新的解决方案O(∩_∩)O哈哈~

曙光乍现

       在叙述具体方案前,先说下自己的想法。我首先觉得我们做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通知采购子系统处理这个更新操作。由于消息消费掉以后不会再有通知了,采购子系统处理过程中发生异常导致更新失败,需要将问题写入本地的日志库,以便通知管理员做后续补偿处理。就这样通过各种办法来达到数据的最终一致性即可。虽然听上去有点坑,但这就是解决方案。没有其他更好的了。或者更新失败后重新调用仓库子系统回滚入库单和库存数据,达到最终一致性!如图所示:

非常有幸能和大家一起分享知识和经验,正是由于大家的无私分享,才让我们得以成长和进步,我最近几年来都很少分享东西,有时候是因为工作很忙没有时间写点东西,有时候也是因为自己懒或是没有什么新东西可以分享给大家的。最后也希望大家对我的分享不足之处给予批评指正,一起进步!

 

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/697684.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【数据结构】单链表(超全)

目录 一、什么是链表?1.1 定义1.2 链表的分类 二、无头单向非循环链表2.1 结构2.2 如何遍历链表数据2.3 尾插2.4 创建新节点2.5 头插2.6 尾删2.7 头删2.8 单链表查找2.9 在pos位置之前插入2.10 删除pos位置数据2.11 在pos位置的后面插入2.12 删除pos位置后面的数据 …

Markdown学习

我展示的是一级标题 我展示的是二级标题 斜体文本 斜体文本 粗体文本 粗体文本 粗斜体文本 粗斜体文本 第一项第二项第三项 第一项第二项第三项 第一项第二项第三项 第一项: 第一项嵌套的第一个元素第一项嵌套的第二个元素 第二项: 第二项嵌套的第…

mysql 常见锁类型

表锁 & 行锁 在 MySQL 中锁的种类有很多,但是最基本的还是表锁和行锁:表锁指的是对一整张表加锁,一般是 DDL 处理时使用,也可以自己在 SQL 中指定;而行锁指的是锁定某一行数据或某几行,或行和行之间的…

ROS2 lifecycle node生命周期节点 官方例程文档翻译

0 官方例程文档 ros2 humble lifecycle demo github repo 1 引言 ROS2引入了受管理节点的概念,受管理的节点又被称为是生命周期节点(LifecycleNode)。在接下来的教程中,我们会解释这些节点的作用,是什么使得它们和普…

【Linux 驱动篇(三)】新字符设备驱动

文章目录 一、新字符设备驱动原理1. 分配和释放设备号2. 新的字符设备注册方法2.1 字符设备结构2.2 cdev_init 函数2.3 cdev_add 函数2.4 cdev_del 函数 二、自动创建设备节点1. mdev 机制2. 创建和删除类3. 创建设备4. 参考示例5. 设置文件私有数据 三、LED 灯驱动程序编写四、…

Q-Learning 原理干货讲解

强化学习 本文是强化学习和Q-Learning算法的概念及原理 项目实战案例可查看下一篇文章:Q-Learning 单路径吃宝箱问题–棋盘格吃宝箱问题–拓扑节点较优路径问题 一. 强化学习概述 1.1 什么是强化学习 基于环境的反馈而行动,通过不断与环境的交互、试错&…

AR急诊急救大平台远程专家会诊方案

随着AR技术的完善,在医院急救中,需要及时把各方面专家汇在一起会诊,在AR这方面有数据采集清晰,资料呈现方便便捷,针对客户需要,我们对业务需求分析,比较市面多种产品,把会议&#xf…

Spring Boot 中的 @Cacheable 注解

Spring Boot 中的 Cacheable 注解 在 Spring Boot 中,缓存是一个非常重要的话题。当我们需要频繁读取一些数据时,为了提高性能,可以将这些数据缓存起来,避免每次都从数据库中读取。为了实现缓存,Spring Boot 提供了一…

Apikit 自学日记:发起文档测试-TCP/UDP

进入某个TCP/UDP协议的API文档详情页,点击文档上方 测试 标签,即可进入 API 测试页,系统会根据API文档的定义的求头部、Query参数、请求体自动生成测试界面并且填充测试数据。 填写/修改请求参数 1.1设置请求参数 与发起HTTP协议测试类似&am…

Django Vue corsheaders跨域问题

跨域问题 记录一下在我自己的django-vue项目里面出现的跨域问题 我的项目之前一直是在本地跑的,因为需要上线测试,所以我就运行在同一个vlan里面 ip段:192.168.1.0/24 突然发现存在跨域问题,我django的接口访问被拦截了。 检查…

Web服务器群集:Nginx+Tomcat实现负载均衡与动静分离集群

目录 一、理论 1.多实例 2.Nginx负载均衡 3.Nginx动静分离 4.配置NginxTomcat负载均衡 5.配置NginxTomcat动静分离集群 6.Nginx 四层代理配置 二、实验 1.配置NginxTomcat负载均衡 2.、配置NginxTomcat动静分离集群 三、问题 1.服务器群集与集群的区别 四、总结 一…

十分钟实现 Android Camera2 视频录制

1. 前言 因为工作中要使用Android Camera2 API,但因为Camera2比较复杂,网上资料也比较乱,有一定入门门槛,所以花了几天时间系统研究了下,并在CSDN上记录了下,希望能帮助到更多的小伙伴。 上两篇文章们使用…

Python主动抛出Warning的艺术:一种提醒用户的优雅方式

简介:Python提供了一个内置的warnings模块,使得开发者可以在代码中主动地发出警告。这对于告知用户某些问题或者对某些即将废弃的特性进行提示尤其有用。本文将重点介绍如何在Python代码中主动抛出警告,并探讨其使用场景和优势。 历史攻略&a…

设计模式3:单例模式:volatile关键字能不能解决多线程计数问题?

先说结论不能: 代码实测下: public class Counter {public volatile static int count 0;public static void inc() {//这里延迟1毫秒,使得结果明显try {Thread.sleep(1);} catch (InterruptedException e) {}count;}public static void ma…

Win10安装CUDA

一、安装Nvidia显卡驱动 安装Nvidia显卡驱动前可以先检查Nvidia显卡驱动是否已安装。搜索 Nvidia控制面板 或 Nvidia Control Panel可以看到当前已经安装的显卡驱动及版本。 如需安装显卡驱动,在官方驱动下载网站找到自己的显卡型号对应的驱动下载并安装:官方驱动…

行业分析| 无人机电力巡检技术的应用

随着现代生活水平的不断提升,人们对各行各业的发展都提出了更高的品质要求,对于电力的需求不断上涨,因此也加速了电力行业的转型升级。基于这一发展状况,我国电力行业逐渐开始选择应用无人机电力巡检等现代高科技技术。 无人机电…

Baumer工业相机堡盟工业相机如何通过BGAPISDK进行定序器编程:VCXG双快门操作(C#)

Baumer工业相机堡盟工业相机如何通过BGAPISDK进行定序器编程:VCXG双快门操作(C#) Baumer工业相机Baumer工业相机BGAPISDK和定序器编程的技术背景Baumer工业相机通过BGAPISDK进行定序器编程功能1.引用合适的类文件2.Baumer工业相机通过BGAPISDK进行定序器…

Keep上市,打响健身科技第一炮?

近些年,大众对于身体健康和审美的需求越来越旺盛,因此也引发了一场无形的健身革命。无论是线下动辄大几千的健身房,还是线上的健身直播经济都受到了不小的关注,在疫情刚开始的那段时间,各地的封控让在线健身成为了一种…

修改滚动条样式 和 那些高度

一、滚动条样式 二、那些高度 网页可见区域宽: document .body.clientWidth; 网页可见区域高: document .body.clientHeight; 网页可见区域宽: document .body.offsetWidth (包括边线的宽); 网页可见区域高: document .body.of…

亚马逊实践 | 构建可持续发展的架构模型

可持续发展概念源于对系统性文明危机和世界问题的科学和社会意识形态研究。世界级的进步学术社群和政治精英在二十世纪末就认识到了这些问题的存在。他们将即将到来的二十一世纪视为充满不确定性、全球灾难进程逐步升级的时代。可持续发展对多个领域产生影响,目前已…