Spring Cloud Stream 整合 RocketMQ

Spring Cloud Stream 整合 RocketMQ

简介

在微服务的开发过程中,可能会经常用到消息中间件,通过消息中间件在服务与服务之间传递消息,不管你使用的是哪款消息中间件,比如RabbitMQ、Kafka和RocketMQ,那么消息中间件和服务之间都有一点耦合性,这个耦合性就是指如果我原来使用的RabbitMQ,现在要替换为RocketMQ,那么我们的微服务都需要修改,变动会比较大,因为这两款消息中间件有一些区别,如果我们使用Spring Cloud Stream来整合我们的消息中间件,那么这样就可以降低微服务和消息中间件的耦合性,做到轻松在不同消息中间件间切换,当然Spring Cloud Stream官方只支持rabbitmq 和 kafka,spring cloud alibaba新写了一个starter可以支持RocketMQ;

按照官方的定义,Spring Cloud Stream 是一个构建消息驱动微服务的框架;

Spring Cloud Stream解决了开发人员无感知的使用消息中间件的问题,因为Spring Cloud Stream对消息中间件的进一步封装,可以做到代码层面对消息中间件的无感知,甚至于动态的切换中间件(rabbitmq切换为rocketmq或者kafka),使得微服务开发的高度解耦,服务可以关注更多自己的业务流程;


核心概念

img

Spring Cloud Stream 内部有几个概念:Binder 、Binding、input、output;

1、Binder: 跟外部消息中间件集成的组件,用来创建Binding,各消息中间件都有自己的 Binder 实现;

比如 Kafka 的实现 KafkaMessageChannelBinder,RabbitMQ 的实现 RabbitMessageChannelBinder 以及 RocketMQ 的实现 RocketMQMessageChannelBinder;

2、Binding: 包括 Input Binding 和 Output Binding;

Binding 在消息中间件与应用程序提供的 Provider 和 Consumer 之间提供了一个桥梁,实现了开发者只需使用应用程序的 Provider 或 Consumer 生产或消费数据即可,屏蔽了开发者与底层消息中间件的接触;

3、input

应用程序通过input(相当于消费者consumer)与Spring Cloud Stream中Binder交互,而Binder负责与消息中间件交互,因此,我们只需关注如何与Binder交互即可,而无需关注与具体消息中间件的交互。

4、Output

output(相当于生产者producer)与Spring Cloud Stream中Binder交互;

组成说明
BinderBinder是应用与消息中间件之间的封装,目前实现了Kafka和RabbitMQ的Binder,通过Binder可以很方便的连接中间件,可以动态的改变消息类型(对应于Kafka的topic,RabbitMQ的exchange),这些都可以通过配置文件来实现;
@Input该注解标识输入通道,通过该输入通道接收消息进入应用程序
@Output该注解标识输出通道,发布的消息将通过该通道离开应用程序
@StreamListener监听队列,用于消费者的队列的消息接收
@EnableBinding将信道channel和exchange、topic绑定在一起

Spring Cloud Stream 应用

消息生产者

1、创建SpringBoot应用31-rocket-spring-cloud-stream;

2、添加依赖:

<dependency>
  <groupId>com.alibaba.cloud</groupId>
  <artifactId>spring-cloud-starter-stream-rocketmq</artifactId>
</dependency>

配置文件

img

# 应用名称
spring.application.name=stream

########## RocketMQ 通用配置
# 客户端接入点,必填   -rocketmq 连接地址
spring.cloud.stream.rocketmq.binder.name-server=localhost:9876

# 日志级别
logging.level.com.alibaba.cloud.stream.binder.rocketmq=INFO

########## Consumer Config  消费者
# input 的配置:
spring.cloud.stream.bindings.input.destination=test-topic
spring.cloud.stream.bindings.input.content-type=text/plain
spring.cloud.stream.bindings.input.group=test-group

########## Produce Config  生产者
# output 的配置如下: bingdings具体生产,消费的桥梁
spring.cloud.stream.bindings.output.destination=test-topic    //目的地保持一致
spring.cloud.stream.bindings.output.content-type=text/plain
spring.cloud.stream.bindings.output.group=test-group

兼容性问题:

注意版本需要使用springboot2.2.5

<spring-boot.version>2.2.5.RELEASE</spring-boot.version>
<spring-cloud-alibaba.version>2.2.1.RELEASE</spring-cloud-alibaba.version>

消息发送:

@EnableBinding(Source.class)
@Service
public class SenderService {

  @Autowired
  private Source source;

  public void send(String msg) throws Exception {
    boolean flag = source.output().send(MessageBuilder.*withPayload*(msg).build());
    System.*out*.println("消息发送:" + flag);
  }
}

消息接收:

@EnableBinding(Sink.class)
public class ReceiveService {

