企业服务架构演进-单体架构的变迁

本篇是企业服务架构演进系列的第二篇,副主题是单体架构的变迁。短短2年,我在进行工程系统开发的时候不知不觉已经踩上了单体架构服务的尾巴,从而迈向分布式微服务的大道。

本系列主要讲解我在之前公司的一些工程设计见闻和思考,整个系列大概有7篇文章。

  1. 企业服务架构演进-引言

2.企业服务架构演进-单体架构的变迁

  1. 企业服务架构演进-从jquery到vue的工程实践
  2. 企业服务架构演进-单库多服务的尴尬
  3. 企业服务架构演进-第三方系统与自研之道
  4. 企业服务架构演进-走上造轮子之路
  5. 企业服务架构演进-重复开发之殇

背景:我2016年就职于北京一家互联网公司,从实习到正式工作都在这家公司度过,技术栈基本基于自研框架,从事企业信息系统开发。为了更清楚的描述下文中的一些信息,我先说明公司的WEB框架,RPC框架,Scheduler框架,MQ框架是不同的技术框架,当然包括DAO组件也是独立的开发包。这些技术中间件由专门的技术平台负责维护。最后我们的工程其实是按集群划分的,也就是说一个WEB/RPC/Scheduler工程都是一个独立的集群,有唯一的集群名和域名。

一、WEB浏览器下的物料管理系统

我刚进入公司实习有幸转到刚筹建成立的信息技术部,并开始做部门成立后的第一个项目。转正之后依然在继续维护并开发新的功能模块。毫无疑问,这个系统是一个单体系统,或者基于一个WEB框架+一个DAO组件即可完成整个后端的业务逻辑。前端采用的是JQuery.js。我们的模板渲染引擎是WEB框架自带的velocity组件。这在当时算是前后端一体的,一个开发人员从表结构设计,DAO接口,Service接口,Controller层,前端JS和HTML等都需要依次开发完成,然后完成自测,最后跟其他功能模块做集成测试。当时的我们还有线上机器的部署权限和线上库的读写权限,也就是说我们也承担着运维的一部分工作。
下面看一下这个单体系统的架构图:
物料管理系统.png

从物理部署上来讲,我们的物料管理系统实际上有2台机器,定时任务有1台机器,后端直接连数据库,由运维部门的人负责部署Nginx集群,另外我们只是用了很少的redis的缓存功能。这里Redis其实也是由数据库部门的人负责运维Redis-Sentinel集群。
下面从包结构上来说,service,controller,repository,util,bean,entity等都放在同一个父包路径下,这里用一个简单的图来标示一下:
主要业务包的流程图.png
最后上一张该物料系统的业务功能大图:
QQ截图20200726140656.png

上面这个图展示了这个系统所呈现的基本业务功能,从上图中我们可以看出这个系统跟进销存系统差不多,只是我们当时是针对于公司业务线需要用的一些物料进行采购,调拨,出库,入库等进行管理。

二、走向RPC的电子合同系统

公司其实在创立之初就引入了RPC框架,由于部门的发展,我负责维护物料系统很长时间,然后慢慢随着业务的调整这个系统最后正常走向下线的道路。在这段时间里部门内很多其他同事已经尝试使用自研RPC框架进行项目开发了,因此我也在正常的项目调整中加入了一个新的项目组,第一次在项目实战中使用RPC框架。新的项目是一个电子合同系统,用来帮助业务线在签署合同方面降本提效。这个项目采用标准的工程架构:1个web工程,1个RPC工程。我当时负责web工程的整体建设,项目组长负责电子合同的设计和架构以及对整个项目的把控,包括与业务方联调,跨部门配合等。此时还没有引入前后端分离的架构思想,因此还是一个人负责整个web端的接口和前端,这里从RPC接口,到web端接口,到前端基本是一个人搞定。前端当时已经摒弃了JQuery.js,而是选择了当时比较火的AngularJS。做这样的决定也是因为当时财务系统已经大量采用了AngularJS做前端的数据绑定和表单渲染,另一方面也帮忙踩了一些坑,我们可以引用一些基于AngularJS封装的组件。
这里我也大概画一下业务流程:
电子合同业务流程.png

