Spring Cloud Stream 结合rocketmq

Spring Cloud Stream 结合rocketmq

官方网址:https://github.com/alibaba/spring-cloud-alibaba/wiki/RocketMQ

你可以在这个地址上下载到相关示例项目,配置项等相关信息

spring-cloud-stream 文档(这个地址似乎只有集合kafaka和rabbit的示例): https://docs.spring.io/spring-cloud-stream/docs/3.2.6/reference/html/

spring-cloud-stream-rocketmq文档 :https://spring-cloud-alibaba-group.github.io/github-pages/2021/en-us/index.html

无语的文档,太简陋了

介绍

stream 介绍

Spring Cloud Stream 是一个用于构建基于消息的微服务应用框架。它基于 SpringBoot 来创建具有生产级别的单机 Spring 应用,并且使用 Spring Integration 与 Broker 进行连接。

Spring Cloud Stream 提供了消息中间件配置的统一抽象,推出了 publish-subscribe、consumer groups、partition 这些统一的概念。

Spring Cloud Stream 内部有两个概念:Binder 和 Binding。

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

比如 Kafka 的实现 KafkaMessageChannelBinderRabbitMQ 的实现 RabbitMessageChannelBinder 以及 RocketMQ 的实现 RocketMQMessageChannelBinder

  • Binding: 包括 Input Binding 和 Output Binding。

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

下图是 Spring Cloud Stream 的架构设计。

大白话:直接理解成和日志门面一个slfj4一个概念即可,这是一个消息的门面,然我们可以替换消息的底层实现,而不用修改代码

rocket binder介绍

这是Spring Cloud Stream Rocket MQ Binder的实现架构:

Rocket MQ Binder的实现依赖于Rocket MQ-Spring框架。RocketMQ Spring框架是Rocket MQ和Spring Boot的集成。它提供了三个主要功能:

@RocketMQTemplate:发送消息,包括同步、异步和事务消息。

@RocketMQTransactionListener:侦听并检查事务消息。

@RocketMQMessageListener:使用消息。

RocketMQMessageChannelBinder是Binder的标准实现,它将在内部构建RocketMQInboundChannelAdapterr和RocketMQMessageHandler。

RocketMQMessageHandler将基于binding配置构造RocketMQTemplate。RocketMQTemplate将转换org.springframework.message(spring消息模块的消息消息类)转换为Rocket MQ消息类org.apache.rocketmq。common.message,然后发送出去。

RocketMQInboundChannelAdapter还将基于binding配置构造RocketMQListenerBindingContainer,RocketMQListenerBindingContainer将启动Rocket q Consumer以接收消息。

前置了解

官方示例包包含了的部分示例,你需要了解相关概念,部分我也是懵逼,大致了解就行,先把示例跑起来

ApplicationRunner

https://blog.csdn.net/weixin_41667076/article/details/121701303

一个接口,其实现类对应的bean被spring管理后,会在项目启动时执行

@Component  //此类一定要交给spring管理
@Order(value=2) //其次执行
public class ConsumerRunnerB implements ApplicationRunner{
	@Override
	public void run(ApplicationArgumers args) throws Exception{
		//代码
		System.out.println("需要在springBoot项目启动时执行的代码2---");
	}
}

函数式接口

https://www.cainiaojc.com/java/java8-functional-interfaces.html

一个函数式接口定义是在接口上加上注解@FunctionalInterface,java自行实现的函数式接口位于java.util.function,函数式接口可以隐式的转换成lmabda 表达式

一般会有一个唯一的未实现方法,我们一般使用lmabda来构建对应函数式接口的实现类

Predicate示例

Predicate入参是泛型指定,可以接收任意类型的参数,返回类型是boolean

package cn.sry1201.recketmq.config;

import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
 
