聊天室服務分析設計

2021-09-23 19:13:22 字數 4264 閱讀 1162

如果你需要寫乙個簡單的聊天室的服務,那麼我想很多網上的demo都可以直接拿來用。但是如果你要做的是給線上百萬甚至千萬級使用者用的服務,那麼,整個結構和聊天室demo是必然不一樣的。本文就從設計乙個大使用者量的聊天室服務的角度出發來思考。

首先使用者量大必然先考慮的問題是服務是單程序還是多程序,單機器還是多機器,單程序代表的是單機上跑乙個服務,單機器代表的是單機上跑乙個或者多個服務,這兩種方案都是不可行的。理由是考慮下面幾個方面:

1 單程序或單機器對機器效能要求較高:由於一台機器上的乙個程序直接服務於這麼多使用者,因此在記憶體,頻寬,cpu等要求上是非常高的。

2 單程序必然導致高記憶體,高記憶體的記憶體**機制會對服務穩定性產生影響

3 單程序的程序容錯性不好。一旦這個程序掛了,那麼整個服務就會處於癱瘓

4 單程序的擴充套件性不好。由於服務使用者必然是從少到多,我們需要的是乙個可以不斷增加機器就能增加抗壓性的服務。

於是考慮使用到多機器多程序(分布式服務)。

談到多程序的架構設計,避免不了的是程序間通訊的問題。兩個程序進行相互通訊分為兩種情況:同機器兩個程序的相互通訊,不同機器的兩個程序的相互通訊

同機器兩個程序間的通訊機制有:unix管道,訊號(比如我們平常使用的ctrl + c),共享儲存(包括把需要通訊的資訊放在檔案或者記憶體中)

不同機器的兩個程序通訊機制就要使用到socket通訊了。通過tcp從乙個程序傳送資訊給另乙個在監聽埠的程序,等候監聽埠的程序會返回需要的結果的整個模型就是rpc模型(遠端服務呼叫)。rpc模型也有兩種,prc via tcp或者rpc via http。rpc via tcp相較於後者傳輸的資料更少(少了http包的那些部分),所以效率更高,但是卻實際上不適合在internet這樣存在著危險的環境中使用。所以rpc via tcp適合在內網的兩個程序間互動使用。

對於聊天室的需求,伺服器一般是假設在內部網路上,不需要對外進行服務提供,所以應該選在rpc via tcp的方式最好。

對於乙個聊天室服務,聊天的實時性是最硬性的需求。這裡該考慮使用什麼技術來完成實時性的需求了。

客戶端每隔一段時間(較短的時間)對服務端進行http請求,詢問是否有傳送給自己的聊天訊息。如果有則返回具體訊息,如果沒有就返回空訊息。

這個方法的缺點有幾個:

a 不實時:較短的時間再短也不能說是實時的。

b 浪費請求:必然是空訊息的請求次數大大多於有訊息的請求次數。那麼意味著大多數的請求實際上都是沒有用的。造成的結果就是浪費了流量,增加了服務端的壓力。

算是第一種方法的優化。具體表現就是傳送了http request之後不立刻返回http response,而是把http請求hold住在服務端,等到有訊息返回的時候,再返回http response。

這種實現方法就將第一種的兩個缺點給解決了。老王這篇文章寫了乙個nginx+lua的實現:

客戶端發起tcp的連線,與伺服器建立連線後,客戶端和伺服器都不關閉連線。如果有訊息到達,服務端就會主動推送訊息給客戶端。這就是「推」的機制。

這個方法看起來是最美好的。因為它能完全保證訊息的實時性,也能最小限度的節約請求。但是放到具體的環境中就有幾個問題了:

首先是在bs架構中,瀏覽器充當客戶端,這個方法就需要瀏覽器能傳送tcp請求。在html5出現之前瀏覽器是只能傳送http請求的。但是好在html5推出了websocket協議,它使用http握手+tcp傳輸的方式來實現了瀏覽器與伺服器的tcp連線。具體的協議細節可以看

還有就是考慮到安全性,如何保證tcp網路包的安全,你可能會用到簽名,加密(對稱、非對稱)等方法。

對於聊天室的需求,websocket是最佳選擇。

下面進行服務元件設計。

使用長連線,必然有個服務元件專門服務於長連線的建立,維護,接收和傳送訊息。我們可以稱之為connector。

這個服務需要是分布式的,有多少使用者就會有多少個長連線,維護這麼多長連線需要使用的是多個機器的服務。而且如果允許的話,建議connector能監聽在80埠,以避免客戶端的防火牆對埠的限制。

connector是暴露在伺服器前端的,後端必然有個服務能處理聊天的業務邏輯,其功能包括:

a 管理聊天室的使用者

b 接收connector傳送的群聊或者單聊的訊息(通過程序間通訊)

c 傳送群聊和單聊的訊息給對應的connector(通過程序間通訊)

這個業務邏輯服務我們稱之為chat

這裡就有幾種設計了

這實際就是一種儲存的功能,那麼能不能使用現有的儲存服務來做呢?nosql(比如redis)?

