JAVA中的基础----什么是消息队列MQ,如何使用RabbitMQ来实现AMQP异步调用

本文开始进入正文前,我们需要先弄清楚一些基础知识,须知而后用。

1.消息队列(MQ)是啥?

 1)消息队列是一种跨进程的通信机制,具有单向传递、单向依赖和异步处理的特性,主要解决多个系统间的消息异步、业务解耦、流量削峰等问题。

 2)消息队列从字面上意思是Message+Queue。Message是信息载体,特征是携带的消息通常具有“可消费”性质;Queue是一个单向通道,特征是先进先出。

 举个“快递服务”的例子,理解一下消息队列的概念:

JAVA中的基础----什么是消息队列MQ,如何使用RabbitMQ来实现AMQP异步调用_第1张图片 图1:快递服务(理解“消息队列”)

快递服务就是一种消息队列MQ,卖家是生产者,买家是消费者,快递包裹是Message,运输线路是Queue。

快递服务(消息队列)的好处:

1)异步处理。生产者制造的商品,快递不需要实时被签收,消费者可以空闲的时候再取件。

2)业务解耦。生产者只关心自己完成订单,不关心卖给了谁;消费者只关心拿到商品,不关系是谁生产的。

3)流量削峰。当促销季,消费者购买的商品太多,快递服务可以提供的仓储功能,可以起到缓冲效果。

快递服务(消息队列)的不足:

1)可用性降低。快递服务一旦瘫痪,商品消费业务就无法进行。

2)业务复杂化增加。商品消费业务,原本是生产者与消费者两者之间的关系,现在有增加了第三方快递服务公司,完成一个商品消费业务复杂化增加。

3)可能一致性问题。生产者通过快递服务发完货就完成了自身的业务,默认商品消费已经成功,但如果消费者对部分商品不满意,拒绝签收所有物品,那么实际上商品消费失败,生产者和消费者双方的数据就会不一致。

     

2.使用MQ实现异步调用

下文结合Spring Boot,使用RabbitMQ来实现AMQP异步调用:

        RabbitMQ不是基于JMS规范,而是基于AMQP(advanced message queuing protocol)高级消息队列协议,采用性能更佳的Erlang语言进行底层技术实现。

JAVA中的基础----什么是消息队列MQ,如何使用RabbitMQ来实现AMQP异步调用_第2张图片 图2:RabbitMQ/AMQP消息通信机制

第一步:RabbitMQ服务启动(下面为登录服务器的执行启停命令)       

      1)service rabbitmq-server start #启动

      2)service rabbitmq-server stop #停止

      3)service rabbitmq-server restart #重启

      4)service rabbitmq-server status #查看状态

      5)service rabbitmq-server etc #查看有哪些命令可以使用

      6) 同时将rabbitmq-server加入到开机自启动服务中,运行以下命令:chkconfig rabbitmq-server on

第二步:开发服务端(消费者,接收端)

1)在pom.xml文件添加maven依赖:


	org.springframework.boot
	spring-boot-starter-parent
	1.5.2.RELEASE


    org.springframework.boot
    spring-boot-starter-amqp

2)在application.properties配置基础信息

#rabbitmq配置
spring.rabbitmq.host=11.11.11.11  #配置服务地址 (按实际情况修改)
spring.rabbitmq.port=5672         #配置端口号
spring.rabbitmq.username=admin
spring.rabbitmq.password=admin
spring.rabbitmq.virtual-host=mytest
#重试3次
spring.rabbitmq.listener.retry.enabled=true
spring.rabbitmq.listener.retry.max-attempts=3

3)新建服务类

MyTestQueueHandler是消费端,监听到指定队列的消息后,执行指定业务。

1.写法一:
public class MyTestQueueHandler {

	@RabbitListener(queues = "${spring.rabbitmq.queue.send.msg}")//监听指定的队列
	public void receive(final String message) {
        //写消费的业务
        //执行message相关的业务,如发短信
	}
    @RabbitListener(queues = "${spring.rabbitmq.queue.send.email}")//监听指定的队列
	public void receive(final String email) {
        //写消费的业务
        //执行email相关的业务,如发邮件
	}
}

2.写法二:
@RabbitListener(queues = "${spring.rabbitmq.queue.send.msg}")//监听指定的队列
public class MyTestQueueHandler {

