rabbitmq入門(mén)

1、介紹

2、MQ優(yōu)勢(shì)

應(yīng)用解耦:提高系統(tǒng)容錯(cuò)性和可維護(hù)性
異步提速:提升用戶(hù)體驗(yàn)和系統(tǒng)吞吐量
削峰填谷:提高系統(tǒng)穩(wěn)定性

代碼
工具類(lèi)-RabbitmqUtils

package nk.gk.wyl.module.rabbitmq.utils;

import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * @author :zhangshuailing
 * @date :2022/10/31 14:48
 * @project_name :pt-rabbitmq
 * @description:
 */
@Slf4j
public class RabbitmqUtils {
    private String ip;
    private Integer port;
    private String username;
    private String password;
    private String virtual;

    public RabbitmqUtils(String ip, Integer port, String username, String password,String virtual) {
        this.ip = ip;
        this.port = port;
        this.username = username;
        this.password = password;
        this.virtual = virtual;
    }

    /**
     * 獲取連接
     * @return
     */
    @SneakyThrows
    public Connection getConnection(){
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost(ip);
        connectionFactory.setPort(port);
        connectionFactory.setUsername(username);
        connectionFactory.setPassword(password);
        connectionFactory.setVirtualHost(virtual);
        try {
            //獲取TCP長(zhǎng)連接
            return connectionFactory.newConnection();
        } catch (IOException e) {
            e.printStackTrace();
            throw new Exception("連接失敗。"+e.getMessage());
        } catch (TimeoutException e) {
            e.printStackTrace();
            throw new Exception("連接超時(shí)。"+e.getMessage());
        }
    }
}

工具類(lèi)-RabbitUtils

package nk.gk.wyl.module.rabbitmq.utils;

import com.rabbitmq.client.Connection;


public class RabbitUtils {
    private static final String ip = "192.168.116.122";
    private static final int port = 5672;//5672是RabbitMQ的默認(rèn)端口號(hào)
    private static final String username = "zhangshuailing";
    private static final String password = "123";
    private static final String virtual = "zsl";

    private volatile Connection connection = null;

