最近在本身捯飭一個網站,有一個功能是須要後端處理完數據把數據發佈到MQ中,前端再從MQ中接收數據。可是前端鏈接MQ又成了一個問題,在網上搜了下資料,點進去一篇IBM DW後發現居然是超哥寫的,真是巧哈~由於超哥寫的很好因此不少我就直接摘抄過來了,他應該不會介意的(逃。html
Web 應用都是基於 HTTP 協議的請求/響應模式,沒法像 TCP 協議那樣保持長鏈接,所以 Web 應用就很難像手機那樣實現實時的消息推送。就目前來看,Web 應用的消息推送方式主要有如下幾種:前端
1.Ajax 短輪詢
Ajax 輪詢主要經過頁面端的 JS 定時異步刷新任務來實現數據的加載,但這種方式實時效果較差,並且對服務端的壓力也較大。web
2.長輪詢
長輪詢主要也是經過 Ajax 機制,但區別於傳統的 Ajax 應用,長輪詢的服務器端會在沒有數據時阻塞請求直到有新的數據產生或者請求超時才返回,以後客戶端再從新創建鏈接獲取數據。但長輪詢服務端會長時間地佔用資源,若是消息頻繁發送的話會給服務端帶來較大的壓力。後端
3.WebSocket 雙向通訊
WebSocket 是 HTML5 中一種新的通訊協議,可以實現瀏覽器與服務器之間全雙工通訊。若是瀏覽器和服務端都支持 WebSocket 協議的話,該方式實現的消息推送無疑是最高效、簡潔的。而且最新版本的 IE、Firefox、Chrome 等瀏覽器都已經支持 WebSocket 協議,Apache Tomcat 7.0.27 之後的版本也開始支持 WebSocket。瀏覽器
在macOS上安裝rabbitmq(提早已經安裝了brew):服務器
brew install rabbitmq
啓動stomp有關的一系列插件websocket
rabbitmq-plugins enable rabbitmq_management rabbitmq_web_stomp rabbitmq_stomp rabbitmq_web_stomp_examples
能夠經過下面這條命令查看已啓動了哪些RabbitMQ插件:session
rabbitmq-plugins list
重啓rabbitmq:併發
brew services restart rabbitmq
咱們能夠在15670端口訪問web-stomp-examples,異步
http://localhost:15670/
其中RabbitMQ運行在15672端口,stomp服務運行在15674端口。
RabbitMQ 有不少第三方插件,能夠在 AMQP 協議基礎上作出許多擴展的應用。Web STOMP 插件就是基於 AMQP 之上的 STOMP 文本協議插件,利用 WebSocket 可以輕鬆實現瀏覽器和服務器之間的實時消息傳遞,具體實現方式以下圖所示:
RabbitMQ Web STOMP 插件能夠理解爲 HTML5 WebSocket 與 STOMP 協議間的橋接,目的也是爲了讓瀏覽器可以使用 RabbitMQ。當 RabbitMQ 消息服務器開啓了 STOMP 和 Web STOMP 插件後,瀏覽器端就能夠輕鬆地使用 WebSocket 或者 SockerJS 客戶端實現與 RabbitMQ 服務器進行通訊。
前端經過stomp鏈接RabbitMQ的代碼以下:
// 初始化 ws 對象 if (location.search == '?ws') { var ws = new WebSocket('ws://localhost:15674/ws'); } else { var ws = new SockJS('http://localhost:15674/stomp'); } // 得到Stomp client對象 var client = Stomp.over(ws); // SockJS does not support heart-beat: disable heart-beats client.heartbeat.outgoing = 0; client.heartbeat.incoming = 0; client.debug = pipe('#second'); // 定義鏈接成功回調函數 var on_connect = function(x) { //data.body是接收到的數據 client.subscribe("/queue/default", function(data) { var msg = data.body; alert("收到數據:" + msg); }); }; // 定義錯誤時回調函數 var on_error = function() { console.log('error'); }; // 鏈接RabbitMQ client.connect('guest', 'guest', on_connect, on_error, '/'); console.log(">>>鏈接上http://localhost:15674");
咱們能夠看到代碼主要包括如下幾個部分:
所以咱們要作的主要是定義這個鏈接成功的回調函數,其中:
client.subscribe("/queue/default", function(data) { var msg = data.body; alert("收到數據:" + msg); });
這個函數的功能是訂閱了一個名爲"default"的queue,當有生產者向該隊列發送數據時,該函數會做爲消費者接收到數據,並觸發回調函數。咱們能夠在回調函數中對接收到的數據進行展現,更新到界面上,從而能夠達到和輪詢同樣的效果。
當咱們把這段js放進html中run起來後,經過Chrome F12工具也能夠看到這樣的提示:
Opening Web Socket... stomp.js:114 Web Socket Opened... stomp.js:114 >>> CONNECT accept-version:1.1,1.0 heart-beat:0,0 host:/ login:guest passcode:guest stomp.js:114 <<< CONNECTED server:RabbitMQ/3.6.6 session:*** heart-beat:0,0 version:1.1 stomp.js:114 connected to server RabbitMQ/3.6.6 stomp.js:114 >>> SUBSCRIBE id:sub-0 destination:/queue/default
當咱們向/queue/default發送一條測試數據「aaa」,前端js就會接收到數據,再看F12工具中的顯示:
<<< MESSAGE subscription:sub-0 destination:/queue/default message-id:*** redelivered:false persistent:1 content-length:3 aaa
能夠看到已經收到了"aaa"這條消息,而且能夠注意到Stomp協議是以文本格式進行數據傳輸的,而RabbitMQ是以二進制傳輸的。經過data.body就能夠獲取到Stomp中的消息的主體了。
關於websocket鏈接MQ進行實時消息推送,當然是實現消息推送的一個很好的方式,可是不得不思考在大量併發狀況下,MQ端是否能承受的了數量龐大的websocket鏈接。MQ將先後端進行了解耦和異步化,可是也可能成爲系統的性能瓶頸。對於這種實現方式,目前只能說是且學且用,性能方面還尚待考究。