Stream消息驱动

Spring Cloud Stream 的 binder 负责与消息中间件交互(和消息中间件解耦,不需要关注具体的消息中间件)应用通过Spring Cloud Stream插入的input(相当于消费者consumer,它是从队列中接收消息的)和output(相当于生产者producer,它是从队列中发送消息的。)通道与外界交流。当需要升级消息中间件,或者是更换其他消息中间件产品时,我们需要做的就是更换对应的Binder绑定器而不需要修改任何应用逻辑。
在这里插入图片描述

Stream内置了三个接口,分别是Source,Sink,Processor

Source是输出通道,通道名是output

public interface Source {
    String OUTPUT = "output";

    @Output("output")
    MessageChannel output();
}

Sink是输入通道,通道名是input

public interface Sink {
    String INPUT = "input";

    @Input("input")
    SubscribableChannel input();
}

Processor即是输入通道,也是输出通道,通道名是output,input

public interface Processor extends Source, Sink {
}

接下来我们创建三个模块stream_client_1(端口8091),stream_client_2(8092)stream_client_3(8093)
三个模块都添加依赖,这里我们为了方便就没有到服务中心注册,我们测试的话,只启动这三个模块就行了。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-stream-binder-kafka</artifactId>
</dependency>

stream_client_1模块作为消息生产者

application.yml文件

server:
  port: 8091
spring:
  application:
    name: producer
  cloud:
    stream:
      kafka:
        binder:
        	#绑定kafka
          brokers: localhost:9092
          #如果设置为false,就不会自动创建Topic 有可能你Topic还没创建就直接调用了。
          auto-create-topics: true
      bindings:
       #使用Source 提供的output,输出通道
        output:
          #消息发往的目的地
          destination: stream-demo
          #消息发送的格式,使用文本的格式发送
          content-type: text/plain

我们创建一个user类,这个user就是我们发送的消息

public class User {
    private int id;
    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

@EnableBinding是用来绑定消息通道

@EnableBinding(Source.class)
public class SendUser {
    @Autowired
    private Source source;
	
    public void sendUser(User user){
    	//发送消息
        source.output().send(MessageBuilder.withPayload(user).build());
    }
}

定义一个controller调用sendUser方法

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private SendUser sendUser;

    @RequestMapping("/sendUser")
    public void sendUser(){
        User user = new User();
        user.setId(1);
        user.setName("小红");
        sendUser.sendUser(user);
    }
}

接着我们创建stream_client_2和stream_client_3模块,两个模块一样

server:
  port: 8092
spring:
  application:
    name: consumer
  cloud:
    stream:
      kafka:
        binder:
          auto-create-topics: true
          brokers: localhost:9092
      bindings:
      #使用Sink的输入通道
        input:
        #上面我们是向stream-demo通道中发送消息
          destination: stream-demo

接收方法

//绑定Sink通道
@EnableBinding(Sink.class)
public class Consumer1 {
	//使用StreamListener监听input通道
    @StreamListener(Sink.INPUT)
    public void getUser(Object obj){
        System.out.println(obj);
    }
}

当我们访问发送消息的方法时,接收端会收到消息:
在这里插入图片描述
在这里插入图片描述
两个接收者都接收到了消息,但是我们有时候只要一个接收者接收消息怎么办?
我们可以将两个接收者放到同一个组中,这样他们就会互相竞争,最终只有一个接收者接收到消息。不同的组不会产生竞争关系。

我们只需要在接收端加上分组就可以了。

bindings:
    input:
      destination: stream-demo
      #两个接收端分到同一个组中会相互竞争
      group: student

这样我们在发送消息时,只会有一个接收端接收到消息。

在这里插入图片描述
在这里插入图片描述

接下来我们做另一个demo,client1发送消息到client2,client2可以拿到消息进行修改,然后转发给cllient3,这样我们就要用到stream提供的第三个通道Processor

接下来我们修改client1发送端,将格式改为json格式

output:
      destination: stream-demo
      content-type: application/json

下面是client2转发端

bindings:
    input:
    	#还是接收client1发送的数据
      destination: stream-demo
    output:
    #2向这个通道发送数据,让3接收这个通道的数据
      destination: transfer
      content-type: application/json

创建一个中转站类

@EnableBinding(Processor.class)
public class Transfer {

	//输入通道是input,输出通道是output
    @ServiceActivator(inputChannel = Processor.INPUT, outputChannel = Processor.OUTPUT)
    public Object transfer(Object obj){
        System.out.println(obj);
        return "加工:"+obj;
    }
}

这里注意要将前面绑定的@EnableBinding(Sink.class)给注释掉,要不然会报重复绑定。

接着是client3修改接收通道

bindings:
    input:
    	#接收2发送的消息
      destination: transfer

在这里插入图片描述
在这里插入图片描述
我们可以看到消息成功转发,而且是json格式。
这里有一个问题没有弄明白,就是client1发送text/plain格式,而client2发送application/json格式,client3收到的却是text的格式?

在实际生产中,我们不可能仅仅使用stream提供的三个通道,这个远远不够的,因此我们要自定义消息通道,其实很简单,就是比这葫芦画瓢。

首先我们来改client1,这是发送端,我们比这模拟一个myOutput通道

public interface MyOutput {
    String MYOUTPUT = "myOutput";

    @Output("myOutput")
    //方法名是什么无所谓
    MessageChannel output();
}

发送方法

//这里我们绑定的是自定义的消息输出通道
@EnableBinding(MyOutput.class)
public class SendUser {
    @Autowired
    private MyOutput myOutput;

    public void sendUser(User user){
        myOutput.output().send(MessageBuilder.withPayload(user).build());
    }
}

修改配置文件

bindings:
	#不要忘了,这个通道要换成我们自定义的
   myOutput:
      destination: stream-demo
      #发送端发送的是text格式
      content-type: text/plain

接着我们修改client3,这是消息接收端,我们来模拟一个myInput通道

public interface MyInput {
    String MYINPUT = "myInput";

    @Input("myInput")
    MessageChannel input();
}

接收方法

@EnableBinding(MyInput.class)
public class Consumer2 {

    @StreamListener(MyInput.MYINPUT)
    public void getUser(Object obj){
        System.out.println(obj);
    }
}

修改配置

bindings:
    myInput:
      destination: transfer

然后我们修改client2,这是中转站,我们模拟transfer通道

public interface MyTransfer {
    String TRANSFERTO = "transferTo";
    String TRANSFERIN = "transferIn";

    @Output("transferTo")
    MessageChannel transferTo();

    @Input("transferIn")
    MessageChannel transferIn();
}

换成我们自己的通道

@EnableBinding(MyTransfer.class)
public class Transfer {

    @ServiceActivator(inputChannel = MyTransfer.TRANSFERIN, outputChannel = MyTransfer.TRANSFERTO)
    public Object transfer(Object obj){
        System.out.println(obj);
        return "加工:"+obj;
    }
}

修改配置

bindings:
    transferIn:
      destination: stream-demo
    transferTo:
      destination: transfer
      #发送的是json格式
      content-type: application/json

最后启动程序:

在这里插入图片描述
在这里插入图片描述
client1发送的是text格式,client2转发的是json格式,但是最后client3收到的却是text格式?这个问题目前还没有搞懂。

猜你喜欢

转载自blog.csdn.net/ROAOR1/article/details/88317848