RabbitMQ消息队列+spring监听mq服务器,接收消费mq消息

最近soa项目要和官网系统对接,实现mq信息监听,保存等一些列操作。项目用的是Maven+SSM框架。然后学习和开发用了两天时间,算是搞定,趁加班时间做个总结。
对于Maven工程的ssm框架,整合RabbitMq首先就是

1.引入依赖:


	org.springframework.amqp
	spring-rabbit
	1.3.5.RELEASE

依赖不多,就这一个就够用了。但是有个坑要注意,如果引入的位置不对,启动会出现莫名的启动报错,我试了好几次,最后放到所有引入依赖的最后面是没问题的~所以,明智的选择是引入成功后先启动项目看看会不会报错,这个时候才引入一个依赖,正常不会报错,如果报错,就是位置不对,及时调整吧。

2.配置文件:context-rabbitMq.xml


  
  

	  
	
	
	
	
	


      
      
  
      
      
	
	  
	  
      
          
      

3.在web.xml中引入配置的文件context-rabbitMq.xml


	contextConfigLocation
	classpath:META-INF/app_config/context/context-*.xml

4.自定义监听类:RabbitMqConsumerListener

自定义的监听类必须实现接口:ChannelAwareMessageListener或者MessageListener.这里最好用Channel…这个。

二者的区别可以从他们的抽象类的入参来说:一个是message(消息实体),一个是channel就是当前的通道。
ChannelAwareMessageListener的抽象类: 
	void onMessage(Message message,Channel channel) .
MessageListener接口的抽象类:  
	void onMessage(Message message).

多出的参数Channel可以很方便的提供监听之外的ack通知RabbitMq服务器功能。
由于MQ服务器对于消费端无反馈的,有重发机制,可能会有一条数据发送多次,并且一致保存在服务器中,久而久之就会由于数据量过大造成内存溢出的危险,而ack机制就是通过消费端发出通知给mq服务器,告诉服务器那条mq已经处理完毕,可以剔除。对于处理异常的,则可以重新回到mq服务器的队列中。而实现手动实现这个ack的关键点就是实现接口:ChannelAwareMessageListener.

如何实现手动ack?
手动ack就是在当前channel里面调用basic***的方法,并传入当前消息的tagId就可以了。

这里的ack通知分为三种情况:

a.消费端正常处理完成一条mq时,ack mq服务器可以移除此条mq的方法

//消息的标识,false只确认当前一个消息收到,true确认所有consumer获得的消息
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
参数解释:
deliveryTag:该消息的index
multiple:是否批量.true:将一次性ack所有小于deliveryTag的消息。

b. 消费端处理完成一条mq时发生异常,ack 会将此条mq重新放到mq服务器队列queue中

//ack返回false,此条mq并重新回到队列
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
参数解释:
deliveryTag:该消息的index
multiple:是否批量.true:将一次性拒绝所有小于deliveryTag的消息。
requeue:被拒绝的是否重新入队列

c.就是消费端主动拒绝mq服务器发送mq过来。

//拒绝消息
channel.basicReject(message.getMessageProperties().getDeliveryTag(), true);
参数解释:
deliveryTag:该消息的index
requeue:被拒绝的是否重新入队列

channel.basicNack 与 channel.basicReject
的区别在于basicNack可以拒绝多条消息,而basicReject一次只能拒绝一条消息

5.自定义类的具体实现:

import java.io.IOException
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener;
import com.rabbitmq.client.Channel;
import com.zlf.cn.action.IRabbitMqConsumerTaskAction;
/**
*使用监听器接收消息
*@author itw_zhanglf02
/
public class RabbitMqConsumerListener implents ChannelAwareMessageListener{
private Logger logger=loggerFactory.getLogger(RabbitMqConsumerListener.class);

@Resource(name=IRabbitMqConsumerTaskAction.ACTION_ID)
private IRabbitMqConsumerTaskAction rabbitMqConsumerTaskAction;

@Override
public void onMessage(Message arg0,Channel channel){
	//业务处理,放到action层,并返回处理成功还是异常的flag
	boolean mqFlag=rabbitMaConsumerTaskAction.saveMq(arg0);
	//还有一个点就是如何获取mq消息的报文部分message?
	//String message=new String(arg0.getBody(),"UTF-8");
	if(mqFlag){
		basicACK(arg0,channle);//处理正常--ack
	}else{
		basicNACK(arg0,channle);//处理异常--nack
	}
}
//正常消费掉后通知mq服务器移除此条mq
private void basicACK(Message message,Channel channel){
try{							channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
}catch(IOException e){
	logger.error("通知服务器移除mq时异常,异常信息:"+e);
}
}
//处理异常,mq重回队列
private void basicNACK(Message message,Channel channel){
try{
channel.basicNack(message.getMessageProperties().getDeliveryTag(),false,true);
}catch(IOExeption e){
	logger.error("mq重新进入服务器时出现异常,异常信息:"+e);
}
}
}

这样就可以了。启动项目,有时候在项目启动完毕,监听程序开始的时候报错,报错信息大致:

org.springframework.amqpAmqpIOException:java.io.IOException....
Case by:com.rabbitmq.client.ShutdownSignalException:channel error;protocol method:#method(reply-code=403,reply-text=ACCESS_REFUSED - access to queue 'ZL.SALETRACE' in vhost 'tele-onlineYA' refused for user 'tel-online',class-id=50,method-id=10)......

这种情况网上给的大都是由于在配置对应权限上有问题,但我这里报的同样的错误,并不是他们说的那种情况,而是由于我们这里做的监听mq服务器名为’ZL.SALETRACE’的queue,如果服务器端现在没有mq,就会报这个错,估计是没有mq,服务器暂时没有分配队列,队列就没有生成,但这个不是啥配置问题,是初始化问题。如果现在服务器端发送mq,还是能迅速的接收到发送的mq的,然后一切就正常了。
所以这个问题归结于服务端没有mq,如果有mq,还是可以正常接收,没啥问题的。这种报错可以忽略。
好了,这就是本次迭代的总结。~

你可能感兴趣的:(rabbitMq专栏)