若是这是第二次看到个人文章,欢迎点击文末连接订阅个人我的公众号(跨界架构师)哟~本文长度为2728字,建议阅读8分钟。html
坚持原创,每一篇都是用心之做~前端
前面聊完的2个章节「数据一致性」和「高可用」其实本质是一个经过提高复杂度让总体更完善的方式。程序员
接下去咱们开始聊一些让系统更简单,更容易维护的东西——「易伸缩」,首当其冲的第一篇文章就是「stateless」,也叫「无状态」。算法
z哥带你先来认识一下「状态」是什么。数据库
以前在「负载均衡」的第四篇(分布式系统关注点——作了「负载均衡」就能够随便加机器了吗?)中提到过一个例子,咱们再翻出来一下。缓存
开发Z哥对运维Y弟喊:“Y弟,如今系统好卡,刚上了一波活动,赶忙帮我加几台机器上去顶一下。”服务器
Y弟回复说:“没问题,分分钟搞定”。微信
而后就发现数据库的压力迅速上升,DBA就吼了:“Z哥,你丫的搞什么呢?数据库要被你弄垮了”。网络
而后客服那边接框也爆炸了,愈来愈多的用户说刚登录后没多久,操做着就退出了,接着登录,又退出了,到底还作不作生意了。数据结构
这个案例中的问题,产生的根本缘由是由于系统中存在着大量「有状态」的业务处理过程。
N.Wirth曾经在它1984年出版的书中将程序的定义经典的归纳为:程序=数据结构+算法。(这个归纳也是这本书的书名)
这是一个颇有意思的启发,受它的影响,z哥认为程序作的事情本质就是“数据的移动和组合”,以此来达到咱们所指望的结果。而如何移动、如何组合是由“算法”来定的,因此z哥延伸出一个新的定义:数据+算法=成果。
经过程序处理所获得的“成果”其实和你平时生活中完成的任何事情所获得的“成果”是同样的。任何一个“成果”都是你经过一系列的“行动”将最开始的“原料”进行加工、转化,最终获得你所指望的“成果”。
好比,你将常温的水,经过“倒入水壶”、“通电加热”等工做后变成了100度的水,就是这样一个过程。
正如烧水的例子,大多数时候获得一个“成果”每每须要好几道“行动”才能完成。
这个时候若是想下降这几道“行动”总的成本(如:时间)该怎么办呢?
天然就是提炼出反复要作的事情,让其只作一次。而这个事情在程序中,就是将一部分“数据”放到一个「暂存区」(通常就是本地内存),以提供给相关的“行动”共用。
可是如此一来,就致使了须要增长一道关系,以表示每个“行动”与哪个「暂存区」关联。由于在程序里,“行动”多是「多线程」的。
这时,这个“行动”就变成「有状态」的了。
题外话:共用同一个「暂存区」的多个“行动”所处的环境常常被称做「上下文」。
咱们再来深刻聊聊「有状态」。
「暂存区」里存的是「数据」,因此能够理解为“有数据”就等价于“有状态”。
「数据」在程序中的做用范围分为「局部」和「全局」(对应局部变量和全局变量),所以「状态」其实也能够分为两种,一种是局部的「会话状态」,一种是全局的「资源状态」。
题外话:由于有些服务端不仅仅负责运算,还会提供其自身范围内的「数据」出去,这些「数据」属于服务端完整的一部分,被称做「资源」。因此,理论上「资源」能够被每一个「会话」来使用,所以是全局的状态。
本文聊的「有状态」都指的是「会话状态」。
与「有状态」相反的是「无状态」,「无状态」意味着每次“加工”的所需的“原料”所有由外界提供,服务端内部不作任何的「暂存区」。而且请求能够提交到服务端的任意副本节点上,处理结果都是彻底同样的。
有一类方法天生是「无状态」,就是负责表达移动和组合的“算法”。由于它的本质就是:
接收“原料”(入参)
“加工”并返回“成果”(出参)
为何网上主流的观点都在说要将方法多作成「无状态」的呢?
由于咱们更习惯于编写「有状态」的代码,可是「有状态」不利于系统的易伸缩性和可维护性。
在分布式系统中,「有状态」意味着一个用户的请求必须被提交到保存有其相关状态信息的服务器上,不然这些请求可能没法被理解,致使服务器端没法对用户请求进行自由调度(例如双11的时候临时加再多的机器都没用)。
同时也致使了容错性很差,假若保有用户信息的服务器宕机,那么该用户最近的全部交互操做将没法被透明地移送至备用服务器上,除非该服务器时刻与主服务器同步所有用户的状态信息。
这两个问题在负载均衡的第四篇(分布式系统关注点——作了「负载均衡」就能够随便加机器了吗?)中也有提到。
可是若是想得到更好的伸缩性,就须要尽可能将「有状态」的处理机制改形成「无状态」的处理机制。
将「有状态」的处理过程改形成「无状态」的,思路比较简单,内容很少。
首先,状态信息前置,丰富入参,将处理须要的数据尽量都经过上游的客户端放到入参中传过来。
固然,这个方案的弊端也很明显:网络数据包的大小会更大一些。
另外,客户端与服务端的交互中若是涉及到屡次交互,则须要来回传递后续服务端处理中所需的数据,以免须要在服务端暂存。
这些改造的目的都是为了尽可能少出现相似下面的代码。
func(){
return i++;
}
而是变成:
func(i){
return i+1;
}
要更好的作好这个「无状态」化的工做,依赖于你在架构设计或者项目设计中的合理分层。
尽可能将会话状态相关的处理上浮到最前面的层,由于只有最前面的层才与系统使用者接触,如此一来,其它的下层就能够将「无状态」做为一个广泛性的标准去作。
与此同时,因为会话状态集中在最前面的层,因此哪怕真的状态丢失了,重建状态的成本相对也小不少。
好比三层架构的话,保证BLL和DAL都不要有状态,代码的可维护性大大提升。
若是是分布式系统的话,保证那些被服务化的程序都不要有状态。除了能提升可维护性,也大大有利于作灰度发布、A/B测试。
题外话:在这里,提到作分层的目的是为了说明,只有将IO密集型程序和CPU密集型程序分离,才是通往「无状态」真正的出路。一旦分离后,CPU密集型的程序天然就是「无状态」了。
如此也能更好的作「弹性扩容」。由于常见的须要「弹性扩容」的场景通常指的就是CPU负荷过大的时候。
最后,若是前面的都不合适,能够将共享存储做为降级预案来运用,如远程缓存、数据库等。而后当状态丢失的时候能够从这些共享存储中恢复。
因此,最理想的状态存放点。要么在最前端,要么在最底层的存储层。
任何事物都是有两面性的,正如前面提到的,咱们并非要全部的业务处理都改形成「无状态」,而只是挑其中的一部分。最终仍是看“价值”,看“性价比”。
好比,将一个以“状态”为核心的即时聊天工具的全部处理过程都改形成「无状态」的,就有点得不偿失了。
相关文章:
若是你喜欢这篇文章,能够点一下左侧的「大拇指」。
这样能够给我一点反馈。: )
谢谢你的举手之劳。
▶关于做者:张帆(Zachary,我的微信号:Zachary-ZF)。坚持用心打磨每一篇高质量原创。本文首发于公众号:「跨界架构师」(ID:Zachary_ZF)。<-- 点击后阅读热门文章
按期发表原创内容:架构设计丨分布式系统丨产品丨运营丨一些思考。
若是你是初级程序员,想提高但不知道如何下手。又或者作程序员多年,陷入了一些瓶颈想拓宽一下视野。欢迎关注个人公众号「跨界架构师」,回复「技术」,送你一份我长期收集和整理的思惟导图。
若是你是运营,面对不断变化的市场一筹莫展。又或者想了解主流的运营策略,以丰富本身的“仓库”。欢迎关注个人公众号「跨界架构师」,回复「运营」,送你一份我长期收集和整理的思惟导图。