public class Java8Tester {
   public static void main(String args[]){
      List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
        
//       Predicate<Integer> predicate = new Predicate<Integer>() {
//          @Override
//          public boolean test(Integer integer) {
//             return false;
//          }
//       };
//       Predicate<Integer> predicate = n -> true
      // n 是一个参数传递到 Predicate 接口的 test 方法
      // n 如果存在则 test 方法返回 true
        
      System.out.println("输出所有数据:");
        
      // 传递参数 n  这里其实就可以理解为定义这个函数的实现类,效果和上方注释代码等同
      eval(list, n->true);
        
      // Predicate<Integer> predicate1 = n -> n%2 == 0
      // n 是一个参数传递到 Predicate 接口的 test 方法
      // 如果 n%2 为 0 test 方法返回 true
        
      System.out.println("输出所有偶数:");
      eval(list, n-> n%2 == 0 );
        
      // Predicate<Integer> predicate2 = n -> n > 3
      // n 是一个参数传递到 Predicate 接口的 test 方法
      // 如果 n 大于 3 test 方法返回 true
        
      System.out.println("输出大于 3 的所有数字:");
      eval(list, n-> n > 3 );
   }
    
   public static void eval(List<Integer> list, Predicate<Integer> predicate) {
      for(Integer n: list) {
        // 调用函数实现的方法
         if(predicate.test(n)) {
            System.out.println(n + " ");
         }
      }
   }
}
Function示例

包含了函数式接口funtion的默认方法的使用

public class FunctionExample2 {
    public static void main(String[] args) {
        // 定义两个函数
        Function<Integer,Integer> function1 = t -> (t - 5);
        Function<Integer,Integer> function2 = t -> (t * 2);
        //Using andThen() method 组合成一个符合函数,先执行第一个函数,再执行第二个函数,得到90
        int a = function1.andThen(function2).apply(50);
        System.out.println(a);
        //Using compose function compose构建一个复合函数,先执行函数2,再执行函数一
        int c = function1.compose(function2).apply(50);
        System.out.println(c);
    }
}
stream示例使用到的

Consumer void accept(T t) 有入参,无返回值 消费型接口
Supplier T get() 无入参,有返回值 供给型接口
Function<T, R> R apply(T t) T类型入参,R类型出参,T和R可以相同 函数型接口

reactor.core.publisher.Flux

待定

反应式编程相关: https://zhuanlan.zhihu.com/p/356997738

https://zhuanlan.zhihu.com/p/95966853

spring-cloud-function

官方文档 :https://spring.io/projects/spring-cloud-function#learn

Spring Cloud Function是一个具有以下高级目标的项目:通过功能促进业务逻辑的实现。将业务逻辑的开发生命周期与任何特定的运行时目标分离,以便相同的代码可以作为web端点、流处理器或任务运行。支持跨无服务器提供商的统一编程模型,以及独立运行(本地或在PaaS中)的能力。在无服务器提供者上启用Spring Boot功能(自动配置、依赖注入、度量)。它抽象了所有的传输细节和基础设施,允许开发人员保留所有熟悉的工具和流程,并牢牢地关注业务逻辑。

需要引入依赖spring-cloud-function-context

Spring Cloud Stream - functional and reactive

基础示例

引入依赖

这里引入了springcloud alibaba 管理的版本,以下两个依赖引入一个即可

        <!-- springcloud alibaba 管理的版本-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-stream-rocketmq</artifactId>
        </dependency>

        <!--需要单独引入-->
<!--        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-stream-binder-rocketmq</artifactId>
        </dependency>-->

需要说明的是这个也不是最新的客户端,可以考虑排除再引入

消息发布者

消息发布者配置
spring:
  cloud:
    stream:
      rocketmq:
        binder:
          name-server: k8s-master:9876 # rcoketmq 命名服务地址
        # RocketMQ Consumer 相关的配置。
        bindings:
          # 通道名称,和spring.cloud.stream.bindings 下的通道名称对应
          producer-out-0:
            producer:
              enable: true # 是否启用producer
              group: output_1 # producer分组
      bindings:
        producer-out-0:
          destination: num # 对于rocketmq,此处定义的是消息的topic
消费者发布消息代码
@RestController
@Slf4j
public class MqController {

