RabbitMQ入門知識

RabbitMQ

應用場景

 

1.RabbitMQ介紹

關於RabbitMQ的介紹詳情,中文文檔地址:http://rabbitmq.mr-ping.com/

特點:異步處理、應用解耦、流量削鋒、日誌處理

2.RabbitMQ安裝

詳情參考:https://blog.csdn.net/qq_41950069/article/details/81346675

 

添加用戶:

 

 

 

 

 

 

 

 

 

 

 

爲新建的用戶添加host:

 

3.Java中使用RabbitMQ

a.簡單隊列

 

耦合性高,生產者和消費者一一對應,隊列名變更的時候需要同時變更

代碼

<dependency>

          <groupId>com.rabbitmq</groupId>

          <artifactId>amqp-client</artifactId>

          <version>4.0.3</version>

      </dependency>

      <dependency>

          <groupId>org.slf4j</groupId>

          <artifactId>slf4j-api</artifactId>

          <version>1.7.10</version>

      </dependency>

      <dependency>

          <groupId>org.slf4j</groupId>

          <artifactId>slf4j-log4j12</artifactId>

          <version>1.7.5</version>

      </dependency>

      <dependency>

          <groupId>log4j</groupId>

          <artifactId>log4j</artifactId>

          <version>1.2.17</version>

      </dependency>

public class ConnectionUtil {

    public static Connection getConnection() throws IOException, TimeoutException {

        ConnectionFactory factory = new ConnectionFactory();

        factory.setHost("127.0.0.1");

        factory.setPort(5672);

        factory.setVirtualHost("/vitual_h");

        factory.setUsername("wen");

        factory.setPassword("wen");

        factory.newConnection();

        return factory.newConnection();

    }

}

public class Sender {

    private static final String QUEUE_NAME = "test_simple_queue";

 

    public static void main(String[] args) throws IOException, TimeoutException {

        Connection connection = ConnectionUtil.getConnection();

        //從連接中獲取通道

        Channel channel = connection.createChannel();

        //創建隊列聲明

        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        String msg = "hello Simple";

        channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());

        System.out.println("--send msg : " + msg);

        channel.close();

        connection.close();

    }

}

public class Receiver {

    private static final String QUEUE_NAME = "test_simple_queue";

 

    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {

        Connection connection = ConnectionUtil.getConnection();

        Channel channel = connection.createChannel();

//        methodOld(channel);

        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        DefaultConsumer consumer = new DefaultConsumer(channel) {

            @Override

            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {

                String string = new String(body, StandardCharsets.UTF_8);

                System.out.println("new api receiver:" + string);

 

            }

        };

        channel.basicConsume(QUEUE_NAME,true,consumer);

    }

 

    private static void methodOld(Channel channel) throws IOException, InterruptedException {

        //定義隊列的消費者

        QueueingConsumer consumer = new QueueingConsumer(channel);

        channel.basicConsume(QUEUE_NAME, true, consumer);

        while (true) {

            QueueingConsumer.Delivery delivery = consumer.nextDelivery();

            String msg = new String(delivery.getBody());

            System.out.println("[receive msg]" + msg);

        }

    }

}

 

b.工作隊列WorkQueue

 

代碼

public class Sender {

    private static final String QUEUE_NAME = "test_work_queue";

 

    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {

        Connection connection = ConnectionUtil.getConnection();

        Channel channel = connection.createChannel();

 

        channel.queueDeclare(QUEUE_NAME,false,false,false,null);

 

        for (int i=0;i<50;i++){

            String msg="hello "+i;

            channel.basicPublish("",QUEUE_NAME,null,msg.getBytes());

            System.out.println("發送消息成功 :"+msg);

            Thread.sleep(1*20);

        }

        channel.close();

        connection.close();

    }

}

public class Receiver1 {

    private static final String QUEUE_NAME = "test_work_queue";

 

