什么叫服务无状态化
我看过很多资料,对于服务无状态化的定义千差万别,但是有一种定义我觉得是最恰当的:同一服务(进程)冗余部署N份,N份完全对等,请求提交到任何一份冗余服务上,处理结果完全一样。这里有个问题和大家讨论下:服务(进程)里面存储数据是否能作为服务无状态的评判标准?我们拿之前水平分层的架构来分析:
正常情况下如果要在客户端展示数据,请求就是从网关层到业务逻辑层到数据访问层最后到数据库拿到数据后再一层一层返回。这个时候我们的服务绝对可以说是无状态的。但是如果我们要展示的数据是不经常变化的,我们可能会使用缓存,这个时候我们的架构会演化成以下样子:
演变成这个样子后我们的不经常变化的数据存储在缓存中,这个时候我们的服务也还是可以说是无状态的。但是如果我觉得这样跟缓存还是有网络开销,我就想把我不经常变化的数据存在数据访问层呢?
这个时候我的服务(进程)中存储了数据,但是这时候我们能说当我们冗余N份之后我们的服务是有状态的吗?答案显然不是的。所以我们说:同一服务(进程)冗余部署N份,N份完全对等,请求提交到任何一份冗余服务上,处理结果完全一样就是无状态的服务。
为什么要做服务无状态化
我们为什么要做服务的无状态化设计呢?其实原因无非两个:
扩容服务
缩容服务
对于扩容服务,很简单,当我们的服务集群撑不住业务的时候,要加服务节点,如果服务是无状态的我们能很快速就完成这样的操作。我们拿水平分层的数据访问层来举例:
对于缩容服务?你们觉得什么时候会有服务缩容出现呢?你们觉得业务部门会主动来找你要缩容服务吗?想都别想!只有当某个服务挂了的时候,才会出现被动的服务缩容的情况。还是拿水平分层的数据访问层来举例:
服务无状态化案例
我们还是拿用户登录这个业务来分析服务无状态设计与实践。一般流程是这样的:
用户向服务器发送用户名和密码。
服务器验证通过后,在当前对话(session)里面保存相关数据,比如用户角色、登录时间等等。
服务器向用户返回一个 session_id,写入用户的 Cookie。
用户随后的每一次请求,都会通过 Cookie,将 session_id 传回服务器。
服务器收到 session_id,找到前期保存的数据,由此得知用户的身份。
Session存放在网关层,这样当我们网关层做水平扩展的时候就是有状态的了,所以这样不行。那我们怎么办呢?我们可以把Session信息保存在客户端,每次请求都发回服务器。JWT就是这种方案的一个代表。
这样我们的网关层就无状态化了,但是Session信息保存在客户端会存在丢失的问题,比如用户主动清除了客户端的LocalStorage。还有JWT本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限,就会出现CSRF问题。那我们怎么办呢?我们可以把Session写入缓存。这个地方要注意,有很多朋友会选择存在数据库或别的持久层,其实这是不对的,因为Session信息本就有过期时间的。
这样一来我们能保证网关服务是无状态的,然后我们的Session数据也能做到高可用。