	@RabbitHandler
	public void receive1(final String message) {
        //写消费的业务
        //执行message相关的业务,如发短信
	}
    
    @RabbitHandler
	public void receive2(final String message) {
        //写消费的业务
        //执行message相关的业务,如发短信2
	}
}

RabbitMqConfig配置一下queue队列信息 :(未配置:spring.rabbitmq.queue.send.email,请自行对比增加)

@Configuration
public class RabbitMqConfig {
	// 发送短信-queue队列
	@Value("${spring.rabbitmq.queue.send.msg}")
	private String sendMessageQueue;

	// 发送短信-direct类型
	@Value("${spring.rabbitmq.exchange.direct.send.msg}")
	private String sendMessageDirect;

	// 发送短信-bindingKey绑定
	@Value("${spring.rabbitmq.binding.send.msg}")
	private String sendMessageBinding;

	//sendMessageDirect类型
	@Bean(name = "sendMessageDirect")
	public DirectExchange sendMessageDirect() {
		return new DirectExchange(sendMessageDirect);
	}

	//sendMessageQueue队列
	@Bean(name = "sendMessageQueue")
	public Queue sendMessageQueue() {
		return new Queue(sendMessageQueue, true);
	}

	//sendMessageBinding绑定
	@Bean(name = "sendMessageBinding")
	public Binding sendMessageBinding(@Qualifier("sendMessageDirect") final DirectExchange directExchange,
			@Qualifier("sendMessageQueue") final Queue queue) {
		return BindingBuilder.bind(queue).to(directExchange).with(sendMessageBinding);
	}	
}

第三步:开发客户端(生产者,发送端)

1)在pom.xml文件添加

同上(开发服务端)

2)在application.properties配置基础信息

同上(开发服务端)

3)新建消费类:

SendMqUtil先定义MQ消息发送公共方法

@Component
public class SendMqUtil {
	@Autowired
	private AmqpTemplate amqpTemplate;
	/**
	 * 发送消息到指定routingKey(使用默认exchange)-直接指定queue
	 */
	public void send(final String routingKey, final String content) {
		try {
			amqpTemplate.convertAndSend(routingKey, content);
		} catch (final Exception e) {
			log.error("【消息队列发送失败:routingKey=" + routingKey + ",content=" + content + "】");
			log.error(e.getMessage(), e);
		}
	}
	/**
	 * 发送消息到指定routingKey(使用默认exchange)-直接指定queue
	 */
	public boolean sendEx(final String routingKey, final String content) {
		try {
			amqpTemplate.convertAndSend(routingKey, content);
			return true;
		} catch (final Exception e) {
			log.error("【消息队列发送失败:routingKey=" + routingKey + ",content=" + content + "】");
			log.error(e.getMessage(), e);
			return false;
		}
	}
	/**
	 * 发送消息到指定的交换器和routingKey
	 */
	public void send(final String exchange, final String routingKey, final String content) {
		try {
			amqpTemplate.convertAndSend(exchange, routingKey, content);
		} catch (final Exception e) {
			log.error("【消息队列发送失败:exchange=" + exchange + ",routingKey=" + routingKey + ",content=" + content + "】");
			log.error(e.getMessage(), e);
		}
	}
	/**
	 * 发送消息到指定的交换器和routingKey
	 */
	public boolean sendEx(final String exchange, final String routingKey, final String content) {
		try {
			amqpTemplate.convertAndSend(exchange, routingKey, content);
			return true;
		} catch (final Exception e) {
			log.error("【消息队列发送失败:exchange=" + exchange + ",routingKey=" + routingKey + ",content=" + content + "】");
			log.error(e.getMessage(), e);
			return false;
		}
	}
}

SendMqComponent:实现具体的消息推送 

@Component
public class SendMqComponent {
	@Autowired
	private SendMqUtil sendMqUtil;
	// 发送短信-direct类型
	@Value("${spring.rabbitmq.exchange.direct.send.message}")
	private String sendMessageDirect;
	// 发送消息-routingKey
	@Value("${spring.rabbitmq.routingKey.send.message}")
	private String sendMessageRoutingKey;
	/**
	 * 发送短信
	 */
	public void sendMessage(final String message) {
		log.debug("sendMessage:" + message);
		sendMqUtil.send(sendMessageDirect, sendMessageRoutingKey, message);
	}
}

 

你可能感兴趣的:(java,编程基础,spring,spring,boot,spring,cloud)