原文地址:https://microservices.io/patterns/monolithic.html
场景描述假设你正在开发一个大型服务端企业应用,有如下需求:
必须支持多种客户端,包括:WEB 端浏览器、WAP 端浏览器以及原生移动 APP。
对外暴露公共 API 用于调用
处理 HTTP 请求,或者消息,执行对应的业务逻辑。
访问数据库,缓存或者持久化响应的数据
与其他系统进行通信,交换所需的信息
返回 HTTP 响应,指定好特定的序列化方式,例如 JSON、 XML 等等
根据业务逻辑与功能,设计并划分出不同逻辑模块
这样的一个应用,你会如何设计架构并部署呢?
考虑因素这是一个团队开发的项目,有一个独立团队负责
团队成员会发生变化,新加入的成员必须快速上手项目
应用程序必须易于理解并修改
期望能实现应用的持续集成与部署
必须可以多实例部署应用程序,以满足可伸缩性和可用性要求。
想用比较新的技术(框架、编程语言等)
解决方案使用单体架构,例如:
一个 Java WAR 文件启动的程序
一个单目录 Rails 或者 NodeJS 程序
举例假设现在正在设计一个电商应用,功能包括接收来自客户的订单(StoreFrontUI),验证并维护库存余额(Inventory Service),验证并维护用户可用余额(Accounting Service),下单成功并发货(Shipping Service)。这个应用被设计成一个单体架构应用,例如:JavaWeb 应用程序由运行在Web容器(如 Tomcat )上的单个 WAR 文件组成。Rails 应用程序由部署在 Nginx 或 Tomcat 上的 JRuby 或 Nginx 上的单一目录层次结构组成。可以在负载均衡器后面部署多个实例,以扩展和提高可用性。
分析这种解决方案的好处有:
开发简单,当前的 IDE 基本都是按照开发单体应用程序开发的。
部署简单,只要把一个文件或者目录部署到 Web 容器里即可。
扩容简单,通过在负载均衡器后面部署多个实例就能实现扩容。
但是,随着产品不断迭代,这个单体应用程序将会变得越来越大,团队的规模也越来越大,这种单体设计就会有一些缺点,并且这些缺点会变得越来越严重:
单体应用代码在同一个代码库,这个代码库会越来越大,使开发人员感觉会很头大,特别是那些刚加入团队的开发人员。应用程序将很难理解和修改,因此,开发速度通常会被减缓。另外,由于没有明确的模块边界,代码内部的模块化会随着时间的推移而越来越模糊。此外,由于很难理解如何正确实现更改,并且可能还需要兼容老版本的错误,因此代码的质量会随着时间的推移而下降,慢慢堆积成为屎山。
IDE 的压力会很大。代码库越大,IDE 会更慢,IDE 一般为了智能补全代码的功能,会对代码做索引并加载到内存中。臃肿的代码会拖慢 IDE,降低开发效率。
Web 容器压力变大。程序越臃肿,启动时间会被拖长,导致代码调试变慢,同时部署时间也会变长。
持续集成部署难度越来越大。为了更新一个组件,您必须重新部署整个应用程序。这会导致所有业务,不管是否有更新,都被影响或者中断。同时,如果出现问题,回滚时间也会增长。因此,这限制了程序不能持续频繁更新。
不能灵活扩展。不同业务模块可能压力不同,以及压力大的时间段可能也不同,但是每次扩容,都需要所有模块一块扩容,造成了浪费。
故障扩散。如果有一个模块出了问题导致内存泄漏,那么整个业务都会受到影响。
团队分工的障碍。例如,我们可能希望有UI团队、会计团队、库存团队等等。单块应用程序的问题在于它阻止了团队独立工作。小组必须协调他们的开发工作和重新部署。对于一个团队来说,进行更改和更新生产要困难得多。
需要长期使用同一个技术栈。一种单一的体系结构迫使您与您在开发开始时所选择的技术堆栈(在某些情况下,与该技术的特定版本)结合在一起。有了单体应用程序,就很难逐步采用一种较新的技术。比如你使用的框架停止更新,或者过时了,在单体应用下很难逐步采用一个新的框架实现。