微服务架构
这段是背景引入,了解的大佬可以直接跳到后面。
基本概念
微服务最主要的功能是根据业务拆分成一个一个的子服务,实现功能的去耦合,每一个微服务提供单个业务功能的服务,各司其职,从技术角度看就是一种灵活独立的单元,能够自行单独启动和关闭,一般每个服务都拥有自己的数据库模块。
在传统的IT行业软件大多都是各种独立系统的堆砌,这些系统的问题总结来说就是扩展性差,可靠性不高,维护成本高。到后面引入了SOA服务化,但是,由于 SOA 早期均使用了总线模式,这种总线模式是与某种技术栈强绑定的,比如J2EE。这导致很多企业的遗留系统很难对接,切换时间太长,成本太高,新系统稳定性的收敛也需要一些时间。最终 SOA 变得很笨重,在一些中小企业的场景中并不适合。
微服务与单体架构区别
单体架构所有的模块全都耦合在一块,代码量大,维护困难,微服务每个模块就相当于一个单独的项目,代码量明显减少,遇到问题也相对来说比较好解决。 单体架构所有的模块都共用一个数据库,存储方式比较单一,微服务每个模块都可以使用不同的存储方式(比如有的用redis,有的用mysql等),数据库也是单个模块对应自己的数据库。 单体架构所有的模块开发所使用的技术一样,微服务每个模块都可以使用不同的开发技术,开发模式更灵活。
微服务和SOA的区别
微服务,从本质意义上看,还是 SOA 架构。但内涵有所不同,微服务并不绑定某种特殊的技术,在一个微服务的系统中,可以有 Java 编写的服务,也可以有 Python编写的服务,他们是靠Restful架构风格统一成一个系统的。所以微服务本身与具体技术实现无关,扩展性强。
微服务的特点
微服务的主要作用在于实现了一套基础架构系统,使得各个服务之间松耦合,在功能上表现为统一的整体。这种所谓的“统一的整体”表现出来是统一风格的界面、权限管理、安全策略、上线过程、日志和审计方法、调度方式和访问入口等等。
微服务设计原则
单一职责原则 每个微服务只需实现自己的业务逻辑,比如教学模块,它只需要处理教学相关业务逻辑就可以,其它的内容拆分出去;
服务自治原则 各个微服务从开发、测试、发布、运维等都是彼此独立的,他们的存储数据库也彼此分开,降低了各自的依赖;
轻量级通信原则 首先是彼此通信的语言非常的轻量,其次是通信方式需要跨语言、跨平台,这是为了让每个微服务都有足够的独立性,可以不受技术的限制。常见的方式是通过REST API或RPC的方式进行通信;
接口明确原则 一般微服务之间会存在调用关系,为了尽量避免某个微服务的接口变化而导致其它微服务牵一发而动全身,在设计之初就要考虑到常用的情况,让接口尽量做到通用灵活,尽量避免大范围的改动。
游戏公司为什么不使用微服务架构?
这里我想引用几个知乎大佬们的回答,可以给我们带来很多启发和思考:
陈宏基的回答
比如 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 本身就是状态。
对服务端集群来说,同时有无数个王者荣耀的比赛在进行,每个都可以看成一个沙盒,每个沙盒都处于一个不同的状态:塔被推了几个了,你被杀了几次了,对面几个超神了,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密集型的。业务层涉及到大量的数据计算。你看,是不是感觉差别天差地别。同时,游戏服务器特有的很多特性,在微服务上实现,复杂很多。
同时,游戏服务器特有的很多特性,在微服务上实现,复杂很多。
某知乎用户的回答
做过棋牌游戏(游戏最简单的一种),可以尝试说几个点:
微服务本身是为了应对业务逻辑的复杂,需要要的新的组织接口的方式。游戏本身逻辑其实没有这么复杂,比如大厅就是一些基本功能,修改帐号,登录等。游戏本身就是游戏本身的逻辑。
游戏逻辑服务器本身(比如斗地主等棋牌)因为网络响应性能要求问题(玩家对每个操作的反馈时长敏感度远高于业务系统),所以游戏服务器都是有状态的,状态就存在内存,偶尔会接受redis,mysql等是绝对不可以的接受的,关系行数据库仅用来定时异步持久化数据,仅游戏服务器而言持久化在redis即可。
游戏服务器一般纯需要主动推送,所以第一代微服务网关就没办法满足需求, tcp的没有网关用,spring cloud gateway的web socket也许可以用(但是从防攻击角度讲端游用TCP绝对比web socket合理)。
服务间通信rpc首先ribbon,feign等并不是合适,因为都是基于http的,用Http存在一个消息乱序问题,比如玩家出牌两次,在http就可能出现次序不一致。游戏服务器集群一般使用长连接互联。可能需要用dubbo?(听说是长连接)
游戏逻辑服务器(比如斗地主服务器),一般是不能用spring mvc做的,因为线程模型完全不同。多线程模型处理游戏性能差还非常复杂,一般都是使用单进程/线程 驱动固定数量房间的方式(这也是为何服务器一定有状态,一定不能直接读写mysql)。一般就直接netty了
自动扩容在游戏这边叫做开服,早就有固定流程和工具和限流方式了
游戏很多操作不存在服务降级熔断,不行就要直接报错给用户。
大厅服务器登录注册等的确可以做微服务,但是其实也不是做微服务,就是几个接口有自动水平扩容的方案即可。服务注册发现用处不大,开服都是确定的事情,还有一系列运营手段配合,关服也是绝对不能随便关的。
游戏处理的流量真的不算多,你在线1万的棋牌游戏已经很赚钱了,10W就是个特别厉害的产品了。
一些独立的服务器比如充值之类的需要微服务化么?只能说这种服务器都需要微服务处理了,项目组做梦都能笑醒。
虽然上面说了很多点,但是其实也是可以考虑用spring cloud改造的,因为游戏集群一样有注册中心,需要服务发现,需要编排启动顺序,只是spring cloud没有为了游戏设计而已,比如至少要完全支持 webflux吧(没有仔细研究),需要一个单线程的长连接最好支持protobuf rpc框架吧(集成服务发现相关功能与接口),网关支持tcp或者至少封装或者暴露一些netty的decoder encoder(或者允许注入)等等skynet等不在讨论范围之内。
个人看法
首先抛出观点:微服务应该是业务能力的拆解,不是功能的拆解。比如说王者荣耀里面,其实对战系统、商城系统、聊天系统等从理论上来说已经满足是拆分之后的单个服务的,至此,虽然没有实际采用微服务架构但是其理论上依然满足微服务的说法。
再其次,如果采用微服务架构,那么从性能和延迟、复杂性和维护成本、数据一致性、开发效率和资源利用率等方面来说就需要多做一些考虑了。
性能和延迟:游戏需要实时响应,并提供流畅的游戏体验。微服务架构引入了网络通信的开销,可能会增加延迟并降低性能,这对于需要高速响应的游戏来说是不可接受的。
复杂性和维护成本:微服务架构涉及到拆分应用为多个独立的服务,每个服务都需要独立进行开发、测试、部署和维护。这增加了整体架构的复杂性,并且需要更多的人力资源来管理这些服务,这可能会增加开发和运维的成本。
数据一致性:游戏中的数据通常需要保持一致性,例如玩家的装备、道具、任务进度等。微服务架构中,不同的服务可能会有自己的数据存储和管理方式,确保数据的一致性变得更加困难。游戏公司可能更倾向于使用单体架构中的共享数据库,以确保数据一致性和简化数据管理。
开发效率:游戏开发周期通常紧凑,需要高效的迭代和快速的发布。微服务架构可能会增加开发和测试的复杂性,并且需要更多的集成测试和部署工作。单体架构在这方面更容易管理,可以提高开发效率和快速迭代的能力。
资源利用率:微服务架构通常需要运行多个独立的服务实例,这可能导致资源的浪费。对于大规模的在线游戏,需要考虑服务器硬件资源的有效利用以支持大量玩家同时在线,单体架构可能更容易实现资源的优化分配。
目前来看,微服务更多的还是用在web系统、大型互联网应用、电子商务平台、金融科技系统等方面在,越是体量大的,复杂度高的,异构型强的就越适合采用微服务架构。所以啊,虽然这个技术火热,但是也万万不可迷信它啊!