Spring Boot 集成 RabbitaMQ-确认消息

Spring Boot 集成 RabbitaMQ-确认消息 - 简书

springBoot-rabbit MQ-设置手动确认ACK-Channel shutdown异常

Spring Boot 2 集成 RabbitaMQ

简介
MQ全称(Message Queue)又名消息队列,是一种异步通讯的中间件。是分布式系统中重要的组件,主要解决应用解耦,异步消息,流量削锋等问题,实现高性能,高可用,可伸缩和最终一致性架构。常见的MQ有kafka、activemq、zeromq、rabbitmq 等等

环境信息

jdk1.8.0_45
Spring Boot 2.0.1.RELEASE

Maven 依赖

   <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-amqp</artifactId>
    </dependency>

连接配置
application.properties 文件增加以下配置

#==========================================================
#  RabbitMQ 连接配置
#==========================================================
# 连接用户名
spring.rabbitmq.username=guest
# 连接密码
spring.rabbitmq.password=guest
# 服务地址
spring.rabbitmq.host=172.18.1.1
# 服务端口号
spring.rabbitmq.port=5672
# 在RabbitMQ中可以虚拟消息服务器VirtualHost,每个VirtualHost相当月一个相对独立的RabbitMQ服务器,每个VirtualHost之间是相互隔离的。exchange、queue、message不能互通。
spring.rabbitmq.virtual-host=/
# 手动ACK 不开启自动ACK模式,目的是防止报错后死循环重复消费错误消息,默认为 none
spring.rabbitmq.listener.simple.acknowledge-mode=manual
# 最大重试次数
spring.rabbitmq.listener.simple.retry.max-attempts=2
# 是否开启消费者重试
spring.rabbitmq.listener.simple.retry.enabled=true
# 重试间隔时间(毫秒)
spring.rabbitmq.listener.simple.retry.initial-interval=3000
# 重试次数超过上面的设置之后是否丢弃(false不丢弃时需要写相应代码将该消息加入死信队列)
spring.rabbitmq.listener.simple.default-requeue-rejected=false

测试代码
配置队列

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.boot.autoconfigure.amqp.SimpleRabbitListenerContainerFactoryConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * RabbitMQ 配置
 * 
 * @author swordshake
 * @since 1.0
 */
@Slf4j
@Configuration
public class RabbitDemoConfig {

  public static final String DEMO_ROUTING_KEY = "dev.demo.register.manual.queue";

  /**
   * 配置RabbitMQ连接模板
   * 
   * @param connectionFactory
   * @return
   */
  @Bean
  public RabbitTemplate rabbitTemplate(CachingConnectionFactory connectionFactory) {
    connectionFactory.setPublisherConfirms(true);
    connectionFactory.setPublisherReturns(true);
    RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
    rabbitTemplate.setMandatory(true);
    rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> log
        .info("消息发送成功:correlationData({}),ack({}),cause({})", correlationData, ack, cause));
    rabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> log
        .info("消息丢失:exchange({}),route({}),replyCode({}),replyText({}),message:{}", exchange,
            routingKey, replyCode, replyText, message));
    return rabbitTemplate;
  }

  /**
   * 定义队列。
   * 
   * @return
   */
  @Bean
  public Queue demoQueue() {
    // 参数1:队列名称,参数2:是否持久化。
    return new Queue(DEMO_ROUTING_KEY, true);
  }

  /**
   * 一个消费者的情况下用于保证消息队列按顺序一条一条消费的容器配置。
   * 
   * @param configurer
   * @param connectionFactory
   * @return
   */
  @Bean
  public SimpleRabbitListenerContainerFactory orderContainerFactory(
      SimpleRabbitListenerContainerFactoryConfigurer configurer,
      ConnectionFactory connectionFactory) {
    SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
    factory.setPrefetchCount(1); // 用于保证一次只pop一条消息,只有当本条消息确认后才继续pop下一条。
    configurer.configure(factory, connectionFactory);
    return factory;
  }

}

生产者

// RabbitDemoMsg .java
@Data
@AllArgsConstructor
public class RabbitDemoMsg implements Serializable {
  private static final long serialVersionUID = -447646130662400154L;
  private String id;
  private String name;
}

// 用于生成消息的测试类
@Api(tags = "测试用的服务")
@RestController
@RequestMapping("/testservice")
public class RSTestController {

  @Autowired
  private RabbitTemplate rabbitTemplate;

  @ApiOperation("测试RabbitMQ")
  @GetMapping(value = "sendMsg")
  public void sendMsg(@RequestParam String p) {
    RabbitDemoMsg msg = new RabbitDemoMsg("1", "测试RabbitMQ" + p);
    rabbitTemplate.convertAndSend(RabbitDemoConfig .DEMO_ROUTING_KEY, msg);
  }
}

消费者

