RabbitMQ基本使用看这篇就够了

1.简单介绍

MQ全称(Message Queue)又名消息队列,是一种异步通讯的中间件。可以将它理解成邮局,发送者将消息传递到邮局,然后由邮局帮我们发送给具体的消息接收者(消费者),具体发送过程与时间我们无需关心,它也不会干扰我进行其它事情。常见的MQkafkaactivemqzeromqrabbitmq 等等。
RabbitMQ是一个遵循AMQP协议,由面向高并发的erlanng语言开发而成,用在实时的对可靠性要求比较高的消息传递上,支持多种语言客户端。

基础概念

  • Broker:简单来说就是消息队列服务器实体
  • Exchange:消息交换机,它指定消息按什么规则,路由到哪个队列
  • Queue:消息队列载体,每个消息都会被投入到一个或多个队列
  • Binding:绑定,它的作用就是把exchangequeue按照路由规则绑定起来
  • Routing Key:路由关键字,exchange根据这个关键字进行消息投递
  • vhost:虚拟主机,一个broker里可以开设多个vhost,用作不同用户的权限分离
  • producer:消息生产者,就是投递消息的程序
  • consumer:消息消费者,就是接受消息的程序
  • channel:消息通道,在客户端的每个连接里,可建立多个channel,每个channel代表一个会话任务

常见引用场景

  • 邮箱发送:用户注册后投递消息到rabbitmq中,由消息的消费方异步的发送邮件,提升系统响应速度
  • 流量削峰:一般在秒杀活动中应用广泛,秒杀会因为流量过大,导致应用挂掉,为了解决这个问题,一般在应用前端加入消息队列。用于控制活动人数,将超过此一定阀值的订单直接丢弃。缓解短时间的高流量压垮应用。
  • 订单超时:利用rabbitmq的延迟队列,可以很简单的实现订单超时的功能,比如用户在下单后30分钟未支付取消订单

2.准备RabbitMQ环境

由于RabbitMQ安装麻烦,这里测试以docker环境为例。首先Linux安装docker,不会的可以参考docker安装/ docker常用命令

然后下载并启动RabbitMQ

docker run -d --hostname my-rabbit --name pikachues-rabbit -p 5672:5672 -p15672:15672 rabbitmq:3-management

3.测试

RabbitMQ有四种消息交换机。分别为direct(默认),fanout, topic, 和headers,不同类型的交换机转发消息的策略有所区别。下面用springboot测试四种消息交换机。

0.准备测试环境

依赖
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
配置文件
spring.rabbitmq.host=rabbitmq安装的ip地址
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.port=5672

1.direct

直接交换器,工作方式类似于单播,Exchange会将消息发送完全匹配ROUTING_KEY的Queue。

direct配置
@Configuration
public class RabbitDirectConfig {
    public final static String DIRECTNAME = "pikachues-direct";
    @Bean
    Queue queue() {
        return new Queue("hello.pikachues");
    }
    @Bean
    DirectExchange directExchange(){
        return new DirectExchange(DIRECTNAME,true,false);
    }

    @Bean
    Binding binding(){
        return BindingBuilder.bind(queue()).to(directExchange()).with("direct");
    }
}
消息接收
@Component
public class DirectReceiver {
    @RabbitListener(queues = "hello.pikachues")
    public void handler1(String msg){
        System.out.println(">>>handler"+msg);
    }
}
测试
@RunWith(SpringRunner.class)
@SpringBootTest
public class RabbitmqDemoApplicationTests {

	@Autowired
	RabbitTemplate rabbitTemplate;

	@Test
	public void direct() {
		rabbitTemplate.convertAndSend("hello.pikachues","hello pikachues");
	}
}

2.fanout

广播是式交换器,不管消息的ROUTING_KEY设置为什么,Exchange都会将消息转发给所有绑定的Queue

fanout配置
@Configuration
public class RabbitFanoutConfig {
    public static final String FANOUTNAME = "pikachues-fanout";

    @Bean
    Queue queueOne(){
        return new Queue("queue-one");
    }

    @Bean
    Queue queueTwo(){
        return new Queue("queue-two");
    }

    @Bean
    FanoutExchange fanoutExchange(){
        return new FanoutExchange(FANOUTNAME,true,false);
    }

    @Bean
    Binding bindingOne(){
        return BindingBuilder.bind(queueOne()).to(fanoutExchange());
    }
    @Bean
    Binding bindingTwo(){
        return BindingBuilder.bind(queueTwo()).to(fanoutExchange());
    }
}
消息接收
@Component
public class FanoutReceiver {