這種設計是完全可以的,redis的儲存是放在記憶體中,訪問的效率是***的,唯一無法保證的可能就是網路傳輸了。

缺點很明顯:

a 業務邏輯「獲取聊天室使用者」的請求量一定非常大,這個請求如果是通過網路傳輸來請求的話,那麼對網路和儲存服務的穩定性等要求較高。一旦網路不穩定,那麼服務必然受到影響(但或許想想,什麼服務不是這樣呢的)。

b 儲存服務是否要分布式的呢?估計也是需要的,那麼可能還需要建議乙個hash演算法來定位和保證擴充套件性。

使用儲存服務的好處當然也不可忽略,比如可以使用現成的配套服務(比如redis的持久化),現成的資料結構儲存(比如redis的zset等結構),以及監控等服務。

在這個聊天室需求裡面,我們可能更傾向於不使用儲存功能,直接把管理聊天室使用者的功能放在chat服務中。那麼我們就需要儲存每個使用者在哪個connector中儲存的情況了,就是儲存使用者session資訊。

從效率角度上來說,乙個聊天室之在乙個chat程序中是最為好的方法。因為減少了程序間通訊。聊天室內部的訊息傳遞只要在程序內部進行互動就行了。

但是考慮下這麼個極端情況,某天搞乙個活動,乙個聊天室人數非常多,另乙個聊天室人數卻很少,兩個聊天室是分布在不同的程序中的,那麼就會導致的情況可能就是乙個程序占用非常多資源,另乙個程序卻非常空閒。這個時候多麼希望空閒的程序能夠分擔繁忙程序的服務!

其實作為聊天室的需求,第二種情況是比較極端的了,所以可以不用考慮。但是或許在其他的場景中(遊戲等)這種情況就需要重點考慮了。

下面考慮一些配套服務元件

首先是connector的分配問題,我們不應該直接將connector的選擇拋給客戶端,由客戶端決定連線哪個connector,因為這樣很容易會造成connector的負載不平衡的問題。

所以我們設計在connector之前使用乙個gate服務,算是一種負載均衡服務,它的行為是:

客戶端先連線gate,gate告知客戶端應該連線哪個connector,並將connector的位址埠等資訊返回,後客戶端連線指定的connector

但是這裡其實也是存在安全隱患的,如果攻擊性的客戶端跳過gate的負載均衡,直接連線connector,那麼可能導致某個connector崩潰,可能會出現雪崩效應了。安全的問題,深究下去是個很複雜的工程。

分布式系統最頭疼的可能是服務管理了,不管是程序的啟動,關閉,更新等操作,如果一台一台機器乙個乙個程序進行操作很容易導致操作者的崩潰。所以就需要乙個服務能一次性進行所有機器的啟動,關閉,重啟等操作。

我們希望達到的最終結果是啟動了主伺服器的master程序,本地和遠端的connector,chat,gate程序都會根據配置檔案進行啟動。當然本地的程序啟動相對容易,不管是什麼語言都應該會有command.exec類似的命令來啟動,遠端的程序啟動則稍微麻煩些:

歸結起來有兩種方法:

1 ssh進行遠端機器呼叫命令。這個就要求進行ssh的帳號在其他機器上有存在,並且在~/.ssh下儲存了對應的rsa等金鑰資訊。那麼就可以使用這個帳號來呼叫遠端機器的程序了。

這個方法有個不好的地方,就是帳號許可權,一般出於安全考慮,這個ssh的帳號不可能是root帳號。但是不用root帳號啟動,就可能遇到各種限制(比如無法占用80埠來啟用connector)

2 在每個機器上啟動乙個啟動和關閉聊天服務的服務,我們叫做deploy。這個deploy對master提供rpc服務,master通過rpc呼叫來傳送「啟動/關閉/更新 程序」的命令。

這個方法的好處就是可以使用root來啟動deploy了,但是不好的地方是deploy只能由手動啟動了...

chatofpomelo是在網易的pomelo遊戲服務框架上搭建的乙個聊天服務。使用這個例子,我們就能很好的了解分布式聊天伺服器的架構

在官網上是有個架構圖的,原圖稍微複雜了點,我這裡做一下修改:

聯絡到上面的分析可能就更好理解這個圖了。

任何設計都是存在弊端的,但是只要這個弊端不會在需求中被體現,即能滿足需求,就是乙個好設計,所以說在不明確的需求面前,任何設計都不能說是正確的。

mysql 聊天室 聊天室php mysql 六

聊天室php mysql 六 相應的 資料庫 phpmyadmin mysql dump 主機 localhost 3306 資料庫 study28 資料表的結構 chat user create table chat user userid varchar 20 not null,passwd v...

聊天室程式

伺服器 include include include include include include include include define servport 8081 伺服器端口號 define bufsize 200 最大傳輸量 int main int args,char argv s...

簡單聊天室

include include include include include include include include include include pthread t thread 2 void send msg void ip msg if connect sockfd,struct ...