    public static final String TOPIC = "TopicTest";

    public static final String TAG = "TagA";
    public static final String[] tags = new String[] { "TagA", "TagB", "TagC", "TagD",
            "TagE" };

    @Autowired
    private StreamBridge streamBridge;


    // http://127.0.0.1:8013/rocketmq-application/send/msg?msg=abc
    @RequestMapping("/send/msg")
    public String hello(@RequestParam(name = "msg", defaultValue = "hello world") String msg) {
        for (int i = 0; i < 100; i++) {
            String key = "KEY" + i;
            Map<String, Object> headers = new HashMap<>();
            headers.put(MessageConst.PROPERTY_KEYS, key);
            headers.put(MessageConst.PROPERTY_TAGS, tags[i % tags.length]);
            headers.put(MessageConst.PROPERTY_ORIGIN_MESSAGE_ID, i);
            Message<SimpleMsg> message = new GenericMessage(
                    new SimpleMsg(msg + " : " + i), headers);
            streamBridge.send("producer-out-0", message);
        }

        return "消息发送成功";
    }

}
成功截图

function消息发布者示例

配置文件

通道名称和函数名称涉及到约定配置

官方文档:https://docs.spring.io/spring-cloud-stream/docs/3.2.6/reference/html/spring-cloud-stream.html#_functional_binding_names

input - <functionName> + -in- + <index>

output - <functionName> + -out- + <index>

你可以指定函数绑定的通道名称

--spring.cloud.stream.function.bindings.uppercase-in-0=input
--spring.cloud.stream.bindings.input.destination=my-topic
spring:
  cloud:
    stream:
      function:
        definition: producer
      # RocketMQ Consumer 相关的配置。
      rocketmq:
        binder:
          name-server: k8s-master:9876 # rcoketmq 命名服务地址
          group: output_common # 多个通道生产消息,生产者组在此处配置,否则多个通道生产者组不一致报错spring.cloud.stream.rocketmq.bindings.《channelname》.producer.group中配置的无效,
      bindings:
        producer-out-0:
          destination: num

代码

大致是spring cloud stream 根据配置中的名称拿到了容器中的函数实例,调用Supplier实例的get方法,会被循环调用,每调用一次,发送一次消息,消息的主题

@Configuration
@Slf4j
public class RocketMQComprehensive {


	@Bean
	public Supplier<Flux<User>> producer() {

		return () -> Flux.interval(Duration.ofSeconds(2)).map(id -> {
			User user = new User();
			user.setId(id.toString());
			user.setName("freeman");
			user.setMeta(new StringObjectMapBuilder()
					.put("hobbies", Arrays.asList("movies", "songs")).put("age", 21)
					.get());
			return user;
		}).log();
	}

}

function消息消费者示例

@StreamListener @Input 等注解按照显示已经被弃用,而且使用起来报错,所以消费者示例就这一个

配置文件
spring:
  cloud:
    stream:
      function:
        definition: consumer
      rocketmq:
        binder:
          name-server: k8s-master:9876 # rcoketmq 命名服务地址
          group: output_common # 要求相同角色的消费者拥有完全相同的订阅和消费者组,以正确地实现负载平衡。它必须是全球独一无二的。生产者组在概念上聚合完全相同角色的所有生产者实例,这在涉及事务消息时尤其重要。对于非事务性消息,只要每个进程都是惟一的就没关系。进一步讨论请参见这里。
        # RocketMQ Consumer 相关的配置。
        bindings:
          # 通道名称,和spring.cloud.stream.bindings 下的通道名称对应
          consumer-in-0:
            enable: true # 是否启用consumer,默认true
      bindings:
        consumer-in-0:
          destination: num
          group: consumer_group




代码
@Configuration
@Slf4j
public class RocketMQComprehensive {

	@Bean
	public Consumer<User> consumer() {
		return num -> {
			log.info("接收到消息:"+ num.toString());
		};
	}

}

官方普通消息示例

配置

