使用StreamBridge实现RabbitMq 消息收发 && ack确认 && 延时消息

使用StreamBridge实现RabbitMq && 延时消息

Maven依赖

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
        </dependency>

延时消息需要安装插件

下载地址:link
1.下载完成放到rabbitmq安装目录plugins下
2.执行命令启用插件
3.重启mq

rabbitmq-plugins enable rabbitmq_delayed_message_exchange  // 启用插件
//重启mq
rabbitmq-server stop
rabbitmq-server start

Exchanges -> add a new exchange -> type 出现x-delayed-message即安装成功

Exchanges -> add a new exchange -> type 出现x-delayed-message即安装成功

yml配置

spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: xxxx
    password: xxxx

  function:
      # 与消费者对应(消费者方法名称)
      definition: ackMessage;normal;delay
    stream:
      rabbit:
        bindings:
          ackMessage-in-0:
            consumer:
              acknowledge-mode: manual  # manual手动确认 ,auto 自动确认
          delay-in-0:
            consumer:
              delayedExchange: true # 开启延时
          delay-out-0:
            producer:
              delayedExchange: true  # 开启延时

      bindings:
        delay-in-0:
          destination: delay.exchange.cloud  # mq对应交换机
          content-type: application/json
          consumer:
            acknowledge-mode: auto # manual手动确认 ,auto 自动确认
          group: delay-group	# 消息组
          binder: rabbit
        delay-out-0:
          destination: delay.exchange.cloud
          content-type: application/json
          group: delay-group
          binder: rabbit

        ackMessage-in-0:
          destination: ackMessage.exchange.cloud
          content-type: application/json
          consumer:
            acknowledge-mode: manual # manual手动确认 ,auto 自动确认
          group: ackMessage-group
          binder: rabbit
        ackMessage-out-0:
          destination: ackMessage.exchange.cloud
          content-type: application/json
          group: ackMessage-group
          binder: rabbit
        normal-in-0:
          destination: normal.exchange.cloud
          content-type: application/json
          consumer:
            acknowledge-mode: auto # manual手动确认 ,auto 自动确认
          group: normal-group
          binder: rabbit
        normal-out-0:
          destination: normal.exchange.cloud
          content-type: application/json
          group: normal-group
          binder: rabbit

接口controller


import com.alibaba.fastjson2.JSON;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDateTime;

/**
 * @Description: RabbitmqController
 */
@Slf4j
@RestController
@AllArgsConstructor
@RequestMapping("/mq")
public class MqController {

	//消息发送者
    private final RabbitMqProducer rabbitMqProducer;

    /**
     * 发送普通消息Rabbitmq
     * bindingName 绑定队列名称
     * @param msg 消息内容
     */
    @GetMapping("/sendMessage/{msg}/{bindingName}")
    public R<Void> sendMessage(@PathVariable("msg") String msg, @PathVariable("bindingName") String bindingName) {
        log.info(bindingName + "发送消息: " + msg);
        rabbitMqProducer.sendMsg(msg, bindingName);
        return R.ok();
    }

    /**
     * 发送延迟消息
     *
     * @param message  消息实体
     * @return
     */
    @PostMapping("/sendDelayedMessage")
    public R<Void> sendDelayedMessage(@RequestBody Message message) {
        log.info(MqTExchangesEnum.delay + "发送延时消息: " + LocalDateTime.now() + "  " + message);
        rabbitMqProducer.sendDelayMsg(JSON.toJSONString(message), message.getBindingName(), message.getSeconds());// 延迟时间(秒)
        return R.ok();
    }
}

发送者

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.stream.function.StreamBridge;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Component;

import java.util.UUID;

/**
 * RabbitMq消息生产者
 */
@Component
public class RabbitMqProducer {

    @Autowired
    private StreamBridge streamBridge;

    /**
     * @Description RabbitMq消息生产者
     * @Param msg 消息内容
     * @Param bindingName  exchange绑定queue名称
     **/
    public void sendMsg(String msg, String bindingName) {
        // 构建消息对象
        Messaging messaging = new Messaging().setMsgId(UUID.randomUUID().toString()).setMsgText(msg);
        Message<Messaging> message = MessageBuilder.withPayload(messaging).build();
        streamBridge.send(bindingName, message);
    }