  @StreamListener("input")
  public void receiveInput1(String receiveMsg) {
    System.*out*.println("input 接收到的消息: " + receiveMsg);
  }
}

可以通过调用SenderService中的方法进行发送信息,也可以通过在启动类中的Main方法中进行调用SenderService的方法进行发送信息:

@SuppressWarnings("all")
@EnableBinding(value = {Source.class, Sink.class}) //使得Source生效
@SpringBootApplication
public class StreamApplication implements CommandLineRunner {
    @Autowired
    private SenderService senderService;
    @Autowired
    private ReceiveService receiveService;
    public static void main(String[] args) {
        SpringApplication.run(StreamApplication.class, args);
    }
    //在main中调用发送信息方法
    @Override
    public void run(String... args) throws Exception {
        senderService.send("hello rocketmq");
    }
}

img


Spring Cloud Stream自定义信道

在前面的案例中,我们已经实现了一个基础的 Spring Cloud Stream 消息传递处理操作,但在操作之中使用的是系统提供的 Source (output)、Sink(input),接下来我们来看一下自定义信道名称;

public interface MySource {
    String OUTPUT1 = "output1";
    @Output(MySource.OUTPUT1)
    MessageChannel output1();
    String OUTPUT2 = "output2";
    @Output(MySource.OUTPUT2)
    MessageChannel output2();
}
public interface MySink {
    String INPUT1 = "input1";
    @Input(MySink.INPUT1)
    SubscribableChannel input1();
    String INPUT2 = "input1";
    @Input(MySink.INPUT2)
    SubscribableChannel input2();
}
server.port=8090
# 应用名称
spring.application.name=stream

########## RocketMQ 通用配置
# 客户端接入点,必填   -rocketmq 连接地址
spring.cloud.stream.rocketmq.binder.name-server=localhost:9876

# 日志级别
logging.level.com.alibaba.cloud.stream.binder.rocketmq=INFO

########## Consumer Config  消费者
# input 的配置:
spring.cloud.stream.bindings.input.destination=test-topic
spring.cloud.stream.bindings.input.content-type=text/plain
spring.cloud.stream.bindings.input.group=test-group

########## Produce Config  生产者
# output 的配置如下: bingdings具体生产,消费的桥梁
spring.cloud.stream.bindings.output.destination=test-topic    //目的地保持一致
spring.cloud.stream.bindings.output.content-type=text/plain
spring.cloud.stream.bindings.output.group=test-group

########## 自定义
# input 的配置:
spring.cloud.stream.bindings.input1.destination=test-topic1
spring.cloud.stream.bindings.input1.content-type=text/plain
spring.cloud.stream.bindings.input1.group=test-group1

# output 的配置:
spring.cloud.stream.bindings.output1.destination=test-topic1
spring.cloud.stream.bindings.output1.content-type=text/plain
spring.cloud.stream.bindings.output1.group=test-group1

SpringCloudStream RocketMQ事务消息

Apache RocketMQ在4.3.0版中已经支持分布式事务消息,这里RocketMQ采用了2PC的思想来实现了提交事务消息,同时增加一个补偿逻辑来处理二阶段超时或者失败的消息,如下图所示:

img

上图说明了事务消息的大致方案,其中分为两个流程:正常事务消息的发送及提交、事务消息的补偿流程;

1.事务消息发送及提交:

(1) 发送消息(half消息);

(2) 服务端响应消息写入结果;

(3) 根据发送结果执行本地事务(如果写入失败,此时half消息对业务不可见,本地逻辑不执行);

(4) 根据本地事务状态执行Commit或者Rollback(Commit操作生成消息索引,消息对消费者可见)

2.补偿流程:

(1) 对没有Commit/Rollback的事务消息(pending状态的消息),从服务端发起一次“回查”;

(2) Producer收到回查消息,检查回查消息对应的本地事务的状态;

(3) 根据本地事务状态,重新Commit或者Rollback;

其中,补偿阶段用于解决消息Commit或者Rollback发生超时或者失败的情况;

事务消息一共有三种状态:提交状态、回滚状态、中间状态;

TransactionStatus.CommitTransaction: 提交事务,代表消费者可以消费此消息;

TransactionStatus.RollbackTransaction: 回滚事务,代表消息将被删除,不能被消费;

TransactionStatus.Unknown: 中间状态,代表需要检查消息队列来确定状态;

MQ内部逻辑:

package com.springcloud.stream.stream.Transaction;


import org.apache.rocketmq.spring.annotation.RocketMQTransactionListener;
import org.apache.rocketmq.spring.core.RocketMQLocalTransactionListener;
import org.apache.rocketmq.spring.core.RocketMQLocalTransactionState;
import org.springframework.messaging.Message;

