背景
為應對更多用戶使用socket的場景,准備對存放websocket的服務器進行多點搭建並配置負載均衡。
問題
服務器上了多點負載均衡以后,基於socket的部分功能發生了有規律的失效,查看后台日志發現了原因。
基於socket的功能使用的session存放在其他負載均衡的服務器上,所以在當前服務器無法實現相應操作。
舉個實例,有兩台加了負載的socket服務器分別為A、B。服務器A擁有用戶1的socket連接即session句柄,服務器B擁有用戶2的session句柄,
此時client端發送給用戶1一條消息,消息內容是“kill程序員祭天消息”,由於socket服務器的負載均衡策略,該消息可能被發送到A或B服務器。
當A服務器接收到該請求時,A服務器可以完成給用戶1發送消息的任務,因為A服務器擁有可以給用戶1發送消息的句柄。但是服務器B接收到此消息的時候,B嘗試着尋找用戶1的句柄但是在列表當中沒有找到該句柄,所以無法將消息發送出去,此時消息丟失了。換言服務器只能與當前建立連接的用戶進行通信。
解決方案的嘗試
不可行的方案
redis集中管理連接,這種方式是最開始想到的一種方式,這種方法的好處在於redis集中管理用戶連接。該方法預期將用戶的連接信息存放在redis服務器,當服務器接收到發送消息請求時,去redis獲取與用戶的連接,然后發送消息。
然而這種方法夭折了~~,問題在於session管理着與用戶的長連接,該數據無法存入redis。筆者嘗試着將session以HashMap、Object形式存入redis,此時redis報錯。另外嘗試將session以Json形式存入redis,但是在session轉為Json的時候發現session是不能被序列化的,自然不能轉為JSON格式了。
可行的方案
使用kafka廣播形式,將消息廣播到各服務器。首先將client端的請求吐入kafka,並且配置各接收服務器使用廣播方式接收,即使用不同群組接收同一個topic,這樣每個服務器都能接收到該請求,但是只有一個服務器可以將消息發出。該方法存在缺點但確實解決了此問題。該方法缺點在於,雖然保證的socket的負載均衡,但是服務器down掉以后,該服務器與用戶的長連接就消失了。
哪路神仙有更好的方案,歡迎在評論區回復~~