RabbitMQ 基本操作

105 阅读2分钟

种类

  1. 基本消息队列(BasicQueue)

  2. 工作消息队列(WorkQueue)

  3. 发布订阅(Publish Subscribe)

    3.1 广播(Fanout Exchange)

    3.2 路由(Direct Exchage)

    3.3 主题(Topic Exchage)

SpringAMQP

AMQP (Advanced Message Queuing Protocol):在应用程序或之间传递消息的开放标准

SpringAMQP:基于AMQP的一套API规范,提供了模板来发送和接收消息

基本消息队列

只包含三个角色:

publisher: 消息发布者,将消息发布到queue

queue: 存储消息内容

consumer: 负责接受并缓存信息

publisher对应代码:

package cn.itcast.mq.helloworld;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import org.junit.Test;

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

public class PublisherTest {
    @Test
    public void testSendMessage() throws IOException, TimeoutException {
        // 1.建立连接
        ConnectionFactory factory = new ConnectionFactory();
        // 1.1.设置连接参数,分别是:主机名、端口号、vhost、用户名、密码
        factory.setHost("127.0.0.1");
        factory.setPort(5672);
        factory.setVirtualHost("/");
        factory.setUsername("guest");
        factory.setPassword("123");
        // 1.2.建立连接
        Connection connection = factory.newConnection();

        // 2.创建通道Channel
        Channel channel = connection.createChannel();

        // 3.创建队列
        String queueName = "simple.queue";
        channel.queueDeclare(queueName, false, false, false, null);

        // 4.发送消息
        String message = "hello, rabbitmq!";
        channel.basicPublish("", queueName, null, message.getBytes());
        System.out.println("发送消息成功:【" + message + "】");

        // 5.关闭通道和连接
        channel.close();
        connection.close();

    }
}

consumer对应代码:

package cn.itcast.mq.helloworld;

import com.rabbitmq.client.*;

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

public class ConsumerTest {

    public static void main(String[] args) throws IOException, TimeoutException {
        // 1.建立连接
        ConnectionFactory factory = new ConnectionFactory();
        // 1.1.设置连接参数,分别是:主机名、端口号、vhost、用户名、密码
        factory.setHost("127.0.0.1");
        factory.setPort(5672);
        factory.setVirtualHost("/");
        factory.setUsername("guest");
        factory.setPassword("123");
        // 1.2.建立连接
        Connection connection = factory.newConnection();

        // 2.创建通道Channel
        Channel channel = connection.createChannel();

        // 3.创建队列
        String queueName = "simple.queue";
        channel.queueDeclare(queueName, false, false, false, null);

        // 4.订阅消息
        channel.basicConsume(queueName, true, new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope,
                                       AMQP.BasicProperties properties, byte[] body) throws IOException {
                // 5.处理消息
                String message = new String(body);
                System.out.println("接收到消息:【" + message + "】");
            }
        });
        System.out.println("等待接收消息。。。。");
    }
}

改造基本消息队列

父工程导入AMQP依赖:

<!--AMQP依赖,包含RabbitMQ-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

在publisher和consumer中编写application.yml

spring:
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: guest
    password: 123
    virtual-host: /

image.png

publisher编写测试类:

@SpringBootTest
@RunWith(SpringRunner.class)
public class SpringAmqpTest {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void testSendMsg(){
        String name = "simple.queue";
        String massage = "hello,amqp!";
        rabbitTemplate.convertAndSend(name,massage);
    }
 }

consumer编写消费逻辑,监听simple.queue:

@Component
public class AmqpListener {

  @RabbitListener(queues = "simple.queue")
    public void listenerSimple(String msg){
        System.out.println(msg);
    }
 }

工作队列

publisher发送50条信息:

@Test
public void workQueue() throws InterruptedException {
    String name = "simple.queue";
    String massage = "hello,amqp!__";
    for (int i = 0; i < 50; i++) {
        rabbitTemplate.convertAndSend(name,massage+i);
        Thread.sleep(20);
    }
}

创建两个工作队列:

@RabbitListener(queues = "simple.queue")
public void listenerWorker1(String msg) throws InterruptedException {
    System.out.println("111111111:"+msg);
    Thread.sleep(20);
}

@RabbitListener(queues = "simple.queue")
public void listenerWorker2(String msg) throws InterruptedException {
    System.out.println("222222222:"+msg);
    Thread.sleep(200);
}

上述消费者将会平均分配消息队列内容,无论处理速度

若需要考虑消费队列处理能力,可设置消费预取限制

listener:
  simple:
    prefetch: 1

image.png

发布、订阅模式

与之前的区别:允许将一个消息发送给多个消费者,实现方式是加入了交换机

将队列1和队列2绑定到交换机

