消息隊列實踐一之RabbitMQ消息推送(解決服務器錯誤:Whoops! Lost connection to ws://localhost:15674/ws)

一、寫在前面的問題和解決辦法

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版本對應網站

在這裏插入圖片描述
Erlang下載網站
RabbitMQ下載網站

我選啦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完整代碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章