溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

WebSocket如何實(shí)現(xiàn)服務(wù)器消息推送客戶端

發(fā)布時(shí)間:2021-11-20 15:38:41 來(lái)源:億速云 閱讀:417 作者:小新 欄目:編程語(yǔ)言

這篇文章主要為大家展示了“WebSocket如何實(shí)現(xiàn)服務(wù)器消息推送客戶端”,內(nèi)容簡(jiǎn)而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學(xué)習(xí)一下“WebSocket如何實(shí)現(xiàn)服務(wù)器消息推送客戶端”這篇文章吧。

  一、背景

  項(xiàng)目需要做一個(gè)消息能夠?qū)崟r(shí)獲取的功能,系統(tǒng)日活躍量達(dá)到10000,產(chǎn)生的消息是活躍量的數(shù)倍,如果采用 Http 的方式輪詢(xún)后端服務(wù),會(huì)使得后端服務(wù)壓力過(guò)大而奔潰,因此需要一種新的技術(shù)方式來(lái)改變 “拉” 的方式。

  二、解決方案

  經(jīng)過(guò)各種 Google、百度 后發(fā)現(xiàn)可以使用 html5 的新技術(shù) WebSocket ,將現(xiàn)有 “拉”消息的方式改變成 “推” 的模式,大大的減少服務(wù)器壓力。

  三、具體實(shí)現(xiàn)

  實(shí)例采用 Spring Boot 框架,

  引入 pom 依賴(lài)

  org.springframework.boot

  spring-boot-starter-websocket

  org.springframework.boot

  spring-boot-starter-undertow

  org.springframework.boot

  spring-boot-starter-web

  org.springframework.boot

  spring-boot-starter-tomcat

  WebSocket 服務(wù)可采用 websocket-api 或 spring-websocket 開(kāi)發(fā),我們采用 websocket-api 的注解開(kāi)發(fā)方式:

  package com.gridsum.techpub.systemhistory.api.server;

  import org.slf4j.Logger;

  import org.slf4j.LoggerFactory;

  import org.springframework.stereotype.Service;

  import javax.websocket.*;

  import javax.websocket.server.PathParam;

  import javax.websocket.server.ServerEndpoint;

  import java.io.IOException;

  import java.util.Objects;

  import java.util.Set;

  import java.util.concurrent.CopyOnWriteArraySet;

  /**

  * @author ouyangrongtao

  * @version 1.0

  * @description WebSocketServer

  * @date 2019/12/23 10:16

  **/

  @ServerEndpoint("/websocket/{sid}")

  @Service

  public class WebSocketServer {

  private static final Logger logger = LoggerFactory.getLogger(WebSocketServer.class);

  private ClientInfo clientInfo;

  /**

  * 存放每個(gè)客戶端對(duì)應(yīng)的 ClientInfo 對(duì)象。

  */

  private static final Set WEB_SOCKET_SET = new CopyOnWriteArraySet<>();

  /**

  * 連接建立成功調(diào)用的方法

  *

  * @param session 會(huì)話

  * @param sid 客戶端

  */

  @OnOpen

  public void onOpen(Session session, @PathParam("sid") String sid) {

  //加入set中

  this.clientInfo = new ClientInfo(sid, session);

  WEB_SOCKET_SET.add(clientInfo);

  logger.info("有新窗口開(kāi)始監(jiān)聽(tīng):[{}],當(dāng)前在線人數(shù)為[{}]", sid, WEB_SOCKET_SET.size());

  try {

  this.sendMessage(session, "連接成功");

  } catch (IOException e) {

  logger.error("websocket IO異常");

  }

  }

  /**

  * 連接關(guān)閉調(diào)用的方法

  */

  @OnClose

  public void onClose() {

  //從set中刪除

  WEB_SOCKET_SET.remove(this.clientInfo);

  logger.info("有一連接關(guān)閉!當(dāng)前在線人數(shù)為:[{}]", WEB_SOCKET_SET.size());

  }

  /**

  * 

  * @param message 客戶端發(fā)送過(guò)來(lái)的消息

  */

  @OnMessage

  public void onMessage(String message) {

  logger.info("收到來(lái)自窗口[{}]的信息:[{}]", this.clientInfo.getSid(), message);

  //發(fā)消息

  for (ClientInfo item : WEB_SOCKET_SET) {

  try {

  this.sendMessage(item.getSession(), message);

  } catch (IOException ignored) {

  }

  }

  }

  /**

  * 錯(cuò)誤時(shí)調(diào)用

  * @param session 會(huì)話

  * @param error 錯(cuò)誤信息

  */

  @OnError

  public void onError(Session session, Throwable error) {

  logger.error("發(fā)生錯(cuò)誤", error);

  }

  /**

  * 給 sid 發(fā)送消息

  * @param message 消息

  * @param sid sid

  */

  public void sendMessage(String message, String sid) {

  logger.info("推送消息到窗口[{}],推送內(nèi)容:[{}]", sid, message);

  ClientInfo client = WEB_SOCKET_SET.parallelStream()

  .filter(item -> item.getSid().equals(sid)).findFirst().orElse(null);

  if (client != null) {

  try {

  this.sendMessage(client.getSession(), message);

  } catch (IOException ignored) {

  }

  }

  }

  /**

  * 實(shí)現(xiàn)服務(wù)器主動(dòng)推送

  * @param session session

  * @param message message

  * @throws IOException IOException

  */鄭州哪個(gè)婦科醫(yī)院好 http://www.sptdfk.com/

  private void sendMessage(Session session, String message) throws IOException {

  session.getBasicRemote().sendText(message);

  }

  class ClientInfo {

  /**

  * 接收sid

  */

  private String sid = "";

  /**

  * 客戶端

  */

  private Session session;

  public ClientInfo() { }

  private ClientInfo(String sid, Session session) {

  this.sid = sid;

  this.session = session;

  }

  private String getSid() {

  return sid;

  }

  private Session getSession() {

  return session;

  }

  @Override

  public boolean equals(Object o) {

  if (this == o) {

  return true;

  }

  if (o == null || getClass() != o.getClass()) {

  return false;

  }

  ClientInfo that = (ClientInfo) o;

  return Objects.equals(sid, that.sid);

  }

  @Override

  public int hashCode() {

  return Objects.hash(sid);

  }

  }

  }

  前端代碼

  運(yùn)行 WebSocketClient1000001

  來(lái)一個(gè)發(fā)消息的接口

  /**

  * 發(fā)送消息給客戶端

  * @author ouyangrongtao

  */

  @RestController

  public class WebSocketController {

  private WebSocketServer webSocketServer;

  @Autowired

  public WebSocketController(WebSocketServer webSocketServer) {

  this.webSocketServer = webSocketServer;

  }

  @PostMapping("/socket/push")

  public boolean pushToWeb(@RequestBody Map content) {

  webSocketServer.sendMessage(content.get("message"), content.get("cid"));

  return true;

  }

  }

  到此已經(jīng)基本寫(xiě)完。使用 Postman 調(diào)用發(fā)消息的接口,發(fā)現(xiàn)客戶端可以收到發(fā)送的消息。

  四、問(wèn)題記錄

  在做的時(shí)候,因?yàn)轫?xiàng)目用的 Tomcat 容器,導(dǎo)致 Tomcat 相關(guān)包與 WebSocket 依賴(lài)有沖突,最終項(xiàng)目不能啟動(dòng),解決方式只需要將 Tomcat 容器改為 Undertow 。

  org.springframework.boot

  spring-boot-starter-undertow

  org.springframework.boot

  spring-boot-starter-web

  org.springframework.boot

  spring-boot-starter-tomcat

  異常信息:

  Caused by: java.lang.IllegalStateException: javax.websocket.server.ServerContainer not available

  at org.springframework.util.Assert.state(Assert.java:73)

  at org.springframework.web.socket.server.standard.ServerEndpointExporter.afterPropertiesSet(ServerEndpointExporter.java:106)

  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1753)

  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1690)

  ... 16 common frames omitted

以上是“WebSocket如何實(shí)現(xiàn)服務(wù)器消息推送客戶端”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI