rabbit-基礎請求模式

基礎請求模式簡圖:

服務A 發送消息給rabbit的隊列,服務B監聽這個隊列,發現隊列有消息了就獲取消息。

代碼:

1、maven依賴

<dependency>
            <groupId>com.rabbitmq</groupId>
            <artifactId>amqp-client</artifactId>
            <version>4.0.2</version>
        </dependency>

2、開發代碼

import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

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

/**
 * 工具類
 * Created by py
 * 2020/4/19
 */
public class RabbitUtils {

    public static Connection getInstance() throws IOException, TimeoutException {
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("127.0.0.1");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("admin_py");
        connectionFactory.setPassword("123456");
        connectionFactory.setVirtualHost("/adminPy");
        Connection connection = connectionFactory.newConnection();
        return connection;
    }
}
import com.example.springcloud.eurekaclinet1demo.uitl.RabbitUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

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

/**
 * 消息生產者
 * Created by py
 * 2020/4/19
 */
public class SendRabbit {

    public static void main(String[] args) throws IOException, TimeoutException {
        /** 獲取一個連接 */
        Connection instance = RabbitUtils.getInstance();
        /** 從連接中創建通道 */
        Channel channel = instance.createChannel();
        /** 在通道中聲明一個隊列
         * 因爲我們要往隊列裏面發送消息,這是後就得知道往哪個隊列中發送,就好比在哪個管子裏面放水
         * */
        boolean durable=false;//數據是否持久化:即rabbit服務關閉了,rabbit中的信息是否會保存
        boolean exclusive=false;//信息只允許一個交換機處理,還是允許多個交換機處理
        boolean autoDelete=false;//隊列中信息處理完是否自動刪除該隊列
        //simple_queue:隊列名稱
        channel.queueDeclare("simple_queue",durable,exclusive,autoDelete,null);
        String msg = "This is simple queque!";
        //將消息發送到隊列simple_queue中
        channel.basicPublish("","simple_queue",null,msg.getBytes());
        channel.close();
        instance.close();
        System.out.println("信息發送成功");
    }




}
import com.example.springcloud.eurekaclinet1demo.uitl.RabbitUtils;
import com.rabbitmq.client.*;

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

/**
 * 消息消費者
 * Created by py
 * 2020/4/19
 */
public class RecRabbit {


    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        Connection instance = RabbitUtils.getInstance();
        Channel channel = instance.createChannel();
        //聲明隊列 如果能確定是哪一個隊列 這邊可以刪掉,不去掉 這裏會忽略創建
        channel.queueDeclare("simple_queue",false,false,false,null);
        //定義消費者用來處理收到的消息
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                super.handleDelivery(consumerTag, envelope, properties, body);
                //將消息轉成字符串
                String message = new String(body, "UTF-8");
                System.err.println("new==Received '" + message + "'");
            }
        };
        //simple_queue:隊列名稱
        //true:自動確認;消費者直接告訴rabbit消息以處理,此時rabbit會將該條信息從simple_queue隊列中刪除
        //defaultConsumer:用於具體處理接受消息的方法
        channel.basicConsume("simple_queue",true,defaultConsumer);
    }
}

QA:當客戶端請求量增大之後一臺服務器難以支持大量的數據請求,那這個時候我們需要對服務進行集羣操作,那rabbit如何將消息發給集羣的服務呢?

/**
 * 生產者
 * 輪詢策略 Round-robin 將消息平均的分發給每一個消費者
 * Created by py
 * 2020/4/19
 */
public class SendRabbit {

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection instance = RabbitUtils.getInstance();
        Channel channel = instance.createChannel();
        channel.queueDeclare("work_queue",false,false,false,null);
        for (int i = 0; i < 50; i++) {
            String msg = "This is work queue!==="+i;
            channel.basicPublish("","work_queue",null,msg.getBytes());
        }
        channel.close();
        instance.close();
    }
}
/**
 * 消費者1
 * Created by py
 * 2020/4/19
 */
public class FristConsumer {

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection instance = RabbitUtils.getInstance();
        Channel channel = instance.createChannel();
        channel.queueDeclare("work_queue",false,false,false,null);
        DefaultConsumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                super.handleDelivery(consumerTag, envelope, properties, body);
                String str = new String(body,"utf-8");
                System.out.println("FristConsumer==="+str);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        boolean autoAck = true; //消息的確認模式自動應答
        channel.basicConsume("work_queue",autoAck,consumer);
    }
}

import com.example.springcloud.eurekaclinet1demo.uitl.RabbitUtils;
import com.rabbitmq.client.*;

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

/**
 * 消費者2
 * Created by py
 * 2020/4/19
 */
