首先,要理解架构,我们需要先理解几个有关系又相似的概念,包括:系统和子系统、模块和组件、框架和架构。
我们以一个学生管理系统为例。
一个可用的、完整的学生管理系统便是一个系统,系统一般是具有完整性的。
而学生管理系统又可以包括用户子系统,学生信息管理子系统,成绩管理子系统等等,我们可以明显看出其中差距,一个系统一般会有一个或者多个子系统构成。多个子系统相互协作构成较大的系统,划分子系统的原则往往是按照业务场景来,并不是绝对的,所以子系统也可以再次细分为多个子系统。系统也有可能是其他更大系统的子系统。
模块和组件往往容易被滥用,它们都是系统的组成部分,只是拆分的角度不同, 从逻辑的角度去拆分系统,得到的单元就是模块,例如学生管理系统可分为成绩模块,学生信息模块,而从物理的角度去拆分,又可以拆分为 web服务、MySQL、nosql、NGINX 等。
划分模块的目的是对职责进行分离,而划分组件的目的却是为了复用,因此组件一般是具有独立性的,可以替换的,跟一个完整的零件似的,例如我们使用 Apache、Tomcat、NGINX 等
应该没有人不熟悉框架了,框架其实主要是提供了规范性和基本的功能代码包,例如 go 的 gin、beego、go-zero 等框架,主要是定义了基本的目录结构以及提供了一些基本的代码处理和工具包,可以帮助开发快速去使用。
架构的定义相比之下比较模糊,不同的角度或者维度,可以将系统划分为不同的结构(架构),例如从物理部署的角度来说,它的架构可能是这样的:
我们可以说这是一种纯技术选择的架构,这也是大多数人熟悉的架构定义,使用了哪些技术栈(语言、工具、框架),彼此之间的关联关系是什么。
当然,代码结构也是一种架构,例如我们常说的 MVC 架构,这可是前几年校招必问的题目,这是从开发规范的角度来定义的。
而从业务角度来看,这样的模块划分关系图也是架构
架构的终极定义:
参考李老师的说法,架构的定义应该是软件系统的顶层结构,首先“系统是一群关联个体组成”,这些“个体”可以是“子系统”“模块”“组件”等;架构需要明确系统包含哪些“个体”,其次,系统中的个体需要“根据某种规则”运作,架构需要明确个体运作和协作的规则。
怎么样才算是单体架构,像博主身边有些同学就觉得,不是微服务架构就是单体架构,其实这样理解是不大正确的,单体架构风格就是简单地意味着所有应用代码被部署并运行在单一节点的单一进程中,第一个词“部署”的意思是运行时代码的组织方式,无论代码在物理上是存储在一个还是多个代码库之中。而第二个词“节点”的意思是即便是在横向扩展的情况下我们将应用部署到了多个服务器,它依然是一个单体。
以常见的 lmnp 架构为例,就是一个单体架构,所有的应用代码都是跑在了一个进程里面。内部相关的函数调用均在进程内处理,而非进程间通信。例如我司的大单体架构,代码量也已达到了百万级别(git clone下来几个G大小啊),常见的单体架构往往是这样的。
当然这个图忽略了负载均衡、cdn 相关的内容。我们可以清晰的看到,基本上,一个系统会包含很多的模块,并且不同的模块共用相同的资源(MySQL、Redis…),整体依赖性是十分的强,并且如果某个模块代码出现问题导致MySQL挂了,也就意味着整个系统就挂了。在上一篇《浅谈微服务》中,我说明了单体架构和微服务架构的优劣势。在这里我不在赘述。
重点记住这句话:无能力不微服务化、无能力不微服务化、无能力不微服务化
重点的事情讲三遍
微服务所带来的的技术复杂性、人力成本是一般中小公司无法承受的。
我们可以看看上面这张图。随着时间的推移,单体应用的复杂度只会呈线性去增长,而微服务架构确实复杂度呈线性较少。但是要搭起微服务这一套是十分复杂的。写代码只是其中的一小部分,想搭起适合公司的微服务体系,往往没有你想象中那么简单。当公司慢慢趋于稳定了,有较多的盈利了,这个时候为了后期的发展,就可以(必须)得考虑微服务架构了,找几个牛逼的技术专家+运维+领域专家+一堆开发,用DDD 去拆分领域,部分人继续维护老代码,部分人开始进行重构升级工作(大多数公司都是这么弄的),毕竟不能一刀切,重构的工作往往耗时较久,日常的迭代需求还是要开发的。这也是为什么搞微服务重构的公司会大量的招人,就是为了给重构让出必要的资源,为了让重构变的更有业务价值,往往会揪出当前业务的痛点,进行梳理,这样对公司来说,重构也是对业务有所帮助的。
所以,之所以要考虑微服务,主要是因为领导者预见了未来,并在资源充足的情况下所作出的决策。
用人话来说,就是有钱了为了提升工作效率,就去买了 Mac(当然也可能买其他性能较好的电脑,即选择其他的架构,不过Mac是开发公认较好的工具了),如果没钱还是继续用旧电脑刚吧。
微服务其中一个重要的核心就是去中心化,我们可以看看百度百科对其定义:
去中心化,不是不要中心,而是由节点来自由选择中心、自由决定中心。简单地说,中心化的意思,是中心决定节点。节点必须依赖中心,节点离开了中心就无法生存。在去中心化系统中,任何人都是一个节点,任何人也都可以成为一个中心。任何中心都不是永久的,而是阶段性的,任何中心对节点都不具有强制性。
中心化容易引发的一个问题就是单点奔溃。比如说我们的网站只有一个网关,如果这个网关挂了,没有其他的备用网关,那么整个系统也就凉了。在这里又不得聊起分布式,分布式一定是去中心化吗?
其实不然,去中心化一定是分布式的实现,但是分布式却不一定是去中心化。我们举个例子,MySQL的主从集群是去中心化吗?
一般来讲,存在主/从关系的系统是中心化系统。在这类系统中,主节点负责系统的整体运行管理,从节点则负责运行主节点安排的任务。在这种系统中,一般主节点出故障容易引起系统的崩溃,而从节点出问题则一般不会引起系统的崩溃。
这就意味着,由原本的中央集权控制,变成了人民自己当家做主(依靠的规则就是法律)
微服务架构在不同的公司有着不同的实现方式,不同的框架和语言,都有着自己的特色。例如 Java 的spring cloud, go 的 go-zero 框架,博主主要涉及的是 go + grpc 的模式。我简单画了一下微服务化的架构图。
我们通过图中可以看到,服务是位于最底层的。不同的服务一般拥有自己的资源库,例如自己的MySQL、Redis。这样,只要保证向前兼容,就可以随便的升级改造服务接口,不需要通知调用方做调整,拆分服务是微服务设计的最重要的一环,一般来说,没有绝对的拆分姿势,不过有一些公认的拆分规则:
说起 DDD 就比较烦了,太抽象了,等博主学完一波再来搞它。
以上图为例(不同公司的设计不同,这里只是举了一个例子)
实际上,图示请求链路是: 外网-> API GATEAWAY -> frontend -> bff -> microservice
这里要重点说明的是 API GATEAWAY 和 BFF 。
其实这张图是相对比较成熟的架构了(听说小破站也是这么搞的)。很多公司搞微服务既没有 API GATEAWAY 和 BFF, 更没有按服务拆分数据库。只是单纯将原本的后端代码拆分重构成微服务,对整体的架构并没有太大的变化,可以说做的并不彻底。
不过这也是受公司组织以及规模来决定的。有能力有资源当然要做彻底一点。
像以前,可能会存在这种直连的模式。
这种方式,虽然是按照 SOA 服务化架构去设计,但是对外暴露了很多微服务,也因为没有统一的出口容易遇到很多的问题:
界内有这么一个说法,当某一层不能实现一些事情的时候,我们就在上面再加一层。
通过 API GATEAWAY 我们可以不再直接对外暴露服务,并且 API GATEAWAY 还可以帮我们实现一些通用的鉴权,限流等操作。这样就不需要每个服务自己去实现相关的逻辑。
那 bff 到底是干嘛的呢?
BFF 可以认为是一种适配服务,将后端的微服务进行
适配(主要包括聚合裁剪和格式适配等逻辑),向其他客户端
设备暴露友好和统一的 API,有了BFF,后端在设计微服务的时候,就可以尽量的避免业务逻辑侵入。业务逻辑让 bff 那一层去实现。
像我司,bff 是通过 nodejs 实现的,我们后端在实现完微服务后,前端会写 bff 通过 rpc 调用来调用我们的后端服务。然后前台再去调用 bff。
同时为了保证系统的高可用,往往这些玩意也都不会进行单节点部署。因此,微服务架构和分布式架构基本是无法分隔的。