    public static void main(String[] args) throws IOException, TimeoutException {

        Connection connection = ConnectionUtil.getConnection();

        Channel channel = connection.createChannel();

        channel.queueDeclare(QUEUE_NAME,false,false,false,null);

 

        DefaultConsumer consumer = new DefaultConsumer(channel) {

            @Override

            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {

                String msg = new String(body, StandardCharsets.UTF_8);

                System.out.println("[1] receive msg :"+msg);

 

                try {

                    Thread.sleep(2000);

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }finally {

                    System.out.println("[1] done ");

                }

            }

        };

        boolean autoAck=true;

        channel.basicConsume(QUEUE_NAME,autoAck,consumer);

 

 

    }

}

Receiver2的代碼與Receiver1的代碼一致

 

現象:

消費者1和消費者2處理的消息數量是一樣的,中間件採用輪詢分發(round-robin)

公平分發fair dipatch

 

代碼

public class Sender {

    private static final String QUEUE_NAME = "test_work_queue";

 

    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {

        Connection connection = ConnectionUtil.getConnection();

        Channel channel = connection.createChannel();

 

        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        //限制中間件每次發送一個消息,且消息消費者沒有確認消息消費的情況下,channel不在發送消息,一次只發送一個消息

        int prefetchCount = 1;

        channel.basicQos(prefetchCount);

        for (int i = 0; i < 50; i++) {

            String msg = "hello " + i;

            channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());

            System.out.println("發送消息成功 :" + msg);

            Thread.sleep(1 * 20);

        }

        channel.close();

        connection.close();

    }

}

public class Receiver1 {

    private static final String QUEUE_NAME = "test_work_queue";

 

    public static void main(String[] args) throws IOException, TimeoutException {

        Connection connection = ConnectionUtil.getConnection();

        //匿名內部類訪問外部類的局部變量需要聲明爲final,否則,變量出棧後就被清除

        final Channel channel = connection.createChannel();

        channel.queueDeclare(QUEUE_NAME,false,false,false,null);

        //保證每次處理一個請求

        channel.basicQos(1);

        DefaultConsumer consumer = new DefaultConsumer(channel) {

            @Override

            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {

                String msg = new String(body, StandardCharsets.UTF_8);

                System.out.println("[1] receive msg :"+msg);

 

                try {

                    Thread.sleep(2000);

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }finally {

                    System.out.println("[1] done ");

                    channel.basicAck(envelope.getDeliveryTag(),false);

                }

            }

        };

        //關閉自動應答

        boolean autoAck=false;

        channel.basicConsume(QUEUE_NAME,autoAck,consumer);

    }

}

public class Receiver2 {

    private static final String QUEUE_NAME = "test_work_queue";

 

    public static void main(String[] args) throws IOException, TimeoutException {

        Connection connection = ConnectionUtil.getConnection();

        final Channel channel = connection.createChannel();

        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

 

        //保證每次處理一個請求

        channel.basicQos(1);

 

        DefaultConsumer consumer = new DefaultConsumer(channel) {

            @Override

            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {

                String msg = new String(body, StandardCharsets.UTF_8);

                System.out.println("[2] receive msg :" + msg);

 

                try {

                    Thread.sleep(1000);

                } catch (InterruptedException e) {

                    e.printStackTrace();

                } finally {

                    System.out.println("[2] done ");

                    channel.basicAck(envelope.getDeliveryTag(),false);

 

                }

            }

        };

        //關閉自動應答

        boolean autoAck=false;

        channel.basicConsume(QUEUE_NAME, autoAck, consumer);

    }

}

 

消息應答與消息持久化

        boolean autoAck=false;

        channel.basicConsume(QUEUE_NAME, autoAck, consumer);

  1. boolean autoAck=true;(自動確認模式)一旦RabbitMQ將消息分發給消費者,就會從內存中刪除,這種情況下,發生正在執行的消費者進程被關閉,消息將消失。
  2. boolean autoAck=false;(手動模式)RabbitMQ將消息發送給消費者後不會馬上刪除內存中的消息,只有收到消費者消費消息的確認請求後纔會從內存中刪除消息。