    public  Connection getConnection(){
        if(connection != null){
            return connection;
        }
        try {
            connection = new RabbitmqUtils(ip,port,username,password,virtual).getConnection();
            return connection;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

工具類(lèi)-RabbitConstant

package nk.gk.wyl.module.rabbitmq.utils;

public class RabbitConstant {
    public static final String QUEUE_HELLOWORLD = "helloworld";
    public static final String QUEUE_SMS = "sms";
    public static final String EXCHANGE_WEATHER = "weather";
    public static final String EXCHANGE_WEATHER_ROUTING = "weather_routing";
    public static final String QUEUE_BAIDU = "baidu";
    public static final String QUEUE_SINA = "sina";
    public static final String EXCHANGE_WEATHER_TOPIC = "weather_topic";
}

2、MQ六個(gè)工作模式

1》簡(jiǎn)單模式 Hello word

image.png

一個(gè)生產(chǎn)者對(duì)應(yīng)一個(gè)消費(fèi)者??!

代碼
生產(chǎn)者-Consumer

package nk.gk.wyl.module.rabbitmq.helloword;
import com.rabbitmq.client.*;
import lombok.SneakyThrows;
import nk.gk.wyl.module.rabbitmq.utils.RabbitConstant;
import nk.gk.wyl.module.rabbitmq.utils.RabbitUtils;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
 * @author :zhangshuailing
 * @date :2022/10/31 14:48
 * @project_name :pt-rabbitmq
 * @description:
 */
public class Consumer {
    public static void main(String[] args) throws IOException, TimeoutException {
        //獲取TCP長(zhǎng)連接
         Connection conn = new RabbitUtils().getConnection();
        //創(chuàng)建通信“通道”,相當(dāng)于TCP中的虛擬連接
        Channel channel = conn.createChannel();
        //創(chuàng)建隊(duì)列,聲明并創(chuàng)建一個(gè)隊(duì)列,如果隊(duì)列已存在,則使用這個(gè)隊(duì)列
        //第一個(gè)參數(shù):隊(duì)列名稱(chēng)ID
        //第二個(gè)參數(shù):是否持久化,false對(duì)應(yīng)不持久化數(shù)據(jù),MQ停掉數(shù)據(jù)就會(huì)丟失
        //第三個(gè)參數(shù):是否隊(duì)列私有化,false則代表所有消費(fèi)者都可以訪(fǎng)問(wèn),true代表只有第一次擁有它的消費(fèi)者才能一直使用,其他消費(fèi)者不讓訪(fǎng)問(wèn)
        //第四個(gè):是否自動(dòng)刪除,false代表連接停掉后不自動(dòng)刪除掉這個(gè)隊(duì)列
        //其他額外的參數(shù), null
       channel.queueDeclare(RabbitConstant.QUEUE_HELLOWORLD,false, false, false, null);
        //從MQ服務(wù)器中獲取數(shù)據(jù)
        //創(chuàng)建一個(gè)消息消費(fèi)者
        //第一個(gè)參數(shù):隊(duì)列名
        //第二個(gè)參數(shù)代表是否自動(dòng)確認(rèn)收到消息,false代表手動(dòng)編程來(lái)確認(rèn)消息,這是MQ的推薦做法
        //第三個(gè)參數(shù)要傳入DefaultConsumer的實(shí)現(xiàn)類(lèi)
       channel.basicConsume(RabbitConstant.QUEUE_HELLOWORLD, false, new DefaultConsumer(channel){
            @SneakyThrows
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String message = new String(body);
                System.out.println("消費(fèi)者接收到的消息:"+message);

                System.out.println("消息的TagId:"+envelope.getDeliveryTag());
                //false只確認(rèn)簽收當(dāng)前的消息,設(shè)置為true的時(shí)候則代表簽收該消費(fèi)者所有未簽收的消息
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        });
    }
}

消費(fèi)者-Consumer

package nk.gk.wyl.module.rabbitmq.helloword;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import nk.gk.wyl.module.rabbitmq.utils.RabbitConstant;
import nk.gk.wyl.module.rabbitmq.utils.RabbitUtils;
import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * @author :zhangshuailing
 * @date :2022/10/31 14:50
 * @project_name :pt-rabbitmq
 * @description:
 */
public class Producer {

    public static void main(String[] args) throws IOException, TimeoutException {
        //獲取TCP長(zhǎng)連接
        Connection conn = new RabbitUtils().getConnection();
        //創(chuàng)建通信“通道”,相當(dāng)于TCP中的虛擬連接
        Channel channel = conn.createChannel();
        //創(chuàng)建隊(duì)列,聲明并創(chuàng)建一個(gè)隊(duì)列,如果隊(duì)列已存在,則使用這個(gè)隊(duì)列
        //第一個(gè)參數(shù):隊(duì)列名稱(chēng)ID
        //第二個(gè)參數(shù):是否持久化,false對(duì)應(yīng)不持久化數(shù)據(jù),MQ停掉數(shù)據(jù)就會(huì)丟失
        //第三個(gè)參數(shù):是否隊(duì)列私有化,false則代表所有消費(fèi)者都可以訪(fǎng)問(wèn),true代表只有第一次擁有它的消費(fèi)者才能一直使用,其他消費(fèi)者不讓訪(fǎng)問(wèn)
        //第四個(gè):是否自動(dòng)刪除,false代表連接停掉后不自動(dòng)刪除掉這個(gè)隊(duì)列
        //其他額外的參數(shù), null
        channel.queueDeclare(RabbitConstant.QUEUE_HELLOWORLD,false, false, false, null);
        String message = "hello world";
        //四個(gè)參數(shù)
        //exchange 交換機(jī),暫時(shí)用不到,在后面進(jìn)發(fā)布訂閱時(shí)才會(huì)用到
        //隊(duì)列名稱(chēng)
        //額外的設(shè)置屬性
        //最后一個(gè)參數(shù)是要傳遞的消息字節(jié)數(shù)組
        channel.basicPublish("", RabbitConstant.QUEUE_HELLOWORLD, null,message.getBytes());
        channel.close();
        conn.close();
        System.out.println("===發(fā)送成功===");
    }
}

2》工作隊(duì)列模式 work queues

image.png

一個(gè)生產(chǎn)者對(duì)應(yīng)多個(gè)消費(fèi)者,但是一條消息只能有一個(gè)消費(fèi)者獲得消息?。?!
輪詢(xún)分發(fā)就是將[消息隊(duì)列中的消息,依次發(fā)送給所有消費(fèi)者。一個(gè)消息只能被一個(gè)消費(fèi)者獲取。

代碼
生產(chǎn)者-Producer

package nk.gk.wyl.module.rabbitmq.workqueue;
import com.alibaba.fastjson.JSON;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import nk.gk.wyl.module.rabbitmq.utils.RabbitConstant;
import nk.gk.wyl.module.rabbitmq.utils.RabbitUtils;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
 * @author zhangshuailing
 * 生產(chǎn)者
 */
public class Producer {
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = new RabbitUtils().getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(RabbitConstant.QUEUE_SMS, false, false, false, null);
        for(int i = 1 ; i <= 100 ; i++) {
            String jsonSMS = "先生【"+i+"】您的車(chē)票已預(yù)訂成功";
            channel.basicPublish("" , RabbitConstant.QUEUE_SMS , null , jsonSMS.getBytes());
        }
        System.out.println("發(fā)送數(shù)據(jù)成功");
        channel.close();
        connection.close();
    }
}

消費(fèi)者-多個(gè)

package nk.gk.wyl.module.rabbitmq.workqueue;
import com.rabbitmq.client.*;
import nk.gk.wyl.module.rabbitmq.utils.RabbitConstant;
import nk.gk.wyl.module.rabbitmq.utils.RabbitUtils;
import java.io.IOException;
/**
 * @author zhangshuailing
 * 消費(fèi)者
 */
public class SMSSender1 {
    public static void main(String[] args) throws IOException {
        Connection connection = new RabbitUtils().getConnection();
        final Channel channel = connection.createChannel();
        channel.queueDeclare(RabbitConstant.QUEUE_SMS, false, false, false, null);
        //如果不寫(xiě)basicQos(1),則自動(dòng)MQ會(huì)將所有請(qǐng)求平均發(fā)送給所有消費(fèi)者
        //basicQos,MQ不再對(duì)消費(fèi)者一次發(fā)送多個(gè)請(qǐng)求,而是消費(fèi)者處理完一個(gè)消息后(確認(rèn)后),在從隊(duì)列中獲取一個(gè)新的
        channel.basicQos(1);//處理完一個(gè)取一個(gè)
        channel.basicConsume(RabbitConstant.QUEUE_SMS , false , new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String jsonSMS = new String(body);
                System.out.println("SMSSender1-短信發(fā)送成功:" + jsonSMS);

                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                channel.basicAck(envelope.getDeliveryTag() , false);
            }
        });
    }
}
package nk.gk.wyl.module.rabbitmq.workqueue;

import com.rabbitmq.client.*;
import nk.gk.wyl.module.rabbitmq.utils.RabbitConstant;
import nk.gk.wyl.module.rabbitmq.utils.RabbitUtils;

import java.io.IOException;

/**
 * @author zhangshuailing
 * 消費(fèi)者
 */
public class SMSSender2 {

    public static void main(String[] args) throws IOException {
        Connection connection = new RabbitUtils().getConnection();
        final Channel channel = connection.createChannel();
        channel.queueDeclare(RabbitConstant.QUEUE_SMS, false, false, false, null);
        //如果不寫(xiě)basicQos(1),則自動(dòng)MQ會(huì)將所有請(qǐng)求平均發(fā)送給所有消費(fèi)者
        //basicQos,MQ不再對(duì)消費(fèi)者一次發(fā)送多個(gè)請(qǐng)求,而是消費(fèi)者處理完一個(gè)消息后(確認(rèn)后),在從隊(duì)列中獲取一個(gè)新的
        channel.basicQos(1);//處理完一個(gè)取一個(gè)
        channel.basicConsume(RabbitConstant.QUEUE_SMS , false , new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String jsonSMS = new String(body);
                System.out.println("SMSSender2-短信發(fā)送成功:" + jsonSMS);
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                channel.basicAck(envelope.getDeliveryTag() , false);
            }
        });
    }
}

3》發(fā)布訂閱模式 Publish/Subscribe

image.png

一個(gè)消費(fèi)者將消息首先發(fā)送到[交換器],交換器綁定到多個(gè)隊(duì)列,然后被監(jiān)聽(tīng)該隊(duì)列的消費(fèi)者所接收并消費(fèi)。
ps:X表示交換器,在RabbitMQ中,交換器主要有四種類(lèi)型:direct、fanout、topic、headers,這里的交換器是 fanout。下面我們會(huì)詳細(xì)介紹這幾種交換器。
  兩個(gè)消費(fèi)者獲得了同一條消息。即就是,一個(gè)消息從交換機(jī)同時(shí)發(fā)送給了兩個(gè)隊(duì)列中,[監(jiān)聽(tīng)]這兩個(gè)隊(duì)列的消費(fèi)者消費(fèi)了這個(gè)消息;
如果沒(méi)有隊(duì)列綁定交換機(jī),則消息將丟失。因?yàn)榻粨Q機(jī)沒(méi)有存儲(chǔ)能力,消息只能存儲(chǔ)在隊(duì)列中。

4》路由模式 Routing

image.png

生產(chǎn)者將消息發(fā)送到direct交換器,在綁定隊(duì)列和交換器的時(shí)候有一個(gè)路由key,生產(chǎn)者發(fā)送的消息會(huì)指定一個(gè)路由key,那么消息只會(huì)發(fā)送到相應(yīng)key相同的隊(duì)列,接著監(jiān)聽(tīng)該隊(duì)列的消費(fèi)者消費(fèi)消息。
也就是讓消費(fèi)者有選擇性的接收消息。
路由模式,是以路由規(guī)則為導(dǎo)向,引導(dǎo)消息存入符合規(guī)則的隊(duì)列中。再由隊(duì)列的消費(fèi)者進(jìn)行消費(fèi)的。

5》主題模式 topics

image.png

上面的路由模式是根據(jù)路由key進(jìn)行完整的匹配(完全相等才發(fā)送消息),這里的通配符模式通俗的來(lái)講就是模糊匹配。
符號(hào)“#”表示匹配一個(gè)或多個(gè)詞,符號(hào)“*”表示匹配一個(gè)詞。
與路由模式相似,但是,主題模式是一種模糊的匹配方式。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容