消息中间件Rabbitmq(二)-使用详解

https://blog.csdn.net/Dante_003/article/details/79377908

Rabbitmq 是基于amqp(高级消息队列协议)实现的队列技术,在他之上可以完成多种类型的消息转发模型。 
下面列举一些常用的消息转发场景,在rabbitmq中是怎样实现的。

1.原理

先来看一下rabbitmq消息转发的原理,便于理解消息转发以及怎样实现常见的消息转发模型。 
这里写图片描述

1.1生产者

1.生产者创建连接,并创建通道连接rabbitmq 
2.使用routing key绑定exchange和队列,可以绑定多个routing key到不同的队列。 
3.生产者生产消息发送给exchange 
4.exchange根据routing key匹配到对应队列名字,把消息转发到指定的queue上,消息会暂存在队列中,等待消费者来消费。

1.2消费者

1.消费者创建连接,并创建通道连接rabbitmq 
2.消费者消费指定队列消息

注意:exchange、queue都必须要提前创建或者使用系统默认的也可以。

1.3exchange

exchange分为4种,分别是 
1.direct,直接转发。exchange通过精确匹配routing key发送消息给队列 
2.fanout,广播。会将消息广播到所有绑定到这个exchange的队列,无视发送消息时指定的routingkey。 
3.topic,发布、订阅。routing可以可以使用通配符(#、*)来根据主题发送消息到不同的队列。 
4.Headers,头信息。根据头信息的参数来决定发送到哪个队列。

1.4队列

队列是用来存储消息,在代码中可以创建临时队列,临时队列的属性是durable (false)、exclusive(true)、autoDelete(true)的队列,消息处理完自动删除。

队列的几种属性

  • durable 
    是否持久化,队列会一直存在。
  • exclusive 
    队列是否对当前连接特有,其它连接不能使用,当前连接断开后队列会消失。 
    exclusive和durable是互斥的。
  • autoDelete 
    队列不再使用时会自动删除。

2.入门例子,消息转发

最简单的例子,生产者生产消息到队列,消费者从队列取数据,这个例子也是消息列队最基本、最常用的模型。 
这里写图片描述

下面是建立连接的代码,后面的例子不再重复写这个。

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

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

public class RabbitmqUtil {
    private static Connection connection;

    // 获取连接
    public static Connection getConnection() {
        if (connection != null) {
            return connection;
        }
        String userName = "admin";
        String password = "admin";
        String host = "192.168.1.248";
        int port = 5672;
        boolean recoveryEnabled = true;
        // connection factory
        ConnectionFactory factory = new ConnectionFactory();
        factory.setUsername(userName);
        factory.setPassword(password);
        factory.setHost(host);
        factory.setPort(port);
        factory.setAutomaticRecoveryEnabled(recoveryEnabled);
        try {
            connection = factory.newConnection();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
        return connection;
    }

    // 获取通道
    public static Channel getChannel() {
        Channel channel = null;
        Connection connection2 = getConnection();
        try {
            channel = connection2.createChannel();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return channel;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

生产者、消费者

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

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;

//简单例子
public class Test1 {
    private String queueName = "testQueue";
    private String routingKey = "routingKey";
    private String exchange = "testExchange";

    public void produce() {
        Channel channel = null;
        try {
            // 创建通道
            channel = RabbitmqUtil.getChannel();
            // 创建队列,持久、非专用、非自动删除的队列
            channel.queueDeclare(queueName, true, false, false, null);
            // 创建一个exchange,使用rabbitmq内置默认exchange也可以,默认的exchange是""一个direct类型
            channel.exchangeDeclare(exchange, "direct");
            // 使用routing key绑定exchange和queue
            channel.queueBind(queueName, exchange, routingKey);
            for (;;) {
                channel.basicPublish(exchange, routingKey, null, "test".getBytes());
                System.out.println("生产者:test");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                channel.close();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (TimeoutException e) {
                e.printStackTrace();
            }
        }
    }

    public void consume() {
        Channel channel = null;
        try {
            channel = RabbitmqUtil.getConnection().createChannel();
            DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties,
                        byte[] body) throws IOException {
                    super.handleDelivery(consumerTag, envelope, properties, body);
                    String message = new String(body, "UTF-8");
                    System.out.println(" 接收消息:'" + message + "'");
                }

            };
            channel.basicConsume(queueName, true, defaultConsumer);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
        }
    }

    public static void main(String[] args) {
        final Test1 test1 = new Test1();
        new Thread() {
            @Override
            public void run() {
                test1.produce();
            }

        }.start();
        new Thread() {
            @Override
            public void run() {
                test1.consume();
            }

        }.start();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85

3.点对点通信

在rabbitmq中也可以实现点对点的通信,没有消息队列,所以不会存储消息,生产者和消费者之间直接通信。 
说是没有队列,其实是创建了一个临时队列,这个队列不会持久化、自动删除、通道专用。 
如下图。 
这里写图片描述 
在代码实现时,生产者只发送消息给exchnage,不绑定队列;消费者代码中创建临时队列,并绑定到exchange开始消费消息。

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

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;

//点对点通信
public class Test2 {
    private String routingKey = "routingKey";
    private String exchange = "testExchange";

    public void produce() {
        Channel channel = null;
        try {
            // 创建通道
            channel = RabbitmqUtil.getChannel();
            // 创建一个exchange,使用rabbitmq内置默认exchange也可以,默认的exchange是""一个direct类型
            channel.exchangeDeclare(exchange, "direct");
            for (;;) {
                channel.basicPublish(exchange, routingKey, null, "test".getBytes());
                System.out.println("生产者:test");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                channel.close();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (TimeoutException e) {
                e.printStackTrace();
            }
        }
    }

    public void consume() {
        Channel channel = null;
        try {
            channel = RabbitmqUtil.getConnection().createChannel();
            // 在消费者代码中创建临时队列,并绑定到指定的exchange
            String queue = channel.queueDeclare().getQueue();
            channel.queueBind(queue, exchange, routingKey);
            DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties,
                        byte[] body) throws IOException {
                    super.handleDelivery(consumerTag, envelope, properties, body);
                    String message = new String(body, "UTF-8");
                    System.out.println(" 接收消息:'" + message + "'");
                }

            };
            channel.basicConsume(queue, true, defaultConsumer);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
        }
    }

    public static void main(String[] args) {
        final Test2 test1 = new Test2();
        new Thread() {
            @Override
            public void run() {
                test1.produce();
            }

        }.start();
        new Thread() {
            @Override
            public void run() {
                test1.consume();
            }

        }.start();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83

4.消息复制

像下图这样,两个队列使用相同的routing key绑定到同一个exchange上,消息会复制分发到两个队列中。代码不再实现,和例子1一样,只不过多创建一个队列。

利用这种模型可以实现多种场景的例子,例如日志收集,一个队列需要采集所有类型的日志,一个队列只采集错误日志,这样通过rabbitmq,就真正实现了一个消息录入,多种消息模式的转发。 
这里写图片描述

5.广播模式

会将消息广播到所有绑定到这个exchange的队列,无视发送消息时指定的routingkey。

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

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;

//广播模式
public class Test3 {
    private String queueName = "testQueue";
    private String routingKey = "routingKey";
    private String exchange = "testExchange1";

    public void produce() {
        Channel channel = null;
        try {
            // 创建通道
            channel = RabbitmqUtil.getChannel();
            // 创建队列,持久、非专用、非自动删除的队列
            channel.queueDeclare(queueName, true, false, false, null);
            // 创建一个exchange,使用rabbitmq内置默认exchange也可以,默认的exchange是""一个direct类型
            channel.exchangeDeclare(exchange, "fanout");
            // 使用routing key绑定exchange和queue
            channel.queueBind(queueName, exchange, routingKey);
            for (;;) {
                //发送时的routingkey可以随意指定,所有绑定到这个exchange上的队列都会接收到消息
                channel.basicPublish(exchange, "无视routingkey", null, "test".getBytes());
                System.out.println("生产者:test");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                channel.close();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (TimeoutException e) {
                e.printStackTrace();
            }
        }
    }

    public void consume() {
        Channel channel = null;
        try {
            channel = RabbitmqUtil.getConnection().createChannel();
            DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties,
                        byte[] body) throws IOException {
                    super.handleDelivery(consumerTag, envelope, properties, body);
                    String message = new String(body, "UTF-8");
                    System.out.println(" 接收消息:'" + message + "'");
                }

            };
            channel.basicConsume(queueName, true, defaultConsumer);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
        }
    }

    public static void main(String[] args) {
        final Test3 test1 = new Test3();
        new Thread() {
            @Override
            public void run() {
                test1.produce();
            }

        }.start();
        new Thread() {
            @Override
            public void run() {
                test1.consume();
            }

        }.start();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86

6.发布、订阅

和发布订阅消息一样,可以使用通配符关键字订阅指定的消息。 
发送消息的routingkey和绑定exchange的routingkey可以是一组用”.”分开的词。词里面可以使用通配符 
“*”:表示任意一个关键词 
“#”:表示0个或者多个关键词 
注意:两个词以上的一定要用”.”号隔开,上面两个通配符只是通配关键词,并非单个字母,如”my*.my1”这样是错误的。 
下图是引用官网 
这里写图片描述

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

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;

//发布订阅模式
public class Test4 {
    private String queueName = "testQueue";
    private String queueName1 = "testQueue1";
    private String exchange = "testExchange2";

    public void produce() {
        Channel channel = null;
        try {
            // 创建通道
            channel = RabbitmqUtil.getChannel();
            // 创建两个队列接收订阅消息
            channel.queueDeclare(queueName, true, false, false, null);
            channel.queueDeclare(queueName1, true, false, false, null);
            // 创建一个exchange
            channel.exchangeDeclare(exchange, "topic");
            // 给两个队列指定对应的订阅内容
            channel.queueBind(queueName, exchange, "#.2.#");
            channel.queueBind(queueName1, exchange, "#.2.?");
            for (;;) {
                // 发送两个routingkey消息内容
                // 发送routingkey “my test1”,匹配两个队列
                channel.basicPublish(exchange, "2.11", null, "test1".getBytes());
                System.out.println("生产者:test1");
                // 发送routingkey “my test2”,去掉后面的11,两个队列都能接收到
                channel.basicPublish(exchange, "2.13.11", null, "test2".getBytes());
                System.out.println("生产者:test2");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                channel.close();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (TimeoutException e) {
                e.printStackTrace();
            }
        }
    }

    public void consume() {
        Channel channel = null;
        try {
            channel = RabbitmqUtil.getConnection().createChannel();
            DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties,
                        byte[] body) throws IOException {
                    super.handleDelivery(consumerTag, envelope, properties, body);
                    String message = new String(body, "UTF-8");
                    System.out.println(" 接收消息1:'" + message + "'");
                }

            };
            channel.basicConsume(queueName, true, defaultConsumer);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
        }
    }

    public void consume2() {
        Channel channel = null;
        try {
            channel = RabbitmqUtil.getConnection().createChannel();
            DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties,
                        byte[] body) throws IOException {
                    super.handleDelivery(consumerTag, envelope, properties, body);
                    String message = new String(body, "UTF-8");
                    System.out.println(" 接收消息2:'" + message + "'");
                }

            };
            channel.basicConsume(queueName1, true, defaultConsumer);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
        }
    }

    public static void main(String[] args) {
        final Test4 test1 = new Test4();
        new Thread() {
            @Override
            public void run() {
                test1.produce();
            }

        }.start();
        new Thread() {
            @Override
            public void run() {
                test1.consume();
            }

        }.start();
        new Thread() {
            @Override
            public void run() {
                test1.consume2();
            }

        }.start();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120

rabbitmq利用exchange和queue搭配的灵活性已经exchange的类型,可以完成业务场景中各种各样的需求。


猜你喜欢

转载自blog.csdn.net/xiaopihai86/article/details/79699093