品牌网站建设要多少钱,手机制作app需要什么软件,网页微信手机版,免费域名注册发卡网RabbitMQ学习 RabbitMQ相关概念Virtual host数据隔离SpringAMQP第一种 基本消息模型第二种 WorkQueues模型第三种 发布订阅模型#xff08;fanout交换机#xff09;fanout交换机实例 第四种 Direct交换机direct交换机实例基于注解的方式声明——direct交换机 第五种Topic交换… RabbitMQ学习 RabbitMQ相关概念Virtual host数据隔离SpringAMQP第一种 基本消息模型第二种 WorkQueues模型第三种 发布订阅模型fanout交换机fanout交换机实例 第四种 Direct交换机direct交换机实例基于注解的方式声明——direct交换机 第五种Topic交换机基于注解的方式声明——Topic交换机 消息转换器配置JSON转换器 RabbitMQ相关概念 其中包含几个概念 1. publisher生产者也就是发送消息的一方 2. consumer消费者也就是消费消息的一方 3. queue队列存储消息。生产者投递的消息会暂存在消息队列中等待消费者处理 4. exchange交换机负责消息路由。生产者发送的消息由交换机决定投递到哪个队列。 5. virtual host虚拟主机起到数据隔离的作用相当于独立的数据库。每个虚拟主机相互独立有各自的exchange、queue
Virtual host数据隔离
这里的用户都是RabbitMQ的管理或运维人员。目前只有安装RabbitMQ时添加的itheima这个用户。仔细观察用户表格中的字段如下
Nameitheima也就是用户名Tagsadministrator说明itheima用户是超级管理员拥有所有权限Can access virtual host /可以访问的virtual host这里的/是默认的virtual host
对于小型企业而言出于成本考虑我们通常只会搭建一套MQ集群公司内的多个不同项目同时使用。这个时候为了避免互相干扰 我们会利用virtual host的隔离特性将不同项目隔离。一般会做两件事情
给每个项目创建独立的运维账号将管理权限分离。给每个项目创建不同的virtual host将每个项目的数据隔离。
创建用户hmall 并分配administrator权限 此时hmall用户是没有任何virtual host的访问权限 然后切切换用户hmall进行登录并给项目创建一个单独的virtual host,进行项目之间的隔离 由于我们是登录hmall账户后创建的virtual host因此回到users菜单你会发现当前用户已经具备了对/hmall这个virtual host的访问权限了 之前在virual host为/下创建的队列、交换机就看不到了。
SpringAMQP
RabbitMQ官方提供的Java客户端编码相对复杂一般生产环境下我们更多会结合Spring来使用。而Spring的官方刚好基于RabbitMQ提供了这样一套消息收发的模板工具SpringAMQP
RabbitMQ提供了6中消息模型但是第6种RPC,并不属于MQ其实3、4、5都属于订阅模型只不过路由的方式不同。
第一种 基本消息模型
其实就是生产者将消息发送到队列消费者从队列中获取消息队列是存储消息的缓冲区。可以直接向队列发送消息跳过交换机——这种模式比较简单很少在生产中使用。
第二种 WorkQueues模型
Work queues任务模型。简单来说就是让多个消费者绑定到一个队列共同消费队列中的消息主要处理消息堆积可以使用work 模型多个消费者共同处理消息消息处理的速度就能大大提高了。
但WorkQueues 是平均分配给每个消费者并没有考虑到消费者的处理能力。导致1个消费者空闲另一个消费者忙的不可开交。没有充分利用每一个消费者的能力可以在spring中简单的配置可以解决这个问题我们修改consumer服务的application.yml文件添加配置
spring:rabbitmq:listener:simple:prefetch: 1 # 每次只能获取一条消息处理完成才能获取下一个消息第三种 发布订阅模型fanout交换机
发布订阅模型添加两个队列分别各用一个消费者监听设置一个交换机类型为广播fanout生产者将消息发送给交换机而交换机将消息投递给所有与本交换机绑定routingKey的队列中。 交换机只能转发消息不能存储消息有队列绑定就转发到队列里没有队列时候交换机里的消息会立马丢失。
1 可以有多个队列2 每个队列都要绑定到Exchange交换机3 生产者发送的消息只能发送到交换机4 交换机把消息发送给绑定过的所有队列5 订阅队列的消费者都能拿到消息
fanout交换机实例
//发布订阅模式的配置,包括两个队列和对应的订阅者,发布者的交换机类型使用fanout(子网广播),两根网线binding用来绑定队列到交换机
Configuration
public class FanoutExchangeConfig {Beanpublic Queue myQueue1() {Queue queuenew Queue(queue1);return queue;}Beanpublic Queue myQueue2() {Queue queuenew Queue(queue2);return queue;}/*** 声明交换机*/Beanpublic FanoutExchange fanoutExchange(){FanoutExchange fanoutExchangenew FanoutExchange(fanout);return fanoutExchange;}/*** 绑定队列到交换机上*/Beanpublic Binding binding1(){Binding bindingBindingBuilder.bind(myQueue1()).to(fanoutExchange());return binding;}/*** 绑定队列2到交换机上*/Beanpublic Binding binding2(){Binding bindingBindingBuilder.bind(myQueue2()).to(fanoutExchange());return binding;}}查看交换机 绑定到交换机下的队列
第四种 Direct交换机
在Fanout模式中一条消息会被所有订阅的队列都消费。但是在某些场景下我们希望不同的消息被不同的队列消费。这时就要用到Direct类型的Exchange。 在Direct模型下
队列与交换机的绑定不能是任意绑定了而是要指定一个RoutingKey路由key消息的发送方在 向 Exchange发送消息时也必须指定消息的 RoutingKey。Exchange不再把消息交给每一个绑定的队列而是根据消息的Routing Key进行判断只有队列的Routingkey与消息的 Routing key完全一致才会接收到消息
direct交换机实例
direct模式由于要绑定多个KEY会非常麻烦每一个Key都要编写一个binding
//direct直连模式的交换机配置,包括一个direct交换机两个队列4根网线binding
Configuration
public class DirectExchangeConfig {Beanpublic DirectExchange directExchange(){DirectExchange directExchangenew DirectExchange(direct);return directExchange;}Beanpublic Queue directQueue1() {Queue queuenew Queue(directqueue1);return queue;}Beanpublic Queue directQueue2() {Queue queuenew Queue(directqueue2);return queue;}//4个binding将交换机和相应队列连起来Beanpublic Binding bindingblack2(){Binding bindingBindingBuilder.bind(directQueue1()).to(directExchange()).with(black);return binding;}Beanpublic Binding bindingorange(){Binding bindingBindingBuilder.bind(directQueue1()).to(directExchange()).with(orange);return binding;}Beanpublic Binding bindingblack(){Binding bindingBindingBuilder.bind(directQueue2()).to(directExchange()).with(black);return binding;}Beanpublic Binding bindinggreen(){Binding bindingBindingBuilder.bind(directQueue2()).to(directExchange()).with(green);return binding;}}基于注解的方式声明——direct交换机
基于Bean的方式声明队列和交换机比较麻烦Spring还提供了基于注解方式来声明。
例如我们同样声明Direct模式的交换机和队列
RabbitListener(bindings QueueBinding(value Queue(name direct.queue1),exchange Exchange(name hmall.direct, type ExchangeTypes.DIRECT),key {red, blue}
))
public void listenDirectQueue1(String msg){System.out.println(消费者1接收到direct.queue1的消息【 msg 】);
}RabbitListener(bindings QueueBinding(value Queue(name direct.queue2),exchange Exchange(name hmall.direct, type ExchangeTypes.DIRECT),key {red, yellow}
))
public void listenDirectQueue2(String msg){System.out.println(消费者2接收到direct.queue2的消息【 msg 】);
}第五种Topic交换机
Topic类型Exchange可以让队列在绑定BindingKey 的时候使用通配符 BindingKey 一般都是有一个或多个单词组成多个单词之间以“ .”分割例如 item.insert
通配符规则
#匹配一个或多个词*匹配不多不少恰好1个词 举例 item.#能够匹配item.spu.insert 或者 item.spu item.*只能匹配item.spu 基于注解的方式声明——Topic交换机
RabbitListener(bindings QueueBinding(value Queue(name topic.queue1),exchange Exchange(name hmall.topic, type ExchangeTypes.TOPIC),key china.#
))
public void listenTopicQueue1(String msg){System.out.println(消费者1接收到topic.queue1的消息【 msg 】);
}RabbitListener(bindings QueueBinding(value Queue(name topic.queue2),exchange Exchange(name hmall.topic, type ExchangeTypes.TOPIC),key #.news
))
public void listenTopicQueue2(String msg){System.out.println(消费者2接收到topic.queue2的消息【 msg 】);
}消息转换器
在数据传输时它会把你发送的消息序列化为字节发送给MQ接收消息的时候还会把字节反序列化为Java对象。 只不过默认情况下Spring采用的序列化方式是JDK序列化。众所周知JDK序列化存在下列问题
数据体积过大有安全漏洞可读性差
配置JSON转换器
显然JDK序列化方式并不合适。我们希望消息体的体积更小、可读性更高因此可以使用JSON方式来做序列化和反序列化。
在publisher和consumer两个服务中都引入依赖 dependencygroupIdcom.fasterxml.jackson.dataformat/groupIdartifactIdjackson-dataformat-xml/artifactIdversion2.9.10/version
/dependencyBean
public MessageConverter messageConverter(){// 1.定义消息转换器Jackson2JsonMessageConverter jackson2JsonMessageConverter new Jackson2JsonMessageConverter();// 2.配置自动创建消息id用于识别不同消息也可以在业务中基于ID判断是否是重复消息jackson2JsonMessageConverter.setCreateMessageIds(true);return jackson2JsonMessageConverter;
}消息转换器中添加的messageId可以便于我们将来做幂等性判断。 在consumer服务中定义一个新的消费者publisher是用什么类型发送那么消费者也一定要用什么类型接收格式如下
RabbitListener(queues object.queue)
public void listenSimpleQueueMessage(MapString, Object msg) throws InterruptedException {System.out.println(消费者接收到object.queue消息【 msg 】);
}