在《消息順序性為何這麼難?)》中,介紹了一種為了保證 “所有群友展示的群消息時序都是一緻的” 所使用的 “ID 串行化” 的方法:讓同一個群 gid 的所有消息落在同一台伺服器上處理。
ID 串行化是如何實作的呢?
網際網路高可用常見分層架構
用戶端,反向代理層,接入層,服務層,存儲層,這是網際網路常見的高可用分層架構。
畫外音:這個圖用過好多次。
這裡的 “服務層” 至關重要,ID 串行化保證的是,同一個群 gid 的消息落在同一個服務上。
畫外音:服務叢集有很多節點,如果能落在同一個服務節點上,就可以利用這個服務節點做消息串行化。
服務層上下遊細節
服務一般由 RPC 架構實作,上遊調用方是多線程程式,通過 RPC-client 通路服務,而 RPC-client 内部又通過連接配接池 connection-pool 來通路的。
畫外音:為了保證高可用,連接配接池會對叢集中的每個服務都建立連接配接。
如上圖:
(1)上遊是業務應用;
(2)下遊是服務叢集;
(3)業務應用,它又分為了這麼幾個部分:
- 上層是任務隊列 (粉色);
- 中間是工作線程 (藍色),每個工作線程完成實際的業務任務,典型的工作任務是通過服務連接配接池進行 RPC 調用;
- 下層是服務連接配接池 (綠色),所有的 RPC 調用都是通過服務連接配接池往下遊服務發請求執行;
畫外音:橙色是連接配接池中的一條連接配接。
工作線程的典型工作流是這樣的:
void work_thread_routine(){
// 擷取任務
Task t = TaskQueue.pop();
// 任務邏輯處理,組成一個網絡包 packet
Packet p = MakePacket(t);__
// 從 Service 連接配接池擷取一個 Service 連接配接
ServiceConnection c = CPool.GetConnection();
// 通過 Service 連接配接發送封包執行 RPC 請求
c.Send(p);
// 将 Service 連接配接放回 Service 連接配接池
CPool.PutConnection(c);
}
如何保證同一個群 gid 的消息落在同一個服務上呢?
對連接配接池進行少量改動,擷取連接配接時:
CPool.GetConnection()
畫外音:傳回任何一個可用服務連接配接。
更新為
CPool.GetConnection(long id)
畫外音:傳回 id 取模相關聯的服務連接配接。
隻要傳入群 gid,就能夠保證同一個群的請求擷取到同一個連接配接,進而使請求落到同一個服務上。
需要注意的是,連接配接池不關心傳入的 long id 是什麼業務含義:
(1)傳入群 gid,同 gid 的請求落在同一個服務上;
(2)傳入使用者 uid,同 uid 的請求落在同一個服務上;
(3)傳入任何業務 xid,同業務 xid 的請求落在同一個服務上;
ID 串行化通路服務,同一個 id 通路同一個服務,當服務挂掉時,會不會受影響服務可用性?
不會,當有下遊服務挂掉的時候,連接配接池能夠檢測到連接配接的可用性,取模時要把不可用的服務連接配接排除掉。
取模通路服務,是否會影響各連接配接上請求的負載均衡?