    /**
     * 发送延迟消息
     *
     * @param msg
     * @param bindingName
     * @param seconds
     */
    public void sendDelayMsg(String msg, String bindingName, Integer seconds) {
        // 构建消息对象
        Messaging messaging = new Messaging().setMsgId(UUID.randomUUID().toString()).setMsgText(msg);
        Message<Messaging> message = MessageBuilder.withPayload(messaging).setHeader("x-delay", seconds * 1000).build();
        streamBridge.send(bindingName, message);
    }
}

消费者

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpHeaders;
import org.springframework.messaging.Message;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;

import java.time.LocalDateTime;
import java.util.function.Consumer;


/**
 * RabbitMq消息消费者
 */
@Component
@Slf4j
public class RabbitMqConsumer {

    /**
     * mq接收ackMessage消息/手动ack确认
     * @methodName 配置文件对应
     **/
    @Bean
    Consumer<Message<Messaging>> ackMessage() {
        log.info("ackMessage-初始化订阅");
        return obj -> {
            Channel channel = obj.getHeaders().get(AmqpHeaders.CHANNEL, Channel.class);
            Long deliveryTag = obj.getHeaders().get(AmqpHeaders.DELIVERY_TAG, Long.class);
            try {
                log.info("ackMessage-消息接收成功:" + obj.getPayload());
                //业务逻辑处理
                //ack确认
                channel.basicAck(deliveryTag, false);
            } catch (Exception e) {
                //重新回队列-true则重新入队列,否则丢弃或者进入死信队列。
//                    channel.basicReject(deliveryTag, true);
                log.error(e.getMessage());
            }

        };
    }

    /**
     * mq接收normal消息
     **/
    @Bean
    Consumer<Messaging> normal() {
        log.info("normal-初始化订阅");
        return obj -> {
            log.info("normal-消息接收成功:" + obj);
            //业务逻辑处理
        };
    }


    /**
     * mq接收延时消息
     * Messaging 发送实体消息接收实体消息
     **/
    @Bean
    Consumer<Message<Messaging>> delay() {
        log.info("delay-初始化订阅");
        return obj -> {
            Messaging payload = obj.getPayload();
            log.info("delay-消息接收成功:" + LocalDateTime.now() + "  " + payload);
            //业务逻辑处理
        };
    }
}

在 Spring Cloud Stream 中,StreamBridge.send 方法默认是单线程的,因为它使用了 Spring Integration 的消息通道,而 Spring Integration 的消息通道默认是单线程的。但是,你可以通过配置 Spring Integration 的任务执行器来改变这个行为,使其变成多线程的。 具体实现方式如下: 1. 在 `application.properties` 中配置任务执行器: ``` spring.task.execution.pool.core-size=10 spring.task.execution.pool.max-size=20 spring.task.execution.pool.queue-capacity=1000 spring.integration.taskScheduler.poolSize=10 ``` 其中,`spring.task.execution.pool.core-size`、`spring.task.execution.pool.max-size` 和 `spring.task.execution.pool.queue-capacity` 是线程池的核心线程数、最大线程数和队列容量。`spring.integration.taskScheduler.poolSize` 是 Spring Integration 的任务调度器的线程池大小。 2. 注入 `TaskExecutor` 并使用它来发送消息: ```java @Autowired private TaskExecutor taskExecutor; @Autowired private StreamBridge streamBridge; public void sendMessageAsync(String message) { taskExecutor.execute(() -> { streamBridge.send("myDestination", message); }); } ``` 在这个示例中,我们注入了 `TaskExecutor`,并使用它来异步发送消息。在发送消息的过程中,我们使用了 Lambda 表达式来创建一个新的线程,然后在这个新线程中调用 `streamBridge.send` 方法发送消息。 需要注意的是,使用多线程发送消息时需要注意线程安全和性能问题,特别是线程池的大小和队列容量需要根据实际情况进行调整,否则可能会出现线程阻塞、内存溢出等问题。因此建议在具体的项目中,根据实际情况选择使用单线程或多线程发送消息
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值