STOMP協議——基於Websocket實現

今天繼續Websocket之STOMP協議,因爲其設計簡單,在開發客戶端方面使用簡便,在不少種語言上均可以見到其身影,並不是websocket「獨享」。前端

定義

STOMP(Simple/Streaming Text Orientated Messaging Protocol),即簡單(流)文本定向消息協議。屬於消息隊列的一種協議,有點相似於jms。java

做用

提供消息體的格式,容許STOMP客戶端(Endpoints)與任意STOMP消息代理(message broker)進行交互,實現客戶端之間進行異步消息傳送。web

角色介紹

圖片來源《spring in action》spring

  • 生產者客戶端: 給某destination發送消息;
  • 消費者客戶端: 接收所訂閱的destination所推送過來的消息;
  • 請求通道: 接收生產者推送過來的消息的線程池;
  • 相應通道: 推送消息給消費者的線程池;
  • 代理: 消息隊列管理者. 記錄哪些client訂閱了哪一個destination.
  • 應用目的地址: 發送到這類目的地址的消息在到達broker以前, 會先路由到由應用寫的某個方法. 至關於對進入broker的消息進行一次攔截, 目的是針對消息作一些業務處理————圖中的」/app」
  • 非應用目的地址: 發送到這類目的地址的消息會直接轉到broker. 不會被應用攔截————圖中的」/topic」

處理流程

  • 生產者經過發送消息到某個destination
  • 請求通道接受消息
  • 若是目的地址是應用目的地址(/app)則轉到相應的由應用本身寫的業務方法作處理, 再轉到broker
  • 若是目的地址是非應用目的地址(/topic)則直接轉到broker.broker構建消息後再經過相應通道推送消息到全部訂閱此目的地址的消費者

代碼實現

下面使用SpringSecurity和WebSocket-STOMP實現「點對點」消息發送功能:json

啓用STOMP功能

@Configuration
@EnableWebSocketMessageBroker//開啓消息代理
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    /** * 創建鏈接點信息 * @param registry */
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/ws/ep").withSockJS();
         registry.setApplicationDestinationPrefixes("/app");
    }

    /** * 配置消息隊列 * 基於內存的STOMP消息代理 * @param registry */
    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/queue");
    }
}
複製代碼
  • 將 "/ws/ep" 註冊爲一個 STOMP 端點。客戶端在訂閱或發佈消息到目的地路徑前,要鏈接到該端點
  • 以 /app 開頭的消息都會被路由到帶有@MessageMapping 或 @SubscribeMapping 註解的方法中;
  • 以 /queue 開頭的消息都會發送到STOMP代理中,根據所選擇的STOMP代理不一樣,目的地的可選前綴也會有所限制;
  • 以/user開頭的消息會將消息重路由到某個用戶獨有的目的地上。

處理STOMP消息

自定義通訊協議websocket

@Controller
public class WScontroller {

    @Autowired//消息發送模板
    SimpMessagingTemplate simpMessagingTemplate;

    @MessageMapping("/ws/chat")
    public void receiveMessage(String message, Principal principal) {
        String[] split = message.split(";");
        HashMap<String, Object> map = new HashMap<>();
        map.put("username",split[1]);
        map.put("msg",split[0]);
        simpMessagingTemplate.convertAndSendToUser(split[1], "/queue/msg",map);
    }
複製代碼
  • 接收客戶端發來的消息,參數就是消息自己message
  • @MessageMapping 或者 @SubscribeMapping 註解能夠處理客戶端發送過來的消息,並選擇方法是否有返回值。
  • @MessageMapping 指定目的地是「/app/ws/chat」(「/app」前綴是隱含的,由於咱們將其配置爲應用的目的地前綴)。
  • 通訊協議能夠自定義——可自定義參數的格式
  • 能夠接收json格式的數據,傳遞josn數據時不須要添加額外註解@Requestbody
  • 消息發送者不是從前端傳遞過來的,而是從springsecurity中獲取的,防止前端冒充
  • 若是 @MessageMapping 註解的控制器方法有返回值的話,返回值會被髮送到消息代理,只不過會添加上"/topic"前綴。
  • 經過爲方法添加@SendTo註解,重載目的地

客戶端實現

客戶端代碼(UVE)app

<template>
  <div>
    <div>
      <div v-for="(m,index) in ms">{{m.username}}:{{m.msg}}</div>

    </div>
    <el-input v-model="msg"></el-input>
    <el-button @click="sendMsg"></el-button>
  </div>
</template>

<script>
  import "../../lib/sockjs"
  import "../../lib/stomp"
    export default {
        name: "FriendChat",
      data() {
        return {
          msg: '',
          ms: [],
          stomp: null
        };
      },
      mounted() {
        this.intCon();
      },
      methods: {
        // 創建鏈接
        initCon() {
          let _this = this;
           this.stomp = Stomp.over(new SockJS("/ws/ep"));
            this.stomp.connect({},success=>{
              _this.stomp.subscribe("/user/queue/msg",msg=>{
                _this.ms.push(JSON.parse(msg.body));
              })
            },fail=>{

            })
        },
        // 發送消息
        sendMsg() {
          this.stomp.send("/ws/chat",{},this.msg)
        }
      }
    }
</script>
<style scoped>
</style> 
複製代碼
相關文章
相關標籤/搜索