springboot踩雷系列之RabbitMQ(进阶版)

进阶版是在基础版的基础上进行了一些扩展,比如一对多、多对多、格式匹配、回调等等

一对多:

测试类:

/**
     * 单生产者-多消费者
     */
    @RequestMapping("/oneToMany")
    public void oneToMany() {
        for(int i=0;i<10;i++){
            helloSender1.send("oneToMany:"+i);
        }
    }

生产者不变 增加消费者:

import com.example.springboot.entity.User;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

/**
 * 消费者二号
 * <p>
 * @Date 2019/5/23 15:58
 **/
@Component
@RabbitListener(queues = {"testQueue"})
public class ReceiverController2 {

    @RabbitHandler
    public void process(String msg) {
        System.out.println("消费者二号 : " + msg);
    }
}
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

/**
 * 消费者三号
 * <p>
 *
 * @Author
 * @Date 2019/5/23 15:58
 **/
@Component
@RabbitListener(queues = {"testQueue"})
public class ReceiverController3 {

    @RabbitHandler
    public void process(String msg) {
        System.out.println("消费者三号 : " + msg);
    }
}

执行结果:

如此可见,一对多时,消费者随机接收消息

多对多:

测试类

 /**
     * 多生产者-多消费者
     */
    @RequestMapping("/manyToMany")
    public void manyToMany() {
        for(int i=0;i<10;i++){
            helloSender1.send("manyToMany:"+i);
            helloSender2.send("manyToMany:"+i);
        }
    }

增加生产者:

import com.example.springboot.entity.User;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

/**
 * 生产者二号
 * <p>
 * @Date 2019/5/23 15:56
 **/
@Component
public class SendController2 {

    @Autowired
    private AmqpTemplate template;

    public void send(String msg) {
        String context = msg+ LocalDateTime.now();
        System.out.println("生产者二号 : " + context);
        //参数1为队列名称 2为消息内容
        this.template.convertAndSend("testQueue", context);
    }
}

访问结果:

由此可见 并不是所有消息发送完毕之后消费才开始消费

topic  exchange发送(根据定义的格式 选择对应的消费者):

测试类:

/**
     * 参数二比参数一多了一个s  用以区分topic的传输结果
     */
    @RequestMapping("/topicTest")
public void topicTest() {
helloSender1.send( "hi, i am message all","hi, i am message 1","hi, i am messages 2");
}
 

生产者(请注意两个队列的名字不同,"topic.1","topic.only","topic.messages"是关键字):

//数组传输使用  topic exchange
    public void send(String... msgs) {
System.out.println("消息1: " + msgs[0]);
template.convertAndSend("exchange", "topic.1", msgs[0]);
System.out.println("消息2: " + msgs[1]);
template.convertAndSend("exchange", "topic.only", msgs[1]);
System.out.println("消息3: " + msgs[2]);
template.convertAndSend("exchange", "topic.messages", msgs[2]);
 }

消费1:

import com.example.springboot.entity.User;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

/**
 * 消费者一号
 * <p>
 * @Date 2019/5/23 15:58
 **/
@Component
@RabbitListener(queues = {"testQueue","topic.message","fanout.A"})
public class ReceiverController {

    @RabbitHandler
    public void process(String msg) {
        System.out.println("消费者一号 : " + msg);
    }
}

消费者2:

import com.example.springboot.entity.User;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

/**
 * 消费者二号
 * <p>
 *
 * @Author
 * @Date 2019/5/23 15:58
 **/
@Component
@RabbitListener(queues = {"testQueue","topic.messages","fanout.B"})
public class ReceiverController2 {

    @RabbitHandler
    public void process(String msg) {
        System.out.println("消费者二号 : " + msg);
    }
}

增加队列:

  @Bean
    public Queue messageQueue() {
        return new Queue("topic.message");
    }

    @Bean
    public Queue messagesQueue() {
        return new Queue("topic.messages");
    }

    /**
     *下方分别将队列topic.message  topic.messages与exchange绑定并设定此队列可以接受到的消息队列格式
     * 此处Queue的名称必须与上方的初始化队列的方法名一致  根据方法名对应具体的队列
     * 关键字不可随意设置 必须由点隔开
     * 网上有人说*代表一个 #代表多个 经过本人测试*和#的作用是一样的
     *<p>
     */
    @Bean
    Binding bindingExchangeMessage(Queue messageQueue, TopicExchange exchange) {
        return BindingBuilder.bind(messageQueue).to(exchange).with("topic.*");
    }

    @Bean
    Binding bindingExchangeMessages(Queue messagesQueue, TopicExchange exchange) {
        return BindingBuilder.bind(messagesQueue).to(exchange).with("topic.only");
    }