spring:
  cloud:
    stream:
      function:
        definition: producer;consumer;processor
      rocketmq:
        binder:
          name-server: k8s-master:9876 # rcoketmq 命名服务地址
          group: output_common # 要求相同角色的消费者拥有完全相同的订阅和消费者组,以正确地实现负载平衡。它必须是全球独一无二的。生产者组在概念上聚合完全相同角色的所有生产者实例,这在涉及事务消息时尤其重要。对于非事务性消息,只要每个进程都是惟一的就没关系。进一步讨论请参见这里。
        # RocketMQ Consumer 相关的配置。
        bindings:
          # 通道名称,和spring.cloud.stream.bindings 下的通道名称对应
          producer_out_0:
            producer:
              group: output_1 # producer分组 ,建议配置spring.cloud.stream.rocketmq.binder.group(优先更高)
          processor-out-0:
            producer:
              group: output_2
      bindings:
        producer-out-0:
          destination: num
        processor-out-0:
          destination: square
        processor-in-0:
          destination: num
          group: processor_group
        consumer-in-0:
          destination: square
          group: consumer_group

代码

@Configuration
@Slf4j
public class RocketMQComprehensive {


	@Bean
	public Supplier<Flux<User>> producer() {

		return () -> Flux.interval(Duration.ofSeconds(2)).map(id -> {
			User user = new User();
			user.setId(id.toString());
			user.setName("freeman");
			user.setMeta(new StringObjectMapBuilder()
					.put("hobbies", Arrays.asList("movies", "songs")).put("age", 21)
					.get());
			return user;
		}).log();
	}

	@Bean
	public Function<Flux<User>, Flux<User>> processor() {

		return flux -> flux.map(user -> {
			log.info("用户信息:{}" ,user.toString());
			user.setId(String.valueOf(
					Long.parseLong(user.getId()) * Long.parseLong(user.getId())));
			return user;
		});
	}

	@Bean
	public Consumer<User> consumer() {
		return num -> {
			log.info("接收到消息:"+ num.toString());
		};
	}

}

流程说明

1、定义了三个函数,在配置文件中函数producer和通道producer-out-0进行绑定,发送消息到num这个主题上,

2、rocessor和processor-in-0还有processor-out-0 绑定,接收来自num的消息,又再次发布到square这个主题上

3、 consumer和 consumer-in-0绑定,接收square的消息

顺序消息和过滤消息示例

生产者

配置
spring:
  cloud:
    stream:
      function:
        definition: producer
      # RocketMQ Consumer 相关的配置。
      rocketmq:
        binder:
          name-server: k8s-master:9876 # rcoketmq 命名服务地址
          group: output_common
        bindings:
          producer-out-0:
            producer:
              group: output_1
              messageQueueSelector: orderlyMessageQueueSelector # MessageQueue选择器
      bindings:
        producer-out-0:
          destination: orderly
代码

选择器代码

@Component
@Slf4j
public class OrderlyMessageQueueSelector implements MessageQueueSelector {


	@Override
	public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
		Integer id = (Integer) ((MessageHeaders) arg)
				.get(MessageConst.PROPERTY_ORIGIN_MESSAGE_ID);
//mqs.size()是这个topic总共的messagequeue的个数,我这里是8,
//id % OrderlyExample.tags.length 获取1 2 3 4 5
// 那么 1 6 11 16 21 。。。 应该是在同一个messageQueue上,并且tag都是tagA        
		int index = id % OrderlyExample.tags.length % mqs.size();
		return mqs.get(index);
	}

}

消息发送代码

@Configuration
public class OrderlyExample {
    public static final String[] tags = new String[] { "TagA", "TagB", "TagC", "TagD",
            "TagE" };
    @Autowired
    private StreamBridge streamBridge;

