面向微服務的服務端架構

loveli1215 8年前發布 | 31K 次閱讀 微服務 pomelo

一年前的這個時候,小說君在知乎上提了個問題,關于pomelo框架與網易內部部分開源的服務端框架mobile_server。

當然,后來的發展也在預料之中,回答者寥寥。不少回答的同學要么是匿名噴,要么是灌了個水之后刪掉。一年過去,也并沒有哪位既用過mobile_server,又了解過pomelo的同學參與回答,非常遺憾。

考慮到有些同學并不是做游戲服務端的,一是不了解游戲服務端開發中的一些術語,二是可能不太了解前述兩個架構,所以這里就簡單提一下。

這兩個架構實際上主體部分都與之前的「面向中間件的開發模式」一文最后形成的架構大致相似,貼張圖:

當然,除了節點命名的區別之外,兩種架構還是與圖中有所出入。

比如除了Gate之外,仍然有一個獨立的前端服務器,供玩家登錄以及負責為玩家分配要建立長連接的Gate服務器。

另外簡單介紹下:

  • 圖中的Gate是一種反向代理服務器,可以理解為web開發中的nginx。

  • 圖中的中心進程和場景服務器都是應用服務器,可以理解為web開發中的tomcat節點。

  • 圖中的DbProxy既有部分業務邏輯,又具有一定的緩存功能,不過完全不影響將之理解為web開發中的緩存服務器,比如redis。

mobile_server大體如此,而pomelo則是在這個基礎上引入了更多web開發的思路。還是舉一些例子:

  • 看pomelo的官網,我們就能發現,里面用的route、filter、channel,app應用框架的插件化形式加載模塊,以及一些實現的較獨立的monitor、watchdog、admin-tool等,都帶著web開發的影子。

  • pomelo的配置比較統一,都是json。不像游戲服務端框架喜歡用ini或者xml。

  • 對分布式的支持。大部分游戲服務端框架雖然號稱 「 分布式框架 」,其實對分布式的支持很有限,能支持個gate集群或者game集群已經算是相當不錯了。但是服務發現、守護進程這些都不會有。

  • pomelo還借助zookeeper幫一些全局單點做了高可用,當然細節我沒仔細看,可能首先還是要先把這些全局單點搞成無狀態服務才行。

說了這么多,想必各位可能覺得小說君是在安利pomelo。當然不是,pomelo有一個致命的硬傷,就是這是js寫的。且不說在大佬們眼中C#/JAVA寫服務端就已經非主流了,純js簡直死刑,單說團隊協作這塊,js這種弱類型語言對于中等規模的開發團隊簡直是噩夢。這也是pomelo在網易內部都一直不溫不火的原因。但是說來也怪,這些語言上的硬傷都并不能阻擋js成為程序員中的主流。

相比之下,mobile_server雖然是c++搭python,但是由于是bigworld這種老牌引擎改過來的,上層顧慮少,資源投入多,有成功產品用了大家就都會用。但是像一開始提到的那個知乎問題下面,有人答了又刪的答案所說mobile_server就是屌、pomelo就是渣這種論斷,就顯得成敗論英雄了。

扯了這么多八卦,下面我們還是言歸正傳,看看標題「面向微服務的服務端架構」。

十年前,有幾個縮寫比較火,一個是SOA(Service-Oriented Architecture,面向服務架構),一個是SaaS(Software as a Service,軟件即服務)。

兩個概念描述的其實是差不多的意思,那就是業務系統一定要服務化,才能有一個比較高的抽象層次,繼而做到服務級別的復用。

現在兩個概念已經沒什么人提及了,一方面是因為SOA和SaaS還是企業應用這塊吹的比較多的,另一方面是互聯網技術日新月異,框架層出不窮,用一個新框架三分鐘搭出一個業務,誰還關心服務如何復用?

但是,不可否認, 「服務化」一直都是一種比較現代化的解耦思路。因此SOA雖然已經淡出視野,但是后續的類似概念立刻跟上。

「微服務 」是這樣一種概念,邏輯被劃分為一個個的獨立單元,每個單元的邏輯非常簡單,高度內聚,單元之間是正交的。單元與單元是物理隔離的,相互之間借助異步機制通信。如此一來,每個單元就可以稱為一個 「微服務 」。

繼續拿出之前對比的pomelo與mobile_server,小說君之所以更欣賞pomelo,也是因為其在服務化上做的探索要比mobile_server多很多。

pomelo把游戲服務端中一些常見的邏輯集以 「 組件 」的形式呈現,一個pomelo節點可以選擇加載多種不同的 「組件 」。服務器 本身是鴨子類型的,也就是說,只要某個服務器 加載了某些組件,那么這個服務器 就能提供這些服務,屬于這些服務的一個實例。

但是這樣還不夠。

