微服务架构学习与思考(12):从单体架构到微服务架构的演进历程

从单体架构到微服务架构的演进历程

一、单体架构

1.1 什么时候用单体架构

在创业初期或项目开始时,项目整体功能比较少,开发人员也少,且项目需要用最少时间开发出来,用 MVP 方式快速进行市场验证是否可行,这时候就可以用单体架构进行快速开发。

1.2 单体架构设计举例-电商应用

功能分析:

拿淘宝网来举例,现代电商网站功能是很复杂的,有多少功能呢?可以看看我前面的文章《电商产品设计全攻略》读书笔记(https://www.cnblogs.com/jiujuan/p/14452748.html#1574269892) 里的电商管理系统电商平台产品结构 2 小节。

拿淘宝网举例的话,当然是最早期的淘宝网 - -!,它最简单的 3 个功能:商品展示,用户下单,订单中心。这三个功能构成一个最简单的电商业务流程。

展示给用户看的商品页面以及用户购买商品的操作功能。那我们需要对用户和订单进行管理,怎么办?

就需要一个管理后台来对用户订单进行管理。如此简单分析过后,就知道了电商网站应用功能。

应用功能架构图:

image-20230124022153045

程序架构设计:

这时候我们开发的单体应用程序,部署在应用服务器上。程序架构可能采用 MVC 这种程序架构模式。

当然也有可能什么架构都不用,直接撸代码了,所有的程序都混合在一起,这就是所谓的“大泥球”单体,这是一种糟糕的开发方式。

java 里最常用的 MVC 框架,比如 SpringMVC 框架。

  1. 划分模块

比如根据上面电商功能架构图,在程序里可以把电商功能划分为相对应的模块,如用户模块,订单模块,商品模块。

这时程序里不管是前台功能,后台功能都有这些模块。

image-20230124110006299

​ (应用程序模块)

这时候应用程序模块都在一个大的单体项目里,前台功能和管理后台共用一套代码。

  1. 模块里的功能

比如前台商品模块,就有商品列表,商品详情页等页面功能。

  1. 程序开发

编写程序时可能应用 MVC 这种程序设计模式来进行程序代码开发。

程序部署架构图:

image-20230124122806802

编写的程序代码部署到应用服务器上,用户的所有数据存储到 MySQL 数据库里。

程序和 MySQL 都部署在同一台服务器上。

二、单体架构演进

2.1 MySQL 性能瓶颈-缓存

随着项目上线,公司对项目加大力度推广和运营,用户数越来越多。

有一天,用户投诉说,商品详情页面浏览好慢。如是你一番操作猛如虎,发现详情页显示慢,性能瓶颈出现在数据库 MySQL 上,

MySQL 在用户访问高峰时,扛不住那么大的访问量。这时你想到的解决方法,可以用缓存来缓存一部分数据,不必每次都到 MySQL 取数据,

可以用 Redis 来缓存部分商品信息数据。如是,增加一个 Redis 缓存,架构图如下:

image-20230124122020910

2.2 MySQL 读写分离

这时候,你也可能想到另外的一种方法:数据的读写分离,减轻对 MySQL 访问压力。

image-20230124122113934

经过上面 2 种措施改进后,商品详情页访问速度开始变快,访问正常了。

但是这种舒服日子没过几个月,又有用户开始反馈页面访问比较慢。

你又一番埋头辛苦分析,发现是单台服务器负载高,单台服务器的性能已经到了极限,它已经承载不了那么多用户的访问。

如是你想,把数据存储和应用程序部署到 2 台服务器上,减轻服务器的负载压力。

2.3:数据存储和应用程序服务器分离

于是你申请买了一台服务器,把 MySQL 和 Redis 都部署在这台新买的服务器上,让原来那台服务器负载得到缓解。

image-20230124122240609

新的架构部署成功后,用户访问页面又恢复正常。

但是随着业务发展越来越好,新增用户越来越多,应用服务器的负载又居高不下了。

这时候要增加新的应用服务器了,这样做是最简单的。多台应用服务器形成集群,那怎么访问这些应用服务器?才能使每台服务器负载保持平衡,或者性能好的多接受一些用户访问?这时候就要用到负载均衡了。

2.4 集群-分布式

集群-负载均衡(多台应用服务器)

部署多台应用服务器形成一个应用服务器集群,前面用户通过负载均衡器来进行服务的访问。

image-20230124124859989

比较常用的负载均衡软件有 Nginx、LVS、KeepAlived 等等。

还有硬件负载均衡,比如 F5 等。

部署后,页面访问又恢复了正常。

过了几个月,数据服务器也出现负载过高情况,这时候可以把 Redis 缓存和 MySQL 分离,部署到不同服务器上。

随着数据量增加,把 Redis 部署为分布式缓存。

数据库分离和 Redis 分布式缓存

image-20230124130955865

把 MySQL 和缓存 Redis 部署到不同的服务器上。

随着缓存数据的增多,Redis 也部署为主从模式,然后到 Redis Cluster 集群模式,也就是 Redis 的分布式缓存。

此时数据存储服务器负载得到缓解,访问恢复正常。

由于业务发展太快,用户变得更多,数据库又出现了性能瓶颈,这时可以对数据库进行分库分表

分库分表

为了进一步的降低数据库由于数据量太多,访问太大而造成的瓶颈,可以对数据库进行分库分表,减轻数据库的访问压力。

三、应用程序发展演进

3.1 应用程序功能变化-硬件发展

上面画的架构图都是后端技术部分,服务端架构从单体到集群再到分布式的演进。

那么前面给用户使用的应用程序呢?也是在变化之中。

比如在《淘宝技术这十年》里的淘宝网的发展变化,刚开始时是一个很简单的 PC 端页面,到后来随着手机普及,移动互联网发展起来,

手机应用就出现了。淘宝 APP 也随之出现。随着国民应用微信逐渐发展壮大,小程序也成为第三种互联网程序应用形式。当然,还有其它终端,比如平板 ipad,自动售货机等等各种终端。

上面是不同硬件出现,程序应用承载出现不同形式。

image-20230124185223955

​ (多终端用户出现后的架构图)

那么淘宝网的功能呢?当然增加了很多。

还孵化出了多种不同的业务应用,比如天猫,1688,支付宝,聚划算,淘宝旺旺等等很多应用。

在今年 2013.1 再去打开淘宝 APP 看看,里面的功能多到眼花缭乱。

现在的电商系统有哪些子系统,系统里都有啥功能,可以看看我之前发布的这篇文章

3.2 多端程序单体架构

多种终端的出现,当然不是一下子就出来的,都有一个发展过程,只不过到写这篇文章为止,出现了上面说的PC,手机 APP、小程序,平板等多个终端,最常用的还是前面 3 种。

最开始开发程序时,应用程序要适应多个终端,最简单的方式就是拷贝 PC 端的代码到多个终端程序里。按照上面 1.2 小节的电商最简单业务功能模块实现,多终端应用程序功能架构如下:

image-20230124193811484

​ (后端功能模块架构图)

上面的三种终端程序应用,还是使用同一个 MySQL 数据库,也及是说订单数据、用户数据等都存储在一个库中。

可能会问,怎么区分订单来自哪一个终端?

可以给订单数据一个类型标识来进行区分,订单是从哪一个终端过来的。

那管理后台呢?

多终端程序可以共用一个管理后台。

多终端的后台功能模块都搞起来了,但是这种程序模块架构有什么弊端缺点呢?

  1. 代码重复
  2. 增加/修改功能复杂:比如说要修改一个订单模块的功能,需要修改 3 个终端的后台代码
  3. 代码维护复杂:每次维护代码都需要动 3 个后端的代码

那有没有办法可以改进上面所说的情况?

能不能把 3 个后端重复模块代码合并为一个,统一向前端提供服务,当然是可以的。怎么做?

  1. 前后端分离 - 把前后端代码进行分离,前端展示操作页面和后端功能模块分离
  2. 抽象公共模块 - 把多个后端公共模块进行抽象为一个模块,为前端提供统一服务

3.3 前后端分离,抽象公共模块

页面前后端分离,其实在上面 1.1 小节的单体架构中也可以这样实施前后端分离。

多个终端当然也可以进行前后端分离,这样不用开发多个终端的页面,程序代码进行适配就可以了。前端现在有很多种多终端适配的技术。

后端的多个相同功能模块进行抽象,变成一个共用模块,对外提供服务。

这种方式提供功能服务我想到的有 3 种方式:

第一种:单体结构-函数提供接口

后端还是在一个单体工程下面,但是公共功能抽象为一个函数或对象接口,对外提供服务。

后端其他模块引入这个模块然后调用函数或者对象,完成程序功能开发。

应用程序结构图如下:

image-20230124225610407

第二种:用 Maven 当作一个远程包引入

在 java 里,用 maven 可以引入一个远程包进行使用。我们可以用这种方式引入公共功能包。

在 Go 里,用 module 模块方法引入远程包使用。

第三种:RPC 方式调用

这种方式是把应用程序里的公共模块功能变成一个独立的服务,对外提供服务。这个”外“是公司内部的业务可以调用这个服务。公司以外的应用就不可以调用这个服务。

这里也有2种方式,

第一种:只是公共模块独立提供服务,数据库还是共用。

第二种:数据库随着公共模块一起,独立对外提供服务。

我们来讨论第二种情况,既然要作为一个独立的服务存在,它就是自适应自维护的,此时数据库变成独立数据库,跟着它的服务模块一起独立。

此时不仅应用模块进行分离,应用服务器也进行了分离。

这时候就有点微服务的味道了。

image-20230124233108779

四、微服务架构演进

对于微服务的了解,可以看看我前面关于微服务系列文章的讲解,比如下面文章:

微服务的技术架构实际是一个体系,它是由很多技术组成的。

4.1 以 SpringCloud 为基础的微服务技术体系

最开始最 netflix 公司开源的以 springcloud 为基础的微服务技术体系,它把微服务体系开源了。不过后来 netflix 放弃维护它开源的微服务框架。

但是 spring 框架的公司和阿里巴巴都开源了自己的以 springcloud 为基础的微服务体系,阿里巴巴叫 springcloud-alibaba。

阿里还开源了另外一个微服务框架 dubbo。

这些框架都提供了一些主要功能:服务发现和注册,限流熔断,链路追踪,配置中心,网关等功能。

SpringCloud 微服务体系有哪些缺点?

  1. 代码侵入性强 - 业务层中需要加入治理层代码,与治理层混淆在一起
  2. 组件多 - 组件多,学习成本就变高
  3. 治理功能不全 - 比如协议转换、动态请求路由、灰度发布等功能
  4. 无法实现语义无关性 - 只能是一种语言或几种语言实现,无法做到与编程语言无关

针对以上的一些问题,就出现了 Service Mesh 这种架构,它作为一个基础设施层,真正做到与业务解耦,与语言无关,解决复杂网络下微服务与微服务之间通信问题。

其实就是把通信相关功能分离出来,与业务系统彻底解耦。

4.2 Service Mesh

Service Mesh 解决复杂网络下微服务与微服务之间通信问题,它的实现形态一般为轻量级的网络代理,与应用以边车(SideCar)模式部署。

第一代ServiceMesh

image-20230125001047147

​ (from:https://philcalcado.com/2017/08/03/pattern_service_mesh.html ,Phil Calçado)

来看一个全局图:

image-20230125001700722

​ (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。

image-20230125002338290

​ (from:https://philcalcado.com/2017/08/03/pattern_service_mesh.html ,Phil Calçado)

image-20230125002415442

​ (from:https://philcalcado.com/2017/08/03/pattern_service_mesh.html ,Phil Calçado)

springcloud 与 ServiceMesh 的区别:

image-20230125002930584

​ (from:https://medium.com/codex/a-spring-cloud-compatible-service-mesh-6ce58c571012)

五、参考

posted @ 2023-01-25 00:47  九卷  阅读(2595)  评论(0编辑  收藏  举报