作者 | 菜v菜

责编 | maozz

YY妹:菜菜哥,你换形象啦?

菜菜:这么巧,你也换啦!听说是不会画画的菜嫂经过九牛二虎之力的功劳哦!结果.....

YY妹:前几天我出去面试了,面试官问我微服务的知识,我回答的可好了

菜菜:看来微服务你真的下工夫研究了呀

YY妹:是呀是呀,但是碰到一个问题,有状态的服务是什么意思呢?

菜菜:看来你又挂在这个问题上了,且听这次分解

简介

对于初学者,心里对“有状态服务”的理解可能比较模糊,但是从面向对象编程思想的角度去理解也许会明朗很多。面向对象编程思想提倡的是用编程语言去描述世间万物,所以面向对象编程的语言都会提供描述对象的容器以及对象行为的表达方式。

举一个很简单的栗子,在c#或者java中,表达对象的容器就是class,对象的行为通过一系列的接口或者函数来表达。更进一步,对象抽象出来之后,大多数对象都有自己的内部状态,体现到代码上也就是常见的类的属性。

面向对象编程的基本思想本质上是对现实世界的一种抽象,万物皆可抽象。

根据业务把对象抽象出来之后,每一个实例化的对象其实都可以有自己的状态,比如:在最常见的游戏场景中,每一个玩家都是“玩家"这类对象的一个实例,每一个玩家都有自己的名字,性别,等级,HP等属性,这些属性本质上就是玩家的状态,随着时间的推移,每个玩家的HP,等级等属性会随之变化,这些变化其实就是这个玩家状态的变化。

对应到有状态的服务也是如此,之所以称之为有状态,是因为服务内部的对象状态会随着业务有着对应的变动,而这些变动只发生在这个服务内部,在外界看来,这个服务好像是有状态的。

有状态的服务本质上是一些有状态对象的集合,这些对象状态的变化只发生在当前服务进程中。

优势和劣势

有状态服务之所以被称为有状态,一个很大的原因是它可以追溯状态的变化过程,也就是说一个有状态的服务保存着状态变化的记录,并可以根据这些历史记录恢复到指定的状态,这在很多场景下非常有用。

举一个很简单的栗子:我们平时玩的斗地主游戏,三个玩家,当有一个玩家因为网络原因掉线,经过一段时间,这个玩家又重新上线,需要根据某些记录来恢复玩家掉线期间系统自动出牌的记录,这些出牌记录在这个业务中其实就是这个玩家的状态变化记录。在有状态的服务中,很容易做到这一点。

其实实际开发中很多场景不需要记录每个状态的变化,只保留最新状态即可,不单单是因为保存每个状态的变化需要大量的存储和架构设计,更因为是很多业务根本不需要这些状态变化记录,业务需要的只是最新的状态,所以大部分有状态的服务只保存着最新的状态。

有状态的服务在设计难度上比无状态的服务要大很多,不仅仅是因为开发设计人员需要更好的抽象能力,更多的是一致性的设计问题。

现代的分布式系统,都是由多个服务器组成一个集群来对外提供服务,当一个对象在服务器A产生之后,如果请求被分配到了服务器B上,这种情况下有状态的服务毫无意义,为什么呢?

当一个相同的业务对象存在于不同的服务器上的时候,本质上就违背了现实世界的规则,你能说一个人,即出生在中国,又出生在美国吗?

所以有状态的服务对于一致性问题有着天然的要求,这种思想和微服务设计理想不谋而合,举个栗子:一个用户信息的服务,对外提供查询修改能力,凡是用户信息的业务必须通过这个服务来实现。同理,一个对象状态的查询修改以及这个对象的行为,必须由这个对象的服务来完成。

有状态的服务要求相同业务对象的请求必须被路由到同一个服务进程。

因此,有状态的服务对于同一个对象的横向扩容是做不到的,就算是做的到,多个相同对象之间的状态同步工作也必然会花费更多的资源。

在很多场景下,有状态的服务要注意热点问题,例如最常见的秒杀,这里并非是说有状态服务不适合大并发的场景,反而在高并发的场景下,有状态的服务往往表现的比无状态服务更加出色。

Actor模型

在众多的并发模型中,最适合有状态服务设计的莫过于Actor模型了,actor模型天生就具备了一致性这种特点,让我们在对业务进行抽象的时候,不必考虑一致性的问题,而且每一个请求都是异步模式,在对象内部修改对象的状态不必加锁,这在传统的架构中是做不到的。

基于actor模型,系统设计的难点在于抽象业务模型,一旦业务模型稳定,我们完全可以用内存方式来保存对象状态(也可以定时去持久化),内存方式比用其他网络存储(例如redis)要快上几个量级。

既满足了一致性,又可以利用进程内对象状态来应对高并发业务场景,何乐而不为呢?

有不少同学问过,actor模型要避免出现热点问题,就算有内存状态为其加速,那并发数还是超过actor的处理能力怎么办呢?

其实和传统做法类似,所有的高并发系统设计无非就是“分”一个字,无论是简单的负载均衡,还是复杂的分库分表策略,都是分治的一种体现。一台服务器不够,我就上十台,百台.....

所有的高并发系统设计都是基于分治思想,把每一台服务器的能力发挥到极致,难度最大的还是其中的调度算法。

用actor模型来应对高并发,我们可以采用读写分离的思想,主actor负责写请求,并利用某种通信机制把状态的变化通知到多个从actor,从actor负责对外的读请求,这个DB的读写分离思想一致,其中最难的当属actor的状态同步问题了,解决问题的方式千百种,总有一种适合你,欢迎你留言写下你认为最好的解决方案。

案例(玩家信息服务)

由于笔者是c#出身,对c#的Actor服务框架Orleans比较熟悉,这里就以Orleans为例,其他语言的coder不要见怪,Orleans是一个非常优秀的Actor模型框架,而且支持最新的netcore 3.0版本,地址为:https://github.com/dotnet/orleans  有兴趣的同学可以去看一下,而且分布式事物已经出正式版,非常给力。其他语言的也非常出色java:https://github.com/akka/akka

golang:https://github.com/AsynkronIT/protoactor-go

1. 首先我们定义玩家的状态信息

//玩家的信息,其实也就是玩家的状态信息public class Player {/// <summary>/// 玩家id,同时也是玩家这个服务的主键/// </summary>public long Id { get; set; }/// <summary>/// 玩家姓名/// </summary>public string Name { get; set; }/// <summary>/// 玩家等级/// </summary>public int Level { get; set; }}

2. 接下来定义玩家的服务接口

 /// <summary>/// 玩家的服务接口/// </summary>interface IPlayerService: Orleans.IGrainWithIntegerKey{//获取玩家名称Task<string> GetName();//获取玩家等级Task<int> GetLevel();//设置玩家等级,这个操作会改变玩家的状态Task<int> SetLevel(int newLevel);}

3. 接下来实现玩家服务的接口

public class PlayerService : Grain, IPlayerService{//这里可以用玩家的信息来代表玩家的状态信息,而且这个状态信息又充当了进程内缓存的作用Player playerInfo;public async Task<int> GetLevel(){return (await LoadPlayer()).Level;}public async Task<string> GetName(){return (await LoadPlayer()).Name;}public async Task<int> SetLevel(int newLevel){var playerInfo =await LoadPlayer();if (playerInfo != null){//先进行数据库的更新,然后在更新缓存的状态, 进程内缓存更新失败的几率几乎为0playerInfo.Level = newLevel;                }return 1;}private async Task< Player> LoadPlayer(){if (playerInfo == null){var id = this.GetPrimaryKeyLong();//这里模拟的信息,真实环境完全可以从持久化设备进行读取playerInfo= new Player() { Id = id, Name = "玩家姓名", Level = 1 };}return playerInfo;}}

以上只是一个简单案例,有状态的服务还有更多的设计方案,以上只供参考

本文为作者投稿,版权归作者个人所有。

【End】

0基础学习python,每天5分钟,进步一点点

https://edu.csdn.net/topic/python115?utm_source=csdn_bw

热 文 推 荐 

物联网大变局:LoRa 正式获批!

☞C++控制台打飞机小游戏 | CSDN 博文精选

Twitter 有权删除去世用户的账号吗?

☞我收集了12款自动生成器,无聊人士自娱自乐专用

☞阿里程序员常用的 15 款开发者工具

☞苹果公司 50% 员工没大学学历,细数不看学历看能力的 IT 大佬!

点击阅读原文,即刻参加调查!

你点的每个“在看”,我都认真当成了喜欢

程序员该如何利用“有状态的服务”升级打怪?相关推荐

  1. 女生相亲了个年薪50万的程序员,看到对方工作状态后:要不要继续

    介绍了个年薪50万的程序员,看到对方工作状态后:要不要继续 在职场上,单身的大龄男女是越来越多,至于原因有很多种,比如说岗位的原因,类似程序员这样的:还有就是员工圈子太小,根本没有机会认识异性:最后就 ...

  2. 好程序员云计算培训分享云计算中微服务是什么?

    好程序员云计算培训分享云计算中微服务是什么?微服务架构是一种架构模式,它提倡将单一应用程序划分成一组小的服务,服务之间相互协调.互相配合,为用户提供最终价值.每个服务运行在其独立的进程中,服务和服务之 ...

  3. 程序员如何打造属于自己的云笔记服务

    https://www.cnblogs.com/smallSevens/p/7710373.html 程序员如何打造属于自己的云笔记服务 目录 前言 介绍 安装 启动服务 使用配置 APP访问 前言 ...

  4. 程序员遇到什么样的工作状态应该果断跳槽走人?

    [CSDN 编者按]一朝码农深似海,入职以来,每天都在嚷嚷着不想干了,但多半是过过"嘴瘾",说说也就罢了,最终往往还是迫于种种因素留下来继续搬砖.但总有一些"忍无可忍&q ...

  5. 作为一名程序员,如何利用自己的技术去创业和赚钱?

    在悟空问答上看到这个问题,就想分享自己的经历,以下是我在悟空问答上的回答,希望各位多多交流 很喜欢这个问题,正想分享做为程序员的我是如何利用自己的技术赚钱的经历.从零基础自学编程,到成为全栈开发工程师 ...

  6. 程序员要学会利用周末时间,自我提升

    hello,大家好,我是张张,「架构精进之路」公号作者. 人的一生中,有七分之二的时间都是周末.如何度过周末对我们的生活质量有着巨大的影响. 对于程序员来讲,有的人利用周末来玩耍休息,有的人利用周末提 ...

  7. 30岁程序员的几种职业状态

    为什么想探讨下这个话题 自己也到了这个阶段,见到各类程序员多了,对这个话题也多了些敏感性 昨天和几个好朋友吃饭聊了下近况,发现30岁后,大家的职场状态开始有了很大不同,以前一起聚餐聊天感觉不明显 然后 ...

  8. 人人自媒体的时代,程序员该如何利用好自己的优势?我记住了这些神器...

    无可否认,随着互联网的不断发展完善,我们现在的生活无时无刻都在与网络打交道.伴随着流量这个名词的冲击,我们对自媒体这一行业也开始了不断的摸索,基本在我的圈子里,做自媒体的太多太多了,有图文自媒体,短视 ...

  9. 如何避免程序员和产品经理打架?“微服务”或将成终极解决方案

    程序员与产品经理打架,早已称不上互联网圈的新闻.怀揣改变世界的远大抱负,却要每天和多变刁钻的需求战斗,这是许多程序员的"生存困境".那么除了板砖和拼死不从,程序员就没有别的对付变态 ...

最新文章

  1. linux shell cgi post,linux下shell处理cgi的方法--post get
  2. R语言创建频数表和列联表
  3. vim 按照字段排序文件
  4. delphi面向对象(1)
  5. java标点符号用什么意思_java怎么统计字符串内的标点符号?
  6. VTK修炼之道61:体绘制_光线投影+最大密度投影+等值面法
  7. 读书笔记_代码大全2第十一章_选择好的变量名
  8. 云湖共生-释放企业数据价值
  9. C语言第七次作业---要死了----
  10. (转) Hadoop1.2.1安装
  11. 中国联通最快明年实现2G全面退网;苹果发布iOS 14.3后火速撤回:原因未知;Angular 11.0.0 正式发布|极客头条
  12. 复旦大学计算机学院专业硕士学费,复旦大学计算机在职研究生学费一年要交的学费多少?...
  13. 403 Forbidden nginx/1.6.2
  14. 阿尔法编程python答案_C语言程序设计-阿尔法编程(编程答案)
  15. 新生宝宝办证-STEP1-出生证明办理
  16. 抢手机型ROOT测评 ROOT大师成功率达99%
  17. 深圳软件测试培训:事件与事件处理
  18. android第三方支付的接入(支付宝,微信,银联,京东,百度等)
  19. 麻省理工公开课:线性代数 正交向量与子空间
  20. Arduino开发板使用DHT11温湿度传感器的方法

热门文章

  1. java 2和java有什么区别
  2. 关于printk显示问题(转)
  3. 用SQL产生连续的自然数
  4. 《Linux编程》学习笔记 ·001【基本操作、常用命令】
  5. [FFmpeg] 多个图片合成视频
  6. 【js】【cornerstone】cornerstone使用url方式加载图像
  7. 命令行 编译 android6,Android: m, mm以及mmm编译命令以及make snod的使用
  8. ctf 监听端口_从一道ctf题目学到的绕过长度执行命令姿势 - 华域联盟|chu
  9. 小C语言程序----词法分析程序输出单词
  10. 【OpenCV】傅里叶变换