分布式系统关注点—“无状态”详解

一、初识“状态”
我们首先举个例子。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

我们再来深入聊聊“有状态”。

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

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

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

本文聊的“有状态”都指的是“会话状态”。

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

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

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

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

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

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

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

但是如果想获得更好的伸缩性,就需要尽量将“有状态”的处理机制改造成“无状态”的处理机制。
三、“无状态”化处理
将“有状态”的处理过程改造成“无状态”的,思路比较简单,内容不多。

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

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

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

(橙色请求,绿色响应)

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

func(){
returni++;
}

而是变成:

func(i){
returni+1;
}

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

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

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

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

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

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

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

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

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

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

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

原文地址:http://blog.51cto.com/14158311/2346124

时间: 2024-08-02 06:44:31

分布式系统关注点—“无状态”详解的相关文章

netstat状态详解

一.生产服务器netstat tcp连接状态................................................................................ 2 1.1生产服务器某个业务LVS负载均衡上连接状态数量............................................... 2 1.2生产服务器某个业务web上连接状态数量...............................................

TCP协议状态详解

原文出自:Vimer的程序世界 1.建立连接协议(三次握手)(1)客户端发送一个带SYN标志的TCP报文到服务器.这是三次握手过程中的报文1.(2) 服务器端回应客户端的,这是三次握手中的第2个报文,这个报文同时带ACK标志和SYN标志.因此它表示对刚才客户端SYN报文的回应:同时又标志SYN给客户端,询问客户端是否准备好进行数据通讯.(3) 客户必须再次回应服务段一个ACK报文,这是报文段3. 2.连接终止协议(四次握手) 由于TCP连接是全双工的,因此每个方向都必须单独进行关闭.这原则是当一

用netstat查看网络状态详解

--用netstat查看网络状态详解 -----------------------------2014/06/11 一.Linux服务器上11种网络连接状态:                                          图:TCP的状态机 通常情况下:一个正常的TCP连接,都会有三个阶段:1.TCP三次握手;2.数据传送;3.TCP四次挥手 注:以下说明最好能结合"图:TCP的状态机"来理解. SYN: (同步序列编号,Synchronize Sequence

[转]iOS应用程序生命周期(前后台切换,应用的各种状态)详解

转载地址:http://blog.csdn.net/totogo2010/article/details/8048652 iOS的应用程序的生命周期,还有程序是运行在前台还是后台,应用程序各个状态的变换,这些对于开发者来说都是很重要的. iOS系统的资源是有限的,应用程序在前台和在后台的状态是不一样的.在后台时,程序会受到系统的很多限制,这样可以提高电池的使用和用户体验. //开发app,我们要遵循apple公司的一些指导原则,原则如下: 1.应用程序的状态 状态如下: Not running

iOS iOS应用程序生命周期(前后台切换,应用的各种状态)详解

iOS应用程序生命周期(前后台切换,应用的各种状态)详解 http://blog.csdn.net/totogo2010/article/details/8048652

启用php-fpm状态详解

status_path 启用php-fpm状态详解

转:iOS应用程序生命周期(前后台切换,应用的各种状态)详解

iOS应用程序生命周期(前后台切换,应用的各种状态)详解 分类: iOS开发进阶2012-10-08 15:35 42691人阅读 评论(30) 收藏 举报 iosapplication任务animationtimerxcode 目录(?)[+] iOS的应用程序的生命周期,还有程序是运行在前台还是后台,应用程序各个状态的变换,这些对于开发者来说都是很重要的. iOS系统的资源是有限的,应用程序在前台和在后台的状态是不一样的.在后台时,程序会受到系统的很多限制,这样可以提高电池的使用和用户体验.

启用nginx status状态详解

nginx和php-fpm一样内建了一个状态页,对于想了解nginx的状态以及监控nginx非常有帮助.为了后续的zabbix监控,我们需要先了解nginx状态页是怎么回事. 1. 启用nginx status配置 在默认主机里面加上location或者你希望能访问到的主机里面. server { listen *:80 default_server; server_name _; location /ngx_status { stub_status on; access_log off; #a

TCP-IP协议、状态详解

今天对TCP-IP协议做一个简单总结.以便日后自己查看. 本文通过两个图来梳理TCP-IP协议相关知识.TCP通信过程包括三个步骤:建立TCP连接通道,传输数据,断开TCP连接通道. 如图1所示,给出了TCP通信过程的示意图. 图1主要包括三部分:建立连接.传输数据.断开连接 一.概述: 1)建立TCP连接很简单,通过三次握手便可建立连接. 2)建立好连接后,开始传输数据.TCP数据传输牵涉到的概念很多:超时重传.快速重传.流量控制.拥塞控制等等. 3)断开连接的过程也很简单,通过四次握手完成断