@Configuration
public class FanoutConfig {
    // fanout
    @Bean
    public FanoutExchange fanoutExchange(){
        return new FanoutExchange("xy.fanout");
    }
    // fanout.queue1
    @Bean
    public Queue fanoutQueue1(){
        return new Queue("fanout.queue1");
    }
    // 绑定队伍1到交换机
    @Bean
    public Binding fanoutBinding1(@Qualifier("fanoutQueue1") Queue fanoutBinding1, FanoutExchange fanoutExchange){
        return BindingBuilder.bind(fanoutBinding1).to(fanoutExchange);
    }


    // fanout.queue2
    @Bean
    public Queue fanoutQueue2(){
        return new Queue("fanout.queue2");
    }
    // 绑定队伍2到交换机
    @Bean
    public Binding fanoutBinding2(@Qualifier("fanoutQueue2")Queue fanoutQueue2, FanoutExchange fanoutExchange){
        return BindingBuilder.bind(fanoutQueue2).to(fanoutExchange);
    }

}

image.png

编写publisher:

@Test
public void sendExchange(){
    //交换机
    String name = "xy.fanout";
    //发送信息
    String msg = "hello,fanout";
    //发送
    rabbitTemplate.convertAndSend(name,"",msg);

}

创建两个consumer:

@RabbitListener(queues = "fanout.queue1")
public void listenerFanout1(String msg) throws InterruptedException {
    System.out.println("fanout.q1:"+msg);
    Thread.sleep(200);
}

@RabbitListener(queues = "fanout.queue2")
public void listenerFanout2(String msg) throws InterruptedException {
    System.out.println("fanout.q2:"+msg);
    Thread.sleep(200);
}

Dircet Exchange

接收到的消息将会根据规则到指定的queue

队列1将会监听关键字中带有red,blue的消息

队列2将会监听关键字中带有red,yellow的消息

@RabbitListener(bindings = @QueueBinding(
        value = @Queue(name = "direct.queue1"),
        exchange = @Exchange(name = "xy.direct",type = "direct"),
        key = {"red","blue"}
))
public void listenerDir1(String msg) throws InterruptedException {
    System.out.println("direc.q1:"+msg);
    Thread.sleep(200);
}

@RabbitListener(bindings = @QueueBinding(
        value = @Queue(name = "direct.queue2"),
        exchange = @Exchange(name = "xy.direct",type = "direct"),
        key = {"red","yellow"}
))
public void listenerDir2(String msg) throws InterruptedException {
    System.out.println("direc.q2:"+msg);
    Thread.sleep(200);
}

发送关键词为red的消息,此时队列1和队列2都可以收到

@Test
public void sendDirExchange(){
    //交换机
    String name = "xy.direct";
    //发送信息
    String msg = "hello,fanout";
    //发生
    rabbitTemplate.convertAndSend(name,"red",msg);

}

Topic Exchange

与DirectExchage类似,区别在于routingKey必须是多个单词的列表,并且以“.”分割

通配符:

#: 代表0或多个单词

*: 代指一个单词

队列1只会接受以china开头的消息,队列2只会接受以news结尾的消息

@RabbitListener(bindings = @QueueBinding(
        value = @Queue("topic.q1"),
        exchange = @Exchange(name="xy.topic",type = ExchangeTypes.TOPIC),
        key="china.#"
))
public void listenerTop1(String msg) throws InterruptedException {
    System.out.println("fanout.top1:"+msg);
    Thread.sleep(200);
}

@RabbitListener(bindings = @QueueBinding(
        value = @Queue("topic.q2"),
        exchange = @Exchange(name="xy.topic",type = ExchangeTypes.TOPIC),
        key="#.news"
))
public void listenerTop2(String msg) throws InterruptedException {
    System.out.println("fanout.top2:"+msg);
    Thread.sleep(200);
}

发送关键词为china.news,此时队列1和队列2都会收到

@Test
public void sendTopicExchange(){
    //交换机
    String name = "xy.topic";
    //发送信息
    String msg = "这是消息内容";
    //发生
    rabbitTemplate.convertAndSend(name,"china.news",msg);

}

消息转换器

测试发送object类型的消息

config中申明队列:

@Bean
public Queue objectQueue(){
    return new Queue("object.queue");
}

consumer:

@RabbitListener(queues = "object.queue")
public void listenerObject(Map msg) throws InterruptedException {
    System.out.println(+msg);
    Thread.sleep(200);
}

publisher:

@Test
public void objectQueue(){
    HashMap<Object, Object> map = new HashMap<>();
    map.put("name","xy");
    map.put("age",22);
    rabbitTemplate.convertAndSend("object.queue",map);
}

默认对象序列化是基于JDK的ObjectOutputStream完成,可修改为JSON方式序列化

在publisher中引入依赖:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>

publisher启动类声明转换器

@Bean
public MessageConverter messageConverter(){
    return new Jackson2JsonMessageConverter();
}

image.png