springboot+RabbitMQ实现一条消息被所有consumer消费

文章描述了一个用户认证中心(AuthorizationCenter,AC)的系统架构,该中心使用JWT进行身份验证,并通过RabbitMQ实现在多个副本间的横向同步,以支持全平台的单点登录功能。当用户登录后,JWT会被发送到RabbitMQ,由其他副本消费并同步,确保所有副本都能识别有效的JWT。配置中详细说明了SpringBoot应用如何集成RabbitMQ,包括消息发送、监听和确认机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

需求背景:

        用户认证中心(Authorization center简称ac)使用jwt实现用户请求身份认证,需要支持多副本部署。系统架构如下:

        用户登录后生成jwt,纵向需要通过socket长连接把jwt下发到应用集成层ws,ws再把jwt下发到应用。前端请求各应用时可以在应用的filter中校验jwt是否有效,无效则向上询问ws jwt是否有效,无效再请求ac jwt是否有效。

        所以,用户登录请求通过负载均衡落到ac副本1(简称ac1)后,ac1生成jwt,除了纵向下发之外,还需要横向同步到ac2 ac3,ac2和ac3再纵向同步jwt,实现全平台的单点登录。

具体需求:

        ac1发送消息到rabbit mq,其他的所有副本ac2和ac3消费消息。

实现方案:

        1.添加依赖

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

        2.添加rabbitMQ配置