//MQ接收,并根据结果运行内部逻辑
@SuppressWarnings("all")
@RocketMQTransactionListener(txProducerGroup = "myTxProducerGroup", corePoolSize = 5, maximumPoolSize = 10)
public class TransactionListenerImpl implements RocketMQLocalTransactionListener {
    /**
     * 执行本地事务:也就是执行本地业务逻辑
     *
     * @param msg
     * @param arg
     * @return
     */
    @Override
    public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) {
        Object num = msg.getHeaders().get("test");

        if ("1".equals(num)) {
            System.out.println("executer: " + new String((byte[]) msg.getPayload()) + " unknown");
            return RocketMQLocalTransactionState.UNKNOWN;
        }
        else if ("2".equals(num)) {
            System.out.println("executer: " + new String((byte[]) msg.getPayload()) + " rollback");
            return RocketMQLocalTransactionState.ROLLBACK;
        }
        System.out.println("executer: " + new String((byte[]) msg.getPayload()) + " commit");
        return RocketMQLocalTransactionState.COMMIT;
    }

    /**
     * 回调检查
     *
     * @param msg
     * @return
     */
    @Override
    public RocketMQLocalTransactionState checkLocalTransaction(Message msg) {
        System.out.println("check: " + new String((byte[]) msg.getPayload()));
        return RocketMQLocalTransactionState.COMMIT;
    }
}

消息发送:

@Component
public class Sender {
    @Autowired
    private MySource mySource;
    public <T> void sendTransactionalMsg(T msg ,int num) throws Exception{
        MessageBuilder builder = MessageBuilder.withPayload(msg)
                .setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON)
                .setHeader("test",String.valueOf(num));
                //.setHeader(RocketMQHeaders.TAGS,"binder");
        Message message = builder.build();

        mySource.outputTX().send(message);
    }
}

自定义信道-重写Source

public interface MySource {
     String OUTPUTTX = "outputTX";
    @Output(MySource.OUTPUTTX)
    MessageChannel outputTX();
}

自定义信道-重写Sink

public interface MySink {
    String INPUTTX = "inputTX";
    @Input(MySink.INPUTTX)
    SubscribableChannel inputTX();
}

消费者接收消息:

@EnableBinding({MySink.class})
public class ReceiveService {
    //spring cloud stream 里面发消息通过sink发送
    @Autowired
    private MySink mySink;
    //消费者端接收到的消息
    @StreamListener("inputTX")
    public void receiveTransactionMessage(String receiveMsg) {
        System.out.println("Transaction_input 接收到的消息: " + receiveMsg);
    }
}

Spring Cloud Stream RocketMQ 配置选项

RocketMQ Binder Properties

*spring.cloud.stream.rocketmq.binder.name-server*

RocketMQ NameServer 地址(老版本使用 namesrv-addr 配置项);

Default: 127.0.0.1:9876.

*spring.cloud.stream.rocketmq.binder.access-key*

阿里云账号 AccessKey。

Default: null.

*spring.cloud.stream.rocketmq.binder.secret-key*

阿里云账号 SecretKey。

Default: null.

*spring.cloud.stream.rocketmq.binder.enable-msg-trace*

是否为 Producer 和 Consumer 开启消息轨迹功能

Default: true.

*spring.cloud.stream.rocketmq.binder.customized-trace-topic*

消息轨迹开启后存储的 topic 名称。

Default: RMQ_SYS_TRACE_TOPIC.

RocketMQ Consumer Properties

下面的这些配置是以 spring.cloud.stream.rocketmq.bindings..consumer. 为前缀的 RocketMQ Consumer 相关的配置。

*enable*

是否启用 Consumer;

默认值: true.

*tags*

Consumer 基于 TAGS 订阅,多个 tag 以 || 分割;

默认值: empty.

*sql*

Consumer 基于 SQL 订阅;

默认值: empty.

*broadcasting*

Consumer 是否是广播消费模式。如果想让所有的订阅者都能接收到消息,可以使用广播模式;

默认值: false.

*orderly*

Consumer 是否同步消费消息模式;

默认值: false.

*delayLevelWhenNextConsume*

异步消费消息模式下消费失败重试策略:

-1,不重复,直接放入死信队列

0,broker 控制重试策略

>0,client 控制重试策略

默认值: 0.

*suspendCurrentQueueTimeMillis*

同步消费消息模式下消费失败后再次消费的时间间隔;

默认值: 1000.

RocketMQ Provider Properties

下面的这些配置是以 spring.cloud.stream.rocketmq.bindings..producer. 为前缀的 RocketMQ Producer 相关的配置;

*enable*

是否启用 Producer;

默认值: true.

*group*

Producer group name;

默认值: empty.

*maxMessageSize*

消息发送的最大字节数;

默认值: 8249344.

*transactional*

是否发送事务消息;

默认值: false.