c.訂閱模式

模型

 

 

Exchange:交換機沒有存儲能力,在RabbitMQ中只有隊列有存儲能力

 

消息消費的特點:消費者只能消費消費者啓動時間後生產者發送的消息,對於消費者啓動之前的消息,消費者無法消費

public class Sender {

    private static final String EXCHANGE_NAME = "test_exchange_fanout";

 

    public static void main(String[] args) throws IOException, TimeoutException {

        Connection connection = ConnectionUtil.getConnection();

        Channel channel = connection.createChannel();

        channel.exchangeDeclare(EXCHANGE_NAME, "fanout");

        String msg = "hello ps";

        channel.basicPublish(EXCHANGE_NAME, "", null, msg.getBytes());

        System.out.println("Send :" + msg);

        channel.close();

        connection.close();

    }

}

public class Receiver1 {

    private static final String QUEUE_NAME = "test_queue_fanout_email";

    private static final String EXCHANGE_NAME = "test_exchange_fanout";

 

 

    public static void main(String[] args) throws IOException, TimeoutException {

        Connection connection = ConnectionUtil.getConnection();

        final Channel channel = connection.createChannel();

        //隊列聲明

        channel.queueDeclare(QUEUE_NAME,false,false,false,null);

        //綁定隊列到交換機

        channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"");

 

        //保證每次處理一個請求

        channel.basicQos(1);

        DefaultConsumer consumer = new DefaultConsumer(channel) {

            @Override

            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {

                String msg = new String(body, StandardCharsets.UTF_8);

                System.out.println("[topic 1] receive msg :"+msg);

 

                try {

                    Thread.sleep(2000);

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }finally {

                    System.out.println("[topic 1] done ");

                    channel.basicAck(envelope.getDeliveryTag(),false);

                }

            }

        };

        //關閉自動應答

        boolean autoAck=false;

        channel.basicConsume(QUEUE_NAME,autoAck,consumer);

    }

}

 

d.路由模式

模型

 

 

消息根據routingkey來匹配到底發送到哪一個隊列

public class Sender {

    private static final String EXCHANGE_NAME = "test_exchange_direct";

 

    public static void main(String[] args) throws IOException, TimeoutException {

        Connection connection = ConnectionUtil.getConnection();

        Channel channel = connection.createChannel();

 

        channel.exchangeDeclare(EXCHANGE_NAME, "direct");

        String msg = "hello exchange";

//        String routingKey = "error";

        String routingKey = "info";

        channel.basicPublish(EXCHANGE_NAME, routingKey, null, msg.getBytes());

        System.out.println("send msg: " + msg);

        channel.close();

        connection.close();

    }

}

public class Receiver1 {

    private static final String QUEUE_NAME = "test_queue_direct";

    private static final String EXCHANGE_NAME = "test_exchange_direct";

 

    public static void main(String[] args) throws IOException, TimeoutException {

        Connection connection = ConnectionUtil.getConnection();

        final Channel channel = connection.createChannel();

        channel.queueDeclare(QUEUE_NAME,false,false,false,null);

        channel.basicQos(1);

        channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"error");

 

        DefaultConsumer consumer = new DefaultConsumer(channel) {

            @Override

            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {

                String msg = new String(body, StandardCharsets.UTF_8);

                System.out.println("[direct 1] receive msg :"+msg);

 

                try {

                    Thread.sleep(1000);

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }finally {

                    System.out.println("[direct 1] done ");

                    channel.basicAck(envelope.getDeliveryTag(),false);

                }

            }

        };

        boolean autoAck=true;

        channel.basicConsume(QUEUE_NAME,autoAck,consumer);

    }

}

public class Receiver2 {

    private static final String QUEUE_NAME = "test_queue_direct2";

