8

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

 2 years ago
source link: https://www.cnblogs.com/jiujuan/p/17066590.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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

一、单体架构#

1.1 什么时候用单体架构#

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

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

功能分析:#

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

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

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

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

应用功能架构图:#

image-20230124022153045

程序架构设计:#

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

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

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

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

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

image-20230124110006299

​ (应用程序模块)

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

  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. 增加/修改功能复杂:比如说要修改一个订单模块的功能,需要修改 3 个终端的后台代码
  2. 代码维护复杂:每次维护代码都需要动 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)

五、参考#


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK