絮絮叨叨
我是在21年转行做游戏开发的,准确来说是转行做开发,只是进的第一家公司是个游戏公司。
入职之前其实已经在浅薄的知识体系里对微服务有了一点点了解,片面的以为互联网应用
跟游戏服务器
的一个发展方向都是拆成微服务。可第一家公司就否决了我这个想法。
毕竟当时经验有限,似乎能理解游戏服务器的微服务化会有什么弊端,可又没办法系统的总结一番。
直到某天我在知乎刷到这么个问题:
为什么游戏公司的server不愿意微服务化?
问题链接:
评论区回答的大佬简直令我茅塞顿开。所以这里组织一些个人认为比较优质的回答,方便自己随时复习吧。
我大概把这些回答分为以下几个角度,方便梳理。
服务侧重点
回答一
原文链接:
比如moba类
游戏,就看LOL的客户端吧,想象一下。
- 账号系统,符文系统,英雄系统,皮肤系统,好友系统,好友之间messaging,这些都是常规操作,如果流量足够大,当然可以用微服务的架构去做。
不过这不是这个游戏的核心
,核心是MOBA
:Multiplayer online battle arena。特性是什么?
- 10个人之间各种
游戏事件
的高速多向通讯 streaming/broadcast/multicast/pubsub各种通讯模式
所以游戏的核心
在于小规模群体之间的高速网络通信。就是对方说的realtime
。多了一个10ms的延迟玩家就要骂娘了。
- 微服务为了把业务完美拆解,把原来的同一个进程里的模块拆分成不同的服务,显著增加额外的网络开销。更别说什么service mesh,各种gateway,proxy,sidecar,简直就是担心延迟太低。
- 微服务基本只有
request/response
的模式。做不了streaming?微服务通常要求应用是无状态的
才能做到水平扩展。streaming
本身就是加入了状态 - 我可以想像,为了提高通讯的性能,一场英雄联盟游戏很可能会使用同一个服务器负责这10个玩家之间的通讯,这样就使得数据可以在本地交换,性能最大化。这对客户端或者说服务端统一网关的要求是必须支持
sticky routing
。假设客户端连接断了,接下来的必须重连之前的同一个服务器
。微服务的stateless,水平扩展要求本身就是反sticky routing的,因为sticky routing本身就是状态。 - 对服务端集群来说,同时有无数个LOL比赛在进行,每个都可以看成一个
沙盒
,每个沙盒都处于一个不同的状态:塔被推了几个了,你被杀了几次了,对面几个超神了,20分钟到了没。 这些都是长时间存在的状态,直到游戏结束,服务端才可以清理一场游戏的状态。所以虽然不用把这些状态写进持久性存储,但是必然会在内存中存在很长时间。都是状态,反正有状态,就别想用微服务。除非你说把这些状态都移到redis
里去,那么在服务器在信息流传输到一半还要做一个remote request
,一来一回,延迟就上升了。总之怎样都不好。(比如想象对方在A你的水晶,每一次A的操作都是一个event,被streaming到服务端的沙盒中,沙盒中有一个流处理器,每次接收到一个你水晶被A的event都会计算一下你水晶爆了没。这个计算需要极快,你是不可能把你水晶生命值的数据存在远端的)
像这类游戏,都是对网络,内存,CPU的优化需求很高,整个游戏进行过程中,几乎不存在什么RPC call
,真的需要remote data
,也应该是prefetch
,就是在游戏刚开始的时候加载好。
微服务
不是什么银弹
,也就是方便拆解一下原来的CRUD应用
罢了而已,一没触及高级的交互方式,二没触及分布式系统
真正的难点:状态
,其实没有大家想的那么有用。之所以感觉上好像微服务改变了互联网,只不过90%的互联网应用都只是简单小规模的CRUD而已。
回答二
原文链接:
这世上没有通用的架构
,所以不要觉得微服务很牛逼。
微服务还真的不适合游戏开发。因为游戏大多是有状态的,而且是低延迟。
微服务业务拆分,很适合无状态的普通服务器。
跟你说一下游戏服务器和普通服务器的区别你就知道为什么了。
互联网和游戏后端开发的区别
游戏特有的
- 保持连接:游戏一般来说需要保持一个
客户端到服务端的连接
,可以对客户端的玩家的行为(移动,攻击,操作,互动,聊天)进行及时的反馈以及主动推送给相关的玩家。所以游戏更多的使用TCP来保持客户端和服务端的连接,少量游戏会使用UDP或者HTTP。 - 保持状态:服务端会保持一份玩家的实体,当玩家进行操作时,下次通信的数据会依赖之前的通信的数据。
- 主动推送:游戏服务器由于保持连接和状态,任何数据的改动可以通过服务端主动通知客户端的方式,这样就只需要推送修改的数据。不需要客户端频繁去刷新。
- 低延迟:很多游戏服务器,尤其是
RPG
,MOBA
,FPS
等游戏,对延迟容忍度非常低,网络拥堵情况下tcp
协议由于重传机制
,拥塞机制导致非常慢,就需要重新设计协议来处理。 - 写频繁:游戏中的每一个操作都可能是一个数据,移动、攻击、交易、经验增长等等,所以游戏通常来说需要定时写数据,否则可能会有DB性能瓶颈。
- 互动:很多游戏是多人游戏,需要玩家互动,这个时候需要保持任何玩家之间都能及时的互动或者沟通,对服务器架构上的设计就有一定的要求,互联网的很多业务分离的
微服务架构
,很难让所有的玩家能及时的沟通。 - **复杂度高:**由于游戏大多数是交互的,所以做游戏后端开发的业务复杂度比互联网是高的,互联网主要是增删改查(高并发下也挺复杂,但是主要是性能复杂度)。但是游戏却是业务复杂度。比如,你做一个战斗系统,
回合制
(梦幻西游)的战斗系统怎么做,怎么保存交互的状态,Moba(王者荣耀)的战斗系统怎么做,怎么处理每一帧的指令。 - IO密集型:游戏大多数是IO密集型的,它的主要瓶颈在网络/磁盘IO,而不是CPU。因为游戏很多多人在线,相互之间同步的数据导致整体的IO非常高。因为CPU并不高,导致很多时候都不需要多线程。
互联网特有的
- 请求响应:互联网应用一般只需要支持请求响应式的通信,最常用的协议是HTTP来做客户端和服务端的通信。互联网应用一般来说只用关心自己的行为,而不太关注其他用户的行为(即使关注,也没有ms级别的响应要求),所以一般来说即使需要读到其他用户的数据,只需要刷新一下页面就好,也不需要
服务端
实时推送。 - 无状态:互联网的应用一般是无状态的,也就是每次进行不同的操作,都需要服务端进行完整的数据读取操作,无法利用上次请求的数据。
- 服务拆分互联网是比较容易做服务拆分的,因为业务相对独立,交互弱,做成微服务架构,依赖关系用网络请求来请求数据。一个很长的调用链拿到所有数据。
- 读频繁:互联网一般是一个读频繁的场景,需要大量的读取数据,只有特定的行为才需要写入。要考虑一定的缓存机制,对数据库的设计要求更高。
- 延迟容忍:可以容忍一定的延迟,100ms和10ms对用户的体验影响并不大。
- 增删改查互联网说到底就是增删改查。(当然1000用户的增删改查和10亿用户的增删改查不一样)。我现在处理十亿级数据千万级QPS的服务。主要考虑高并发下的服务的拆分,架构的设计,微服务化,数据拆分,缓存设计,异步存储,热点处理等等一系列高并发下的性能考虑。但是仅仅业务本身的复杂度相对游戏较低。
- **CPU密集型:**互联网的
服务器
大多是CPU密集型的。业务层涉及到大量的数据计算。
你看,是不是感觉差别天差地别。同时,游戏服务器特有的很多特性,在微服务上实现,复杂很多。
https://zhuanlan.zhihu.com/p/436044085
服务维护
回答一
原文链接:
看来是最近被微服务洗脑了, 个人感觉正常微服务,一个服务必须有 3 个以上
工程师单独维护,才能真的把微服务盘起来。
而且微服务现在最多就是 HTTP 这种协议跑,很占性能的,就算是走 TCP HTTP2 远远不如单体性能,尤其是微服务做业务分叉调用的时候怎么划分,数据事件一致性是非常头痛的一件事。
微服务用的广是 WEB 方面 而且工程师多,业务变来变多,而且它几乎是自己玩自己的。这种场景就非常适合。实时性不需要那么高那种。
我知道很多人,把微服务魔化了,别人要 100 层功力,而你只学到了 1 层。然后就硬搬上来跟别人说这很牛逼。
我看过那么多博客,技术文,唯独就微软官方那一篇微服务技术写的最好,明确的告诉你这种架构适合什么样的场景跟团队,什么样的场景不要用,而不是一些文章无脑吹。
脱离业务的技术架构,就是为了框架而架构,就是没事给自己搬石头砸脚跟。
对于开发者来说,自己去研究研究新技术是值的非常推崇的。但是不考虑实际情况,那就是魔怔了。
为啥有这种感触,因为我是受害人。所以奉劝各位,什么样的团队项目底子业务,就选择最合适自己的架构,不要盲目去跟风,更不要盲目的去对比大厂云云,你的业务跟团队是别人的零头都不够。别人的 HR 团队可能都比你技术团队人数多。
架构分的越单元化,那么需要的人数是翻倍起来维护的,不然你就会发现为什么这个架构我用起来这么啰嗦。不是别人的架构不够好,是你的团队还不需要。
有小伙伴想找微软的微服务架构链接,我放一下:https://learn.microsoft.com/zh-cn/dotnet/architecture/microservices/multi-container-microservice-net-applications/microservice-application-design
开发难度
回答一
原文链接: https://www.zhihu.com/question/359630395/answer/2656652392
…游戏服务器都是几乎都是带大量状态
的,我就问你一个致命问题,游戏里面本来在同个进程内,内存直接可以访问到,走微服务就可能这个服务不在本机设备上面,那就需要走网络传输过去,那么你考虑过延迟问题吗?有考虑过如果连接挂掉?
每个微服务跨本机都是一个 RPC 调用
,这东西是不可靠
的,如果你要可靠行啊,你准备约定一个接口调用多少毫秒超时重传?200/500?(这就存在两个情况:对面已经处理,对面没有处理,但实际上是同一个调用请求)
如果玩家只是放一个技能,需要投射状态呢,因为断开重传等几百毫秒响应,那么玩家体验是啥。
固然,我们可以使用协程,C++也可以,那么编码复杂度有考虑过吗?而且大量的异步编程在游戏服务器上面是很困难的,也就意味着需要更多的游戏服务器开发人员,而且还得要求开发人员的综合素质不能太差。