乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      SpringBoot2.0集成WebSocket,實(shí)現(xiàn)后臺(tái)向前端推送信息

       jackeyqing 2019-04-06

      什么是WebSocket?

      這里寫(xiě)圖片描述
      WebSocket協(xié)議是基于TCP的一種新的網(wǎng)絡(luò)協(xié)議。它實(shí)現(xiàn)了瀏覽器與服務(wù)器全雙工(full-duplex)通信——允許服務(wù)器主動(dòng)發(fā)送信息給客戶(hù)端。

      為什么需要 WebSocket?

      初次接觸 WebSocket 的人,都會(huì)問(wèn)同樣的問(wèn)題:我們已經(jīng)有了 HTTP 協(xié)議,為什么還需要另一個(gè)協(xié)議?它能帶來(lái)什么好處?

      • 答案很簡(jiǎn)單,因?yàn)?HTTP 協(xié)議有一個(gè)缺陷:通信只能由客戶(hù)端發(fā)起,HTTP 協(xié)議做不到服務(wù)器主動(dòng)向客戶(hù)端推送信息。
        這里寫(xiě)圖片描述
        舉例來(lái)說(shuō),我們想要查詢(xún)當(dāng)前的排隊(duì)情況,只能是頁(yè)面輪詢(xún)向服務(wù)器發(fā)出請(qǐng)求,服務(wù)器返回查詢(xún)結(jié)果。輪詢(xún)的效率低,非常浪費(fèi)資源(因?yàn)楸仨毑煌_B接,或者 HTTP 連接始終打開(kāi))。因此WebSocket 就是這樣發(fā)明的。

      話(huà)不多說(shuō),馬上進(jìn)入干貨時(shí)刻。

      maven依賴(lài)

      SpringBoot2.0對(duì)WebSocket的支持簡(jiǎn)直太棒了,直接就有包可以引入

      		<dependency>  
                 <groupId>org.springframework.boot</groupId>  
                 <artifactId>spring-boot-starter-websocket</artifactId>  
             </dependency> 
      

      WebSocketConfig

      啟用WebSocket的支持也是很簡(jiǎn)單,幾句代碼搞定

      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.web.socket.server.standard.ServerEndpointExporter;
      
      /**
       * 開(kāi)啟WebSocket支持
       * @author zhengkai
       */
      @Configuration  
      public class WebSocketConfig {  
      	
          @Bean  
          public ServerEndpointExporter serverEndpointExporter() {  
              return new ServerEndpointExporter();  
          }  
        
      } 
      

      WebSocketServer

      因?yàn)閃ebSocket是類(lèi)似客戶(hù)端服務(wù)端的形式(采用ws協(xié)議),那么這里的WebSocketServer其實(shí)就相當(dāng)于一個(gè)ws協(xié)議的Controller
      直接@ServerEndpoint("/websocket")@Component啟用即可,然后在里面實(shí)現(xiàn)@OnOpen,@onClose,@onMessage等方法

      import java.io.IOException;
      import java.util.concurrent.CopyOnWriteArraySet;
      
      import javax.websocket.OnClose;
      import javax.websocket.OnError;
      import javax.websocket.OnMessage;
      import javax.websocket.OnOpen;
      import javax.websocket.Session;
      import javax.websocket.server.ServerEndpoint;
      import org.springframework.stereotype.Component;
      import cn.hutool.log.Log;
      import cn.hutool.log.LogFactory;
      import lombok.extern.slf4j.Slf4j;
      
      
      @ServerEndpoint("/websocket/{sid}")
      @Component
      public class WebSocketServer {
      	
      	static Log log=LogFactory.get(WebSocketServer.class);
          //靜態(tài)變量,用來(lái)記錄當(dāng)前在線(xiàn)連接數(shù)。應(yīng)該把它設(shè)計(jì)成線(xiàn)程安全的。
          private static int onlineCount = 0;
          //concurrent包的線(xiàn)程安全Set,用來(lái)存放每個(gè)客戶(hù)端對(duì)應(yīng)的MyWebSocket對(duì)象。
          private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();
      
          //與某個(gè)客戶(hù)端的連接會(huì)話(huà),需要通過(guò)它來(lái)給客戶(hù)端發(fā)送數(shù)據(jù)
          private Session session;
      
          //接收sid
          private String sid="";
          /**
           * 連接建立成功調(diào)用的方法*/
          @OnOpen
          public void onOpen(Session session,@PathParam("sid") String sid) {
              this.session = session;
              webSocketSet.add(this);     //加入set中
              addOnlineCount();           //在線(xiàn)數(shù)加1
              log.info("有新窗口開(kāi)始監(jiān)聽(tīng):"+sid+",當(dāng)前在線(xiàn)人數(shù)為" + getOnlineCount());
              this.sid=sid;
              try {
              	 sendMessage("連接成功");
              } catch (IOException e) {
                  log.error("websocket IO異常");
              }
          }
      
          /**
           * 連接關(guān)閉調(diào)用的方法
           */
          @OnClose
          public void onClose() {
              webSocketSet.remove(this);  //從set中刪除
              subOnlineCount();           //在線(xiàn)數(shù)減1
              log.info("有一連接關(guān)閉!當(dāng)前在線(xiàn)人數(shù)為" + getOnlineCount());
          }
      
          /**
           * 收到客戶(hù)端消息后調(diào)用的方法
           *
           * @param message 客戶(hù)端發(fā)送過(guò)來(lái)的消息*/
          @OnMessage
          public void onMessage(String message, Session session) {
          	log.info("收到來(lái)自窗口"+sid+"的信息:"+message);
              //群發(fā)消息
              for (WebSocketServer item : webSocketSet) {
                  try {
                      item.sendMessage(message);
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
          }
      
      	/**
      	 * 
      	 * @param session
      	 * @param error
      	 */
          @OnError
          public void onError(Session session, Throwable error) {
              log.error("發(fā)生錯(cuò)誤");
              error.printStackTrace();
          }
      	/**
      	 * 實(shí)現(xiàn)服務(wù)器主動(dòng)推送
      	 */
          public void sendMessage(String message) throws IOException {
              this.session.getBasicRemote().sendText(message);
          }
      
      
          /**
           * 群發(fā)自定義消息
           * */
          public static void sendInfo(String message,@PathParam("sid") String sid) throws IOException {
          	log.info("推送消息到窗口"+sid+",推送內(nèi)容:"+message);
              for (WebSocketServer item : webSocketSet) {
                  try {
                  	//這里可以設(shè)定只推送給這個(gè)sid的,為null則全部推送
                  	if(sid==null) {
                  		item.sendMessage(message);
                  	}else if(item.sid.equals(sid)){
                  		item.sendMessage(message);
                  	}
                  } catch (IOException e) {
                      continue;
                  }
              }
          }
      
          public static synchronized int getOnlineCount() {
              return onlineCount;
          }
      
          public static synchronized void addOnlineCount() {
              WebSocketServer.onlineCount++;
          }
      
          public static synchronized void subOnlineCount() {
              WebSocketServer.onlineCount--;
          }
      }
      
      

      消息推送

      至于推送新信息,可以再自己的Controller寫(xiě)個(gè)方法調(diào)用WebSocketServer.sendInfo();即可

      @Controller
      @RequestMapping("/checkcenter")
      public class CheckCenterController {
      
      	//頁(yè)面請(qǐng)求
      	@GetMapping("/socket/{cid}")
      	public ModelAndView socket(@PathVariable String cid) {
      		ModelAndView mav=new ModelAndView("/socket");
      		mav.addObject("cid", cid);
      		return mav;
      	}
      	//推送數(shù)據(jù)接口
      	@ResponseBody
      	@RequestMapping("/socket/push/{cid}")
      	public ApiReturnObject pushToWeb(@PathVariable String cid,String message) {  
      		try {
      			WebSocketServer.sendInfo(message,cid);
      		} catch (IOException e) {
      			e.printStackTrace();
      			return ApiReturnUtil.error(cid+"#"+e.getMessage());
      		}  
      		return ApiReturnUtil.success(cid);
      	} 
      } 
      

      頁(yè)面發(fā)起socket請(qǐng)求

      然后在頁(yè)面用js代碼調(diào)用socket,當(dāng)然,太古老的瀏覽器是不行的,一般新的瀏覽器或者谷歌瀏覽器是沒(méi)問(wèn)題的。還有一點(diǎn),記得協(xié)議是ws的哦,如果像我這樣封裝了一些basePath的路徑類(lèi),可以replace(“http”,“ws”)來(lái)替換協(xié)議

          <script> 
          var socket;  
          if(typeof(WebSocket) == "undefined") {  
              console.log("您的瀏覽器不支持WebSocket");  
          }else{  
              console.log("您的瀏覽器支持WebSocket");  
              	//實(shí)現(xiàn)化WebSocket對(duì)象,指定要連接的服務(wù)器地址與端口  建立連接  
                  //等同于socket = new WebSocket("ws://localhost:8083/checkcentersys/websocket/20");  
                  socket = new WebSocket("${basePath}websocket/${cid}".replace("http","ws"));  
                  //打開(kāi)事件  
                  socket.onopen = function() {  
                      console.log("Socket 已打開(kāi)");  
                      //socket.send("這是來(lái)自客戶(hù)端的消息" + location.href + new Date());  
                  };  
                  //獲得消息事件  
                  socket.onmessage = function(msg) {  
                      console.log(msg.data);  
                      //發(fā)現(xiàn)消息進(jìn)入    開(kāi)始處理前端觸發(fā)邏輯
                  };  
                  //關(guān)閉事件  
                  socket.onclose = function() {  
                      console.log("Socket已關(guān)閉");  
                  };  
                  //發(fā)生了錯(cuò)誤事件  
                  socket.onerror = function() {  
                      alert("Socket發(fā)生了錯(cuò)誤");  
                      //此時(shí)可以嘗試刷新頁(yè)面
                  }  
                  //離開(kāi)頁(yè)面時(shí),關(guān)閉socket
                  //jquery1.8中已經(jīng)被廢棄,3.0中已經(jīng)移除
                  // $(window).unload(function(){  
                  //     socket.close();  
                  //});  
          }
          </script> 
      

      運(yùn)行效果

      v1.1的效果,剛剛修復(fù)了日志,并且支持指定監(jiān)聽(tīng)某個(gè)端口,代碼已經(jīng)全部更新,現(xiàn)在是這樣的效果

      這里寫(xiě)圖片描述

      這里寫(xiě)圖片描述
      先打開(kāi)頁(yè)面,指定cid,啟用socket接收,然后再另一個(gè)頁(yè)面調(diào)用剛才Controller封裝的推送信息的方法到這個(gè)cid的socket,即可向前端推送消息。

      后續(xù)

      針對(duì)簡(jiǎn)單IM的業(yè)務(wù)場(chǎng)景,進(jìn)行了一些優(yōu)化,可以看后續(xù)的文章SpringBoot2+WebSocket之聊天應(yīng)用實(shí)戰(zhàn)(優(yōu)化版本)

      主要變動(dòng)是CopyOnWriteArraySet改為ConcurrentHashMap,保證多線(xiàn)程安全同時(shí)方便利用map.get(userId)進(jìn)行推送到指定端口。

      相比之前的Set,Set遍歷是費(fèi)事且麻煩的事情,而Map的get是簡(jiǎn)單便捷的,當(dāng)WebSocket數(shù)量大的時(shí)候,這個(gè)小小的消耗就會(huì)聚少成多,影響體驗(yàn),所以需要優(yōu)化。

      Websocker注入Bean問(wèn)題

      關(guān)于這個(gè)問(wèn)題,可以看最新發(fā)表的這篇文章,在參考和研究了網(wǎng)上一些攻略后,項(xiàng)目已經(jīng)通過(guò)該方法注入成功,大家可以參考。
      關(guān)于controller調(diào)用controller/service調(diào)用service/util調(diào)用service/websocket中autowired的解決方法

      使用Netty構(gòu)建高性能websocket服務(wù)器

      Springboot2構(gòu)建基于Netty的高性能Websocket服務(wù)器(netty-websocket-spring-boot-starter)
      只需要換個(gè)starter即可實(shí)現(xiàn)高性能websocket,趕緊使用吧

      另外也感謝大家的閱讀和評(píng)論,一起進(jìn)步,謝謝!~~

        本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評(píng)論

        發(fā)表

        請(qǐng)遵守用戶(hù) 評(píng)論公約

        類(lèi)似文章 更多