RabbitMQ使用简述
RabbitMQ基于AMQP协议。
AMQP:是一个高级抽象层消息通信协议,RabbitMQ是AMQP协议的实现
RabbitMQ使用:Exchange(交换机)根据routing-key(路由选择键)匹配相应的queue(队列)
Exchange有4中类型:
direct:exchange在和queue进行binding时会设置routingkey,进行消息发送时,只有设置相同的routingkey,交换机才会路由到相应的队列。
topic:和direct差不多,只是topic允许routingkey使用通配符'*','#'.来匹配到相应的queue
fanout:直接将消息路由到所有绑定的队列中
header:和以上三个都不一样,其路由的规则是根据header来判断,其中的header就是以下方法的arguments参数
参考:https://www.cnblogs.com/julyluo/p/6265775.html,https://www.jianshu.com/p/d5675b1c8112
Routing Key:在Consumer在binding exchange和queue时指定;在Producer在发送消息时指定。
demo:
引入jar包:
<!-- RabbitMQ --> <dependency> <groupId>com.rabbitmq</groupId> <artifactId>amqp-client</artifactId> <version>5.0.0</version> </dependency> <dependency> <groupId>org.springframework.amqp</groupId> <artifactId>spring-rabbit</artifactId> <version>2.0.0.RELEASE</version> </dependency>
增加RabbitMQ配置
<!-- rabbitMQ配置 --> <bean id="rabbitConnectionFactory" class="org.springframework.amqp.rabbit.connection.CachingConnectionFactory"> <constructor-arg value="172.16.1.15"/> <property name="username" value="admin"/> <property name="password" value="admin"/> <property name="channelCacheSize" value="8"/> <property name="port" value="5672"></property> </bean> <rabbit:admin connection-factory="rabbitConnectionFactory"/> <!--邮件相关队列--> <rabbit:queue name="test_email_queue" durable="true"/> <!--短信相关队列--> <rabbit:queue name="test_sms_queue" durable="true"/> <!--将队列和交换器通过路由键绑定--> <rabbit:direct-exchange name="TEST_MQ" xmlns="http://www.springframework.org/schema/rabbit" durable="true"> <rabbit:bindings> <rabbit:binding queue="test_email_queue" key="email" ></rabbit:binding> <rabbit:binding queue="test_sms_queue" key="sms" ></rabbit:binding> </rabbit:bindings> </rabbit:direct-exchange> <!-- 创建rabbitTemplate 消息模板类 --> <bean id="rabbitTemplate" class="org.springframework.amqp.rabbit.core.RabbitTemplate"> <constructor-arg ref="rabbitConnectionFactory"></constructor-arg> </bean> <rabbit:listener-container connection-factory="rabbitConnectionFactory"> <rabbit:listener queues="test_email_queue" ref="processUserEmail" method="onMessage"/> <rabbit:listener queues="test_sms_queue" ref="processUserSms" method="onMessage"/> </rabbit:listener-container>
实现类:(生产者写入MQ)
package com.study.demo.service.impl; import com.study.demo.service.IUserReg; import com.study.demo.service.busi.SaveUser; import com.study.demo.vo.User; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.AmqpException; import org.springframework.amqp.core.Message; import org.springframework.amqp.core.MessageProperties; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; /** * * @Description: RabbitMQ实现的异步用户注册 * @author leeSmall * @date 2018年9月18日 * */ @Service @Qualifier("async") public class AsyncProcess implements IUserReg{ private Logger logger = LoggerFactory.getLogger(AsyncProcess.class); @Autowired private RabbitTemplate rabbitTemplate; @Autowired private SaveUser saveUser; public boolean userRegister(User user) { try { saveUser.saveUser(user); rabbitTemplate.send("TEST_MQ","email", new Message(user.getEmail().getBytes(),new MessageProperties())); rabbitTemplate.send("TEST_MQ","sms", new Message(user.getEmail().getBytes(),new MessageProperties())); } catch (AmqpException e) { logger.error(e.toString()); return false; } return true; } }
消费者(监听调用该方法)
package com.study.demo.service.mq; import com.study.demo.service.busi.SendEmail; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.core.Message; import org.springframework.amqp.core.MessageListener; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * * @Description: RabbitMQ消息消费端监听邮件消息类 * @author leeSmall * @date 2018年9月18日 * */ @Component public class ProcessUserEmail implements MessageListener { private Logger logger = LoggerFactory.getLogger(ProcessUserEmail.class); @Autowired private SendEmail sendEmail; public void onMessage(Message message) { logger.info("accept message,ready process......"); sendEmail.sendEmail(new String(message.getBody())); } }
package com.study.demo.service.mq; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.core.Message; import org.springframework.amqp.core.MessageListener; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.study.demo.service.busi.SendSms; /** * * @Description: RabbitMQ消息消费端监听sms消息类 * @author leeSmall * @date 2018年9月18日 * */ @Component public class ProcessUserSms implements MessageListener { private Logger logger = LoggerFactory.getLogger(ProcessUserSms.class); @Autowired private SendSms sendSms; public void onMessage(Message message) { logger.info("accept message,ready process......"); sendSms.sendSms(new String(message.getBody())); } }
使用目的:解耦,加快上文响应时间
使用场景:下单时关于库存的扣减、邮件、短信、消息的推送等需要异步处理的地方
如何使用的:
库存的扣减:
用户下单后,订单系统需要通知库存系统。传统的做法是,订单系统调用库存系统的接口。如下图:
传统模式的缺点:假如库存系统无法访问,则订单减库存将失败,从而导致订单失败,订单系统与库存系统耦合
应用消息队列后的方案:
订单系统:用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户订单下单成功
库存系统:订阅下单的消息,采用拉/推的方式,获取下单信息,库存系统根据下单信息,进行库存操作
假如:在下单时库存系统不能正常使用。也不影响正常下单,因为下单后,订单系统写入消息队列就不再关心其他的后续操作了。实现订单系统与库存系统的应用解耦
库存系统:订阅下单的消息,采用拉/推的方式,获取下单信息,库存系统根据下单信息,进行库存操作
假如:在下单时库存系统不能正常使用。也不影响正常下单,因为下单后,订单系统写入消息队列就不再关心其他的后续操作了。实现订单系统与库存系统的应用解耦
邮件、短信的发送:
串行方式:
并行方式(异步):
消息队列方式(解耦):