SpringBoot+websocket+定時任務(如何及時實時響應服務端數據)

SpringBoot+websocket+定時任務

SpringBoot+websocket

概念

websocket:因爲http協議時基於 請求-響應模型 服務端的每次響應都必須有客戶端發起(瀏覽器)的請求。若是服務端想主動推送消息到客戶端是很難知足的。java

若是必定想使用http來作服務端主動推進,只能客戶端不停的發起輪詢請求,若是訪問量很很大,這種模式會拖垮服務器。形成很大的通訊開銷和服務端流量壓力。

使用websocket能夠完成要求實時性的應用:股票的變更、天氣、彩票、即時通信、通知。git

WebSocket 是web客戶端和服務器之間新的通信方式, 依然架構在HTTP協議之上。使用WebSocket鏈接, web應用程序能夠執行實時的交互, 而不是之前的poll方式。github

WebSocket是HTML5開始提供的一種在單個 TCP 鏈接上進行全雙工通信的協議,能夠用來建立快速的更大規模的健壯的高性能實時的web應用程序。WebSocket通訊協議於2011年被IETF定爲標準RFC 6455,WebSocketAPI被W3C定爲標準。
在WebSocket API中,瀏覽器和服務器只須要作一個握手的動做,而後,瀏覽器和服務器之間就造成了一條快速通道。二者之間就直接能夠數據互相傳送。web

如今大概瞭解了websocket的概念和應用,如今咱們來看看如何在springBoot中集成websocket。spring

配置類

建立一個配置類:
使用@Configuration註解 聲明這個類爲配置類。
使用@EnableWebSocket註解代表這是一個websocket配置類,咱們會在這個類配置一些websocket的參數和地址。
咱們的配置類實現WebSocketConfigurer接口。json

重寫registerWebSocketHandlers方法,瀏覽器

//配置指定地址:/demo的處理器,及通訊容許的域名,這裏使用*,表示匹配全部。
webSocketHandlerRegistry.addHandler(new WebSocketDemoHanlder(),"/demo").setAllowedOrigins("*");


/**
 * @author xuelongjiang
 */
@Configuration
@EnableWebSocket
public class WebsocketConfig  implements WebSocketConfigurer{

@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) {
    webSocketHandlerRegistry.addHandler(new WebSocketDemoHanlder(),"/demo").setAllowedOrigins("*");
}
}

處理器

這裏咱們主要綁定WebSocketSession和咱們的推送目標。安全

若是是聊天室,則根據約定的規則,進行用戶與用戶,用戶與聊天室的綁定。springboot

若是是彩票,推送消息到全部的WebSocketSession。服務器

package com.xuelongjiang.websocketdemo.websocket;

import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
* @author xuelongjiang
 */

public class WebSocketDemoHanlder extends TextWebSocketHandler {

private Logger logger = LoggerFactory.getLogger(WebSocketDemoHanlder.class);

private static Map<String,WebSocketSession> userIdSessionMap = new ConcurrentHashMap();
private static Map<WebSocketSession,String> sessionUserIdMap = new ConcurrentHashMap<>();


@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {

        String  payload = message.getPayload();
        logger.info("websocket請求參數payload:{}",payload);
        JSONObject jsonObject = JSONObject.parseObject(payload);
        String action = jsonObject.getString("action");

        if("register".equals(action)){
            logger.info("註冊websocket鏈接");
            String userId = jsonObject.getString("userId");
            logger.info("userId:{}註冊session:{}",userId,session.getId());
            userIdSessionMap.put(userId,session);
            sessionUserIdMap.put(session,userId);
        }
}

//定時任務獲取session
public  static Map<String,WebSocketSession> getUserIdSessionMap(){
    return userIdSessionMap;
}

@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
    super.afterConnectionEstablished(session);
}

@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {

    //鏈接關閉後移除
    String userId = sessionUserIdMap.get(session);
    userIdSessionMap.remove(userId);
    logger.info("websocket session:{}關閉緣由:{}",session,status);
}
}

到這裏咱們測試一下是否能夠鏈接。

從上面能夠看到咱們已經和服務端創建了鏈接。

websocketSession:能夠理解爲http的一個會話。會話記錄了這一次的通信對象,咱們可使用seesion發送消息給客戶端。

定時任務

在上面咱們用一個線程安全的map,存儲了userId和websocketSession的關係。如今咱們用定時任務來發送消息給鏈接。

在springboot啓動類增長註解@EnableScheduling
因爲websocket和定時任務啓動的時候會報錯。的時候會報錯。增長taskScheduler()方法。

@SpringBootApplication
@EnableScheduling
public class WebsocketdemoApplication {

public static void main(String[] args) {
    SpringApplication.run(WebsocketdemoApplication.class, args);
}


/**
 * 使用 websockt註解的時候,使用@EnableScheduling註解
 * 啓動的時候一直報錯,增長這個bean 則報錯解決。
 * 報錯信息:  Unexpected use of scheduler.
 *https://stackoverflow.com/questions/49343692/websocketconfigurer-and-scheduled-are-not-work-well-in-an-application
 *
 * @return
 */
@Bean
public TaskScheduler taskScheduler(){

    ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
    taskScheduler.setPoolSize(10);
    taskScheduler.initialize();
    return taskScheduler;
    }
}

定時任務

package com.xuelongjiang.websocketdemo.schedule;

import com.xuelongjiang.websocketdemo.websocket.WebSocketDemoHanlder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import java.util.Map;

/**
 * 定時任務
 * @author xuelongjiang
 */
@Service
public class ScheduleTask {

private Logger logger = LoggerFactory.getLogger(ScheduleTask.class);

@Scheduled(cron = "* * 0/1 * * ?")
public void sendMessage(){

    String message = "你好";

    Map<String,WebSocketSession> map = WebSocketDemoHanlder.getUserIdSessionMap();

    WebSocketSession session = map.get("xuelongjiang");//這裏用戶ID的獲取能夠根據具體業務,這裏爲了更簡單的演示。
    if(session != null){
        try {
            session.sendMessage(new TextMessage(message));
        }catch (Exception e){
            logger.error("定時任務異常:{}",e);
        }
    }
}
}

websocket測試地址:http://www.blue-zero.com/WebS...
源碼地址:https://github.com/longjiangx...

關注個人公衆號第一時間閱讀有趣的技術故事
掃碼關注:
也能夠在微信搜索公衆號便可關注我:codexiulian 渴望與你一塊兒成長進步!

相關文章
相關標籤/搜索