当前位置: 首页 > 科技观察

程序员过关--论系统设计的高扩展性

时间:2023-03-17 21:42:08 科技观察

本文仅代表个人观点,不代表行业标准。“MQ是一种通用的高扩展性方法?”面向接口是一种通用的高扩展性方法吗?说到系统设计的三高,每一高都是一个非常庞大的话题,甚至可以用一本书甚至N本书来详细解释。其中,高可扩展性是系统架构的众多目标之一。归根结底,系统架构应该为最终的业务服务。抛开业务谈架构,其实比耍流氓还不要脸。在我们心目中,理想的软件架构应该像搭积木一样简单、快速、高效。但现实往往比996更残酷,前期为了快速上线大部分系统,可扩展性指标并不理想。撇开其他不谈,一个系统要做到完美“对修改关闭,对扩展开放”并不容易。不知大家有没有遇到过修改一个bug又弹出另一个bug的痛苦经历?为了实现系统的高扩展性,其实有很多参考案例,尤其是设计模式。但是今天我还是想谈谈我自己的看法。不管是哪种系统,抽象的其实都是模块之间的交互。模块在这里的含义很广泛,就是可以代表功能,可以代表流程,甚至可以代表流行的微服务,如下图这个形象图是不是很简单?但是要实现A和B之间交互的高度扩展并不容易,这就需要系统设计者想办法在满足A和B正常交互的情况下尽可能的解耦。A和B,只有正确的解耦能够从容应对A、B独立扩容的业务需求。同一个进程中的同一个进程是最常见的存在方式,对应我们平时的代码,表现为函数调用,这里的函数调用可以是同一个模块中的函数调用。比如最典型的三层架构,业务层调用持久层进行数据操作,如下://user业务层publicclassUserBLL{UserDALdal=newUserDAL();publicintAddUser(Useruser){//其他业务returndal.AddUser(user);}}//用户持久层publicclassUserDAL{publicintAddUser(Useruser){//做数据库操作return0;}}真希望在实际项目中代码可以像上面的代码一样简单,毕竟代码和项目一样,简单就是美。这段代码排除了业务,在架构方面存在很多问题。开头用A和B表示。A代表UserBLL,B代表UserDAL。这里最容易看到的是强耦合,即:A对B的依赖性很强,如果B有任何扰动,必然会影响到A的执行,怎么办?于是就有了B的抽象层,对应代码上的IDAL接口层。当然,这个抽象层应该是稳定的。如果每隔几天修改一次抽象层,就说明抽象出了问题。A改为依赖IDAL执行,是系统设计中最常见的面向接口的设计模式。其实更准确的说应该是面向抽象的设计模式。由于引入了稳定的抽象层,不再稳定的实现层可以根据实际业务进行修改。这体现了系统设计中依赖倒置的原则。当然,为了实现依赖倒置,你可能需要使用IOC等技术。项目落地。//用户业务层publicclassUserBLL{IUserDALdal="DependencyInjection";publicintAddUser(Useruser){//其他业务returndal.AddUser(user);}}//用户的持久层抽象publicinterfaceIUserDAL{intAddUser(Useruser);}//用户持久层publicclassUserDAL:IUserDAL{publicintAddUser(Useruser){//执行数据库operationsreturn0;}}不同进程之间的协作是当前分布式模式下的主要交互方式,比如之前的SOA,现在的微服务都是使用分散在不同位置的模块来组装系统,这些模块之间的通信是必要的分布式系统的条件。类似于进程内函数调用,分布式系统也可以抽象为A和B的关系模型,我们要解决的是A和B可以独立变化的问题。现在假设服务A依赖服务B,服务B压力大需要扩容。如果B是无状态的,理论上来说,横向扩展B的容量是很方便的。B的扩容会对A或其他依赖B的系统产生什么影响?依赖方是否可以在不修改任何配置或流程的情况下实现自动适配?函数调用不同。进程之间的通信需要通信协议的支持。最常见的RPC调用是基于TCP协议的,而Restfull是基于http协议的。这些协议的底层需要指定明确的IP和端口。因此,需要某种解决方案,使得依赖方在扩展依赖方时能够感知。如果你聪明的话,你可能会想到“注册中心”。是的,这也是注册中心的主要职责。方案二采用注册中心方式,理论上属于通知依赖方方案。当依赖方感知到依赖方有扩展变化时,需要做出相应的改变。相应的,我们也可以封装依赖方的变化。这时引入了以下代理模式,最常见的是网关模式。在分布式系统中使用网关是好是坏?其实代理模式很常见,比如Nginx作为反向代理和数据库中间件。这些设施对依赖方是透明的,依赖方不会受到依赖方实施的扩展的影响。方案三目前在很多业务中有一个很常见的场景。依赖方在与依赖方通信时不需要知道执行结果。最典型的场景是:一个新用户注册并向用户发送欢迎邮件或短信。如果业务代码中发送邮件或短信的代码是多余的,一旦想增加新的欢迎方式,就必须修改业务代码。不管你有没有抽象层,为了不影响主业务,最大限度的解耦系统,一般这种非主业务会通过消息的方式进行分离。最常见的解决方案是MQ。这也是典型的发布-订阅模型,但是如前所述,调用方无法实时获取业务处理结果。使用MQ对系统进行解耦,实现系统的高扩展性,是一种很常见的方式。优点有很多,这里不再赘述,但是需要注意消息的可靠性,因为消息经过几个环节后,很难保证某个环节出现问题消息将丢失。如果最后写的A和B之间的通信只是单向的,可以理解为上下级关系,但是在微服务的情况下,A和B往往是互相调用的平行兄弟。一些架构师不赞成并行微服务之间的相互调用。这是合理的,因为很容易造成复杂的网络调用模式。如果是MQ消息通信的形式,我也推荐使用MQ来解决问题。耦合服务之间的依赖关系。高度可扩展系统的最终目标是在响应业务变化时以最小的成本实现它。而如何实现系统的可扩展性,不仅仅是上面提到的“面向接口编程”。使用MQ,你知道有什么方案可以帮助系统扩展吗?欢迎您给我留言!!》只要一提到解耦,有些“高手”说一上来就用MQ,是真的吗?如果调用方需要实时业务处理结果怎么办?二维码关注。转载此文,请联系建筑师的修行之路公众号。