本文首發於智客工坊-《socket.io使用者端向webserver傳送訊息實踐》,感謝您的閱讀,預計閱讀時長2min。
Socket.IO
是一個庫,它支援使用者端和伺服器之間的低延遲、雙向和基於事件的通訊。
它構建在WebSocket協定之上,並提供額外的保證,如回退到HTTP長輪詢或自動重新連線。
Socket.IO
目前應用比較多的場景就是網頁的IM實時聊天。
Notes: 在C#中,也有個類庫signalr實現簡單的網頁實時聊天。
Socket.IO
server端有以下幾種不同程式語言的實現:
Socket.IO
client端,大多數主流程式語言的也有實現
本文主要是針對socket.io-client-java
的一次實踐。
我們團隊已經使用Node.js
搭建了webserver,並實現了web使用者端和webserver的訊息互通(即雙方都是基於JavaScript的實現)。
但是,有個特殊業務場景,需要在我們後端業務介面中根據業務狀態變更向指定的IM對談投遞實時訊息。
這種需求的實現方案:
綜合考慮之後,我們選擇了方案2。
因此,技術選型上就只有華山一條路——socket.io-client-java
。
在實現的過程中,也確實是踩了一些坑,所以記錄一下,順便和大家分享一下。
現在我們開始socket.io-client-java
之旅吧!
socket.io-client-java
庫Notice: socket.io使用者端和伺服器端的版本要匹配,否則會連不上或者沒有反應。
根據socket.io-client-java官方檔案給出的版本匹配表格:
Client version | Socket.IO server |
---|---|
0.9.x | 1.x |
1.x | 2.x |
2.x | 3.x / 4.x |
因為我們webserver端的socket.io
版本 "socket.io": "^2.4.1"
。
所以,使用者端我只能選擇1.x,這裡選擇1.0.0。
<dependency>
<groupId>io.socket</groupId>
<artifactId>socket.io-client</artifactId>
<version>1.0.0</version>
</dependency>
maven更新之後,即可使用。
package com.zhike.blogmanager.Msg;
import io.socket.client.IO;
import io.socket.client.Socket;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j;
import org.apache.poi.ddf.EscherColorRef;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.stereotype.Component;
import java.net.URI;
import java.util.Date;
/**
* Created with IntelliJ IDEA.
* User: lenovo
* Date: 2022/6/25
* Time: 21:25
* Description: No Description
*/
@Component
@RequiredArgsConstructor
public class PushMessageManager {
private Socket socket;
/**
* 訊息推播到webserver
* */
public void pushToWebServer() {
//保證只會範例化一次socket
if(socket==null)
{
connentSocket();
System.out.println(socket);
}
//構造JSONObject物件
JSONObject data=bulidMsg();
System.out.println("【使用者端推播訊息】"+data);
//event 要和webserver一致才能接受到訊息
socket.emit("2",data);
if(!socket.connected())
{
socket.connect();
}
}
private void connentSocket(){
try
{
//String url ="http://172.xx.xx.xx:3000";
//String url ="http://172.xx.xx.xx:3001";
String url = "http://172.xx.xx.xx:3002";//web伺服器地址以實際為準
IO.Options options = new IO.Options();
options.forceNew = true;
// 失敗重連時間間隔
options.reconnectionDelay = 1000;
// 連線超時時間
options.timeout = 5000;
socket = IO.socket(URI.create(url), options);
}catch (Exception ex)
{
System.out.println("連線伺服器失敗,error:"+ex);
}
}
/**
* 訊息體構造
* 定義須和webserver保持一致,webserver才能解析
* */
private JSONObject bulidMsg()
{
JSONObject data = new JSONObject();
try {
data.put("type", "1");
data.put("from", "[FromUSerId]");
data.put("to", "[ToUserId]");
data.put("msgContent", "Hello World!");
data.put("msgTime", new Date());
} catch (JSONException e) {
throw new AssertionError(e);
}
return data;
}
}
程式碼中有詳細註釋,不再贅述。
這裡還有個巨坑,不知道是否是socket.io-client-java
的bug。
如果webserver部署了多個應用並被nginx負載,如下:
server {
listen 3000;
server_name localhost;
access_log /data/logs/nginx/webserver/access.log main;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_pass http://webserver-nodes;
# enable WebSockets
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
upstream webserver-nodes {
# enable sticky session based on IP
# ip_hash;
server 172.xx.xx.xx:3001;
server 172.xx.xx.xx:3002;
}
當我設定url="http://172.xx.xx.xx:3000"的時候,就會出現socket會在兩臺webserver之間disconnect,reconnect的情況。
當時同事反饋,使用JavaScript client連線是正常的。
這個研究了兩三天了,嘗試了很多方式依然沒有解決。
所以,最終我們只能指定連線其中一臺webserver。
最後給提了一個issue #715