SpringCloudStream 3.x 新玩法

SpringCloudStream 3.x 新玩法

在前文已经介绍过 SpringCloudStream 的玩法,没看过的小伙伴可以点进去看看。今天带来的是SpringCloudStream 3.x新玩法,通过四大函数式接口的方式进行数据的发送和监听。本文将通过 rabbitMQkafka 的方式进行 demo 演示,以及两种消息中间件的快速切换。

如果还有不知道四大函数式接口的小伙伴,建议先查阅资料后再来看本文。

3.x版本后是 可以看到 @StreamListener 和 @EnableBinding 都打上了@Deprecated 注解。后续的版本更新中会逐渐替换成函数式的方式实现。
既然通过四大函数式接口的方式替换了注解的方式 那么 该如何进行绑定呢?通过:spring.cloud.stream.function.definition: 名称 的方式进行绑定 公开 topic。不管是创建 Consumer 还是 Supplier 或者是 Function Stream都会将其的 方法名称 进行 一个 topic 拆封 和 绑定 假设 创建了一个 Consumer< String > myTopic 的方法,Stream 会将其 拆分成 In 和 out 两个通道
input - < functionName > + -in- + < index >
output - < functionName > + -out- + < index >
格式拆分
myTopic-in-0
myTopic-out-0

还是老规矩先引入 maven 坐标

本demo的springboot版本是 2.6.7 找到 SpringCloudStream kafka 对应的版本,先讲基于kafka。

		<!-- kafka -->
        <dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-stream</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-stream-binder-rabbit</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-stream-binder-kafka</artifactId>
		</dependency>

消费端Consumer

先创建好消费端Consumer 用于监听消息!

@Configuration
public class MgsConsumer {
    @Bean
    public Consumer<Message<String>> myTopicC() {
        return (data) -> {
            String payload = data.getPayload();
            MessageHeaders headers = data.getHeaders();
            Object headerFor = headers.get("for");
            System.out.println("myTopicC 接收一条记录:" + payload);
            System.out.println("getHeaders headerFor:" + headerFor);
        };
    }
}
生产端Producer

这种方式定义suppelier 会 默认1000ms 发送一次记录

@Configuration
public class MgsProducer {
    Integer i = 1;

    @Bean
    public Supplier<Message<MsgUser>> myTopicP() {
        return () -> {
            MsgUser payload = new MsgUser();
            payload.setId(i++);
            payload.setName(Thread.currentThread().getName());
            System.out.println("myTopicP 发送一条记录:" + payload);
            return MessageBuilder
                    .withPayload(payload)
                    .build();
        };
    }
}

修改yml文件
spring:
  cloud:
    stream:
      kafka:
        binder:
          brokers: localhost:9092
# -------------- 分割线 ---------------
      function:
      # 组装和绑定
        definition: myTopicC;myTopicP
      bindings:
        myTopicC-in-0:
          destination: my-topic
          group: my_input_group
       myTopicP-out-0:
          destination: my-topic

打印

myTopicC 接收一条记录:{"name":"scheduling-1","id":1}
getHeaders headerFor:null
myTopicP 发送一条记录:MsgUser(name=scheduling-1, id=2)
myTopicC 接收一条记录:{"name":"scheduling-1","id":2}
getHeaders headerFor:null
myTopicP 发送一条记录:MsgUser(name=scheduling-1, id=3)
myTopicC 接收一条记录:{"name":"scheduling-1","id":3}
getHeaders headerFor:null
myTopicP 发送一条记录:MsgUser(name=scheduling-1, id=4)
myTopicC 接收一条记录:{"name":"scheduling-1","id":4}
getHeaders headerFor:null
myTopicP 发送一条记录:MsgUser(name=scheduling-1, id=5)
myTopicC 接收一条记录:{"name":"scheduling-1","id":5}
getHeaders headerFor:null

可以看到是正常进行创建消息以及消费的,但是实际使用中肯定是需要通过手动的方式进行发送,这个时候我们可以修改一下发送方式。

通过手动发送创建消息

关键对象 StreamBridge

@RequiredArgsConstructor
@Service
public class SendService {
    private final StreamBridge streamBridgeTemplate;

    public void sendMsg(String msg, Integer id) {
        MsgUser payload = new MsgUser();
        payload.setId(id);
        payload.setName(msg);
        System.out.println("sendMsg 发送一条记录:" + payload);
        streamBridgeTemplate
                .send(
                        "myTopicC-out-0",
                        MessageBuilder.withPayload(payload)
                                .setHeader("for", "这是一个请求头~")
                                .build());
    }
}

修改一下 yml 文件 将 MgsProducer 类注释起来

spring:
  cloud:
    stream:
      kafka:
        binder:
          brokers: localhost:9092
# -------------- 分割线 ---------------
      function:
      	# 组装和绑定
        definition: myTopicC
      bindings:
        myTopicC-in-0:
          destination: my-topic
          group: my_input_group
        myTopicC-out-0:
          destination: my-topic

通过Controller发送了一条记录