    @Bean
    public ApplicationRunner producer() {
        return args -> {
            for (int i = 0; i < 100; i++) {
                String key = "KEY" + i;
                Map<String, Object> headers = new HashMap<>();
                headers.put(MessageConst.PROPERTY_KEYS, key);
                headers.put(MessageConst.PROPERTY_TAGS, tags[i % tags.length]);
                headers.put(MessageConst.PROPERTY_ORIGIN_MESSAGE_ID, i);
                Message<SimpleMsg> msg = new GenericMessage(
                        new SimpleMsg("Hello RocketMQ " + i), headers);
                streamBridge.send("producer-out-0", msg);
            }
        };
    }


}

消费者

配置
spring:
  cloud:
    stream:
      function:
        definition: consumer
      rocketmq:
        binder:
          name-server: k8s-master:9876 # rcoketmq 命名服务地址
          group: output_common # 要求相同角色的消费者拥有完全相同的订阅和消费者组,以正确地实现负载平衡。它必须是全球独一无二的。生产者组在概念上聚合完全相同角色的所有生产者实例,这在涉及事务消息时尤其重要。对于非事务性消息,只要每个进程都是惟一的就没关系。进一步讨论请参见这里。
        # RocketMQ Consumer 相关的配置。
        bindings:
          consumer-in-0:
            consumer:
              # tag: {@code tag1||tag2||tag3 }; sql: {@code 'color'='blue' AND 'price'>100 } .
              subscription: 'TagA || TagC || TagD'
              push:
                orderly: true
      bindings:
        consumer-in-0:
          destination: orderly
          group: orderly-consumer

sql的过滤延时,仅做记录,非测试配置

# 对应的需要发消息时在请求头里添加相关配置
#           consumer-in-0:
#             consumer:
#             tag: {@code tag1||tag2||tag3 }; sql: {@code 'color'='blue' AND 'price'>100 } .
#               subscription: sql:(color in ('red1', 'red2', 'red4') and price>3)
代码
@Configuration
@Slf4j
public class OrderlyExample {

	// 主要是观察是否接收到过滤条件之外的数据,
    // 然后接收到的统一tags的数据是否从小到达有序排列
    @Bean
    public Consumer<Message<SimpleMsg>> consumer() {
        return msg -> {
            String tagHeaderKey = RocketMQMessageConverterSupport
                    .toRocketHeaderKey(MessageConst.PROPERTY_TAGS).toString();
            log.info(Thread.currentThread().getName() + " Receive New Messages: "
                    + msg.getPayload().getMsg() + " TAG:"
                    + msg.getHeaders().get(tagHeaderKey).toString());
            try {
                Thread.sleep(100);
            }
            catch (InterruptedException ignored) {
            }
        };
    }
}

说明

似乎没啥好说的,看到上面的配置和注释吧

事务消息

事务消息是消息发送者端配置,用于保证发送消息和本地事务的原子性

      rocketmq:
        binder:
          name-server: localhost:9876
        bindings:
          producer-out-0:
            producer:
              group: output_1
              transactionListener: myTransactionListener  # 指定监听器
              # 设置消息为事务消息,原始的api使用专门的事务消息生产者TransactionMQProducer
              producerType: Trans 
@Component("myTransactionListener")
public class TransactionListenerImpl implements TransactionListener {

	/**
	 * 执行本地事务,由当前发送消息的线程执行
	 */
	@Override
	public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
		Object num = msg.getProperty("test");

		if ("1".equals(num)) {
			System.out.println("executer: " + new String(msg.getBody()) + " unknown");
			return LocalTransactionState.UNKNOW;
		}
		else if ("2".equals(num)) {
			System.out.println("executer: " + new String(msg.getBody()) + " rollback");
			return LocalTransactionState.ROLLBACK_MESSAGE;
		}
		System.out.println("executer: " + new String(msg.getBody()) + " commit");
		return LocalTransactionState.COMMIT_MESSAGE;
	}

	/**
	 * broker确定本地事务是否成功的回调接口,这个是单独配置的线程池中的线程执行
	 */
	@Override
	public LocalTransactionState checkLocalTransaction(MessageExt msg) {
		System.out.println("check: " + new String(msg.getBody()));
		return LocalTransactionState.COMMIT_MESSAGE;
	}

}

	@Bean
	public ApplicationRunner producer() {
		return args -> {
			for (int i = 1; i <= 4; i++) {
				MessageBuilder builder = MessageBuilder
						.withPayload(new SimpleMsg("Hello Tx msg " + i));
				builder.setHeader("test", String.valueOf(i)).setHeader(
						MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON);
                // 不太确定这行代码是否有作用,以及改怎么配置
				builder.setHeader(RocketMQConst.USER_TRANSACTIONAL_ARGS, "binder");
				Message<SimpleMsg> msg = builder.build();
				streamBridge.send("producer-out-0", msg);
				System.out.println("send Msg:" + msg.toString());
			}
		};
	}