import java.io.IOException;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.hd123.saleservice.config.RabbitConfig;
import com.hd123.saleservice.config.RabbitDemoConfig;
import com.hd123.saleservice.rs.impl.rabbit.RabbitDemoMsg;
import com.rabbitmq.client.Channel;

import lombok.extern.slf4j.Slf4j;

/**
 * 队列消费者。
 * 
 * @author swordshake
 * @since 1.0
 */
@Component
@Slf4j
public class RabbitDemoHandler {

  @Autowired
  private RabbitTemplate rabbitTemplate;

  @RabbitListener(queues = {
      RabbitConfig.DEMO_ROUTING_KEY }, containerFactory = "orderContainerFactory") // 使用自定义的容器工厂,内已配置消息一次只能消费一条
  public void handler(RabbitDemoMsg demo, Message message, Channel channel) throws IOException {
    log.info("[Rabbit DEMO Handler 监听的消息]-[{}]", demo.toString());
    try {

      Thread.currentThread().sleep(10000);
      int i = 1 / 0;
      // 手动确认消费消息
      channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
    } catch (Exception e) {
      log.error("", e);
      // ========消费失败处理方式:1、重新入栈消费 (重复消费错误数据会死循环)=======
      // channel.basicRecover(false); // 重新压入MQ,参数表示是否能被其它消费者消费,效果类似第三种处理方式开启重新入栈的场景,不同的是它会触发 ListenerContainerConsumerFailedEvent

      // ========消费失败处理方式:2、转到其它队列,比如延迟队列进行特殊处理;然后继续消费下一条消息。(推荐做法)=======
      rabbitTemplate.convertAndSend(RabbitDemoConfig.DEMO_DEAD_LETTER_ROUTING_KEY, demo);
      channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);

      // ========消费失败处理方式:3、拒绝并重新入栈(重复消费错误数据会死循环)=======
      // channel.basicReject(message.getMessageProperties().getDeliveryTag(), true);
      // // 第二个参数表示是否重新入栈,为false会直接丢弃当前消息;为true时会重新放入原消息队列位置,重新消费。

      // ========消费失败处理方式:4、抛异常,启用了最大重试次数后会被阻塞到unacked消息中=======
      // throw new IOException(e); //根据application.properties
      // 配置的最大重试次数进行重试,超过的话进入unacked状态。由于本消息未应答,因此下一条消息会被本消息阻塞,不会继续处理。会导致 Ready 消息堆积。
    }
  }
}

springBoot-rabbit MQ-设置手动确认ACK-Channel shutdown异常

发现MQ配置的时候,如果配置了json解析器。如下:

@Bean
public MessageConverter messageConverter() {
return new ContentTypeDelegatingMessageConverter(new Jackson2JsonMessageConverter());
}
则程序会走自动确认消费,配置文件的配置就不生效了(这个不知道为什么,也可能springboot中某些配置的先后顺序的问题)

这个问题解决办法就是重写一下这个东东,用代码设置手动确认。就OK了

@Bean
public RabbitListenerContainerFactory<?> rabbitListenerContainerFactory(ConnectionFactory connectionFactory){
    SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
    factory.setConnectionFactory(connectionFactory);
    factory.setMessageConverter(new Jackson2JsonMessageConverter());
    factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);             
    return factory;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot 可以通过集成 JAX-RS 规范来使用 RESTful 服务。下面是一些步骤,帮助您在 Spring Boot 应用程序中使用 JAX-RS: 1. 添加 JAX-RS 依赖 将以下依赖项添加到您的 pom.xml 文件中: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jersey</artifactId> </dependency> ``` 2. 创建 REST 资源类 创建一个类,并使用 @Path 注解将其标记为 REST 资源类。在这个类中,您可以使用 JAX-RS 注解定义 RESTful 资源的行为。 ```java import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; @Path("hello") public class HelloResource { @GET @Produces(MediaType.TEXT_PLAIN) public String sayHello() { return "Hello, World!"; } } ``` 3. 创建一个 Application 类 创建一个继承自 javax.ws.rs.core.Application 的类,并使用 @ApplicationPath 注解将其标记为应用程序的根路径。 ```java import javax.ws.rs.ApplicationPath; import javax.ws.rs.core.Application; @ApplicationPath("api") public class JaxrsApplication extends Application { } ``` 4. 启动应用程序 在您的 Spring Boot 应用程序中添加以下代码来启动 JAX-RS: ```java import org.glassfish.jersey.server.ResourceConfig; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } @Bean public ResourceConfig resourceConfig() { return new ResourceConfig(); } } ``` 5. 启动应用程序并测试 启动应用程序,访问 URL http://localhost:8080/api/hello,您应该可以看到 "Hello, World!" 这个字符串。 这就是在 Spring Boot 应用程序中集成 JAX-RS 的基本步骤。您可以根据您的需求,使用更多的 JAX-RS 注解来定义 RESTful 资源类的行为。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值