public class SecondConsumer {

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection instance = RabbitUtils.getInstance();
        Channel channel = instance.createChannel();
        channel.queueDeclare("work_queue",false,false,false,null);
        DefaultConsumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                super.handleDelivery(consumerTag, envelope, properties, body);
                String str = new String(body,"utf-8");
                System.out.println("SecondConsumer==="+str);
                try {
                    Thread.sleep(2500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        boolean autoAck = true; //消息的確認模式自動應答
        channel.basicConsume("work_queue",autoAck,consumer);
    }
}

這樣可以實現消息輪詢的發送給兩個服務器,但是有一個問題,SecondConsumer 執行一個請求sleep2.5S,FristConsumer 執行一個請求sleep0.5S,將請求平均的發送給兩臺服務器的話會導致FristConsumer 有很長的一段空閒時間,SecondConsumer的接收的那部分請求要很久才能完成。

QA:如何讓生產端的數據儘可能快的執行完呢?

放棄輪詢發送,根據消費端的消費情況來推送消息,即哪個服務器執行完成了就給他下一條消息,讓FristConsumer 會多執行部分請求,SecondConsumer少執行部分請求,這樣就能儘可能快的消費完生產者創建的消息了。

/**
 * 生產者
 * Fair dispatch(公平分發) 處理完當前信息在分發下一個信息
 * Created by py
 * 2020/4/19
 */
public class SendRabbit {
    final static String queue = "work_queue_durable";
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection instance = RabbitUtils.getInstance();
        Channel channel = instance.createChannel();
        //設置rabbit信息持久化,爲true時rabbit會將消息寫入磁盤中
        boolean durable = true;
        //PS:rabbit不支持修改已聲明的隊列,
        //若一個隊列聲明的時候爲設置持久化,聲明後在設置持久化就會報錯,
        //想要操作要麼把隊列刪除重新聲明該隊列,要命聲明一個新隊列設置持久化
        channel.queueDeclare(queue,durable,false,false,null);

        int prefetchCount = 1;
        //每個消費者發送確認信號之前,消息隊列不發送下一個消息過來,一次只處理一個消息
        //限制發給同一個消費者不得超過1條消息
        channel.basicQos(prefetchCount);
        // 發送的消息
        for (int i = 0; i < 50; i++) {
            String msg = "This is work queue!==="+i;
            channel.basicPublish("","work_queue",null,msg.getBytes());
        }
        channel.close();
        instance.close();
    }
}
/**
 * 消費者1
 * Created by py
 * 2020/4/19
 */
public class FristConsumer {
    final static String queue = "work_queue_durable";
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection instance = RabbitUtils.getInstance();
        Channel channel = instance.createChannel();
        //設置rabbit信息持久化,爲true時rabbit會將消息寫入磁盤中
        boolean durable = true;
        //PS:rabbit不支持修改已聲明的隊列,
        //若一個隊列聲明的時候爲設置持久化,聲明後在設置持久化就會報錯,
        //想要操作要麼把隊列刪除重新聲明該隊列,要命聲明一個新隊列設置持久化
        channel.queueDeclare(queue,durable,false,false,null);
        //每個消費者發送確認信號之前,消息隊列不發送下一個消息過來,一次只處理一個消息
        //限制發給同一個消費者不得超過1條消息
        channel.basicQos(1);

        DefaultConsumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                super.handleDelivery(consumerTag, envelope, properties, body);
                String str = new String(body,"utf-8");
                System.out.println("FristConsumer==="+str);
                try {
                    Thread.sleep(1500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    //手動回覆rabbit消息已處理
                    channel.basicAck(envelope.getDeliveryTag(),false);
                }
            }
        };
        //消息的確認模式:禁止自動應答
        boolean autoAck = false;
        channel.basicConsume("work_queue",autoAck,consumer);
    }
}
import com.example.springcloud.eurekaclinet1demo.uitl.RabbitUtils;
import com.rabbitmq.client.*;

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

/**
 * 消費者2
 * Created by py
 * 2020/4/19
 */
public class SecondConsumer {
    final static String queue = "work_queue_durable";
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection instance = RabbitUtils.getInstance();
        Channel channel = instance.createChannel();
        //設置rabbit信息持久化,爲true時rabbit會將消息寫入磁盤中
        boolean durable = true;
        //PS:rabbit不支持修改已聲明的隊列,
        //若一個隊列聲明的時候爲設置持久化,聲明後在設置持久化就會報錯,
        //想要操作要麼把隊列刪除重新聲明該隊列,要命聲明一個新隊列設置持久化
        channel.queueDeclare(queue,durable,false,false,null);
        //每個消費者發送確認信號之前,消息隊列不發送下一個消息過來,一次只處理一個消息
        //限制發給同一個消費者不得超過1條消息
        channel.basicQos(1);
        DefaultConsumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                super.handleDelivery(consumerTag, envelope, properties, body);
                String str = new String(body,"utf-8");
                System.out.println("SecondConsumer==="+str);
                try {
                    Thread.sleep(2500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    //手動回覆rabbit消息已處理
                    channel.basicAck(envelope.getDeliveryTag(),false);
                }
            }
        };
        //消息的確認模式:禁止自動應答
        boolean autoAck = false;
        channel.basicConsume("work_queue",autoAck,consumer);
    }
}

PS:此處添加了一些新的參數,以實現我們的需求

每次只發送一條數據,等對方告知數據完成消費在發送下一條數據
channel.basicQos(1);
//prefetchSize:數據的大小;不寫是不限制數據大小
//prefetchCount:設置一次發送幾條數據
//global:默認false
public void basicQos(int prefetchSize, int prefetchCount, boolean global)
//手動回覆rabbit消息已處理
channel.basicAck(envelope.getDeliveryTag(),false);
//deliveryTag:當前這條消息的標識,
//multiple:true:多條消息,false:1條消息;正常設置爲false
public void basicAck(long deliveryTag, boolean multiple);
//消息的確認模式:禁止自動回覆rabbit,這裏設置爲false配合basicAck()一起使用
boolean autoAck = false;
channel.basicConsume("work_queue",autoAck,consumer);

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