单块系统的问题

在软件技术发展过程的很长一段时间内,软件系统都表现为一种单块系统。时至今日,很多单块系统仍然在一些行业和组织中得到开发和维护。所谓单块系统,简单讲就是把一个系统所涉及的各个组件都打包成一个一体化结构并进行部署和运行。在Java EE领域,这种一体化结构很多时候就体现为一个WAR包,而部署和运行的环境就是以Tomcat为代表的各种应用服务器。下图展示的就是一个典型的单块系统,我们可以看到在应用服务器上同时运行着面向用户的Web组件、封装业务逻辑的Service组件和完成数据访问的Dao(Data Access Object,数据访问对象)组件,这些组件都作为一个整体进行统一的开发、部署和维护。

显然,上图中所示的单块系统有其存在和发展的固有优势。当团队规模并不是太大的时候,一个单块应用可以由一个开发者团队进行独立维护。该团队的成员能够对单块应用进行快速学习、理解和修改,因为其结构非常简单。同时,因为单块系统的表现形式就是一个独立的WAR包,想要对它进行集成、部署以及实现无状态集群相对也比较简单,通常只要采用负载均衡机制并运行该单块系统的多个实例就能达到系统伸缩性要求。

但在另一方面,随着公司或者组织业务的不断扩张、业务结构不断的变化以及用户量的不断增加,单块架构的优势已逐渐无法适应互联网时代的快速发展,面临着越来越多的挑战。在使用单块系统时,我们不得不面对以下问题:

1. 业务复杂度

对于大多数系统而言,架构设计是为了满足业务需求。衡量架构好坏与否的一个重要方面是看其面对复杂业务变更时所应该具有的灵活性,也就是我们通常所说的可扩展性(Extensibility)。可扩展性是指系统在经历不可避免的变更时足够灵活,针对提供这样的灵活性所要付出的成本进行平衡的能力。所谓可扩展,扩展的是业务。可扩展性的示意图如下所示,当往SystemA中添加新业务NewSubSystem时,如果不需要改变原有的各个子系统而只需把新业务封闭在一个新的子系统中就能完成整体业务的升级,我们就可以认为系统具有较好的可扩展性。显然,单块系统不具备良好的可扩展性,因为对系统业务进行任何一处的修改,都需要重新构建整个系统并进行发布。单块系统内部没有根据业务结构进行合理的业务拆分是导致其可扩展性低下的主要原因。

2. 代码腐化

在软件开发过程中,代码腐化在一定程度是一种不可避免的现象,关键是腐化的时间和程度与整个产品生命周期之间的关联关系。在产品的鼎盛时期,如果出现大量的代码腐化会对产品的发展带来巨大危害。在单块系统中,由于缺乏合理的业务和技术实现边界,随着产品业务功能的增多,当出现缺陷时,有可能引起缺陷的原因组合就会比较多,这会导致分析缺陷、定位缺陷、修复缺陷的成本相应增高,也就意味着缺陷的平均修复周期可能会花费更长时间,从而影响到产品的正常迭代和演进。同时,随着功能不断叠加,单块系统的代码结构也日益复杂,在开发人员对全局功能缺乏深度理解的情况下,修复一个缺陷的同时还有可能引入其它的缺陷,在很多技术团队并不具备完善的自动化测试机制的客观条件下,很可能导致出现问题越修越多的不良循环。

3. 团队问题

互联网行业由于产品价值与市场时机密切相关,普遍崇尚快速试错和迭代发布,很多公司或组织会在比较短的时间内扩充产品功能和开发团队,也就意味着对于过程资产建设和人才培养等方面并不会投入太多的成本,这就要求新加入团队的成员能够快速融入团队并进行代码开发和维护。然后,由于在单块系统中所有的业务和代码在很大程度上无序的混合在一起,存在大量错综复杂的业务和代码结构、由于历史原因所造成的迥然不同的开发风格以及看似复杂但已经不被使用的遗留代码,使得新员工了解行业背景、熟悉应用程序业务、配置本地开发环境等看似简单的任务都变得并不简单。

另一方面,由于单块系统的集中式管理方式,使得系统内部的技术体系和开发方式很难得到扩展。随着应用程序的复杂性逐渐增加以及功能越来越多,如果团队希望尝试引入新的框架和技术,或者对现有技术栈升级,通常都会面临不小的风险。也即意味着初始的技术选型严重限制了单块系统将来采用不同开发语言或框架的能力。但互联网行业中技术体系变化和业务体系变化一样快速,技术体系的无法扩展和升级也会导致现有团队中不同出身和开发背景的成员对系统代码产生一种开发惰性,也无法吸引到优秀的开发人员。

4. 伸缩性问题

前面讲到单块系统的可扩展性很差,实际上它的可伸缩性同样很有问题。所谓可伸缩(Scalability),伸缩的是性能,即当系统性能出现问题时,如果我们只需要简单添加应用服务器等硬件设备就能避免系统出现性能瓶颈,那么该系统无疑具备较高的可伸缩性。通常,我们会考虑采用水平伸缩的方法实现可伸缩性。当考虑水平伸缩时,一般的做法是建立一个集群,通过在集群中不断的添加新节点,然后借助前端的负载均衡器,将用户的请求按照某种算法分配到不同的节点上。但是,由于单块系统的所有程序代码都运行在服务器上的同一个进程中,内存密集型和CPU密集型并存,也就要求所有应用的服务器都必须有足够的内存和强劲的CPU来满足需求。这种方法成本会比较高,而且资源利用率通常都比较低下。

以下图为例,单块系统中的组件A的负载已经达到了80%,也就是到了不得不对系统运行能力进行扩容的时候。但同一系统的其它两个组件B和C的负载还没有到其处理能力的20%。由于单块系统中的各个组件是打包在同一个WAR包中的,因此通过添加一个额外的系统运行实例虽然可以将需要扩容组件的负载降低一半,但是显然其他组件的利用率变得更为低下,造成资源浪费。另一方面,对于那些需要保持类似会话(Session)数据的需求而言,扩容之后的运行机制在如何保持各个服务器之间数据的一致性也存在较大的实现难度。

针对以上集中式单块系统所普遍存在的问题,基本的解决方案就要依赖于分布式系统的合理构建,这是下一篇内容。

如果对文章感兴趣,可以关注我的微信公众号:程序员向架构师转型,或扫描下面的二维码。

我出版了《系统架构设计:程序员向架构师转型之路》、《向技术管理者转型:软件开发人员跨越行业、技术、管理的转型思维与实践》、《微服务设计原理与架构》、《微服务架构实战》等书籍,并翻译有《深入RabbitMQ》和《Spring5响应式编程实战》,欢迎交流

发布了92 篇原创文章 · 获赞 9 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/lantian08251/article/details/98947471