延时消息

	@Bean
	public ApplicationRunner producerDelay() {
		return args -> {
			for (int i = 0; i < 100; i++) {
				String key = "KEY" + i;
				Map<String, Object> headers = new HashMap<>();
				headers.put(MessageConst.PROPERTY_KEYS, key);
				headers.put(MessageConst.PROPERTY_ORIGIN_MESSAGE_ID, i);
				// 主要是这一行设置延时级别
				headers.put(MessageConst.PROPERTY_DELAY_TIME_LEVEL, 2);
				headers.put("a", "userproperties");
				Message<SimpleMsg> msg = new GenericMessage(
						new SimpleMsg("Delay RocketMQ " + i), headers);
				streamBridge.send("producer-out-0", msg);
			}
		};
	}

rocketmq5.0支持设置到具体是时刻, 其直接代码是这样的

message.setDeliverTimeMs(System.currentTimeMillis() + 10_000L);

可以尝试排除rocketmq4.9.4的依赖,然后引入5.0的依赖,大概率可能不行,有可能会是这个配置

MessageConst.PROPERTY_CONSUME_START_TIMESTAMP

广播消息

默认是集群模式,而不是广播模式,这个实在消费者端设置的

      rocketmq:
        binder:
          name-server: localhost:9876
        bindings:
          consumer-in-0:
            consumer:
              messageModel: BROADCASTING

消息重试

消费端配置

上一级配置是rocketmq

          consumer-in-0:
            consumer:
              ## According to the configured number of `max-reconsume-times`,
              ## the server will re-push the message according to whether the client's consumption is successful or not
              push:
                max-reconsume-times: 3
	@Bean
	public Consumer<Message<SimpleMsg>> consumer() {
		return msg -> {
			throw new RuntimeException("mock exception.");
		};
	}

比较全的配置文件

配置来自官网说明的配置项,地址前面已经提供,使用这个配置你可能启动不了应用,仅做参考

spring:
  cloud:
    stream:
      rocketmq:
        binder:
          name-server: k8s-master:9876 # rcoketmq 命名服务地址
          enable-msg-trace: true # 是否为 Producer 和 Consumer 开启消息轨迹功能 默认true,感觉还需要结合服务端配置使用
          access-key: #阿里云账号 AccessKey。
          secret-key: #阿里云账号 SecretKey。消息轨迹开启后存储的 topic 名称。
          customized-trace-topic: # 消息轨迹开启后存储的 topic 名称。 默认RMQ_SYS_TRACE_TOPIC
        # RocketMQ Consumer 相关的配置。
        bindings:
          # 通道名称,和spring.cloud.stream.bindings 下的通道名称对应
          producer-out-0:
            producer:
              enable: true # 是否启用producer
              group: output_1 # producer分组
              maxMessageSize: 8249344 #消息发送的最大字节数。默认8249344
              transactional: false # 是否发送事务消息 默认false
              sync: false # 是否使用同步方式发送消息,就是发送完需要等发送的结果
              vipChannelEnabled: true # 默认true 是否走vip通道发送消息,也就是broker fastRemotingServer端口发送消息
              compressMessageBodyThreshold: 4096 # 消息体压缩阈值,默认超过4k会压缩
              retryTimesWhenSendFailed: 2 # 在同步发送消息的模式下,消息发送失败的重试次数。默认2
              retryTimesWhenSendAsyncFailed: 2 # 在异步发送消息的模式下,消息发送失败的重试次数。默认2
              retryNextServer: false # 消息发送失败的情况下是否重试其它的 broker。
            consumer:
              enable: true # 是否启用consumer
              tags: tagA||tagB # Consumer 基于 TAGS 订阅,多个 tag 以 || 分割。
              sql: "TAGS is not null and TAGS in ('TagA', 'TagB')" # 基于sql过来消息
              broadcasting: false # Consumer 是否是广播消费模式。如果想让所有的订阅者都能接收到消息,可以使用广播模式。默认值false
              orderly: false # 是否有序消费消息,需要客户端发送消息到同一个message queue
              delayLevelWhenNextConsume: 0 #异步消费消息模式下消费失败重试策略 -1,不重复,直接放入死信队列 0,broker 控制重试策略 >0,client 控制重试策略 默认值: 0.
              suspendCurrentQueueTimeMillis: 3000 # 顺序消息消费失败后,再次消费的时间间隔
          processor-out-0:
            producer:
              group: output_2
      bindings:
        producer-out-0:
          destination: num # 对于rocketmq,此处定义的是消息的topic
        processor-out-0:
          destination: square
        processor-in-0:
          destination: num
          group: processor_group
        consumer-in-0:
          destination: square
          group: consumer_group