三、分布式定时任务与容器

在公司的3年多,做过很多系统的定时任务,也大概看了一些定时任务的源码,这里针对定时任务框架其实也有一个演进的过程。定时任务框架依赖zookeeper集群和Quartz,启动一个容器进程,连接到zookeeper集群。我们通过定时任务管控平台设置要执行的类和方法,设置cron表达式,设置执行的机器IP即可配置完成。经过架构设计变迁以及架构部的升级要求我们后面申请新的定时任务工程将以web容器为基础,而不是单独一个jar或者一个lib容器作为启动环境。相当于对整个技术栈进行了升级,后面的更新和维护会更加方便。
定时任务.png
如上图是未改造之前的部署形态,每个job中可以执行多个任务方法,每个job相当于一个定时任务工程。当我们升级之后scheduler容器本质上就是一个web容器了。

四、企业服务系统的生态

上面仅仅列举了两个系统来说明我从单体系统到分布式系统的变迁之路,在这个路上也有很多其他系统,这些系统慢慢组成了企业服务系统的一个雏形,这里我列举一下大概有哪些系统(已下线和正在运行的系统):
1.work系统(各个子系统的入口,公司公告,feed,消息,审批等功能模块)
工程架构简述:仅有WEB工程和少量的表+少量的RPC接口服务组成
2.inpass系统(SSO单点登录系统,服务于全公司内部系统)
工程架构简述:2个WEB工程,1个RPC工程,一个客户端jar包
3.会议室系统(服务于全公司进行会议室预定,权限设置,抢占等功能)
工程架构简述:仅有WEB工程,前后端不分离+AngularJS,定时任务代码当时也在WEB工程中,没有迁移。
4.行政系统
工程架构简述:仅有WEB工程,前后端分离+VUE
5.HR系统
工程架构简述:多个WEB工程,多个RPC工程,前后端分离+VUE
6.招聘系统
工程架构简述:多个WEB工程,多个RPC工程,前后端分离+VUE
7.权限系统
工程架构简述:多个WEB工程,多个RPC工程,前后端分离+VUE
8.流程平台
工程架构简述:多个WEB工程,多个RPC工程,前后端不分离+AngularJS
9.电子合同系统
工程架构简述:3个WEB工程,1个RPC工程,前后端不分离+AngularJS
10.feed流系统
工程架构简述:1个WEB工程,1个RPC工程,前后端分离+VUE
11.工单系统
工程架构简述:3个WEB工程,1个RPC工程,前后端分离+VUE
12.工程研发效能系统
13.财务系统
工程架构简述:1个WEB工程,少量RPC接口,VUE&AngularJS
14.代码生成服务系统
工程架构简述:1个WEB工程+VUE
15.请假系统
工程架构简述:2个WEB工程+1个RPC+VUE
16.钉钉端微应用(移动审批,会议室预定,请假,单点登录验证,招聘,hr入转调离)
工程架构简述:1个WEB 工程+VUE
全览大图.png

五、服务拆分怎么拆分?

