小白科普:“无状态”那点事儿

2018 年 3 月 28 日 码农翻身 刘欣

前言: 周一推送的干货文章有点难了,今天来个轻松一点儿的!

软件大师正在闭目修炼, 最小的一名弟子慢慢走了进来。


大师,弟子有一事不明,甚是烦恼。

说来听听,让为师给你排解一下。


我经常听师兄们争论‘无状态’, 说‘无状态’在软件编程中是好事情, 可是到底什么是状态? 什么是无状态?


大师睁开眼来,写下一行字:  y=f(x),然后又闭上了眼睛。



(奇怪地问道)这不就是一个函数吗?我初中就学过, 给定一个x,函数经过计算(比如求平方)就能得到一个y。

没错,这就是一个纯函数,对于相同的输入,总是得到相同的输出,不依赖于外界的状态。


这也没什么啊!

你想想,要是有多个线程在一个CPU上并发调用这个函数,会不会有问题?


不会。

如果是有多个线程在多个CPU上并行执行这个函数,会不会有问题?


不会。

为什么?


因为每次调用都不会在这个函数中保留数据, 调用完了就完了,每一次调用都是崭新的调用,并且第一次和第一百次之间没有任何关系。

因为那个函数不保存状态,所以无论是并发还是并行,都没有问题。


嗯,明白。

你再想想你常用的HTTP,每次访问一个静态HTML页面的时候,对于服务器来讲,是不是就相当于调用了一个函数,函数输入:一个URL路径, 函数输出:HTML页面。


那这么说来,这个服务器也不会记录每次请求的是谁,只要执行这个'函数调用'就可以了。

你说说,这样的HTTP协议有什么好处?


由于没有状态,如果一个服务器访问量过大,我可以轻松地添加新的服务器来处理请求。



“孺子可教也,这就是所谓水平扩展(scale-out)。


水平扩展? 难道还有垂直扩展(scale-up)?

对,垂直扩展就是通过增加CPU,内存,硬盘等方式来提高单个服务器的处理能力。由于单台机器总是有上限的,所以想应对海量用户的访问,提高可用性,还得靠水平扩展。现在你体会到无状态的好处了吧?


明白了,大师,在服务器端无状态确实是个美好的世界, 可是现实很残酷,没有状态不行啊,一个人登录了,我们得记住他是谁吧,他往购物车里加入商品,我们也得记下来吧。

那你们怎么记啊?


肯定用Session来保存状态啊!

服务器一旦引入状态,就没法轻松地水平扩展了吧!


是的,该怎么办?

这里边办法很多,例如让'状态'在各个服务器之间进行复制,但最常用的是把状态转移存储到另外一个地方,尽量服务器恢复到无状态的'y=f(x)'。


(注:实际情况下,图中服务器之前还有负责负载均衡的服务器)


奥,这样一来,又可以水平扩展了! 对了大师,我刚才听到师兄们提到‘无状态对象’,他们说就是一个对象没有实例变量,或者实例变量是final的。这么说对吧?

嗯,这种情况下,说‘无状态对象’ 有点不准确了,更准确的词是‘不可变对象’(Immutable Object),比如:


public final class Complex{
   private final int a;
   private final int b;
   public Complex(int a, int b){
       this.a = a;
       this.b = b;
   }
   public Complex add(Complex other){
       return new Complex(a + other.a, b+other.b);
   }
}


奥,这个类的对象一旦创建,就不能再改变了, 我看到了那个add方法,它不是对现有对象的修改,而是返回了一个全新的对象。

这样的话当多个线程调用add对象的时候,都是线程安全的。 我这里有一副图画,是LISP大师送给我的,形象地展示了可变 vs 不可变, 你拿去吧:



那代价也有点大啊,每次都创建新对象!我们用Spring,其中的Controller, Service被大量地并发调用,肯定不能用这种方法了。

是的,你们用的Controller, Service 默认都是单例,运行期只有一个实例,他们的方法应该是y=f(x)这样的无状态方法,轻易不要在里边放置共享的实例变量,要不然多线程并发操作就可能出问题了。



可是我们的Controller 一般都要放个Service的实例变量啊 ,比如这个LoginController中的userService, 多个线程同时访问这个共享的userService,岂不就出问题了?


@Controller
public class LoginController {
   @Autowired
   private UserService userService;
   ......对userService的使用略......
}

你误入歧途了,把无状态和无共享的实例变量画了等号,你想想,如果LoginController调用的userService 的方法也是类似 y=f(x), 会有线程安全问题吗?


嗯...... 好像是没有问题。 无论是Controller还是Service都是纯函数调用而已。 但是如果确实需要共享的变量该怎么办?

很简单,使用ThreadLocal,把这个变量存到各个线程当中,让他们互不干扰,就线程安全了。