    @RabbitListener(queues = "queue-one")
    public void handler1(String msg){
        System.out.println("FanoutReceiver:handler1"+msg);
    }

    @RabbitListener(queues = "queue-two")
    public void handler2(String msg){
        System.out.println("FanoutReceiver:handler2"+msg);
    }
}
测试
@Test
	public void testFanout(){
		rabbitTemplate.convertAndSend(RabbitFanoutConfig.FANOUTNAME,null,"hello Fanout");
	}

3.topic

主题交换器,工作方式类似于组播,Exchange会将消息转发和ROUTING_KEY匹配模式相同的所有队列,比如,ROUTING_KEYuser.stockMessage会转发给绑定匹配模式为 * .stock,user.stock, * . * 和#.user.stock.#的队列。( * 表是匹配一个任意词组,#表示匹配0个或多个词组)

topic配置
@Configuration
public class RabbitTopicConfig {

    public static final String TOPICNAME = "pikachues-topic";

    @Bean
    TopicExchange topicExchange(){
        return new TopicExchange(TOPICNAME,true,false);
    }

    @Bean
    Queue xiaomi(){
        return new Queue("xiaomi");
    }

    @Bean
    Queue huaiwei(){
        return new Queue("huawei");
    }

    @Bean
    Queue phone(){
        return new Queue("phone");
    }

    @Bean
    Binding xiaomiBinding(){
        return BindingBuilder.bind(xiaomi()).to(topicExchange()).with("xiaomi.#");
    }

    @Bean
    Binding huaiweiBinding(){
        return BindingBuilder.bind(huaiwei()).to(topicExchange()).with("huawei.#");
    }
    @Bean
    Binding phoneBinding(){
        return BindingBuilder.bind(phone()).to(topicExchange()).with("#.phone.#");
    }

}
消息接收
@Component
public class TopicReceiver {
    @RabbitListener(queues = "xiaomi")
    public void handler1(String msg) {
        System.out.println("TopicReceiver:handler1:" + msg);
    }

    @RabbitListener(queues = "huawei")
    public void handler2(String msg) {
        System.out.println("TopicReceiver:handler2:" + msg);
    }

    @RabbitListener(queues = "phone")
    public void handler3(String msg) {
        System.out.println("TopicReceiver:handler3:" + msg);
    }
}
测试
@Test
	public void testTopic(){
		rabbitTemplate.convertAndSend(RabbitTopicConfig.TOPICNAME,"xiaomi.news","小米新闻");
		rabbitTemplate.convertAndSend(RabbitTopicConfig.TOPICNAME,"vivo.phone","小米手机");
		rabbitTemplate.convertAndSend(RabbitTopicConfig.TOPICNAME,"huawei.phone","华为手机");
	}

4.headers

消息体的header匹配(ignore)

headers配置
@Configuration
public class RabbitHeaderConfig {

    public static final String HEADERNAME = "pikachues-header";

    @Bean
    HeadersExchange headersExchange(){
        return new HeadersExchange(HEADERNAME, true,false);
    }

    @Bean
    Queue queueName(){
        return new Queue("name-queue");
    }
    @Bean
    Queue queueAge(){
        return new Queue("age-queue");
    }
    @Bean
    Binding bindingName(){
        Map<String,Object> map = new HashMap<>();
        map.put("name","pikachues");
        return BindingBuilder.bind(queueName()).to(headersExchange()).whereAny(map).match();
    }

    @Bean
    Binding bindingAge(){
        return BindingBuilder.bind(queueAge()).to(headersExchange()).where("age").exists();
    }
}
消息接收
@Component
public class HeaderReceiver {

    @RabbitListener(queues = "name-queue")
    public void handler1(byte[] msg) {
        System.out.println("HeaderReceiver:handler1:" + new String(msg, 0, msg.length));
    }

    @RabbitListener(queues = "age-queue")
    public void handler2(byte[] msg) {
        System.out.println("HeaderReceiver:handler2:" + new String(msg, 0, msg.length));
    }
}
测试
@Test
	public void testHeaders(){
		Message nameMsg = MessageBuilder.withBody("hello pikachues".getBytes()).setHeader("name","pikachues").build();
		Message ageMsg = MessageBuilder.withBody("hello 99".getBytes()).setHeader("age","99").build();
		rabbitTemplate.convertAndSend(RabbitHeaderConfig.HEADERNAME,null,nameMsg);
		rabbitTemplate.convertAndSend(RabbitHeaderConfig.HEADERNAME,null,ageMsg);
	}

4.其他

如果想了解RabbitMQ的基本原理请看这篇文章或者springboot官网介绍

猜你喜欢

转载自blog.csdn.net/qq_41262903/article/details/106131293