从单体架构到微服务架构的演进历程
一、单体架构
1.1 什么时候用单体架构
在创业初期或项目开始时,项目整体功能比较少,开发人员也少,且项目需要用最少时间开发出来,用 MVP 方式快速进行市场验证是否可行,这时候就可以用单体架构进行快速开发。
1.2 单体架构设计举例-电商应用
功能分析:
拿淘宝网来举例,现代电商网站功能是很复杂的,有多少功能呢?可以看看我前面的文章《电商产品设计全攻略》读书笔记(https://www.cnblogs.com/jiujuan/p/14452748.html#1574269892) 里的电商管理系统和电商平台产品结构 2 小节。
拿淘宝网举例的话,当然是最早期的淘宝网 - -!,它最简单的 3 个功能:商品展示,用户下单,订单中心。这三个功能构成一个最简单的电商业务流程。
展示给用户看的商品页面以及用户购买商品的操作功能。那我们需要对用户和订单进行管理,怎么办?
就需要一个管理后台来对用户订单进行管理。如此简单分析过后,就知道了电商网站应用功能。
应用功能架构图:
程序架构设计:
这时候我们开发的单体应用程序,部署在应用服务器上。程序架构可能采用 MVC 这种程序架构模式。
当然也有可能什么架构都不用,直接撸代码了,所有的程序都混合在一起,这就是所谓的“大泥球”单体,这是一种糟糕的开发方式。
java 里最常用的 MVC 框架,比如 SpringMVC 框架。
- 划分模块
比如根据上面电商功能架构图,在程序里可以把电商功能划分为相对应的模块,如用户模块,订单模块,商品模块。
这时程序里不管是前台功能,后台功能都有这些模块。
(应用程序模块)
这时候应用程序模块都在一个大的单体项目里,前台功能和管理后台共用一套代码。
- 模块里的功能
比如前台商品模块,就有商品列表,商品详情页等页面功能。
- 程序开发
编写程序时可能应用 MVC 这种程序设计模式来进行程序代码开发。
程序部署架构图:
编写的程序代码部署到应用服务器上,用户的所有数据存储到 MySQL 数据库里。
程序和 MySQL 都部署在同一台服务器上。
二、单体架构演进
2.1 MySQL 性能瓶颈-缓存
随着项目上线,公司对项目加大力度推广和运营,用户数越来越多。
有一天,用户投诉说,商品详情页面浏览好慢。如是你一番操作猛如虎,发现详情页显示慢,性能瓶颈出现在数据库 MySQL 上,
MySQL 在用户访问高峰时,扛不住那么大的访问量。这时你想到的解决方法,可以用缓存来缓存一部分数据,不必每次都到 MySQL 取数据,
可以用 Redis 来缓存部分商品信息数据。如是,增加一个 Redis 缓存,架构图如下:
2.2 MySQL 读写分离
这时候,你也可能想到另外的一种方法:数据的读写分离,减轻对 MySQL 访问压力。
经过上面 2 种措施改进后,商品详情页访问速度开始变快,访问正常了。
但是这种舒服日子没过几个月,又有用户开始反馈页面访问比较慢。
你又一番埋头辛苦分析,发现是单台服务器负载高,单台服务器的性能已经到了极限,它已经承载不了那么多用户的访问。
如是你想,把数据存储和应用程序部署到 2 台服务器上,减轻服务器的负载压力。
2.3:数据存储和应用程序服务器分离
于是你申请买了一台服务器,把 MySQL 和 Redis 都部署在这台新买的服务器上,让原来那台服务器负载得到缓解。
新的架构部署成功后,用户访问页面又恢复正常。
但是随着业务发展越来越好,新增用户越来越多,应用服务器的负载又居高不下了。
这时候要增加新的应用服务器了,这样做是最简单的。多台应用服务器形成集群,那怎么访问这些应用服务器?才能使每台服务器负载保持平衡,或者性能好的多接受一些用户访问?这时候就要用到负载均衡了。
2.4 集群-分布式
集群-负载均衡(多台应用服务器)
部署多台应用服务器形成一个应用服务器集群,前面用户通过负载均衡器来进行服务的访问。
比较常用的负载均衡软件有 Nginx、LVS、KeepAlived 等等。
还有硬件负载均衡,比如 F5 等。
部署后,页面访问又恢复了正常。
过了几个月,数据服务器也出现负载过高情况,这时候可以把 Redis 缓存和 MySQL 分离,部署到不同服务器上。
随着数据量增加,把 Redis 部署为分布式缓存。
数据库分离和 Redis 分布式缓存
把 MySQL 和缓存 Redis 部署到不同的服务器上。
随着缓存数据的增多,Redis 也部署为主从模式,然后到 Redis Cluster 集群模式,也就是 Redis 的分布式缓存。
此时数据存储服务器负载得到缓解,访问恢复正常。
由于业务发展太快,用户变得更多,数据库又出现了性能瓶颈,这时可以对数据库进行分库分表
分库分表
为了进一步的降低数据库由于数据量太多,访问太大而造成的瓶颈,可以对数据库进行分库分表,减轻数据库的访问压力。
三、应用程序发展演进
3.1 应用程序功能变化-硬件发展
上面画的架构图都是后端技术部分,服务端架构从单体到集群再到分布式的演进。
那么前面给用户使用的应用程序呢?也是在变化之中。
比如在《淘宝技术这十年》里的淘宝网的发展变化,刚开始时是一个很简单的 PC 端页面,到后来随着手机普及,移动互联网发展起来,
手机应用就出现了。淘宝 APP 也随之出现。随着国民应用微信逐渐发展壮大,小程序也成为第三种互联网程序应用形式。当然,还有其它终端,比如平板 ipad,自动售货机等等各种终端。
上面是不同硬件出现,程序应用承载出现不同形式。
(多终端用户出现后的架构图)
那么淘宝网的功能呢?当然增加了很多。
还孵化出了多种不同的业务应用,比如天猫,1688,支付宝,聚划算,淘宝旺旺等等很多应用。
在今年 2013.1 再去打开淘宝 APP 看看,里面的功能多到眼花缭乱。
现在的电商系统有哪些子系统,系统里都有啥功能,可以看看我之前发布的这篇文章。
3.2 多端程序单体架构
多种终端的出现,当然不是一下子就出来的,都有一个发展过程,只不过到写这篇文章为止,出现了上面说的PC,手机 APP、小程序,平板等多个终端,最常用的还是前面 3 种。
最开始开发程序时,应用程序要适应多个终端,最简单的方式就是拷贝 PC 端的代码到多个终端程序里。按照上面 1.2 小节的电商最简单业务功能模块实现,多终端应用程序功能架构如下:
(后端功能模块架构图)
上面的三种终端程序应用,还是使用同一个 MySQL 数据库,也及是说订单数据、用户数据等都存储在一个库中。
可能会问,怎么区分订单来自哪一个终端?
可以给订单数据一个类型标识来进行区分,订单是从哪一个终端过来的。
那管理后台呢?
多终端程序可以共用一个管理后台。
多终端的后台功能模块都搞起来了,但是这种程序模块架构有什么弊端缺点呢?
- 代码重复
- 增加/修改功能复杂:比如说要修改一个订单模块的功能,需要修改 3 个终端的后台代码
- 代码维护复杂:每次维护代码都需要动 3 个后端的代码
那有没有办法可以改进上面所说的情况?
能不能把 3 个后端重复模块代码合并为一个,统一向前端提供服务,当然是可以的。怎么做?
- 前后端分离 - 把前后端代码进行分离,前端展示操作页面和后端功能模块分离
- 抽象公共模块 - 把多个后端公共模块进行抽象为一个模块,为前端提供统一服务
3.3 前后端分离,抽象公共模块
页面前后端分离,其实在上面 1.1 小节的单体架构中也可以这样实施前后端分离。
多个终端当然也可以进行前后端分离,这样不用开发多个终端的页面,程序代码进行适配就可以了。前端现在有很多种多终端适配的技术。
后端的多个相同功能模块进行抽象,变成一个共用模块,对外提供服务。
这种方式提供功能服务我想到的有 3 种方式:
第一种:单体结构-函数提供接口
后端还是在一个单体工程下面,但是公共功能抽象为一个函数或对象接口,对外提供服务。
后端其他模块引入这个模块然后调用函数或者对象,完成程序功能开发。
应用程序结构图如下:
第二种:用 Maven 当作一个远程包引入
在 java 里,用 maven 可以引入一个远程包进行使用。我们可以用这种方式引入公共功能包。
在 Go 里,用 module 模块方法引入远程包使用。
第三种:RPC 方式调用
这种方式是把应用程序里的公共模块功能变成一个独立的服务,对外提供服务。这个”外“是公司内部的业务可以调用这个服务。公司以外的应用就不可以调用这个服务。
这里也有2种方式,
第一种:只是公共模块独立提供服务,数据库还是共用。
第二种:数据库随着公共模块一起,独立对外提供服务。
我们来讨论第二种情况,既然要作为一个独立的服务存在,它就是自适应自维护的,此时数据库变成独立数据库,跟着它的服务模块一起独立。
此时不仅应用模块进行分离,应用服务器也进行了分离。
这时候就有点微服务的味道了。
四、微服务架构演进
对于微服务的了解,可以看看我前面关于微服务系列文章的讲解,比如下面文章:
微服务的技术架构实际是一个体系,它是由很多技术组成的。
4.1 以 SpringCloud 为基础的微服务技术体系
最开始最 netflix 公司开源的以 springcloud 为基础的微服务技术体系,它把微服务体系开源了。不过后来 netflix 放弃维护它开源的微服务框架。
但是 spring 框架的公司和阿里巴巴都开源了自己的以 springcloud 为基础的微服务体系,阿里巴巴叫 springcloud-alibaba。
阿里还开源了另外一个微服务框架 dubbo。
这些框架都提供了一些主要功能:服务发现和注册,限流熔断,链路追踪,配置中心,网关等功能。
SpringCloud 微服务体系有哪些缺点?
- 代码侵入性强 - 业务层中需要加入治理层代码,与治理层混淆在一起
- 组件多 - 组件多,学习成本就变高
- 治理功能不全 - 比如协议转换、动态请求路由、灰度发布等功能
- 无法实现语义无关性 - 只能是一种语言或几种语言实现,无法做到与编程语言无关
针对以上的一些问题,就出现了 Service Mesh 这种架构,它作为一个基础设施层,真正做到与业务解耦,与语言无关,解决复杂网络下微服务与微服务之间通信问题。
其实就是把通信相关功能分离出来,与业务系统彻底解耦。
4.2 Service Mesh
Service Mesh 解决复杂网络下微服务与微服务之间通信问题,它的实现形态一般为轻量级的网络代理,与应用以边车(SideCar)模式部署。
第一代ServiceMesh
(from:https://philcalcado.com/2017/08/03/pattern_service_mesh.html ,Phil Calçado)
来看一个全局图:
(from:https://philcalcado.com/2017/08/03/pattern_service_mesh.html ,Phil Calçado)
绿色:应用服务
蓝色:SideCar
第二代 ServiceMesh:istio
第一代 Service Mesh 是由独立运行的单机服务代理构成,为了提供统一的控制入口,演进出了统一的控制面板,称为 control plane。
控制面板(control plane)和数据面板(data plane,即边车代理)进行交互,比如策略下发、数据采集等。这就是以Istio为代表的第二代Service Mesh。
(from:https://philcalcado.com/2017/08/03/pattern_service_mesh.html ,Phil Calçado)
(from:https://philcalcado.com/2017/08/03/pattern_service_mesh.html ,Phil Calçado)
springcloud 与 ServiceMesh 的区别:
(from:https://medium.com/codex/a-spring-cloud-compatible-service-mesh-6ce58c571012)
五、参考
-
https://philcalcado.com/2017/08/03/pattern_service_mesh.html Pattern: Service Mesh,详细介绍了微服务到 Servie Mesh的演进
-
https://medium.com/codex/a-spring-cloud-compatible-service-mesh-6ce58c571012
-
https://www.cnblogs.com/jiujuan/p/14452748.html 《电商产品设计全攻略》读书笔记
-
https://www.cnblogs.com/jiujuan/p/13301055.html 微服务架构学习与思考(04):微服务技术体系