org.springframework.cloud.stream.config.BindingProperties

被弃用的注解

@EnableBinding
@StreamListener # 按照示例使用报错,找不到对应的bean
@Input # 同样失败

报错

1、Dispatcher has no subscribers for channel

Dispatcher has no subscribers for channel 'rocketmq-application.processor-in-0'.; 

---- Dispatcher has no subscribers,

将function的配置定义在bindings前面,或者手动指定绑定关系(不确定)

      function:
        definition: producer;consumer;processor

就最终的解决来看,你可以往前翻看一下报错,我这里是由于消息接收后处理异常,大致是id接收了一个string类型的,所以异常了,然后导致后续问题,比如可能因为异常,所以消息订阅者就直接关闭了,详细的话可能还需要了解源码或原理

2、Property ‘group’ is required - producerGroup

多个通道生产者组的名称统一配置

Exception thrown while building outbound endpoint
Property 'group' is required - producerGroup

就我当前这个版本而言,如果定义了多个生产者,那么生产者组需要统一定义,但是不影响消费者组的定义

      rocketmq:
        binder:
          name-server: k8s-master:9876 # rcoketmq 命名服务地址
          group: output_common # 要求相同角色的消费者拥有完全相同的订阅和消费者组,以正确地实现负载平衡。它必须是全球独一无二的。生产者组在概念上聚合完全相同角色的所有生产者实例,这在涉及事务消息时尤其重要。对于非事务性消息,只要每个进程都是惟一的就没关系。进一步讨论请参见这里。
        # RocketMQ Consumer 相关的配置。

n.RemotingTooMuchRequestException: sendDefaultImpl call timeout

其他

我看下载的示例项目中有这样的配置

        bindings:
          consumer-in-0:
            consumer:
              messageModel: BROADCASTING

可能是对应rokcetMq 消费者类org.apache.rocketmq.client.consumer.DefaultMQPushConsumer的一个属性

private MessageModel messageModel;

然后如果以后有找不到的配置,可以尝试这样,当然仅仅是猜测,这块我没试验

还有就是官方示例中的配置部分和官方文档上写明的配置不一样,可能够有效吧,由于部分示例我这边没有进行测试,所以如果示例中的配置不好使,可以参考本文中比较全的配置文件

还有springCloud bus 结合rocketmq的架构,这个之后再说吧,https://blog.csdn.net/weixin_43847283/article/details/122419187

关联信息

  • 关联的主题:
  • 上一篇:
  • 下一篇:
  • image: 20221111/1
  • 转载自:
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 的详细文档。通过这种方式,我们可以快速构建消息驱动的微服务应用程序,并且具有高可靠、高吞吐量、高可扩展性等特点。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值