大师,再讲讲ThreadLocal吧!

为师累了,要休息下,这里有篇文章,你拿去看吧:《一个故事讲明白线程的私家领地:ThreadLocal



后记:这篇文章缘起于在知识星球“码农翻身”的一个提问,在知识星球,我会回答朋友们各种各样的提问,同时分享我日常的所思所想,对一些技术、工具、IT事件的看法,编程学习的方法、经验教训,职场和职业发展的问题等等。


欢迎大家加入!对于加入知识星球的同学提供特别的福利,即2017编程提高群第一季第二季的部分“加餐”视频


漫谈计算机组成原理和计算机编程语言

程序的机器级表示

漫谈操作系统之虚拟内存

进程和线程

新人在职场

Java并发编程

如何用Antlr实现自定义的简单脚本语言

缓存

BTree及其在数据库的应用

分布式文件系统FastDFS

登录查看更多
0

相关内容

服务器,也称伺服器,是提供计算服务的设备。由于服务器需要响应服务请求,并进行处理,因此一般来说服务器应具备承担服务并且保障服务的能力。
服务器的构成包括处理器、硬盘、内存、系统总线等,和通用的计算机架构类似,但是由于需要提供高可靠的服务,因此在处理能力、稳定性、可靠性、安全性、可扩展性、可管理性等方面要求较高。
【2020新书】从Excel中学习数据挖掘,223页pdf
专知会员服务
91+阅读 · 2020年6月28日
【ICMR2020】持续健康状态接口事件检索
专知会员服务
18+阅读 · 2020年4月18日
Python数据分析:过去、现在和未来,52页ppt
专知会员服务
101+阅读 · 2020年3月9日
TensorFlow Lite指南实战《TensorFlow Lite A primer》,附48页PPT
专知会员服务
70+阅读 · 2020年1月17日
近期必读的12篇KDD 2019【图神经网络(GNN)】相关论文
专知会员服务
63+阅读 · 2020年1月10日
【强化学习】深度强化学习初学者指南
专知会员服务
182+阅读 · 2019年12月14日
在K8S上运行Kafka合适吗?会遇到哪些陷阱?
DBAplus社群
9+阅读 · 2019年9月4日
渗透某德棋牌游戏
黑白之道
12+阅读 · 2019年5月17日
终于有人把云计算、大数据和人工智能讲明白了
Python开发者
3+阅读 · 2018年6月13日
我是一个爬虫
码农翻身
12+阅读 · 2018年6月4日
推荐一些适合小白练手的Python项目
数据挖掘入门与实战
6+阅读 · 2018年5月17日
传说中的马尔科夫链到底是个什么鬼?
R语言中文社区
6+阅读 · 2018年2月27日
自然语言处理(4)之中文文本挖掘流程详解(小白入门必读)
机器学习算法与Python学习
5+阅读 · 2017年12月22日
Python3爬虫之入门和正则表达式
全球人工智能
7+阅读 · 2017年10月9日
Learning to See Through Obstructions
Arxiv
7+阅读 · 2020年4月2日
Arxiv
4+阅读 · 2018年9月25日
VIP会员
相关VIP内容
【2020新书】从Excel中学习数据挖掘,223页pdf
专知会员服务
91+阅读 · 2020年6月28日
【ICMR2020】持续健康状态接口事件检索
专知会员服务
18+阅读 · 2020年4月18日
Python数据分析:过去、现在和未来,52页ppt
专知会员服务
101+阅读 · 2020年3月9日
TensorFlow Lite指南实战《TensorFlow Lite A primer》,附48页PPT
专知会员服务
70+阅读 · 2020年1月17日
近期必读的12篇KDD 2019【图神经网络(GNN)】相关论文
专知会员服务
63+阅读 · 2020年1月10日
【强化学习】深度强化学习初学者指南
专知会员服务
182+阅读 · 2019年12月14日
相关资讯
在K8S上运行Kafka合适吗?会遇到哪些陷阱?
DBAplus社群
9+阅读 · 2019年9月4日
渗透某德棋牌游戏
黑白之道
12+阅读 · 2019年5月17日
终于有人把云计算、大数据和人工智能讲明白了
Python开发者
3+阅读 · 2018年6月13日
我是一个爬虫
码农翻身
12+阅读 · 2018年6月4日
推荐一些适合小白练手的Python项目
数据挖掘入门与实战
6+阅读 · 2018年5月17日
传说中的马尔科夫链到底是个什么鬼?
R语言中文社区
6+阅读 · 2018年2月27日
自然语言处理(4)之中文文本挖掘流程详解(小白入门必读)
机器学习算法与Python学习
5+阅读 · 2017年12月22日
Python3爬虫之入门和正则表达式
全球人工智能
7+阅读 · 2017年10月9日
Top
微信扫码咨询专知VIP会员