分布式系统关注点——「无状态」详解

如果这是第二次看到我的文章,欢迎下方扫码订阅我的个人公众号(跨界架构师)哟~ ?
本文长度为2728字,建议阅读8分钟。
坚持原创,每一篇都是用心之作~

前面聊完的2个章节「数据一致性」和「高可用」其实本质是一个通过提升复杂度让整体更完善的方式。

接下去我们开始聊一些让系统更简单,更容易维护的东西——「易伸缩」,首当其冲的第一篇文章就是「stateless」,也叫「无状态」。

z哥带你先来认识一下「状态」是什么。

一、初识「状态」

之前在「负载均衡」的第四篇(分布式系统关注点——做了「负载均衡」就可以随便加机器了吗?)中提到过一个例子,我们再翻出来一下。

开发Z哥对运维Y弟喊:“Y弟,现在系统好卡,刚上了一波活动,赶紧帮我加几台机器上去顶一下。”

Y弟回复说:“没问题,分分钟搞定”。

然后就发现数据库的压力迅速上升,DBA就吼了:“Z哥,你丫的搞什么呢?数据库要被你弄垮了”。

然后客服那边接框也爆炸了,越来越多的用户说刚登陆后没多久,操作着就退出了,接着登陆,又退出了,到底还做不做生意了。

这个案例中的问题,产生的根本原因是因为系统中存在着大量「有状态」的业务处理过程。

二、「有状态」和「无状态」

N.Wirth曾经在它1984年出版的书中将程序的定义经典的概括为:程序=数据结构+算法。(这个概括也是这本书的书名)

这是一个很有意思的启发,受它的影响,z哥认为程序做的事情本质就是“数据的移动和组合”,以此来达到我们所期望的结果。而如何移动、如何组合是由“算法”来定的,所以z哥延伸出一个新的定义:数据+算法=成果

通过程序处理所得到的“成果”其实和你平时生活中完成的任何事情所得到的“成果”是一样的。任何一个“成果”都是你通过一系列的“行动”将最开始的“原料”进行加工、转化,最终得到你所期望的“成果”。

图片描述

比如,你将常温的水,通过“倒入水壶”、“通电加热”等工作后变成了100度的水,就是这样一个过程。

正如烧水的例子,大多数时候得到一个“成果”往往需要好几道“行动”才能完成。

图片描述

这个时候如果想降低这几道“行动”总的成本(如:时间)该怎么办呢?

自然就是提炼出反复要做的事情,让其只做一次。而这个事情在程序中,就是将一部分“数据”放到一个「暂存区」(一般就是本地内存),以提供给相关的“行动”共用。

图片描述

但是如此一来,就导致了需要增加一道关系,以表示每一个“行动”与哪一个「暂存区」关联。因为在程序里,“行动”可能是「多线程」的。

这时,这个“行动”就变成「有状态」的了。

图片描述

题外话:共用同一个「暂存区」的多个“行动”所处的环境经常被称作「上下文」。


我们再来深入聊聊「有状态」。

「暂存区」里存的是「数据」,所以可以理解为“有数据”就等价于“有状态”。

「数据」在程序中的作用范围分为「局部」和「全局」(对应局部变量和全局变量),因此「状态」其实也可以分为两种,一种是局部的「会话状态」,一种是全局的「资源状态」

题外话:因为有些服务端不单单负责运算,还会提供其自身范围内的「数据」出去,这些「数据」属于服务端完整的一部分,被称作「资源」。所以,理论上「资源」可以被每个「会话」来使用,因此是全局的状态。

本文聊的「有状态」都指的是「会话状态」。


与「有状态」相反的是「无状态」,「无状态」意味着每次“加工”的所需的“原料”全部由外界提供,服务端内部不做任何的「暂存区」。并且请求可以提交到服务端的任意副本节点上,处理结果都是完全一样的

有一类方法天生是「无状态」,就是负责表达移动和组合的“算法”。因为它的本质就是:

  1. 接收“原料”(入参)
  2. “加工”并返回“成果”(出参)


为什么网上主流的观点都在说要将方法多做成「无状态」的呢?

因为我们更习惯于编写「有状态」的代码,但是「有状态」不利于系统的易伸缩性和可维护性。

在分布式系统中,「有状态」意味着一个用户的请求必须被提交到保存有其相关状态信息的服务器上,否则这些请求可能无法被理解,导致服务器端无法对用户请求进行自由调度(例如双11的时候临时加再多的机器都没用)。

同时也导致了容错性不好,倘若保有用户信息的服务器宕机,那么该用户最近的所有交互操作将无法被透明地移送至备用服务器上,除非该服务器时刻与主服务器同步全部用户的状态信息。

这两个问题在负载均衡的第四篇(分布式系统关注点——做了「负载均衡」就可以随便加机器了吗?)中也有提到。


但是如果想获得更好的伸缩性,就需要尽量将「有状态」的处理机制改造成「无状态」的处理机制。

三、「无状态」化处理

将「有状态」的处理过程改造成「无状态」的,思路比较简单,内容不多。

首先,状态信息前置,丰富入参,将处理需要的数据尽可能都通过上游的客户端放到入参中传过来。

图片描述

当然,这个方案的弊端也很明显:网络数据包的大小会更大一些

另外,客户端与服务端的交互中如果涉及到多次交互,则需要来回传递后续服务端处理中所需的数据,以避免需要在服务端暂存。