访问结果:

fanout fanoutExchange(所有fanout.开头的队列都可以接受消息)
测试类:
 /**
     * 测试是否所有fanout.开头的队列都可以接受消息  消费者三号未实现fanout.开头的队列 测试是否可以接受到消息
     */
    @RequestMapping("/fanoutTest")
    public void fanoutTest() {
        helloSender1.sendFanout("fanout message");
    }

消费者1:

import com.example.springboot.entity.User;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

/**
 * 消费者一号
 * <p>
 * @Date 2019/5/23 15:58
 **/
@Component
@RabbitListener(queues = {"testQueue","topic.message","fanout.A"})
public class ReceiverController {

    @RabbitHandler
    public void process(String msg) {
        System.out.println("消费者一号 : " + msg);
    }
}

消费者2:

import com.example.springboot.entity.User;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

/**
 * 消费者二号
 * <p>
 * @Date 2019/5/23 15:58
 **/
@Component
@RabbitListener(queues = {"testQueue","topic.messages","fanout.B"})
public class ReceiverController2 {

    @RabbitHandler
    public void process(String msg) {
        System.out.println("消费者二号: " + msg);
    }
}

生产者:

//fanout exchange使用  routingKey些什么都可以
    public void sendFanout (String msg) {
        System.out.println("生产者一号 : " + msg);
        template.convertAndSend("fanoutExchange", "nonentityQueue",msg);
    }

访问结果:

回调消息
测试类
/*
     *回调消息
     */
    @RequestMapping("/confimCallBacK")
    public void confimCallBacK(){
        String msg ="生产者:发送回调消息";
        SendCallBack.sendConfirm(msg);
    }

生产者:

import com.example.springboot.entity.User;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import tk.mybatis.mapper.util.StringUtil;

import java.time.LocalDateTime;
import java.util.UUID;

/**
 * 生产者
 * <p>
 *
 * @Author
 * @Date 2019/5/23 15:56
 **/
@Component
public class SendCallBackController implements RabbitTemplate.ConfirmCallback{
    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void sendConfirm(String msg) {
        rabbitTemplate.setConfirmCallback(this);
        CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
        System.out.println("回调消息主键: " + correlationData.getId());
        rabbitTemplate.convertAndSend("exchange", "callbackQueue", msg, correlationData);
    }

    //回调函数 实现RabbitTemplate.ConfirmCallback接口中的方法
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        System.out.println("执行回调: " + correlationData.getId());
    }
}

增加配置类:

import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
/**
 * RabbitMQ的回调实现
 * <p>
 * @Date 2019/5/24 15:11
 **/
public class RabbitCallBack {
    //@Value 获取配置文件的对应的值  此处设置是为了设定回调地址
    @Value("${spring.rabbitmq.host}")
    private String addresses;

    @Value("${spring.rabbitmq.port}")
    private String port;

    @Value("${spring.rabbitmq.username}")
    private String username;

    @Value("${spring.rabbitmq.password}")
    private String password;

    @Value("${spring.rabbitmq.virtual-host}")
    private String virtualHost;

    @Value("${spring.rabbitmq.publisher-confirms}")
    private boolean publisherConfirms;

    @Bean
    /*
     *可设置为prototype singleton也就是不同的作用域
     * singleton类似于单例模式 返回的都是一个实例 spring容器只会保存一个Bean    线程之间资源 完全共享
     * prototype每一次请求都会生成一个新的bean  多线程情况下必须一个线程对应一个独立的实例
     * 无状态的可以使用singleton  有状态的使用prototype
     *<p>
     */
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public RabbitTemplate getRabbitTemplate() {
        RabbitTemplate template = new RabbitTemplate(getConnectionFactory());
        return template;
    }

    @Bean
    public ConnectionFactory getConnectionFactory() {
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
        connectionFactory.setAddresses(addresses+":"+port);
        connectionFactory.setUsername(username);
        connectionFactory.setPassword(password);
        connectionFactory.setVirtualHost(virtualHost);
        connectionFactory.setPublisherConfirms(publisherConfirms);
        return connectionFactory;
    }

增加配置:

访问结果:

本文参考了很多博客之类的,因为看完了之后没保存,导致找不到了。。。

另附上源代码一份:

https://github.com/duxianshenga/demo1.2

猜你喜欢

转载自www.cnblogs.com/huayuxiaoxiang/p/10919273.html