*sync*

是否使用同步得方式发送消息;

默认值: false.

*vipChannelEnabled*

是否在 Vip Channel 上发送消息;

默认值: true.

*sendMessageTimeout*

发送消息的超时时间(毫秒);

默认值: 3000.

*compressMessageBodyThreshold*

消息体压缩阀值(当消息体超过 4k 的时候会被压缩);

默认值: 4096.

*retryTimesWhenSendFailed*

在同步发送消息的模式下,消息发送失败的重试次数;

默认值: 2.

*retryTimesWhenSendAsyncFailed*

在异步发送消息的模式下,消息发送失败的重试次数;

默认值: 2.

*retryNextServer*

消息发送失败的情况下是否重试其它的 broker;

默认值: false.

**

是否发送事务消息;

默认值: false.

*sync*

是否使用同步得方式发送消息;

默认值: false.

*vipChannelEnabled*

是否在 Vip Channel 上发送消息;

默认值: true.

*sendMessageTimeout*

发送消息的超时时间(毫秒);

默认值: 3000.

*compressMessageBodyThreshold*

消息体压缩阀值(当消息体超过 4k 的时候会被压缩);

默认值: 4096.

*retryTimesWhenSendFailed*

在同步发送消息的模式下,消息发送失败的重试次数;

默认值: 2.

*retryTimesWhenSendAsyncFailed*

在异步发送消息的模式下,消息发送失败的重试次数;

默认值: 2.

*retryNextServer*

消息发送失败的情况下是否重试其它的 broker;

默认值: false.

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Cloud StreamSpring Cloud 生态系统中的一部分,它提供了一种简单且可扩展的方式来构建消息驱动的微服务应用程序。而 RocketMQ 是一款开源的分布式消息中间件,它具有高可靠、高吞吐量、高可扩展性等特点。在 Spring Cloud Stream 中,我们可以通过集成 RocketMQ 来实现消息驱动的微服务应用程序。 下面是 Spring Cloud Stream 集成 RocketMQ 的详细文档: 1. 添加依赖 首先,我们需要添加 Spring Cloud StreamRocketMQ 的相关依赖。在 pom.xml 文件中添加以下依赖: ``` <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-stream</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-stream-binder-rocketmq</artifactId> </dependency> <dependency> <groupId>org.apache.rocketmq</groupId> <artifactId>rocketmq-spring-boot-starter</artifactId> <version>2.2.0</version> </dependency> ``` 2. 配置 RocketMQ 在 application.properties 文件中添加 RocketMQ 的相关配置,例如: ``` spring.cloud.stream.rocketmq.binder.namesrv-addr=127.0.0.1:9876 spring.cloud.stream.rocketmq.binder.group=rocketmq-group rocketmq.name-server=127.0.0.1:9876 rocketmq.producer.group=rocketmq-producer-group rocketmq.consumer.group=rocketmq-consumer-group ``` 3. 定义消息通道 在 Spring Cloud Stream 中,消息是通过消息通道来传递的。我们需要定义输入通道和输出通道,例如: ``` public interface MyChannel { String INPUT = "my_input"; String OUTPUT = "my_output"; @Input(INPUT) SubscribableChannel input(); @Output(OUTPUT) MessageChannel output(); } ``` 4. 发送消息 我们可以通过注入 MessageChannel 来发送消息,例如: ``` @Autowired @Qualifier(MyChannel.OUTPUT) private MessageChannel myOutput; public void sendMessage(String message) { myOutput.send(MessageBuilder.withPayload(message).build()); } ``` 5. 接收消息 我们可以通过注入 SubscribableChannel 来接收消息,例如: ``` @StreamListener(MyChannel.INPUT) public void handleMessage(Message<String> message) { log.info("Received message: {}", message.getPayload()); } ``` 6. 集成 RocketMQ 消费者 我们也可以通过集成 RocketMQ 的消费者来接收消息,例如: ``` @Slf4j @Component @RocketMQMessageListener(consumerGroup = "${rocketmq.consumer.group}", topic = MyChannel.INPUT) public class MyRocketMQConsumer implements RocketMQListener<String> { @Override public void onMessage(String message) { log.info("Received message: {}", message); } } ``` 7. 集成 RocketMQ 生产者 我们也可以通过集成 RocketMQ 的生产者来发送消息,例如: ``` @Slf4j @Component public class MyRocketMQProducer { @Autowired private RocketMQTemplate rocketMQTemplate; public void sendMessage(String message) { rocketMQTemplate.convertAndSend(MyChannel.OUTPUT, message); } } ``` 以上就是 Spring Cloud Stream 集成 RocketMQ 的详细文档。通过这种方式,我们可以快速构建消息驱动的微服务应用程序,并且具有高可靠、高吞吐量、高可扩展性等特点。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值