續一在這里:http://www.shnenglu.com/converse/archive/2009/01/14/71993.html
可能不太成熟,歡迎討論.
客戶端需要的功能: 登錄, 獲取信息(如自己的資料, 好友的在線狀態,好友資料如簽名, 圖片, 其它資料等), 客戶之間發送消息
服務器端:
有以下幾種服務器:
登錄服務器:
負責管理客戶端登入/登出,驗證登錄客戶端的合法性等,與客戶端發送心跳包維持連接的狀態,在客戶端登錄之后, 告知消息服務器該用戶上線, 消息服務器查詢是否有該用戶的離線消息將消息發送給用戶.
同時, 登錄服務器是僅有的C2S服務器, 也就是說所有要發送給客戶端的消息都需要經過登錄服務器發送, 而給客戶端發送的消息都是采用udp形式發送(包括心跳包), udp容易丟失, 因此需要在發送之后對端有回應, 否則就要再試發送.
登錄服務器和下面的資料服務器,消息服務器以及其它的內部服務器之間采用TCP長連接保持連接.
資料服務器:
負責管理客戶資料, 如圖片,個人說明,好友分組, 好友等等, 這些都需要cache, 如果在cache中查詢不到才去數據庫中查詢.
客戶端登錄之后首先往好友服務器查詢自己的資料(圖片,個人說明,好友在線狀態等), 其中查詢好友在線狀態需要和登錄服務器進行交互.資料服務器只是一個最外部對外面開放的服務器, 底下下設各種與好友資料相關的服務器, 如圖片服務器, 個人資料服務器, 好友在線狀態服務器等.
消息服務器:
負責存取離線消息.
對外暴露的只有登錄服務器而已, 而資料服務器和消息服務器隱藏在登錄服務器之后.
大致如圖:
客戶端1 .... 客戶端n
\ | / ----> udp發送消息
登錄服務器
/ \ ----->tcp長連接
資料服務器 消息服務器
/ \ ------>tcp長連接
圖片服務器 個人資料服務器
幾個可能的性能問題:
1) 當用戶量上來的時候服務器如何擴容?考慮采用不同的IP地址綁定同一個域名的形式, 也就是說登錄服務器單獨一個域名, 但是擴容之后的不同登錄服務器都綁定在這個域名上, 由客戶端的DNS域名解析自己決定與哪個登錄服務器進行通信.
2) 服務器之間采用tcp長連接, 如果其中一個服務器宕機, 如何處理?需要有好的服務器平滑切換備份機器的技術, 以及好的監控服務器機制.
3) 如何高效存取離線消息?
考慮如下一個方案:有0x00-0xff個目錄, 每個目錄有0x00-0xff個文件, 在類似bdb這樣的數據庫中存放一條記錄, key是用戶id, value是"目錄:文件", 查詢發送給某個用戶的離線消息時首先到這個數據庫中得到相應的目錄和文件, 再取出所有給該用戶的消息(消息在這個文件中有自己的一套格式).當用戶量上來的時候, 需要增加前面說的目錄和文件數量.
4) 如何保存用戶的在線狀態?也就是說,如何迅速做到知道哪些用戶是否在線?這個是不能用cache實現的, 因為cache存放的應該是那些訪問頻繁同時不關注服務器停止之后是否會丟失的數據, 如果用數據庫來保存, 那么數據的增刪很頻繁.
================= 分割線 ======================================
補充:
1)關于在線狀態服務器:單獨拿出一個服務器做這個保存在線狀態的服務器, 在內存中保存用戶的在線狀態,這臺服務器與登錄服務器相連, 有用戶登錄時發包加一條數據,登出時也發包刪除一條數據, 由于直接放在內存中, 有以下的優缺點:優點是速度快, 而且由于是一臺單獨的服務器做這個功能, 即使是千萬級別的用戶同時在線按照現在服務器的硬件配置也足以保存;缺點是假如這個服務器掛了, 數據會丟失, 但是考慮到前面的登錄服務器會每隔幾秒給客戶端發送一個心跳包查詢在線狀態,因此即使有誤差也可以控制在很短的時間里面.
因此, 現在的架構變成了登錄服務器下面還隱藏著一個保存用戶在線狀態的服務器.
2) 離線消息服務器:擴容的時候可以考慮把這部分的服務器做成分布式的, 也就是說, 暴露在最外面的是一臺服務器, 當查詢某個用戶消息的請求到來時, 再根據hash等方式計算出真正所在的服務器, 而這一部分服務器是分布式的, 類似于memcached那樣的.
此時架構就變成了消息服務器下面隱藏著很多真正存放消息的服務器.
最后的架構如下: