QueueingConsumer在Rabbitmq客户端3.x版本中用的如火如荼,但是在4.x版本开初就被标记为@Deprecated。
Consumer的消费模式有Pull 和 Push两种,而经常用到的就是Push模式,Push模式在3.x的用法demo如下:
QueueingConsumer consumer = new QueueingConsumer(channel);
channel.basicQos(1);
channel.basicConsume(QUEUE_NAME, false, "consumer_zzh",consumer);
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println(" [X] Received '" + message + "'");
channel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);
break;
}
在官方文档中推荐使用继承DefaultConsumer的方式:
boolean autoAck = false;
channel.basicConsume(queueName, autoAck, "myConsumerTag",
new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag,
Envelope envelope,
AMQP.BasicProperties properties,
byte[] body)
throws IOException
{
String routingKey = envelope.getRoutingKey();
String contentType = properties.getContentType();
long deliveryTag = envelope.getDeliveryTag();
// (process the message components here ...)
channel.basicAck(deliveryTag, false);
}
});
QueueingConsumer内部其实是一个LinkBlockingQueue,它将从broker端接受到的信息先暂存到这个LinkBlockingQueue中,然后消费端程序在从这个LinkBlockingQueue中take出消息。试下一下,如果我们不take消息或者说take的非常慢,那么LinkBlockingQueue中的消息就会越来越多,最终造成内存溢出。
这个问题可以通过设置Basic.Qos来很好的解决。
DefaultConsumer还有一下方法:
@Override
public void handleConsumeOk(String consumerTag) {
this._consumerTag = consumerTag;
}
@Override
public void handleCancelOk(String consumerTag) {
// no work to do
}
@Override
public void handleCancel(String consumerTag) throws IOException {
// no work to do
}
@Override
public void handleShutdownSignal(String consumerTag, ShutdownSignalException sig) {
// no work to do
}
@Override
public void handleRecoverOk(String consumerTag) {
// no work to do
}
进入basicConsume方法内部看一看,他有三个实现但最终都是ChannerlN.class下的 public String basicConsume(String queue, final boolean autoAck, String consumerTag, boolean noLocal, boolean exclusive, Map
//自己的consumer
DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String routingKey = envelope.getRoutingKey();
String contentType = properties.getContentType();
long deliveryTag = envelope.getDeliveryTag();
// (process the message components here ...)
channel.basicAck(deliveryTag, false);
log.info("i'm ack message:{}", properties.getCorrelationId());
}
};
/**
* 回调 autoAck 默认是false
*/
channel.basicConsume(DurableConfig.QUEUE_NAME, defaultConsumer);
//源码1
public String basicConsume(String queue, final boolean autoAck, String consumerTag, boolean noLocal, boolean exclusive, Map arguments, final Consumer callback) throws IOException {
Method m = (new com.rabbitmq.client.AMQP.Basic.Consume.Builder()).queue(queue).consumerTag(consumerTag).noLocal(noLocal).noAck(autoAck).exclusive(exclusive).arguments(arguments).build();
BlockingRpcContinuation k = new BlockingRpcContinuation(m) {
public String transformReply(AMQCommand replyCommand) {
String actualConsumerTag = ((ConsumeOk)replyCommand.getMethod()).getConsumerTag();
ChannelN.this._consumers.put(actualConsumerTag, callback);
ChannelN.this.metricsCollector.basicConsume(ChannelN.this, actualConsumerTag, autoAck);
ChannelN.this.dispatcher.handleConsumeOk(callback, actualConsumerTag); /**在初始化调用了这个*/
return actualConsumerTag;
}
};
this.rpc(m, k);
try {
if (this._rpcTimeout == 0) {
return (String)k.getReply();
} else {
try {
return (String)k.getReply(this._rpcTimeout);
} catch (TimeoutException var11) {
throw this.wrapTimeoutException(m, var11);
}
}
} catch (ShutdownSignalException var12) {
throw wrap(var12);
}
}
//源码2
public void handleConsumeOk(final Consumer delegate, final String consumerTag) {
this.executeUnlessShuttingDown(new Runnable() {
public void run() {
try {
delegate.handleConsumeOk(consumerTag); /**调用了这个 我们重写的或者默认的 消费者的方法*/
} catch (Throwable var2) {
ConsumerDispatcher.this.connection.getExceptionHandler().handleConsumerException(ConsumerDispatcher.this.channel, var2, delegate, consumerTag, "handleConsumeOk");
}
}
});
}
自己写的确认方法
@Slf4j
@Service
@RabbitListener(queues = DurableConfig.QUEUE_NAME)
public class DurableConsumer {
@RabbitHandler
public void doHandle(String content, Message message, Channel channel, CorrelationData correlationData) {
try {
log.info("content {}", content);
log.info("message {}", message);
log.info("channel {}", channel);
log.info("correlationData {}", correlationData.getId());
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
log.info("*************id:{}", message.getMessageProperties().getCorrelationId());
} catch (IOException e) {
e.printStackTrace();
}
}
}
String basicConsume(String queue, Consumer callback) throws IOException;
String basicConsume(String queue, boolean autoAck, Consumer callback) throws IOException;
String basicConsume(String queue, boolean autoAck, Map arguments, Consumer callback) throws IOException;
String basicConsume(String queue, boolean autoAck, String consumerTag, Consumer callback) throws IOException;
String basicConsume(String queue, boolean autoAck, String consumerTag, boolean noLocal, boolean exclusive, Map arguments, Consumer callback) throws IOException;
channel.basicAck(deliveryTag, false);
首先template和家庭的容器都要引入messageConvert
@Bean
public RabbitTemplate rabbitTemplate1(@Qualifier("cachingConnectionFactory1") CachingConnectionFactory cachingConnectionFactory, ConfirmCallBackListener confirmCallBackListener, ReturnCallBackListener returnCallBackListener) {
RabbitTemplate rabbitTemplate = new RabbitTemplate();
rabbitTemplate.setConnectionFactory(cachingConnectionFactory);
rabbitTemplate.setMandatory(true);
rabbitTemplate.setConfirmCallback(confirmCallBackListener);
rabbitTemplate.setReturnCallback(returnCallBackListener);
rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());--------------this
return rabbitTemplate;
}
@Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(@Qualifier("cachingConnectionFactory1") CachingConnectionFactory connectionFactory){
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setMessageConverter(new Jackson2JsonMessageConverter()); --------------this
factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
return factory;
}
其次 不能同时发送一个对象和CorrelationData,不然会在解析参数的时候出错.原因往下看:
@Slf4j
@RestController
public class Producer {
@Autowired
@Qualifier("rabbitTemplate1")
RabbitTemplate rabbitTemplate;
private Gson gson = new Gson();
@PostMapping("safe")
public void sendMessage(String exNAME, String routingKey) {
Beauty beauty = new Beauty();
beauty.setAge(18);
beauty.setWeight(53.2);
beauty.setName("meili");
beauty.setIntroduce("beauty");
String mid = UUID.randomUUID().toString();
CorrelationData correlationData = new CorrelationData(mid);
rabbitTemplate.convertAndSend(exNAME, routingKey, beauty);
log.info("there has a beauty : {}", gson.toJson(beauty));
}
}
@Slf4j
@Service
@RabbitListener(queues = SafeConfig.QUEUE_NAME, containerFactory = "rabbitListenerContainerFactory")
public class Consumer {
@RabbitHandler
public void doHandle(@Payload Beauty content, Message message, Channel channel) {
try {
log.info("content {},message {},channel {},correlationData ", content, message, channel);
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
log.info("----------------id:{}", message.getMessageProperties().getCorrelationId());
} catch (IOException e) {
e.printStackTrace();
}
}
}
为什么不能同时存在自定义对象和CorrelationData,同时存在的时候可以看到类似的报错:org.springframework.messaging.converter.MessageConversionException: Cannot convert from [com.hyq.rabbitmq.safemessage.Beauty] to [org.springframework.amqp.rabbit.connection.CorrelationData]
我们根据提示的信息可以走进去源码看看:在解析每一个参数的时候 paramter都是不同的参数,如果你同时传送以上两个参数,循环就是两次一人一次,但是问题在于Object payload = message.getPayload(); 这个每次获取的都是你自定义的对象,所以在convert的时候,就必定有一次正确,有一次时把你的对象转为CorrelationData,所以就会报错,提示你不能那么转换。
protected Object[] getMethodArgumentValues(Message> message, Object... providedArgs) throws Exception {
if (ObjectUtils.isEmpty(this.getMethodParameters())) {
return EMPTY_ARGS;
} else {
MethodParameter[] parameters = this.getMethodParameters();
Object[] args = new Object[parameters.length];
for(int i = 0; i < parameters.length; ++i) {
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
args[i] = findProvidedArgument(parameter, providedArgs);
if (args[i] == null) {
if (!this.resolvers.supportsParameter(parameter)) {
throw new MethodArgumentResolutionException(message, parameter, formatArgumentError(parameter, "No suitable resolver"));
}
try {
args[i] = this.resolvers.resolveArgument(parameter, message);
} catch (Exception var9) {
if (this.logger.isDebugEnabled()) {
String error = var9.getMessage();
if (error != null && !error.contains(parameter.getExecutable().toGenericString())) {
this.logger.debug(formatArgumentError(parameter, error));
}
}
throw var9;
}
}
}
return args;
}
}
@Nullable
public Object resolveArgument(MethodParameter parameter, Message> message) throws Exception {
Payload ann = (Payload)parameter.getParameterAnnotation(Payload.class);
if (ann != null && StringUtils.hasText(ann.expression())) {
throw new IllegalStateException("@Payload SpEL expressions not supported by this resolver");
} else {
Object payload = message.getPayload();
if (this.isEmptyPayload(payload)) {
if (ann != null && !ann.required()) {
return null;
} else {
String paramName = this.getParameterName(parameter);
BindingResult bindingResult = new BeanPropertyBindingResult(payload, paramName);
bindingResult.addError(new ObjectError(paramName, "Payload value must not be empty"));
throw new MethodArgumentNotValidException(message, parameter, bindingResult);
}
} else {
Class> targetClass = parameter.getParameterType();
Class> payloadClass = payload.getClass();
if (ClassUtils.isAssignable(targetClass, payloadClass)) {
this.validate(message, parameter, payload);
return payload;
} else {
if (this.converter instanceof SmartMessageConverter) {
SmartMessageConverter smartConverter = (SmartMessageConverter)this.converter;
payload = smartConverter.fromMessage(message, targetClass, parameter);
} else {
payload = this.converter.fromMessage(message, targetClass);
}
if (payload == null) {
throw new MessageConversionException(message, "Cannot convert from [" + payloadClass.getName() + "] to [" + targetClass.getName() + "] for " + message);
} else {
this.validate(message, parameter, payload);
return payload;
}
}
}
}
}
http://www.importnew.com/25242.html
https://www.jianshu.com/p/df231c152754
https://blog.csdn.net/vbirdbest/article/details/78699913