    private static final String EXCHANGE_NAME = "test_exchange_direct";

 

    public static void main(String[] args) throws IOException, TimeoutException {

        Connection connection = ConnectionUtil.getConnection();

        final Channel channel = connection.createChannel();

        channel.queueDeclare(QUEUE_NAME,false,false,false,null);

        channel.basicQos(1);

        channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"error");

        channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"info");

        channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"warning");

        DefaultConsumer consumer = new DefaultConsumer(channel) {

            @Override

            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {

                String msg = new String(body, StandardCharsets.UTF_8);

                System.out.println("[direct 2] receive msg :"+msg);

 

                try {

                    Thread.sleep(1000);

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }finally {

                    System.out.println("[direct 2] done ");

                    channel.basicAck(envelope.getDeliveryTag(),false);

                }

            }

        };

        boolean autoAck=true;

        channel.basicConsume(QUEUE_NAME,autoAck,consumer);

    }

}

 

因爲隊列根據routingKey來消費消息,耦合程度較高

e.Topic

模型

 

 

將路由鍵和某模式匹配

#     匹配一個或多個

*      匹配一個

Eg:Good.#

 

public class Sender {

    private static final String EXCHANGE_NAME = "test_exchange_topic";

 

    public static void main(String[] args) throws IOException, TimeoutException {

        Connection connection = ConnectionUtil.getConnection();

        Channel channel = connection.createChannel();

        channel.exchangeDeclare(EXCHANGE_NAME,"topic");

 

        String msg = "商品。。。。。";

//        channel.basicPublish(EXCHANGE_NAME,"goods.add",null,msg.getBytes());

        channel.basicPublish(EXCHANGE_NAME,"goods.delete",null,msg.getBytes());

        System.out.println("send msg :"+msg);

        channel.close();

        connection.close();

    }

}

public class Receiver1 {

    private static final String QUEUE_NAME = "test_queue_topic_1";

    private static final String EXCHANGE_NAME = "test_exchange_topic";

 

    public static void main(String[] args) throws IOException, TimeoutException {

        Connection connection = ConnectionUtil.getConnection();

        final Channel channel = connection.createChannel();

        channel.queueDeclare(QUEUE_NAME,false,false,false,null);

        channel.basicQos(1);

        channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"goods.add");

 

        DefaultConsumer consumer = new DefaultConsumer(channel) {

            @Override

            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {

                String msg = new String(body, StandardCharsets.UTF_8);

                System.out.println("[topic 1] receive msg :"+msg);

 

                try {

                    Thread.sleep(1000);

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }finally {

                    System.out.println("[topic 1] done ");

                    channel.basicAck(envelope.getDeliveryTag(),false);

                }

            }

        };

        boolean autoAck=false;

        channel.basicConsume(QUEUE_NAME,autoAck,consumer);

    }

}

public class Receiver2 {

    private static final String QUEUE_NAME = "test_queue_topic_2";

    private static final String EXCHANGE_NAME = "test_exchange_topic";

 

    public static void main(String[] args) throws IOException, TimeoutException {

        Connection connection = ConnectionUtil.getConnection();

        final Channel channel = connection.createChannel();

        channel.queueDeclare(QUEUE_NAME,false,false,false,null);

        channel.basicQos(1);

        channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"goods.#");

 

        DefaultConsumer consumer = new DefaultConsumer(channel) {

            @Override

            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {

                String msg = new String(body, StandardCharsets.UTF_8);

                System.out.println("[topic 2] receive msg :"+msg);

 

                try {

                    Thread.sleep(1000);

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }finally {

                    System.out.println("[topic 2] done ");

                    channel.basicAck(envelope.getDeliveryTag(),false);

                }

            }

        };

        boolean autoAck=false;

        channel.basicConsume(QUEUE_NAME,autoAck,consumer);

    }

}

 

 

 

RabbitMQ的消息確認機制(事務+confirm)

