WEB與游戲開發的一些區別
前言
最近由於在准備期末考,以及准備實習。其實都沒好好寫過博客,但今天由於個人身邊的一些事,所以對做web和做游戲開發的區別做個記錄,以下都是從網上搜索到的資料文章,感覺可以解決一些疑惑,有的文章無法貼原作者的鏈接,因為實在找不到原作者,如有侵權,聯系刪!!!
-
需要事先了解的點(復習點)
-
HTTP協議的特點
簡單快速:
客戶向服務器請求服務時,只需傳送請求方法和路徑請求方法常用的有GET,HEAD,POST每種方法規定了客戶與服務器聯系的類型不同由於HTTP協議簡單,使得HTTP服務器的程序規模小,因而通信速度很快。
靈活:
HTTP允許傳輸任意類型的數據對象正在傳輸的類型由內容類型加以標記。
無連接:
無連接的含義是限制每次連接只處理一個請求服務器處理完客戶的請求,並收到客戶的應答后,即斷開連接采用這種方式可以節省傳輸時間。
無狀態:
HTTP協議是無狀態協議無狀態是指協議對於事務處理沒有記憶能力缺少狀態意味着如果后續處理需要前面的信息,則它必須重傳,這樣可能導致每次連接傳送的數據量增大。另一方面,在服務器不需要先前信息時它的應答就較快支持B / S及C / S模式
以下是 HTTP 請求/響應的步驟:
1、客戶端連接到Web服務器
一個HTTP客戶端,通常是瀏覽器,與Web服務器的HTTP端口(默認為80)建立一個TCP套接字連接。例如,http://www.oakcms.cn。
2、發送HTTP請求
通過TCP套接字,客戶端向Web服務器發送一個文本的請求報文,一個請求報文由請求行、請求頭部、空行和請求數據4部分組成。
3、服務器接受請求並返回HTTP響應
Web服務器解析請求,定位請求資源。服務器將資源復本寫到TCP套接字,由客戶端讀取。一個響應由狀態行、響應頭部、空行和響應數據4部分組成。
**4、釋放連接TCP連接**
若connection 模式為close,則服務器主動關閉TCP連接,客戶端被動關閉連接,釋放TCP連接;若connection 模式為keepalive,則該連接會保持一段時間,在該時間內可以繼續接收請求;
5、客戶端瀏覽器解析HTML內容
客戶端瀏覽器首先解析狀態行,查看表明請求是否成功的狀態代碼。然后解析每一個響應頭,響應頭告知以下為若干字節的HTML文檔和文檔的字符集。客戶端瀏覽器讀取響應數據HTML,根據HTML的語法對其進行格式化,並在瀏覽器窗口中顯示。
-
Socket通信服務器
Socket服務器主要用於提供高效、穩定的數據處理、消息轉發等服務,它直接決定了前台應用程序的性能。
我們先從整體上認識一下Socket服務器,Socket服務器從架構上一般分為:網絡層、業務邏輯層、會話層、數據訪問層
(一) 網絡層
網絡層主要用於偵聽socket連接、創建socket、接受消息、發送消息、關閉連接。作為socket通信服務器,網絡層的性能相當重要,所以我們在設計網絡層時,要着重在以下幾方面獲得突破:最大連接數、最大並發數、秒處理消息數。如何突破呢?下面我為大家介紹幾種網絡層常用到的一些技術和技巧(具體實現,我將在博文中逐一具體闡述):
1)Buffer管理
每一個SocketAsyncEventArgs對象(以下簡稱SAEA)在內存中都有其對應的緩存空間,如果不對這些緩存空間進行同一管理,當SAEA對象逐漸增多時,這些SAEA對象的緩存空間會越來越大,它們在系統內存中不是連續的,造成很多內存碎片,而且這些緩存不能重復利用,當創建、銷毀SAEA對象時,造成CPU很多額外消耗,影響服務器性能。面對這問題如何解決呢?用Buffer池管理!
2)雙工通信
Socket服務器提高通信效率是一個永恆的話題,提高通信效率有很多種方法,雙工通信就是其中之一。一個SAEA對象在同一時刻只能用來接收數據或發送數據,有人想,如果一個SAEA對象在同一時刻既能發送數據又能接受數據,那肯定會提高socket通信效率。恩,很有想法!可是你能讓你的頭在同一時刻既往左轉又往右轉嗎?答案是不行的,那如何實現雙工通信呢?既然一個SAEA對象在同一時刻只能做一件事,那我自定義DuplexSAEA對象,在該對象中封裝兩個SAEA,一個用於接受,一個用於發送,問題不就解決了嗎。
3)poolOfAcceptEventArgs
poolOfAcceptEventArgs是個什么東西?它不是個東西,是一個容器,一個容納AcceptSAEA對象的容器。給你兩個socket服務器,你能很快判別兩個服務器性能的優異嗎?很簡單,你瞬間向一台服務器打入5、6萬的連接,看看會不會都連上,如果都連上,說明這台socket服務器的並發處理連接的能力還是不錯的。那如何提高socket服務器的並發連接能力呢?答案:poolOfAcceptEventArgs!
4)消息隊列調度器
消息隊列調度器主要分為兩種:接受消息隊列、發送消息隊列。為什么要用消息隊列呢?主要是提高socket服務器的吞吐量。首先我們定義一個隊列Queue,然后編寫N個調度器,不斷從隊列中調度消息,接受隊列調度器用於將消息拋至業務邏輯層處理,發送隊列調度器用於調用網絡層發送消息接口,向指定端口發送數據。
5)心跳掃描
有一個困惑:客戶端連接socket服務器,連接沒有斷開,但客戶端掛了,這樣這條連接在socket服務器中就成了釘子戶,落地生根不走了!一個釘子戶還可以忍受,千千萬萬個呢?那就崩潰了!怎樣解決這個問題呢?定時掃描每條連接,如果該條連接在超時時間內沒有IO響應,則關閉它。
6)粘包
服務器在接受消息包時,如果兩個數據包同時被你服務器收了怎么辦?你會把他當成一個數據包嗎?如果一個數據包斷了,分成兩次被你服務器收了,你會把他們拼接起來嗎?這些就是粘包了,怎么解決?正則表達式掃描!
7)多線程編程
Socket服務器的編程就是多線程編程,面對多線程,線程間怎樣同步、怎樣避免死鎖?多線程訪問公共資源如何處理,在下面的博文中,我將會為大家具體闡述。
(二) 業務邏輯層
網絡層將解包后的消息包拋至業務邏輯層,業務邏輯層收到消息包后,解析消息類型,然后轉入相應的處理流程處理。
網絡層應提供發送消息的接口供業務邏輯層調用,因為網絡層不會主動發送消息,發送消息的操作是由業務邏輯層來控制的,所以業務邏輯層應根據具體的業務應用,封裝不同功能的發送消息的方法。
(三) 會話層
會話層主要用於記錄在線用戶信息,該層隸屬於業務邏輯層。既然隸屬於業務邏輯層,那為什么還要獨立出來呢?這主要為以后分布式開發拓展用,試想,一台服務器最大能支持多少人同時在線?中國有多少人?如果1億人同時在線,你一台服務器能支持得了嗎?答案肯定是否定的,所以要分布式開發。分布式開發涉及到用戶信息同步的問題,所以會話層就要獨立出來了。
(四) 數據訪問層
數據庫執行效率是整個socket服務器的瓶頸?為什么呢?舉個例子:假設我們的socket服務器的秒處理消息的條數為3000,每處理一條消息都會保存歷史記錄,那么,如果數據訪問層不想拖網絡層的后腿,那么他的執行sql語句的效率也必須達到每秒3000!如果socket服務器和數據庫服務器部署在同一網段上,這個速度是沒有問題的,但如果數據庫服務器部署在外網呢?你的sql語句的執行效率能達到那么高嗎?很困難!
再思考一個問題:如果網絡層執行線程和數據庫執行線程是同一個線程,那么網絡層的處理必須等待數據庫執行完畢后,才能進行!如果數據庫執行效率比較慢,那對整個socket服務器將是一個毀滅性的打擊。
那么怎樣將數據訪問層與網絡層分離,讓他們互不影響?如何提高數據庫執行效率,讓網絡層的處理速度和數據訪問層的處理速度達到一個平衡?答案:連接池+sql調度器+主從數據庫
原文:https://www.cnblogs.com/tianzhiliang/archive/2010/10/28/1863684.html
-
解釋一:
原文未找到!!!
在一間游戲公司的兩個部門待過, 前一個部門以做web開發為主,后一個部門做游戲開發,我在兩邊都是做后端的。在游戲部門待的時間不長, 不敢說已經深入了解游戲開發技術細節,我僅把我已經接觸到的內容與之前擅長的web技術做對比,一來作為工作日志記錄, 二來希望能給想從web轉游戲的同學提供一個預先學習的方向,少走一些彎路。
這一系列內容我會連載發布,而不是一次性講清楚所有內容, 畢竟當前還不敢狂妄的表示已經了解游戲開發的種種細節。
通用性
即使不同類型的軟件開發也是具有一定相似性的,這種相似性隨軟件類型的不同而不同。 如web前端開發與web后端開發差異就挺大, 前端程序運行在瀏覽器中,后端程序運行在服務器上;前端程序操縱的目標是網頁元素,后端程序操縱的目標是存儲在服務器上的數據。前端和后端相似的地方估計也就編程語言使用的一些基礎概念了,所以前后端程序員崗位轉變存在一定難度,比如讓一個沒有任何后端經驗的前端程序員立馬上手寫后端程序,幾乎不可能。
然而web服務器和游戲服務器的差異就沒這么大了,它們用的是相同的編程語言, 比如說java;它們用的是相同的數據庫軟件,比如mysql和redis;它們都運行在服務器端,比如linux server和windows server,且對穩定性要求及其嚴格。擁有這幾處相同點兩種程序在宏觀上是完全相似的,對應程序員工作的轉換也不存在硬性的技術障礙, 如果程序員技術基礎扎實,完全可以平滑過渡。
差異性
因為業務的不同,web服務器和游戲服務器勢必存在不同之處,然而這種不同並非技術上的不同,而是套路上的不同。
服務器類型的不同
web程序使用http服務,瀏覽器和服務器之間是http協議通信。游戲服務器通常是一個socket服務器,與游戲客戶端之間保持長連接,如果是網頁H5游戲,那么使用的也是全雙工的websocket協議。通常,使用http協議的web服務器不用程序員費事去管理網絡連接,程序員只要專注業務邏輯即可。而使用socket或者web socket等協議進行長連接通信卻需要程序員手動編程管理,比如說斷線重連游戲狀態恢復機制,就需要手動處理網絡連接。這表示socket編程難度大於http編程,從而導致游戲服務器編程大於web服務器編程。可這並不能表示游戲服務器編程不同於web編程, 如果一個web程序員不了解socket編程原理,那也不能算一個優秀的web程序員,畢竟http是以socket為基礎的。
傳輸數據格式的不同
在web前后端傳輸數據除了使用http標准的鍵值對格式以外使用最多的是json,json被使用的一個最重要的原因是與JS無縫兼容,高效方便。然而,這種優勢在游戲客戶端中不存在,人家游戲客戶端又不使用JavaScript編程,所以游戲客戶端和服務器之間有更合適的數據傳輸時格式存在。我接觸到的是谷人希家的protocol buffer協議, 它相對於json的優點是體積小。經我測試,同樣的數據內容,使用protocol buffer格式存儲大小只有使用json存儲的三分之一甚至更低,即使在某些特殊情況下也不會超過json的一半。
嚴格驗證數據格式,有xml xsd的功效。而json數據格式驗證相對寬松,只要符合json語法就行,因此容易出錯。反之,protocol buffer則更加穩定。至於缺點最嚴重的是使用麻煩, 需要借助谷人希的第三方工具才行。protocol buffer的使用細節,這里不作講解。分布式處理業務
我接觸到的游戲服務器是微服務的一種形態, 整個游戲服務器的邏輯被分割成很多服務模塊,分別運行在不同的服務器上。然而,我無法理解的是每個模塊之間的通信居然使用socket,而不是更流行的http。游戲客戶端與服務器之間使用socket連接可以理解, 然而,服務器各模塊之間也使用socket卻有些使我莫名其妙,雖然這會使服務器之間通信性能有所提升,卻會帶來編寫代碼任務過於復雜,穩定性下降等問題,為了些許性能提升而喪失項目的維護性,有點得不償失。 不過也有可能我還沒有理解其中奧秘,判斷過於片面。
極端的性能敏感
游戲中實時對戰模塊必須使用c/c++實現,原因是JVM執行垃圾收集時會造成虛擬機停頓,也就是stop the world。在虛擬機技術發展日新月異的今天, gc停頓依舊會對游戲體驗造成影響,因而必須使用老掉牙的c++, 這使我感到震驚。另外, 游戲中大多數數據被放在redis中而非mysql也使我意外,數據持久化存儲顯然不是redis的優勢,拿性能換穩定和安全,這種做法略顯激進。
以上內容是我當前對於web開發與游戲服務器開發不同之處的見解,如有謬誤請指出。 此外,在之后的學習和實踐中的心得體會,會在之后的文章中繼續發布。
解釋二:
游戲服務器和Web服務器的區別
有些對游戲服務器的介紹可能會說,游戲服務器是一個需要長期運行的程序,然后怎么怎么樣。我個人認為Web服務器一樣的需要長期運行,也需要響應不定點不定時來自用戶的請求。兩者從宏觀上來看其實沒有本質的區別。同時Web服務器也會對於穩定性和性能有要求,游戲服一般分為大小服,我們這里都按照小服舉例子。
3.1 狀態
首先要提到的就是狀態。可能你會聽說過一個概念,游戲服務器是有狀態的,而Web服務器是無狀態的。什么意思呢?Web服務器的數據流大多直接會到數據庫中。而游戲服務器的數據流首先會到內存中,然后定期的寫入數據庫(落地)。
換句話說,游戲服務器本身的數據與數據庫中的數據在運行期間會存在一個數據不一致的窗口。如果此時游戲服務器宕機了,那么就會造成數據首先到的內存數據與數據庫存的數據不一致。
而Web服務器則不會有這樣的問題,Web所有的數據狀態都會落地,而且可以針對操作加上事務,不用擔心因為操作失敗而引入臟數據。正因為有了狀態的約束,游戲服務器就會很慎重的使用內存、CPU。以求在資源有限的情況下,最大化的提高的承載量,並且降低服務延遲。當然,Web服務器會為了降低某個接口的響應時間而去做對應的優化。
3.2 擴容
在Web服務器中,如果你不能評估一個服務所面臨的壓力,又不想因為瞬時的熱點訪問導致服務直接不可用的話,完全可以設置成自動擴容,因為每個服務只是單純的接收請求,然后處理請求、返回結果,不會將數據保存在服務器的內存中。要有數據存到內存,那也是在Redis中。而Redis數據丟失對數據的一致性基本沒有影響。
但是在游戲服務器這邊很難做到像Web那樣靈活。首先,數據的流向不是數據庫,而是內存。
舉個很簡單的例子,玩家的主城被攻打着火了,如果有了自動擴容,很有可能在落地的窗口內,玩家再請求一次,請求到了另一個實例。主城又沒有着火了。因為數據都會先存在內存中。
再舉一個例子,玩家氪金買了一個禮包。然后退出游戲,落地窗口內再次上線沒了。這就不是單純的數據問題了,玩家這是花了真金白銀買的道具,突然就沒了,一兩個還好處理,如果多個玩家都出現這樣的問題,那這就屬於嚴重的線上事故了。修復數據的工作量十分的大。
所以,對於一個游戲服務器,所能使用的內存和CPU的資源是非常有限的,不像Web服務器可以不用花很大的代價做到橫向擴展。這也就是為什么游戲服務器會十分十分的注重代碼的性能以及穩定性。
3.3 穩定
就像上面說的例子,如果游戲服務器運行中出了BUG,導致服務直接不可用,或者說通過這個BUG刷到了大量的道具,將是一個非常嚴重的線上事故。
而對於Web服務器來說,如果是管理系統之類的,有可能會有臟數據值得一提的是,臟數據對於Web來說,排查起來也是一件很頭疼的事情。如果沒有臟數據,只是服務暫且不可用,而且如果用的是微服務架構,重啟服務的代價是相對來說比較小的,只有正在重啟的服務的業務是不可用的,其余的部分則可以正常的訪問。
而對於游戲服務器來說,服務器重啟影響的是全服的玩家。玩家在停服期間,甚至連游戲都進不了,特別的影響玩家體驗。而且,如果停服之前服務器的數據落地出現了問題,服務重啟之后會將數據從數據庫load到內存中,此時同樣會造成數據不一致的問題。
3.4 性能
從我的經驗來看,在做Web服務器的時候,沒有為了減少GC的壓力,為了少占用內存去做過多的優化。當然這是因為項目本身的體量不大,如果QPS很高的話,Web服務器同樣很需要注重性能,只不過游戲服務器需要一直特別注意這個方面。
不過在Web,如果訪問量很大的話導致單個服務不能扛住壓力,大部分人首先想到的解決方案應該就是搞多個實例,畢竟可以做到很輕松的橫向擴展。
在游戲服務器里,會把服務器的資源看的相當的寶貴。例如,能不落地的字段就絕對不要落地,某個字段的值可以通過已知的條件算出來的,就盡量不要定義在代碼里。不過這也要看具體情況權衡運算量和調用的頻率。因為上線之后,如果遇到了數據不一致,維護的數據越少,修復數據的難度就越小。
3.5 嚴謹
這一點上來說,我認為是兩者都很關注的一個重點。只不過,在游戲服務器的某些情況中,如果服務器拋出異常或者panic。其造成的后果會被游戲特殊的環境放大。
例如,召回你的在外部隊失敗了,那么部隊就會一直在外面且不可用。這跟在瀏覽器中點一個按鈕沒有反應比起來,影響相對較小。而且使用微服務架構,在修復問題之后可以以很低的成本來重啟對應的服務,而游戲服務器中還要修復一次數據。
再舉一個很極端的例子,點擊商店,玩家要准備氪金了。但是卻發現進不了商店,也可能不能獲取商品列表。這些會直接影響到游戲的體驗,甚至收入。
而對於Web來說,服務器的穩定性同樣很重要。不然根據業務的不同,造成后果的嚴重性也有可能不同。影響了用戶體驗,就會直接影響到產品的口碑。
3.6 數據傳輸格式
熟悉Web的都知道,數據傳輸格式是JSON。而在游戲服務器中是Protobuf,是由Google開發的數據傳輸格式,與JSON類似。Protobuf是二進制的,二進制數據量會比JSON更小一點。而且,如果傳輸的字段是空值,就不會被傳輸。而JSON如果是空值,一樣的也會被傳輸。
無論是在什么樣的環境中,舉個例子,Node.js和Java中,Protobuf的性能表現都比JSON好。在Java中,Protobuf甚至要比JSON快了接近80%。如果Java的服務之間通信有了性能瓶頸, 可以考慮服務之間使用RPC來通信。
但是凡事都具有兩面性。Protobuf的缺點仍然存在:
- 文檔較少
- 社區與JSON的對比起來
- 可讀性沒有JSON好
問題
對這塊僅僅做了個簡單的了解,也並不知道大佬們眼中的區別是怎樣的,以及那種多人聯網棋牌類游戲用Java可以開發嗎?如果可以,可以用什么方式呢?個人沒有一個准確的認識,希望有大佬可以給我指正以及講述一下這里面的一些原理。