从上面看大部分系统里的内容都是CRUD,但是这些CRUD工程怎么设计,是独立还是整合为一个工程还是有讲究的,假如我们把hr或者招聘看做一个服务的话,那么该拆分几个工程来表示呢?该怎么梳理工程之间的关系呢?一个工程搞定一切还是说以微服务的思想来一个子模块或者大功能整一个新的工程?又或者说有第三方(其他业务线部门或者外部系统)调用企业内部系统是否需要新建工程去隔离核心业务呢?
基于公司的技术栈和当时组长制定的策略,我这边总结了一些常见的工程套路如下:
1.WEB->DB
第一种:这里从工程的角度来说,就是一个web系统包括了service,controller,dao,bean,entity,是最简单的一种工程设计了。
2.RPC->DB
第二种:根据需求或者场景如果不需要WEB工程(可视化管理界面),或者仅仅需要一些RPC接口访问DB就可以提供服务,这也是RPC中最简单的一种工程设计。RPC中包括了client jar,server jar,根据dto,entity,service,dao做了模块和包的分层。核心思想就是将业务逻辑下沉到server中的service包中,通过client包里的接口对外发布。防止WEB或者API工程涉及过多业务逻辑导致代码不好维护。
3.WEB->RPC-DB
4.API->RPC-DB
第三种和第四种:这两种本质上其实是一样的,为啥分为两种呢?区别就在于API只有HTTP 接口和文档,WEB除了接口和文档之外还有可视化界面(囊括了前后端分离的场景)UI等,另外一方面WEB工程也在接口内容上跟API不同,WEB可以指定一些跳转策,业务上可以聚合多个RPC接口为页面提供数据。
5.Scheduler->RPC-DB
第五种:这种方式在做过一些项目之后我是比较推荐采用的,虽然跟第三种一样写代码麻烦了些,但是在工程设计和业务逻辑下沉方面会好很多,RPC工程会聚合实现整个服务所需要的各种接口,升级包或者dto/entity均以RPC client为准会让工程迭代更为顺畅,当然也不是没有缺点。就是一点点小的改动可能都要动RPC工程。
6.Scheduler->DB
第六种:这种方式其实有点另类,一般情况下Scheduler工程很少会单独作为一个服务,除非是任务调度类的。Scheduler工程中主要有service,job,entity,dao这几个包。当这种情况存在于一个服务中的时候就要小心了,除非scheduler工程很少改动否则建议使用第五种方式。因为RPC->DB这一层其实也解决了访问数据源的问题。上层API和WEB都不需要单独申请数据源。另外,如果一个scheduler中有很多定时任务方法要执行,涉及到比较多的业务逻辑的话那么势必会有代码冗余,最后一个缺点就是一旦有新功能升级很大可能就是两个工程都要拉分支去改,无意增加了维护成本。

六、如何建设一个分布式系统?

基于上面的讨论内容,我们来解决一个实际的问题:如何建设一个分布式系统?
不管什么规模,基于现有的技术底座:WEB,RPC,Scheduler,DAO等其他技术中间件我们如何更好更合理的设计不同工程模块。是将A模块放到WEB中合理还是放到RPC中合理。假设我们有一个业务系统需要建设,涉及到管理后台,定时任务,移动端,RPC服务,与公司其他部门系统进行交互,与外部第三方系统进行交互来完成整个业务系统的功能。那么我们如何像搭积木一样更好更有效率的去完成这些工程呢?这里有一些我个人的思考和需要着重注意的地方:

  1. 需要有WEB,RPC,移动端API,对外调用API,Scheduler这几个工程。
  2. 与公司内其他部门系统交互可以在RPC&移动端API中提供交互接口。
  3. WEB工程需要接入公司的单点登录系统&权限系统等基础服务。
  4. RPC工程承担了CRUD的核心操作,因此,如果涉及敏感接口,则建议增加鉴权机制。
  5. 对外调用API工程,一般情况下我们对外调用的API基本上还是以HTTP协议为主,此时就要考虑到一些安全性问题,比如接口限流,防爬虫,数据加密等。
  6. 移动端API通常与对外调用API有一样的问题,但是一般情况下基于钉钉微应用或者其他小程序载体等的企业信息化操作都需要先登录鉴权。
  7. Scheduler工程其实在一个稍微复杂的业务系统中肯定会存在,如果需要定时任务的地方比较少,业务也比较简单那么可以集成在WEB工程或者RPC工程中,通过架构部提供的scheduler插件可以解决定时任务容器集成到WEB框架或者RPC框架的问题。如果业务比较复杂那么建议单独建立工程,初始化集群去搞。
  8. 上面设计的核心思想就是要将业务逻辑下沉到RPC service中去,通过合理的分包,合理复用接口和设计模式等降低RPC service的复杂度提高RPC工程的质量。将RPC工程作为整个服务的核心,其他工程作为服务的外延,呈现出一个分布式的服务系统。
    架构设计@工程设计@服务稳定性之路
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页