解決消息傳遞的安全性問題:

事務機制

特點:請求重複發送,降低服務器的吞吐量

TxSelect:用戶將當前channel設置成transaction模式

txCommit:用於提交事務

txRollback:回滾事務

 

public class Sender {

    private static final String QUEUE_NAME = "test_queue_tx";

 

    public static void main(String[] args) throws IOException, TimeoutException {

        Connection connection = ConnectionUtil.getConnection();

        Channel channel = connection.createChannel();

 

        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

 

        String msg = "hello tx message";

        try {

 

            channel.txSelect();

            channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());

//            int i = 1 / 0;

            channel.txCommit();

        } catch (Exception e) {

            channel.txRollback();

            e.printStackTrace();

        }

 

    }

}

public class Receicer {

    private static final String QUEUE_NAME = "test_queue_tx";

 

    public static void main(String[] args) throws IOException, TimeoutException {

        Connection connection = ConnectionUtil.getConnection();

        Channel channel = connection.createChannel();

 

        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

 

        channel.basicConsume(QUEUE_NAME, true, new DefaultConsumer(channel) {

            @Override

            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {

                System.out.println("tx receiver msg:"+new String(body, StandardCharsets.UTF_8));

            }

        });

    }

}

 

Confirm模式

生產者端Confirm模式的實現原理

生產者將信道設置成confirm模式,一旦信道進入confirm模式,所有在該信道上面發佈的消息都會被指派一個唯一的ID(從1開始),一旦消息被投遞到所匹配的隊列後,broker就發送一個確認給生產者(包含消息的唯一ID),使得生產者知道消息已經正確到達目的隊列,如果消息是可持久化的,那麼消息將會寫入磁盤後發出,broker回傳給生產者的確認消息中deliver-tag域包含確認消息的序列號,此外broker也可設置basic.ack的multiple域,表示到這個序列號之前的消息都已得到處理。

 

編程模式:

1.普通 發一條 waitForConfirms()

public class Sender {

    private static final String QUEUE_NAME = "test_queue_confirm1";

 

    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {

        Connection connection = ConnectionUtil. getConnection();

        Channel channel = connection.createChannel();

        channel.queueDeclare(QUEUE_NAME,false,false,false,null);

 

        channel.confirmSelect();

        String msg="hello confirm mode ";

        channel.basicPublish("", QUEUE_NAME,null,msg.getBytes());

 

        if (!channel.waitForConfirms()){

            System.out.println("message send failed");

        }else{

            System.out.println("message send successfully");

        }

        channel.close();

        connection.close();

    }

}

public class Receicer {

    private static final String QUEUE_NAME = "test_queue_confirm1";

 

    public static void main(String[] args) throws IOException, TimeoutException {

        Connection connection = ConnectionUtil.getConnection();

        Channel channel = connection.createChannel();

 

        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

 

        channel.basicConsume(QUEUE_NAME, true, new DefaultConsumer(channel) {

            @Override

            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {

                System.out.println("confirm receiver msg:"+new String(body, StandardCharsets.UTF_8));

            }

        });

    }

}

 

2.批量 waitForConfirms()

       批量發送,接收的方式還是一樣

3.異步Confirm模式:提供一個回調方法

       Channel對象提供的ConfirmListener()回調方法只包含deliveryTag(當前Channel發出的消息序號),需要自己爲每一個Channel維護一個unconfirm的消息序號集合,每publish一條數據,集合中元素加1,每次回調handleAck方法,unconfirm集合刪除相應的一條(multiple=false)或(multiple=true)記錄,從程序運行效率上看,unconfirm集合最好採用有序集合sortedSet存儲結構

總結

消息傳遞的方式:

  1. 匿名發送(不綁定交換機,直接使用隊列,單對單)
  2. Fanout(不處理路由鍵,使用交換機,生產者與消費者解耦,通過交換機向消費者分發消息)
  3. Direct(處理路由鍵)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章