无状态服务(stateless service)(续)

数据服务,正如其名,可以向应用提供数据存储与访问的服务。

比如一个游戏场景服务器,可以在玩家进入的时候,向数据服务请求该玩家的数据。逻辑中处理不同玩家的数据交互,修改不同玩家的数据,然后以一定策略再将数据存回数据服务。

同理如一个web应用服务器, client 发一个请求,应用向数据服务请求数据,修改数据,应用向数据服务存回数据。

换言之,如果把服务端看作一个整体,数据服务维护的是client在服务端的状态。

但是,我们之前实现的数据服务可以提供更强大的抽象能力。

我们的数据服务的API结构可以任意定制,基于ORM和代码自动化生成实现了code-first,而且数据服务依赖的基础设施——redis又被证明非常强大:不仅仅是性能极佳,而且提供了多种数据结构抽象。

那么,数据服务是否能在维护client状态之外仍有用武之地?

在web开发中,用缓存维护服务状态是一种很常规的开发思路。 而在游戏服务端开发中,由于场景服务的存在,这种思路通常并不靠谱。

为什么要用数据服务维护其他服务状态?

考虑这样一个问题:

如果服务的状态维护在服务进程中,那么服务进程挂掉,状态就不存在了。但是对于我们来说,服务的状态是比服务进程本身更加重要的——因为进程挂了可以立刻重启,哪怕耽误个1、2s,但是状态没了却意味着这个服务在整个分布式服务端中所提供的语义已经不正确了,即使瞬间就重启好了也没用。

那么为了让服务进程挂掉时不会导致服务状态丢掉,只要分离服务进程的生命周期和服务状态的生命周期就可以了。

这种服务其实就是无状态服务(stateless service)。

贴一下wiki:

Service statelessness is a design principle that is applied within the service-orientation design paradigm, in order to design scalable services by separating them from their state data whenever possible. 

好处:

This results in reduction of the resources consumed by a service as the actual state data management is delegated to an external component or to an architectural extension.

两个关键点: design scalable services, reduction of the resources consumed。

其实描述的是同一个意思,以无状态为原则设计服务可以让服务的横向扩展成本降到最低。

对于web开发程序员来说,无状态服务并不是什么新鲜事,甚至因为无状态服务的一些弊端而改用有状态服务(stateful service)。不过,我们今天并不讨论哪一种更好,只讨论 无状态服务 更适合 什么情景。

无状态服务的实现方式有很多种,在一些C++主导的互联网公司,通常是借助共享内存实现——通过将 服务状态维护在共享内存里,实现服务进程与服务状态的分离。 但是这种做法扩展性不强,很难跨物理机,而且共享内存就这样一个文件安全性很难保障。

很多只接触过游戏服务端的同学并不了解无状态服务,毕竟一直写的是交互性极强的场景服务,而且遇事首先想到的是热更新,不知道其他的hotfix方式。

之前小说君跟有些同事讨论无状态服务,发现对方还会产生相当程度的误解,把「无状态服务」与「无状态客户端」混为一谈。 无状态的游戏客户端意味着网络通信的成本跟内存数据访问的成本一样低——这当然是不可能实现的。

无状态服务就是为了scalability而出现的,无状态服务横向扩展的能力相比于有状态服务大大增强,同时实现负载均衡的成本又远低于有状态服务。

设计分布式系统时,我们无法兼顾一致性C、响应性能A,以及分区容错P。无状态服务更倾向于CP,有状态服务更倾向于AP。但是,即使是现在(比如借助各种一致性协调组件,或者gossip protocol),有状态服务的P与无状态服务的P所能达到的程度是不一样的,后者是真的容错,前者只能做到不把鸡蛋放在一个篮子里。

游戏中可以拆分为无状态服务的业务情景其实有很多,基本上所有服务间交互需求都可以实现为无状态服务。比如切场景服务,因为切场景的请求是有限的,对时延的要求也不会特别高,同理的还有分配房间服务;或者是面向客户端的IM服务、拍卖行服务等等。

回到一开始的问题, 我们可以将服务状态存放在外部设施中,比如数据服务。

数据服务,可以转移无状态服务的状态维护成本,让无状态服务横向扩展的能力更强大。因为状态维护在数据服务中,所以无状态服务开多少个都无所谓。因此无状态服务非常适合计算密集的业务需求。

实际上,我们讲面向微服务,讲服务划分,要解决的根本问题就是让程序员能清楚自己定义每种服务的意图是什么,请求响应式服务、数据同步式服务、流处理式服务,消息流与业务对CAP的需求决定了服务更适合做成无状态还是有状态。

举个游戏开发的例子,假设业务情景是比较常见的全球同服类游戏,那游戏的每种服务理应是由分布式的多个节点共同提供服务,如果服务的消息流是典型的请求响应式,那么实现为无状态服务就更合适:

  • 请求上来,取相关数据,处理,直接返回。整个状态的生命周期保持在一次RPC调用过程中,这描述的就是请求响应的工作方式。

  • 不论是自己开发load balancer,还是用现成的消息broker(比如前篇文章说的RabbitMQ),对无状态服务做 round-robin work distribution都非常简单。

  • 而且,无状态服务本质上是没有扩容成本的,波峰就多开,波谷就少开。

程序员可以为不同服务规划不同的横向扩展方式。对于无状态服务,横向扩展的触发条件就是现在请求数量级或者是节点压力;对于有状态服务,横向扩展就需要借助第三方的服务作为仲裁者,而这个仲裁者可以实现为无状态服务。

当然,无状态服务并不是万能的。 画图解释,有状态服务A与B:

最常见的组织形式,每个有状态服务都是一个进程。

无状态服务A:

其中,节点A与节点B都是无状态服务A的一个实例。

从这两张图就能简单看出, 无状态服务只是把无状态性( statelessness )从服务中独立出来。

好处自然是如前面所说,将进程的生命周期与状态的生命周期解耦,而且我们现在把共享状态寄存在数据服务,那只需要保证数据服务高可用,所有的无状态服务都能高可用。相反,如果是有状态服务想提供高可用保证,就需要各自实现高可用机制。

但是,无状态服务还有两点弊端:

  1. 节点访问状态从进程内变为进程外,没了locality,造成了资源浪费。

  2. 由于操作同一client的数据的不再是唯一的节点,有可能形成数据的不一致(这点下篇文章再聊)。

这也是程序员的日常抉择之一:trade-off。

哪些服务需要设计为无状态服务,哪些服务需要设计为有状态服务,完全是业务需求决定的。两者的设计范式(design paradigm)完全不同。

  • 无状态服务描述的是数据流,Data Shipping。后果就是所有状态的访问与修改都增加了内网时延,因此对于游戏场景服务这种性能优先的服务是不可忍受的。

  • 有状态服务描述的是功能流,Function Shipping。数据具有强局部性,因此有状态服务非常适合场景同步这种交互密集的情景,交互的延迟仅仅是进程内方法调用的开销。

借助无状态服务,我们在架构中就可以逐步干掉类似切场景管理这种单点进程。无状态服务是天然高可用的——任意挂掉一个,仍然能持续提供服务。

整个服务端理论上应该具有整体持续提供服务的能力。也就是说,随便挂掉一个节点,不需要停服。游戏的场景服务挂掉一个节点,不会影响其他任何服务,只是该场景中的玩家短期内无法进行场景相关操作了而已。

而我们见过的大多数游戏服务端架构,处

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值