我們說微服務有一個特點,那就是 「物理隔離 」。如果僅僅是通過插件化來做服務加載,那是無法做到物理隔離的。

典型比如兩個微服務都依賴了同一個lib,這個lib中有一個單例,這樣兩個微服務就都能訪問到這個單例,違反了物理隔離的要求。

除此之外,還有一個致命問題,我們需要畫個架構圖才能看出問題所在。

假設我們有一套服務端架構,采用了 「 微服務 」 的設計理念。其中有兩種微服務分別是A和B,各有4和3個服務實例。

A需要訪問B提供的服務,B需要訪問A提供的服務。那么簡單的連接拓撲圖如下所示:

相互之間需要的連接數是n*m。

當然,這還是比較簡單的情況。在真實應用中,需要互調的微服務不可能只有A和B兩種,更多的情況是A、B、C、D四者,甚至更多微服務之間互調,假設每類服務的實例都有復數個,那連接拓撲簡直沒法看。

那么,面向微服務的服務端架構,如何才能解決上述兩個問題?

其實只要稍微換個思路,就能輕易解決。

首先看物理隔離。如果服務是一個基本單元,那我們如果想做到單元之間的物理隔離,需要用什么樣的容器?

最簡單的就是操作系統級別的,一個進程即是一個單元,而且是絕對物理隔離的。

這樣實現的好處就是足夠簡單——框架簡單,開發起來更簡單。

但是問題也有,想實現一套統一的服務間通信機制就只能依賴進程間通信(同物理機),或網絡(跨物理機)。

其次就是語言級別的,這方面也有比較成熟的方案,那就是Erlang的actor模型。一個actor就是一個單元,同樣是物理隔離。

而且Erlang/OTP還提供了消息通信機制,語言層面做到這些,自然可以在底層做更多的優化,比如同物理機的actor間通信完全可以zero copy。

還有一種是虛擬機級別的,云風的skynet就是如此,lua_State具有物理隔離特性,因此skynet的每個服務單元的容器就是一個lua_State。

再看連接拓撲的問題。這個問題想解決就更簡單了,假設所有的服務都與一個harbor建立連接,由harbor統一路由消息,拓撲就變成了下圖的樣子:

連接數量簡化為了n+m。

這個harbor其實就是一個消息隊列中間件,具體實現中,可以像skynet那樣用一個高度簡化的、數據結構層面上的消息隊列來做,也可以用一些第三方的成熟消息隊列中間件來做。

之后一篇服務端系列文章,小說君就會詳細介紹下如何基于RabbitMQ這個消息隊列中間件來實現這樣一種服務端架構。

微服務與服務劃分作為web開發中的一種比較現代化的理念,已經得到了充分的應用,但是游戲程序員往往對此不以為然。 文章最后,小說君想再聊聊對游戲服務端的意義。

游戲服務端實踐中,針對之前提到的連接拓撲復雜的問題,一種治標不治本的方法是抬高添加新進程的成本,比如如非必要不會允許你增加額外進程,但是這樣更深度的解耦合就成了幻想。

另一方面,游戲服務端的應用層與連接層難以分離。舉個例子,在文章開頭介紹的設計思路中,兩個進程有沒有連接是一種不確定態,設計的時候覺得沒有,結果某個需求來了,不建立連接就很難實現。這樣對于應用層來說,就需要提供連接的概念,某個進程必須先跟其他進程連接建立成功了,然后才能調用其他進程提供的服務。

而實際上,更優雅的設計是應用層完全不關注連接細節,只需要知道其他的進程提供了服務,自己就能獲取到這種服務。

當然,游戲服務端開發中,也是會出現服務劃分的需求的,最典型比如聊天服務。但是,公司內不同項目,往往直接用同一套聊天服務代碼庫,達到代碼級別的復用。這樣做的結果往往就是,每個團隊都會從更早的團隊拿過來聊天業務代碼,然后自己改造改造,成了完全不同的分支,最后連代碼復用都做不到了。

從另一個思路來講,同一款游戲的不同組服務器,其實也只需要同樣的一組聊天服務。但是如果按傳統的模式,一組服務器只能開零或一個聊天服務器,事實上,有可能某10組服務器用1個聊天服務器就夠了,而某1組服務器用1個聊天服務器壓力都有些大。

但是很顯然,如果做了服務劃分的改造,比如聊天服務直接采用一些第三方的IM云服務,這些問題就都煙消云散了。

 

 

來自:http://mp.weixin.qq.com/s?__biz=MzIwNDU2MTI4NQ==&mid=2247483728&idx=1&sn=c2076dbc98de6fbd40b87236f2033925&chksm=973f0fbaa04886ac83c975b7046885f7171be8d26695d23fcab974124ce054a65d10caea3db5#rd

 

 本文由用戶 loveli1215 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!