springboot----11.springboot与消息

MQ

大多数应用中,可以通过消息服务中间件来提升系统异步通信和扩展解耦能力
消息服务中有两个重要概念:消息代理(message broker)和目的地(destination),当消息发送者发送消息以后,将由消息代理接管,消息代理保证消息传递到指定目的地
消息队列主要由两种形式的目的地:
队列(queue):点对点通信(point to point)
主题(topic):发布(publish)/订阅(subscribe)消息通信

  • 点对点式:消息发送者发送消息,消息代理将其放入一个队列中,消息接受者从队列中获取消息内容,消息读取后被移出队列
  • 发布订阅式:发送者(发布者)发送消息到主题,多个接收者(订阅者)监听(订阅)这个主题,那么就会在消息到达时同时收到消息
  • JMS(Java Message Service):Java消息服务,基于JVM消息代理的规范,AvtiveMQ,HornetMQ是JMS实现 - AMQP:高级消息队列协议,也是一个消息代理的规范,兼容JMS

RabbitMQ是AMQP的实现
spring-jms提供了对JMS的支持
SpringBoot只需要导入相应的场景启动器即可

rabbitmq

rabbitmq是一个有erlang开发的AMQP的开源实现。
核心概念:

  • message:消息,消息是不具名的,他由消息头和消息体组成,消息体是不透明的,而消息头则由一系列的可选属性组成,这些属性包括routing-key(路由键)、priority(优先级)、delivery-mode(指出该消息可能需要持久性存储)等
  • publisher:消息的生产者,也是一个向交换器发布消息的客户端应用程序
  • exchange:交换器,用来接收生产者发送的消息并将这些消息路由给服务器中的队列,exchange有四种类型:direct(默认)、fanout、topic和headers
  • queue:消息队列,用来保存消息直到发送给消费者,它是消息的容器,也是消息的终点,一个消息可投入一个或多个队列,消息一直在队列里面,等待消费者连接到这个队列将其取走
  • binding:绑定,用于消息队列和交换器之间的关联,一个绑定就是基于路由键将交换器和消息队列连接起来的路由规则,所以可以将交换器理解成一个由绑定构成的路由表,exchange和queue的绑定可以是多对多的关系
  • connection:网络连接,比如一个TCP连接
  • channel:信道,多路复用连接中的一条独立的双向数据流通道,信道是建立在真实的TCP连接内的虚拟连接,AMQP命令都是通过信道发出去的,不管是发布消息、订阅队列还是接收消息,这些动作都是通过信道完成,因为对于操作系统来说建立和销毁TCP都是非常昂贵的开销,所以引入了信道的概念,以复用一条TCP连接。
  • consumer:消息的消费者,表示一个从消息队列中取得消息的客户端应用程序
  • virtual host:虚拟主机,表示一批交换器、消息队列和相关对象,虚拟主机是共享相同的身份认证和加密环境的独立服务器域,每个vhost本质上就是一个mini版的rabbitmq服务器,拥有自己的队列、交换器、绑定和权限机制,vhost是AMQP概念的基础,必须在连接时指定,rabbitmq默认的vhost是/。
  • broker:表示消息队列服务器实体
    springboot----11.springboot与消息_第1张图片
    AMQP中的消息路由:AMQP中消息的路由过程和java开发者熟悉的JMS存在一些差别,AMQP中增加了exchange和binding的角色,生产者把消息发送到exchange上,消息最终到达队列并被消费者接收,而binding决定交换器的消息应该发送到哪个队列。
    springboot----11.springboot与消息_第2张图片
    Exchange类型:Exchange分发消息时根据类型的不同分发策略有区别,目前共四种类型:direct、fanout、topic、headers。headers匹配AMQP的header而不是路由键,headers交换器和direct交换器完全一致,但性能差很多,目前几乎用不到了。

Direct Exchange

消息中的路由键(routing key)如果和Binding中的binding key一致,交换器就将消息发送到对应的队列中,路由键和队列名完全匹配,是完全匹配、单播的模式。

Fanout Exchange

每个发到fanout类型交换器的消息都会分到所有绑定的队列上去,fanout交换器不处理路由键,只是简单的将队列绑到交换器上,每个发送到交换器的消息都会被转发到与该交换器绑定的所有队列上,类似子网广播,每台子网内的主机都获得了一份复制的消息,fanout类型交换器转发消息是最快的。

Topic Exchange

topic交换器通过模式匹配分配消息的路由键属性,将路由键和某个模式进行匹配,此时队列需要绑定到一个模式上,它将路由键和绑定键的字符串切分成单词,这些单词之间用点隔开,它同样也会识别两个通配符:#*#匹配0个或多个单词,*匹配一个单词。
准备rabbitmq环境:

docker pull rabbitmq:3.8-management #如果不用management版本的话可能会出现一些奇怪的错误,搜不到的那种哦!
docker run -d -p 5672:5672 -p 15672:15672 --name myrabbitmq01 镜像id

创建队列、交换机和绑定规则:
springboot----11.springboot与消息_第3张图片

在这里插入图片描述
springboot----11.springboot与消息_第4张图片
springboot----11.springboot与消息_第5张图片

springboot测试

修改消息转换器:

@Configuration
public class RmqConfig {
     

    @Bean
    public MessageConverter myMessageConverter(){
     
        return new Jackson2JsonMessageConverter(); //使用Jackson的消息转换器
    }
}

测试消息发送和接收:

@SpringBootTest
class Springboot02RabbitmqApplicationTests {
     

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    void send() {
     
        //rabbitTemplate.send(exchange,routingkey,messsage);
        Map<String,Object> map = new HashMap<>();
        map.put("msg","A message");
        map.put("list", Arrays.asList(1,2,3,4,5));
        rabbitTemplate.convertAndSend("exchange.direct","yxy",map);
        System.out.println("发送完毕!");
    }
    @Test
    void receive(){
     
        Object o = rabbitTemplate.receiveAndConvert("yxy");
        System.out.println("消息类型:"+o.getClass());
        System.out.println("消息内容:"+o);
    }

}

springboot----11.springboot与消息_第6张图片
在这里插入图片描述
可以发现序列化和反序列化都是成功的。(自定义的对象也可以,但是一定保证有无参构造方法和set方法,不然会报错);
@RabbitListener:标注在方法上,可以监听特定的消息队列,一旦队列中有消息,立刻接收(需要先开启注解模式,@EnableRabbit,标注在springboot启动类上)

@Service
public class UserService {
     

    /**
     * @RabbitListener:监听消息队列,有消息就接收,速度非常快,刚发完消息,立马就收到了
     * @param user 自动转化类型
     */
    @RabbitListener(queues = {
     "yxy"})
    public void receive(User user){
     
        System.out.println("收到消息:"+user);
    }

    /**
     * @param message 也可以用message来接收消息
     */
    @RabbitListener(queues = {
     "yxy.news"})
    public void receiveMessage(Message message){
     
        System.out.println("消息体:"+message.getBody());
        System.out.println("消息头:"+message.getMessageProperties());
    }
        /**
     * 操作交换机,队列,绑定规则等
     */
    @Test
    void create(){
     
        amqpAdmin.declareExchange(new DirectExchange("amqpadmin.direct"));
        amqpAdmin.declareQueue(new Queue("amqpadmin.queue"));
        amqpAdmin.declareBinding(new Binding(
                "amqpadmin.queue", Binding.DestinationType.QUEUE
                ,"amqpadmin.direct"
                ,"amqpadmin.direct"
                , null));
    }

}

你可能感兴趣的:(springboot)