多人互砍游戏的后台服务器的多线程架构
概述
本文敘述從最簡單的單線模型程進化多線程的模型。不用 spring 而是用普通的 java 代碼,只做了登錄、移動、互砍、傷害計算,英雄死亡等功能的最最簡單的版本,主要討論的是框架架構,不是業務有多絢。
1.服務器處理客戶端的連接和客戶端指令當然用 netty。
2.客戶端用 cocos,本文不討論,協議自定義好了,生成 java 代碼用protoBuf,本文不討論。
3.用到的設計模式,工廠+策略、單例、觀察者。
為什么游戲服務器用單線程
一般會認為單線程處理超高并發的應用時慢,其實絕大部分應用的性能瓶頸是 IO 和 FullGC。 比如網游一般是同步模型的,分區分服務器的架構,即使需要多個服務器之間共享數據,也是由另外的服務器去處理,對客戶端是不可見的。
1.游戲服務器的協議是自定義的(對比web服務器用的是http協議,網絡傳輸直接傳的字符串,游戲服務器傳很小的字節數組)請求的包很小,網絡IO沒問題。
2.游戲服務器是長連接(對比web服務器,session保持幾分鐘的不同),連接建立了就不釋放,然后就一直玩,砍。基本上是客戶端退出了才斷開連接。
3.單線程能保證數據的一致性。比如,多個人互砍,每個人的砍的動作的指令在服務器里是 netty 的不同 EventLoop 的線程處理的,那傷害在這么多個線程里同步加鎖是不切實際的。
好的架構應該是足夠簡單,不可為了炫技搞得很復雜。在增加業務時改動足夠少或者沒有改動而是純增加。
反射和 javaassist 用起來,比如 mybatis 只需要寫接口沒有實現類就可以直接使用,就是用的反射和 assemble(類似與javaassist的一個組件),在運行時直接生成實現類的硬編碼,從而直接使用。
上代碼
代碼實現處理客戶端連接(netty),客戶端的移動、互砍指令(netty)
最普通的單線程架構
為了數據同步,把netty 的多線程處理匯總到一個單線程 mainThreadProcessor 處理。想象一種場景,多人在線的同時(移動,互砍等動作)有很多人同時在登錄,而登錄是要寫數據庫(IO操作)的,那么IO操作會阻塞其他在線的人的動作,砍一刀要1ms,如果IO阻塞500ms,那那么多人還砍個屁啊。所以要把 IO 操作和其他操作分開。
把登錄操作分開到其他線程
這樣,每次用戶登錄都用新的線程處理。這樣雖然解決了IO耗時問題,但是如果一個用戶在操作IO的線程完成之前重復登錄多次呢,線程之間不通信,就會有同樣業務的多條數據被寫進數據庫,業務錯誤。
把固定業務的操作放到固定的線程
這樣,使用一個線程池,用某個算法(比如用戶名的hash對線程池長度取模)定死了某個用戶的登錄在某個線程進行,就算用戶再重復快速登錄,線程也會阻塞等前一個任務執行完,就不會重復寫了。
一般會想到線程間通信來解決此類問題,正如之前所說,線程間通信的做法不推薦。
此處只是用登錄舉例,但凡是IO操作的,都可以一次類推。
代碼太多了,不好貼出來,其實架構上還是有亮點的,比如編碼和解碼自動化,業務邏輯的增加自動化。觀察者用java8 的函數式接口實現等等吧。想共同學習的朋友私信我。
總結
以上是生活随笔為你收集整理的多人互砍游戏的后台服务器的多线程架构的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 手撕 RPC 2
- 下一篇: zookeeper配置中心