GET http://localhost:8080/send?id=331&name=张三
sendMsg 发送一条记录:MsgUser(name=张三, id=331)

myTopicC 接收一条记录:{"name":"张三","id":331}
getHeaders headerFor:这是一个请求头~

能够看到kafka正常的接收到了一条记录,到这里就是收发数据的一个简单示例。

Function的作用

Function< String,String > 范型中有两个参数 一个入参 一个出参,所以在Stream中 可以用来作于一个消息中转站来使用。相当于 top-1 接受到消息 但是我不想处理 我对其数据进行一次处理 发送到 top-2 通道,交给top-2 进行数据的最终处理。

新建一个MgsFunction

@Configuration
public class MgsFunction {

    @Bean
    public Function<String, String> testFunction() {
        return value -> {
            System.out.println("中转 testFunction: " + value);
            return value.toUpperCase();
        };
    }
}

在MgsConsumer中新加一个处理方法


@Configuration
public class MgsConsumer {
    @Bean
    public Consumer<Message<String>> myTopicC() {
        return (data) -> {
            String payload = data.getPayload();
            MessageHeaders headers = data.getHeaders();
            Object headerFor = headers.get("for");
            System.out.println("myTopicC 接收一条记录:" + payload);
            System.out.println("getHeaders headerFor:" + headerFor);
        };
    }
    @Bean
    public Consumer<String> testFunctionQ() {
        return (data) -> {
            System.out.println("testFunctionQ 消息中转后接收一条记录:" + data);
        };
    }
}

修改SendService发送的通道

@RequiredArgsConstructor
@Service
public class SendService {
    private final StreamBridge streamBridgeTemplate;

    public void sendMsg(String msg, Integer id) {
        MsgUser payload = new MsgUser();
        payload.setId(id);
        payload.setName(msg);
        System.out.println("sendMsg 发送一条记录:" + payload);
        streamBridgeTemplate
                .send(
                        "myTopicP-out-0",
                        MessageBuilder.withPayload(payload)
                                .setHeader("for", "这是一个请求头~")
                                .build());
    }
}

修改配置文件

spring:
  cloud:
    stream:
      kafka:
        binder:
          brokers: localhost:9092
# -------------- 分割线 ---------------
      # 组装和绑定
      function:
        definition: myTopicC;myTopicP;testFunction;testFunctionQ
      bindings:
        myTopicC-in-0:
          destination: my-topic
          group: my_input_group
        myTopicC-out-0:
          destination: my-topic
        myTopicP-out-0:
          destination: test-topic
        testFunction-in-0:
          destination: test-topic
          group: my_input_group
        testFunction-out-0:
          destination: test-topic-Q
        testFunctionQ-in-0:
          destination: test-topic-Q
          group: my_input_group
sendMsg 发送一条记录:MsgUser(name=张三, id=331)
2022-12-05 10:59:07.869  INFO 12888 --- [nio-8080-exec-3] o.s.c.s.binder.DefaultBinderFactory      : Retrieving cached binder: kafka
中转 testFunction	: {"name":"张三","id":331}
testFunctionQ 消息中转后接收一条记录:{"NAME":"张三","ID":331}

可以看到 我们这边写了一个消息中转 toUpperCase 的方法是正常使用到了 这边看起来会有点绕,仔细理解一下就会发现挺简单的。

1.发送通道 test-topic
myTopicP-out-0  《topic》  test-topic

2.消费通道 test-topic
testFunction-in-0  《topic》  test-topic

3.然后testFunction-out-0做了一个消息中转 发送给了 test-topic-Q
testFunction-out-0   《topic》  test-topic-Q

4.testFunctionQ-in-0 进行了 test-topic-Q 通道绑定 所以能接收到testFunction-out-0 中转发送出来的消息
testFunctionQ-in-0   《topic》  test-topic-Q
从 kafka切换到rabbtMq

切换起来很简单,修改配置文件即可

spring:
  cloud:
    stream:
# --------------- rabbitmq ---------------
      rabbit:
        binder:
          admin-addresses: localhost:15672
# --------------- kafka ---------------
#      kafka:
#        binder:
#          brokers: localhost:9092
# -------------- 分割线 ---------------
      # 组装和绑定
      function:
        definition: myTopicC;myTopicP;testFunction;testFunctionQ
      bindings:
        myTopicC-in-0:
          destination: my-topic
          group: my_input_group
        myTopicC-out-0:
          destination: my-topic
        myTopicP-out-0:
          destination: test-topic
        testFunction-in-0:
          destination: test-topic
          group: my_input_group
        testFunction-out-0:
          destination: test-topic-Q
        testFunctionQ-in-0:
          destination: test-topic-Q
          group: my_input_group
sendMsg2Sup 发送一条记录:MsgUser(name=张三, id=331)
2022-12-05 11:21:04.221  INFO 14013 --- [nio-8080-exec-3] o.s.c.s.binder.DefaultBinderFactory      : Retrieving cached binder: rabbit
中转 testFunction: {"name":"张三","id":331}
testFunctionQ 消息中转后接收一条记录:{"NAME":"张三","ID":331}

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值