一、寫在前面的問題和解決辦法
1、問題背景:
RabbitMQ,用websock推送消息失敗
2、問題描述:
服務器錯誤:Whoops! Lost connection to ws://localhost:15674/ws
3、解決辦法:
網上查了好久說,加上這個jackson-core的jar包。我加啦啊!沒有用啊!
我還換了版本試了還是報這個錯。
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.11.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.11.0</version>
</dependency>
後來發現是因爲沒有啓用Web STOMP插件,我真是愚蠢的humanbeing~
在RabbitMQ服務器的sbin目錄下
rabbitmq-plugins enable rabbitmq_web_stomp
然後就成功啦~
O_OO_OO_OO_OO_OO_OO_O詳細的學習筆記O_OO_OO_OO_OO_OO_OO_O
二、RabbitMQ學習筆記
最近在學習消息隊列,參考的書籍是《分佈式消息中間件實踐》和網課(叫啥消息隊列高手課,等我後面確認下),作爲一個筆記狂魔,每次學習我都產出大量的筆記,那也不是每篇筆記都分享的,寫這篇是因爲在學習RabbitMQ的時候遇到了好多的問題,一一解決後,整理出來解決的過程和辦法,還有一些自己的理解和總結。
(一)RabbitMQ的簡單介紹
在學習了幾款MQ產品以後,RabbitMQ給我的感覺就是,有點特別,與衆不同。
(1)消息隊列的模式
一般來說,消息隊列有兩種模式,一種是隊列模式(點對點),一種是發佈/訂閱模式,
兩個最大的區別就在於,一份消息能不能被消費多次的問題。
如果發佈/訂閱模式的訂閱者只有一個,那就跟隊列模式一樣了。所以,發佈/訂閱模式是兼容隊列模式的。
爲啥好端端要說這個模式呢?
因爲大多數消息隊列都是發佈/訂閱模式,而RabbitMQ它卻是爲數不多的隊列模式。
那它是不是就不能實現一份消息被消費多次啦?
那也不是的,它雖然是隊列模式,但是它有個special的路由功能,也可以實現消息被消費多次的功能。
(2)
RabbitMQ是基於Erlang語言的,這個語言十分的小衆,而且比較難懂。
對於學Java這些語言的我們來說,就像母語是英語的人去學漢語一樣,比較難。
所以如果有啥問題可能不太好改。
(3)RabbitMQ是AMQP協議的一個開源實現。
這個AMQP是消息協議的一種,用我新買的ipad pro加二代pencil畫了一下這個協議的圖(我照着書上畫的,嘻嘻)。
這個Exchange是消息處理中心Broker裏的。
就是一個路由規則的含義,裏面有Routing Key和Queue的映射關係。
我感覺這個Exchange就像是一個快遞員,收到了生產者Publisher傳過來的消息,就去查一下自己的規則表,看看這個消息發給哪個Queue。
如果找不到對應的Queue,就把消息扔回去給生產者,或者直接丟掉(直接丟掉就是一個很壞的快遞員,哼╭(╯^╰)╮)
我又感覺這個Queue就像不同的菜鳥驛站的站點,嘻嘻。
如果消費者訂閱了消息隊列,消息隊列收到消息就會嘗試將消息傳遞給消費者,如果傳遞不成功,就把消息先存下來,等消費者來拿。(是不是真的很像菜鳥驛站)
然後我又感覺這個Exchange是個法師,如果看到一條消息對應了多個隊列的話,它就會把這條消息複製成多個一毛一樣的消息,發給不同的隊列。(發現一個快遞有好多人需要,就復刻了好多一模一樣的)
哎呀,越寫越多,想寫的還沒寫呢。
(二)環境搭建
1、Erlang和RabbitMQ服務器
前面不是說啦RabbitMQ是基於Erlang語言的,所以要把RabbitMQ服務器啓起來,還得下個Erlang。
需要注意的是,RabbitMQ和Erlang的版本要對應起來的,還不可以瞎下一個。
RabbitMQ和Erlang版本對應網站
我選啦RabbitMQ 3.8.4 和Erlang 22.3
資源我也上傳啦!沒分的找我一下,我再分享百度雲盤,這會兒有點懶。
安裝了以後,要用的命令都在這個sbin裏面,啓動RabbitMQ服務器
到sbin目錄下,執行命令
rabbitmq-server
然後在chrome瀏覽器輸
http://localhost:15672/
然後有這個頁面嘞
賬號和密碼都默認是guest.登錄進去就是醬紫~
哇,雖然還沒有做啥,只是順利的跑出來這個頁面,也是很開心呀~
(三)基於RabbitMQ的消息推送
其實上面寫了辣麼多,還沒寫到我真的想寫的。
前面我也說啦,是學習的《分佈式消息中間件實踐》這本書。前面幾個簡單的例子,我就不寫啦,對着書寫一遍完全沒得問題。
想寫的是這個WebSocket通信的這節例子,啊,跑起來真的艱難,我一定要記錄一下。
實現的功能我簡單介紹一下,就是一個服務端,一個客戶端網頁,然後服務端向RabbitMQ推送一條消息的時候,客戶端的網頁就彈出來這條消息,即一個簡單的廣告推送功能。
1、服務端
(1)pom.xml
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
(2)生產者
寫一個StompProducer.java類
package com.yolanda.rabbitmq.websocket;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
/**
* @Author:Yolanda
* @Date: 2020/6/17 18:16
*/
public class StompProducer {
public static void main(String[] args) throws Exception{
ConnectionFactory factory = new ConnectionFactory();
factory.setUsername("guest");
factory.setPassword("guest");
factory.setHost("localhost");
factory.setVirtualHost("/");
Connection conn = factory.newConnection();
Channel channel = conn.createChannel();
String exchangeName = "exchange-stomp";
channel.exchangeDeclare(exchangeName, "topic");
String routingKey = "shopping.discount";
String message = "<a href=\"https://www.baidu.com\" target=\"_black\">我是廣告</a>";
channel.basicPublish(exchangeName, routingKey, null, message.getBytes());
channel.close();
conn.close();
}
}
這個類的作用就是連上RabbitMQ並往裏面發一條廣告消息。
2、客戶端
客戶端Web網頁頁面接收消息
啊,這個書上的例子沒說客戶端寫啥樣啊,我用springboot寫了一個
(1)目錄結構
(2)pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.yolanda.rabbitmq</groupId>
<artifactId>cha3webclient</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>cha3webclient</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<resources>
<resource>
<includes>
<include>static/**</include>
</includes>
</resource>
</resources>
</build>
</project>
哎呀,就是新建一個springboot的web項目,自帶的那些依賴,然後要配上這段
<resources>
<resource>
<includes>
<include>static/**</include>
</includes>
</resource>
</resources>
不配這個的話,在html裏引用,那些靜態資源讀不到的
(3)新建的index.html
<html lang="en">
<head>
<meta charset="UTF-8">
<title>rabbitMQ消息類型</title>
<link rel="stylesheet" type="text/css" href="default.css">
<link rel="stylesheet" type="text/css" href="jquery.notify.css">
<script type="text/javascript" src="stomp.js"></script>"
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript" src="jquery.notify.js"></script>
</head>
<script type="text/javascript">
$(function () {
// $.notifySetup({sound: 'jquery.notify.wav'});
var client = Stomp.client("ws://localhost:15674/ws");
var onConnect = function () {
client.subscribe("/exchange/exchange-stomp/shopping.discount", function (message) {
$("<p>" + message.body + "</p>").notify({stay: 10000});
});
};
var onError = function (msg) {
$("<p>服務器錯誤:" + msg + "</p>").notify("error");
};
client.connect("guest", "guest", onConnect, onError);
client.heartbeat.incoming = 5000;
client.heartbeat.outgoing = 5000;
});
</script>
<body>
</body>
</html>
(4)springboot的啓動類Cha3webclientApplication.java,默認的沒改過。
package com.yolanda.rabbitmq.cha3webclient;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Cha3webclientApplication {
public static void main(String[] args) {
SpringApplication.run(Cha3webclientApplication.class, args);
}
}
(5)驗證效果~
這個順序還是有點講究的。
Step1.啓動rabbitMQ服務器(前面說過了),到sbin目錄下
rabbitmq-server
Step2.啓用Web STOMP插件(前面也說過了),到sbin目錄下
rabbitmq-plugins enable rabbitmq_web_stomp
Step3.客戶端用springboot跑起來
Chrome輸入localhost:8080
啥也沒有,不要着急
Step4.運行一下剛纔寫的生產者StompProducer.java類
看吧,客戶端網頁收到服務端生產者推送過來的廣告消息啦~
本文所有涉及的服務端,包括靜態資源和客戶端代碼鏈接在這裏
Git完整代碼