程序员过关斩将--论系统设计的高可扩展性
“此文仅仅代表个人意见,并非行业标准
“MQ是万能的高扩展方式?
“面向接口是万能的高扩展方式?
说到系统设计的三高,每一高都是一个很庞大的话题,甚至可以用一本书甚至N本书来详细阐述。其中高可扩展性是系统架构的众多目标之一。归根结底,系统的架构要为最终的业务服务,脱离业务来谈架构其实比耍流氓更无耻。
在我们心目中最理想的软件架构要像搭积木一样简单
,并且快捷,而且高效。但是现实往往比996更残酷,多数的系统在初期为了配合业务快速上线,扩展性这个指标并不理想。别的不谈,一个系统要完美的做到“对修改封闭,对扩展开放”其实一点也不简单,不知道你有没有遇到过修改一个bug蹦出另外一个bug的痛苦经历?
为了做到系统的高扩展性,其实有很多借鉴的案例,尤其是设计模式。但是今天我还是要说一说我自己的看法。无论什么样的系统,抽象起来其实都是模块和模块之间的交互
,这里模块的含义是广义的,即可以代表函数,也可以代表进程,甚至可以代表目前流行的微服务
,如下图所示
图是不是很简单?但是要想把A和B之间的交互做到高扩展其实并不容易,这要求系统的设计者必须要想办法在满足A和B正常交互的情况下尽量解耦A和B,只有正确的解耦,才能从容的应对A和B独立扩展的业务需求
同一进程内
在同一进程内的情况是一种最常见的存在方式,对应到我们平时的代码,表现为函数的调用,而这里的函数调用可以是同一模块内的函数调用,比如最典型的三层架构中,业务层调用持久化层来进行数据的操作,如下代码:
//user 业务层
public class UserBLL
{
UserDAL dal = new UserDAL();
public int AddUser(User user)
{
//其他业务
return dal.AddUser(user);
}
}
//user持久化层
public class UserDAL
{
public int AddUser(User user)
{
//进行数据库操作
return 0;
}
}
我真的希望实际项目中的代码能像以上代码这么简单,毕竟代码就和项目一样,简单即是美。这段代码排除业务之外,从架构来讲也有很多问题,用开头的A和B的方式来表示,A代表的是UserBLL,B代表的是UserDAL,这里最容易看出的就是强耦合,即:A严重依赖于B,如果B有什么风吹草动,势必会影响A的执行。
怎么办呢?所以有了B的抽象层
,对应到代码上是IDAL接口层,当然这个抽象层应该是稳定的,如果三天两头修改抽象层,那说明抽象的有问题。A在执行上改为依赖IDAL,这是系统内设计最常见的面向接口设计模式,其实更准确的说,应该是面向抽象设计模式。由于引入了稳定的抽象层,不再稳定的实现层就可以根据实际的业务去修改,这里体现的是系统设计中依赖倒置
的原则,当然为了实现依赖倒置,你可能需要使用IOC等技术来实现项目落地。
//user 业务层
public class UserBLL
{
IUserDAL dal = "依赖注入";
public int AddUser(User user)
{
//其他业务
return dal.AddUser(user);
}
}
//user的持久化层抽象
public interface IUserDAL
{
int AddUser(User user);
}
//user持久化层
public class UserDAL: IUserDAL
{
public int AddUser(User user)
{
//进行数据库操作
return 0;
}
}
不同进程间
不同的进程之间互相协作是目前分布式模式下主要的交互方式,例如之前的SOA,现在的微服务,都是在利用分散在不同位置的模块来组装系统,这些模块之间的通信是一个分布式系统必备的条件。
和进程内函数调用类似,分布式系统也可以抽象为A和B的关系模型,我们要解决的也是A和B能够独立变化
的问题。现在假设A服务依赖于B服务,B服务由于压力大需要扩容,会有哪些影响呢?
-
B自己内部的状态变化,如果B服务是有状态的,扩展起来可能会设计到数据的迁移等操作,如果B是无状态的,理论来说可以很方便的横向扩展 -
B的扩容对A或者其他依赖于B的系统有什么影响,依赖方能否做到自动适配,而不必修改任何配置
和进程内函数调用不同,进程间的通信需要通讯协议的支持,最常见的RPC调用都是基于TCP协议,Restfull基于http协议,使用这些协议底层都需要指定明确的IP和端口。所以需要某种解决方案在被依赖方扩展的时候,依赖方能够得到感知
。聪明的你可能想到了“注册中心”,不错,这也是注册中心最主要的职责。
解决方案2
用注册中心的方式,理论上属于通知依赖方的方案,在依赖方感知被依赖方有扩展变动的时候,需要作出对应的变化。与之对应的其实我们也可以把变动封装在被依赖方
,这个时候就引入了以下代理模式,最常见的就是网关模式。
其实代理模式非常常见,比如Nginx做反向代理,数据库的中间件。这些设施都是对依赖方透明的,依赖方不会因为被依赖方实施了扩展而受影响。
解决方案3
目前很多业务下有一种很常见的场景,依赖方和被依赖方通信并不需要知道执行结果,最典型的场景像:新用户注册给用户发欢迎邮件或者短信欢迎语。如果业务代码中冗余了发邮件或者短信的代码的话,一旦要添加新的欢迎方式就必须要修改业务代码,无论你是否有抽象层,为了不影响主要的业务又最大化解耦系统,一般都会把这种非主要业务通过消息的方式分离出来。最常见的解决方案就是MQ。这也是典型发布订阅模式,但是这种模式如上所说,调用方并不能实时的得到业务处理结果。
利用MQ来进行系统的解耦,来实现系统的高可扩展是一种非常常见的方式,优势有很多,我不再阐述,但是需要注意消息的可靠性,因为消息经过了几个环节之后,难保某个环节出现问题而丢失消息
。具体的详细介绍可以查看
写在最后
A和B之间的通信如果只是单向的话,可以理解为上下级关系,但是在微服务情况下,A和B很多时候是平行的互相调用的兄弟关系。有的架构师不赞成平行关系
的微服务互相调用,这是有一定道理的,因为这很容易造成复杂的网络调用模式,如果是符合MQ消息的形式通信,我也推荐首推利用MQ来解耦服务间的依赖。
高可扩展性系统的最终目标是在应对业务变化的时候,用最小的代价去实现。而如何实现系统的扩展性,并非只有以上所说的“面向接口编程”,利用MQ这些方式,你还知道哪些可以帮助系统扩展的解决方案吗?欢迎你给我留言!!
“只要一提到解耦,有的“高手”一上来就说利用MQ,真的对吗?如果调用方需要实时的业务处理结果呢?
更多精彩文章
本文分享自微信公众号 - 架构师修行之路(jiagoushixiuxing)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
从单元测试覆盖率看富领域模型到底有多富
从单元测试覆盖率看富领域模型到底有多富 使用了DDD(领域驱动设计)后,代码有什么不一样呢?这可能是程序员们在接触DDD后最关心的一个问题。这个系列文章会对一些优秀的DDD实例代码进行分析,管中窥豹,略见数斑。这是第一篇。 DDD中程序员最关心的部分 DDD(领域驱动设计)是一个复杂而全面的方法,编码只是最后一步。 https://github.com/ddd-crew/ddd-starter-modelling-process 但这是程序员最关心的部分,也是被问到最多的问题。 所以计划写个系列文章对一些优秀的DDD实例代码进行分析,希望能解开一些困惑。 IDDD(《实现领域驱动设计》),是最常被推荐的一本书。书中有一个关于构建Scrum管理软件的例子,我们先来分析这个经典的例子的源码(https://github.com/VaughnVernon/IDDD_Samples)。 第一篇先从单元测试和富领域模型说起。 我所目睹单元测试之怪状 关于自动化的单元测试,无数人都心怀向往。 《The Clean Coder》里边描述的测试金字塔给出了单元测试在整个测试中的位置。 经是好经,但大部...
- 下一篇
用Python爬取英雄联盟(lol)全部皮肤
小三:“怎么了小二?一副无精打采的样子!” 小二:“唉!别提了,还不是最近又接触了一个叫英雄联盟的游戏,游戏中很多皮肤都需要花钱买,但是我钱不够呀...” 小三:“咋得,钱攒够了你还要买呀?还吃不吃饭了?!要我说,你干脆将英雄的炫彩皮肤都爬下来欣赏一下得了,饭钱还给你省下了。” 小二:“你说的也对,毕竟吃饭更重要,那我还是爬取皮肤欣赏一下算了。” 首先,我们打开英雄联盟官网主页,网址为:https://lol.qq.com/main.shtml,然后向下拉,可以看到英雄列表,如图所示: 接着随意选一个英雄点击进入看一下,如图所示: 再点击鼠标右键,接着选择检查,看一下皮肤的 URL,如图所示: 通过观察,可以发现英雄皮肤 URL 组成方式为:https://game.gtimg.cn/images/lol/act/img/skin/big + 英雄id + 皮肤id.jpg。 我们先看皮肤id,也就是看皮肤的个数,选择开发者工具的Network项,之后刷新一下页面,可以发现有一个17.js的请求,17实际就是英雄id,如图所示: 再选择Response项看一下相应数据,如图所示: 我们...
相关文章
文章评论
共有0条评论来说两句吧...