图片描述

这些改造的目的都是为了尽量少出现类似下面的代码。

func(){

    return i++;

}

而是变成:

func(i){

    return i+1;

}

要更好的做好这个「无状态」化的工作,依赖于你在架构设计或者项目设计中的合理分层。

尽量将会话状态相关的处理上浮到最前面的层,因为只有最前面的层才与系统使用者接触,如此一来,其它的下层就可以将「无状态」作为一个普遍性的标准去做。

与此同时,由于会话状态集中在最前面的层,所以哪怕真的状态丢失了,重建状态的成本相对也小很多。

比如三层架构的话,保证BLL和DAL都不要有状态,代码的可维护性大大提高。

如果是分布式系统的话,保证那些被服务化的程序都不要有状态。除了能提高可维护性,也大大有利于做灰度发布、A/B测试。

题外话:在这里,提到做分层的目的是为了说明,只有将IO密集型程序和CPU密集型程序分离,才是通往「无状态」真正的出路。一旦分离后,CPU密集型的程序自然就是「无状态」了。

如此也能更好的做「弹性扩容」。因为常见的需要「弹性扩容」的场景一般指的就是CPU负荷过大的时候。

最后,如果前面的都不合适,可以将共享存储作为降级预案来运用,如远程缓存、数据库等。然后当状态丢失的时候可以从这些共享存储中恢复。

所以,最理想的状态存放点。要么在最前端,要么在最底层的存储层。

图片描述

四、总结

任何事物都是有两面性的,正如前面提到的,我们并不是要所有的业务处理都改造成「无状态」,而只是挑其中的一部分。最终还是看“价值”,看“性价比”。

比如,将一个以“状态”为核心的即时聊天工具的所有处理过程都改造成「无状态」的,就有点得不偿失了。


相关文章:


作者:Zachary

出处:https://www.cnblogs.com/Zacha...


如果你喜欢这篇文章,可以点一下底部的「?」。

这样可以给我一点反馈。: )

谢谢你的举手之劳。


▶关于作者:张帆(Zachary,个人微信号:Zachary-ZF)。坚持用心打磨每一篇高质量原创。欢迎扫描下方的二维码加入哦~。

定期发表原创内容:架构设计丨分布式系统丨产品丨运营丨一些思考

如果你是初级程序员,想提升但不知道如何下手。又或者做程序员多年,陷入了一些瓶颈想拓宽一下视野。欢迎关注我的公众号「跨界架构师」,回复「技术」,送你一份我长期收集和整理的思维导图。
如果你是运营,面对不断变化的市场束手无策。又或者想了解主流的运营策略,以丰富自己的“仓库”。欢迎关注我的公众号「跨界架构师」,回复「运营」,送你一份我长期收集和整理的思维导图。

图片描述


跨界架构师
一名纯正互联网技术人的专栏,专注系统架构、分布式系统,涉猎产品、运营。 加入小圈子 --> 微信同名...
85 声望
31 粉丝
0 条评论
推荐阅读
如何优雅地做系统错误提示?
这里是Z哥的个人公众号每周五11:45 按时送达当然了,也会时不时加个餐~我的第「149」篇原创敬上大家好,我是Z哥。不管是日常的工作中还是生活中,我们每天会用到很多软件系统。不知道你有没有过这样的感受,当...

Zachary阅读 1.3k

PHP转Go实践:xjson解析神器「开源工具集」
我和劲仔都是PHP转Go,身边越来越多做PHP的朋友也逐渐在用Go进行重构,重构过程中,会发现php的json解析操作(系列化与反序列化)是真的香,弱类型语言的各种隐式类型转换,很大程度的减低了程序的复杂度。

王中阳Go10阅读 1.9k评论 2

封面图
万字详解,吃透 MongoDB!
MongoDB 是一个基于 分布式文件存储 的开源 NoSQL 数据库系统,由 C++ 编写的。MongoDB 提供了 面向文档 的存储方式,操作起来比较简单和容易,支持“无模式”的数据建模,可以存储比较复杂的数据类型,是一款非常...

JavaGuide5阅读 789

封面图
计算机网络连环炮40问
本文已经收录到Github仓库,该仓库包含计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等核心知识点,欢迎star~

程序员大彬8阅读 1.1k

与RabbitMQ有关的一些知识
工作中用过一段时间的Kafka,不过主要还是RabbitMQ用的多一些。今天主要来讲讲与RabbitMQ相关的一些知识。一些基本概念,以及实际使用场景及一些注意事项。

lpe2348阅读 1.9k

封面图
Git操作不规范,战友提刀来相见!
年终奖都没了,还要扣我绩效,门都没有,哈哈。这波骚Git操作我也是第一次用,担心闪了腰,所以不仅做了备份,也做了笔记,分享给大家。问题描述小A和我在同时开发一个功能模块,他在优化之前的代码逻辑,我在开...

王中阳Go5阅读 2.3k评论 2

封面图
Redis 发布订阅模式:原理拆解并实现一个消息队列
“65 哥,如果你交了个漂亮小姐姐做女朋友,你会通过什么方式将这个消息广而告之给你的微信好友?““那不得拍点女朋友的美照 + 亲密照弄一个九宫格图文消息在朋友圈发布大肆宣传,暴击单身狗。”像这种 65 哥通过朋...

码哥字节6阅读 1.5k

封面图
85 声望
31 粉丝
宣传栏