spring+rabbitmq+stomp搭建websocket訊息推送(非spring boot方式)

來源:互聯網
上載者:User

前言:

兩年前做過spring+activemq+stomp的ws推送,那個做起來很簡單,但現在公司用的mq中介軟體是rabbitmq,因此需要通過rabbitmq去做ws通訊。仔細搜了搜百度/Google,網上通過spring boot+rabbitmq+stomp的教程文章倒是一搜一大把,可惜目前的項目是非spring boot的,沒法套用。只好自己去搗鼓。搞了幾個小時,終於弄出來了,特此與大家分享下。

RabbitMQ:

 怎麼安裝就不是本篇討論的話題了,自己百度/Google之。rabbitmq預設內建了stomp外掛程式,但是需要自己啟用。命令為:

rabbitmq-plugins enable rabbitmq_stomp

來來來,給個文檔地址參考參考,http://www.rabbitmq.com/stomp.html。預設用guest使用者去串連,密碼也是guest。

這裡有個問題,看rabbitmq設定檔,stomp協議連接埠預設是61613,但是用ws協議串連卻始終串連不上,所以只能用web stomp連接埠,連接埠號碼是15674,這個跟activemq有所區別。(P.S. 此處最好有大神來解惑,或者告知如何用61613來連)

Javascript:

前端代碼擼起來最方便,關鍵是調試也容易,因此先來。

var stompClient = null;var headers = {  login: 'guest',  passcode: 'guest'};function wsConnect(url) {    var ws = new SockJS(url);    stompClient = Stomp.over(ws);    //var ws = new WebSocket(url);    //stompClient = Stomp.over(ws);    // SockJS does not support heart-beat: disable heart-beats    stompClient.heartbeat.outgoing = 0;    stompClient.heartbeat.incoming = 0;    stompClient.connect(headers, function (frame) {        console.log('Connected: ' + frame);        stompClient.subscribe('/topic/test', function (sms) {            var obj = JSON.parse(sms.body)            var count = obj.totalCount;            console.log("count: " + count);        });    });}

然後就串連唄。

$(function(){    var url = "http://host:15674/stomp";    wsConnect(url);});   

 擼完準備測試,當然是選擇chrome嘍,頁面載入後,開啟console控制台,可以看到web socket連上了,前端大功告成。

  

Java:

定義一個StompService類專門用來發送stomp訊息。

import org.springframework.beans.factory.annotation.Value;import org.springframework.messaging.converter.StringMessageConverter;import org.springframework.messaging.simp.stomp.StompHeaders;import org.springframework.stereotype.Component;import org.springframework.web.socket.WebSocketHttpHeaders;import org.springframework.web.socket.client.WebSocketClient;import org.springframework.web.socket.client.standard.StandardWebSocketClient;import org.springframework.web.socket.messaging.WebSocketStompClient;import org.springframework.web.socket.sockjs.client.SockJsClient;import org.springframework.web.socket.sockjs.client.Transport;import org.springframework.web.socket.sockjs.client.WebSocketTransport;import javax.annotation.PostConstruct;import java.util.ArrayList;import java.util.List;/** * stomp服務  rabbitmq做中介軟體 * @author Selwyn * @version $Id: WebSocketConfig.java, v 0.1 9/7/2018 9:59 AM Selwyn Exp $ */@Componentpublic class StompService {    private static final String URL_TEMPLATE = "http://%s:%s/stomp";    @Value("${rabbit.host}")    private String host;    //@Value("${rabbit.stomp.port}")    private Integer port = 15674;    /**     * 串連使用者名稱     */    //@Value("${rabbit.stomp.login}")    private String login = "guest";    /**     * 串連密碼     */    //@Value("${rabbit.stomp.passCode}")    private String passCode = "guest";    private String url;    @PostConstruct    public void init()    {        url = String.format(URL_TEMPLATE, host, port);    }    /**     * 發送stomp訊息     * @param dest  目的地 比如/topic/test     * @param toSend  要發送的資訊     * @param <T>     */    public <T> void connectAndSend(String dest, T toSend)    {        WebSocketClient client = new StandardWebSocketClient();        List<Transport> transports = new ArrayList<>(1);        transports.add(new WebSocketTransport( client) );        //走web stomp的路,必須用socketjs的方式串連,別問我為什麼,我也不清楚        WebSocketClient transport = new SockJsClient(transports);        WebSocketStompClient stompClient = new WebSocketStompClient(transport);        //StompSessionHandlerAdapter預設的payload類型是String, 因此MessageConverter必須是StringMessageConverter        stompClient.setMessageConverter(new StringMessageConverter());        final CustomStompSessionHandler sessionHandler =                new CustomStompSessionHandler(dest, toSend);        WebSocketHttpHeaders headers = new WebSocketHttpHeaders();        headers.setSecWebSocketProtocol("13");        //串連使用者名稱/密碼也是必須的,否則連不上        StompHeaders sHeaders = new StompHeaders();        sHeaders.add("login", this.login);        sHeaders.add("passcode", this.passCode);        //開始串連,回調串連上後發送stomp訊息        stompClient.connect(url, headers, sHeaders, sessionHandler);        //要同步得到發送結果的話,用CountDownLatch來做或者connect結果的future對象做get    }}

然後編寫回調handler類。

import com.alibaba.fastjson.JSON;import lombok.extern.slf4j.Slf4j;import org.springframework.messaging.simp.stomp.StompCommand;import org.springframework.messaging.simp.stomp.StompHeaders;import org.springframework.messaging.simp.stomp.StompSession;import org.springframework.messaging.simp.stomp.StompSessionHandlerAdapter;/** * 自訂stomp session 回調handler * @author Selwyn * @version $Id: CustomStompSessionHandler.java, v 0.1 9/7/2018 3:43 PM Selwyn Exp $ */@Slf4jpublic class CustomStompSessionHandler extends StompSessionHandlerAdapter {    /**     * 要發送的對象,將會json化傳輸出去     */    private Object toSend;    /**     * 目的地,一般是topic地址     */    private String dest;    public CustomStompSessionHandler(String dest, Object toSend) {        this.toSend = toSend;        this.dest = dest;    }    @Override    public void handleFrame(StompHeaders headers, Object payload) {        super.handleFrame(headers, payload);    }    @Override    public void afterConnected(StompSession session, StompHeaders connectedHeaders) {        super.afterConnected(session, connectedHeaders);        String msg = JSON.toJSONString(toSend);        try{            session.send(dest, msg);        }catch(Exception e)        {            log.error("failed to send stomp msg({}) to destination {}", msg, dest);        }finally {            //做完了關閉唄            session.disconnect();        }    }    @Override    public void handleException(StompSession session, StompCommand command, StompHeaders headers, byte[] payload, Throwable exception) {        super.handleException(session, command, headers, payload, exception);        log.error("stomp error: {}", exception);    }    @Override    public void handleTransportError(StompSession session, Throwable exception) {        super.handleTransportError(session, exception);        log.error("stomp transport error: {}", exception);    }    public void setToSend(Object toSend) {        this.toSend = toSend;    }    public void setDest(String dest) {        this.dest = dest;    }}

再自己寫個controller或者寫個單元測試方法,這裡就不給出代碼了,擼完後啟動服務,就可以測試訊息推送了,實踐證明,真香!

結尾:

其實整個過程還沒完,需要考慮到串連中斷等情況,用戶端和服務後台都需要做好重連機制。通過sockjs這種方式串連是沒有心跳機制的,這個比activemq帶的stomp外掛程式要low。個人建議,如果能用spring boot的話盡量用那種方式去實現stomp訊息推送。

  

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.