前端頁面的實時重新整理一般兩種思路,即拉和推的思路:
拉:前端每隔一段時間到背景請求資料;
推:後端需要推送資料時,向前端推送資料。
拉的方式比較耗費資源,因為http是無狀态且單向的通訊協定,後端無法主動xia向前端發送資訊,一般拉為前端不間斷的向服務端發送http請求,這種方式前端和後端都比較頭疼。沒有特殊需求的話,一般使用推的方式。HTML5開始提供websocket解決方式,基于TCP實作用戶端與服務端全雙工通信。websocket隻使用了一個連接配接,避免了連接配接的多次建立;且隻有連接配接初次建立比較複雜,後期通信成本較低。
實作方式,前後端通過心跳保持長連接配接,當後端有資料時,調用通知方法,以消息的方式通知前端。
tomcat7以後開始提供了websocket,基于servlet容器可以使用javax.websocket,後端代碼:
@Component
@ServerEndpoint("/websocket")
public class WebSocketResource implements AcknowledgingMessageListener<Integer, String> {
private static ConcurrentMap<String, Session> socketMap = new ConcurrentHashMap<>();
private Logger logger = LoggerFactory.getLogger(WebSocketResource.class);
@OnOpen
public void onOpen(Session session) {
socketMap.put(session.getQueryString(), session);
}
@OnClose
public void onClose(Session session) {
socketMap.remove(session.getQueryString());
}
@OnMessage
public void onMessage(String message, Session session) {
logger.info("heat beat: " + message + "---" + session.getQueryString());
}
@OnError
public void onError(Throwable error) {
throw new RuntimeException(error);
}
@Override
public void onMessage(ConsumerRecord<Integer, String> consumerRecord, Acknowledgment acknowledgment) {
String message = consumerRecord.value();
if (StringUtil.isBlank(message)) {
return;
}
UnDealMsDto unDealMsDto = CommonUtil.jsonToObject(consumerRecord.value(), UnDealMsDto.class);
sendMessages("refresh-un-deal", socketMap.get(unDealMsDto.getToken()));
}
private void sendMessages(String message, Session session) {
if (StringUtil.isBlank(message) || null == session) {
return;
}
try {
socketMap.get(session.getQueryString()).getBasicRemote().sendText(message);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
此外,spring也提供了對websocket的支援,可以使用spring-websocket包實作,可以擴充 TextWebSocketHandler,spring監聽到 websocket事件後會調用對應的方法。
前端以vue舉例:
initWebSocket() {
this.websoket = new WebSocket("wss://{your.domain}/websocket?678d1a2132894c6e952bcdc9e0debac1")
this.websoket.onopen = this.webSocketOnOpen
this.websoket.onerror = this.webSocketOnError
this.websoket.onmessage = this.webSocketOnMessage
this.websoket.onclose = this.webSocketOnClose
},
webSocketOnOpen () {
let me = this;
setInterval(() => {
me.webSocketSend(JSON.stringify({ type: "ping" }));
}, 30000);
},
webSocketOnError (e) {
console.dir(e)
},
webSocketOnMessage (e) {
console.log('data:')
console.dir(e)
},
webSocketSend (data) {
this.websoket.send(data);
},
webSocketOnClose (e) {
setTimeout(() => {
this.initWebSocket();
}, 500);
},
需要注意的是,沒有使用SSL時,協定字首使用ws,類似http;否則使用wss,類似https。
nginx對
websocket做了支援,如使用nginx轉發,示例:
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 600;
proxy_pass backend;