spring:
  application:
    name: xxx
  rabbitmq:
    addresses: x.x.x.x:5672
    username: admin
    password: admin
    virtual-host: /
    #    启用消息发布ack
    publisher-confirm-type: correlated
    #    启用发布返回
    publisher-returns: true
    template:
      #启用强制信息;默认false
      mandatory: true
      retry:
        # 发送重试是否可用
        enabled: true
        #最大重试次数
        max-attempts: 3
    #消费端配置
    listener:
      simple:
        missing-queues-fatal: false
        #最大/最小的消费者数量
        concurrency: 1
        max-concurrency: 8
        #表示消息确认方式,其有三种配置方式,分别是none、manual和auto;默认auto
        acknowledge-mode: manual
        #监听器抛出异常而拒绝的消息是否被重新放回队列,默认值为true。
        default-requeue-rejected: true
        #每次从mq取消息的条数
        prefetch: 1
        retry:
          enabled: true
          #最大重试次数
          max-attempts: 5
          #最大重试时间间隔
          max-interval: 32000
          #第一次和第二次尝试发布或传递消息之间的间隔
          initial-interval: 2000
          #应用于上一重试间隔的乘数
          multiplier: 2

         3.登录service中发送mq(jwt),即mq provider

        
        @Autowired
        private RabbitTemplate rabbitTemplate;

        public void login(Token token){
            //如果是自己发的mq则不需要消费,直接return
            if(cache.contains(token.getJwt())return;
               
            //下发token到ws层
                        server.getNamespace("/ws1").getBroadcastOperations().sendEvent("LOGIN_EVENT",token,
broadcastAckCallback);

            //获取redis中注册的ac节点信息,如果节点数大于1则需要发送mq同步token
            int acNodeCount= redisTemplate.boundValueOps(nodeKey).get();
            if(acNodeCount>1){
            rabbitTemplate.convertAndSend(""ac-topic-exchange"",
                  AcConstant.LOGIN_ROUTING_KEY,
                  token);
        }

4.MQ consumer

通过@Queue注解中的value属性,用spring spel表达式,在每次启动ac的时候生成一个带有随机字符串的名字,绑定到ac的topic exchange。这样,启动3个ac副本,就有3个queue绑定到了ac的exchange,mq message发送到topic exchange, 通过routing key分发到所有符合规则的queue,就能实现所有副本消费同一条消息。

@RabbitListener(
          bindings = {
                  @QueueBinding(
                          value = @Queue(value = "ac-login-queue- #{T(System).currentTimeMillis()}"),
                          exchange = @Exchange(value = "ac-topic-exchange",
                                  type = ExchangeTypes.TOPIC),
                          key = "ac.login.#")
          },
          ackMode = "MANUAL")
  @RabbitHandler
  public void loginConsumer(Token token, Channel channel, Message message) throws IOException {
    log.info("【loginConsumer】 监听到其他ac节点的登录事件,jwt:{}",token.getJwt());
    long messageId = message.getMessageProperties().getDeliveryTag();
    try{
      listenLogin();
      //手动ack
      channel.basicAck(messageId,false);
    }catch (Exception e){
      //失败ack,消息重新入队
      log.error("loginConsumer消费失败:{}",e.getCause().getMessage());
      channel.basicNack(messageId,false,true);
    }
  }

登出时的实现逻辑相同。 

### 使用Spring BootRabbitMQ实现数据批量接收与处理 #### 创建项目并引入依赖 为了使用Spring BootRabbitMQ进行消息的批量接收与处理,首先需要创建一个新的Spring Boot项目,并在`pom.xml`文件中加入必要的依赖项。这些依赖项包括Spring Boot Starter AMQP以及任何其他可能需要用到的支持库。 ```xml <dependencies> <!-- RabbitMQ support --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> <!-- Other dependencies as needed... --> </dependencies> ``` #### 配置RabbitMQ连接参数 接着,在项目的application.properties或application.yml文件里定义好RabbitMQ服务器的相关配置信息,比如主机地址、端口、用户名密码等[^3]。 对于YAML格式的应用程序属性文件来说: ```yaml spring: rabbitmq: host: localhost port: 5672 username: guest password: guest ``` #### 编写生产者代码 编写一个简单的控制器类作为消息生产者,用来向指定队列发送多条测试消息。这里假设每条消息都是JSON字符串形式表示的对象实例。 ```java @RestController @RequestMapping("/api/messages") public class MessageProducerController { @Autowired private AmqpTemplate amqpTemplate; @PostMapping("/sendBatch") public ResponseEntity<String> sendMessages(@RequestBody List<MyMessageObject> messages) { int count = 0; for (MyMessageObject msg : messages) { String jsonMsg = new Gson().toJson(msg); this.amqpTemplate.convertAndSend("myQueue", jsonMsg); // 发送到名为 "myQueue" 的队列 ++count; } return ResponseEntity.ok(String.format("%d messages sent.", count)); } } ``` #### 设置消费者逻辑以支持批处理模式 为了让消费者能够高效地处理大批量到来的数据包而不至于造成性能瓶颈,应该调整消费者的监听器设置使其能够在接收到多个消息一次性触发回调函数执行业务逻辑而不是逐个响应。可以通过自定义ListenerContainerFactory来自行控制并发线程数和其他高级选项来优化消费效率[^4]。 下面是一个基于@RabbitListener注解的方式来进行批量化读取的例子: ```java @Component public class BatchConsumer { private static final Logger logger = LoggerFactory.getLogger(BatchConsumer.class); @Value("${rabbitmq.consumer.batch-size}") private Integer batchSize; @RabbitListener(queues = "${queue.name}", containerFactory="batchContainerFactory") public void receive(List<Message> messages, Channel channel) throws Exception{ try { // Process the batch of messages here... processBatch(messages); // Acknowledge all received messages after successful processing. for(Message message : messages){ Long deliveryTag = ((AMQP.BasicProperties)message.getMessageProperties()).getDeliveryTag(); channel.basicAck(deliveryTag , false ); } } catch(Exception e){ logger.error("Error occurred while receiving a batch of messages:",e); throw e; // Re-throw exception to let it be handled by error handler if any. } } private void processBatch(List<Message> messages){ // Implement your business logic here based on incoming data set size and content type etc.. System.out.println("Processing "+messages.size()+" items..."); } } @Configuration public class RabbitConfig { @Bean(name ="batchContainerFactory") public SimpleRabbitListenerContainerFactory simpleRabbitListenerContainerFactory(ConnectionFactory connectionFactory, ObjectMapper objectMapper){ SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory(); factory.setConnectionFactory(connectionFactory); factory.setMessageConverter(new Jackson2JsonMessageConverter(objectMapper)); // Set concurrency level according to expected load pattern. factory.setConcurrentConsumers(1); factory.setMaxConcurrentConsumers(Runtime.getRuntime().availableProcessors()); // Enable batching mode with custom batch size limit per poll cycle. factory.setBatchSize(batchSize); return factory; } } ``` 通过上述方法可以有效地利用Spring Boot框架的强大功能配合RabbitMQ完成高效的异步通信任务,特别是在面对高吞吐率应用场景下的大数据集传输需求时表现出色。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值