微服务 – Spring Cloud – Stream

Stream 是什么? 为什么要用Stream?

SpringCloud Stream是一个构建消息驱动微服务的框架,应用程序通过inputs或者 outputs来与SpringCloud Stream中的binder进行交互。其实就是为了适配底层消息队列的一个抽象出来的中间件。

使用 Stream 是为了 解决使用不同的消息队列技术所造成技术结构上的不同所带来的困扰。减少底层消息队列学习的一个成本,方便消息队列技术的迁移

如何使用 案列 - Rabbitmq

结合RabbitMQ的使用案例

Rabbitmq的安装不再赘述。小编采用的docker安装方式

1、消息生产者

引入pom依赖

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

配置文件

server:
  port: 8001

spring:
  application:
    name: cloud-stream-provider
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
  cloud:
    stream:
      binders:
        defaultRabbit:
          type: rabbit
      bindings:
        output:
          destination: HelloExchange
          content-type: application/json
          default-binder: defaultRabbit

eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:7001/eureka/
  instance:
    lease-renewal-interval-in-seconds: 2
    lease-expiration-duration-in-seconds: 5
    instance-id: sender-8001.com
    prefer-ip-address: true

业务类

public interface IMessageProvider {

    String send();

}
@EnableBinding(Source.class)
public class IMessageProviderImpl implements IMessageProvider {

    @Resource
    private MessageChannel output;

    @Override
    public String send() {
        String serial = UUID.randomUUID().toString();
        output.send(MessageBuilder.withPayload(serial).build());
        System.out.println("===========serial: " + serial);
        return null;
    }

}

@RestController
public class SendMessageController {

    @Resource
    private IMessageProvider messageProvider;

    @GetMapping(value = "/sendMessage")
    public String sendMessage() {
        return messageProvider.send();
    }

}

主启动类

@SpringBootApplication
public class RabbitProviderMain {

    public static void main(String[] args) {
        SpringApplication.run(RabbitProviderMain.class, args);
    }

}

**2、消息消费者1 **

引入pom依赖

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

配置文件

group: 就是queue name 。 同一个队列中多个消费者处于竞争关系,多个消费者属于不同的组,会出现重复消费的问题。 g roup也是消息持久化的配置

server:
  port: 8003

spring:
  application:
    name: cloud-stream-consumer2
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
  cloud:
    stream:
      binders:
        defaultRabbit:
          type: rabbit
      bindings:
        input:
          destination: HelloExchange
          content-type: application/json
          default-binder: defaultRabbit
          group: consumer1

eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:7001/eureka/
  instance:
    lease-renewal-interval-in-seconds: 2
    lease-expiration-duration-in-seconds: 5
    instance-id: receiver-8003.com
    prefer-ip-address: true

业务类

@RestController
@Component
@EnableBinding(Sink.class)
public class ReceiveMessageController {

    @Value("${server.port}")
    private String serverPort;

    @StreamListener(Sink.INPUT)
    public void  input(Message<String> message) {
        System.out.println("消费者2号, ============ 接收到的消息: " + message.getPayload() + "\t port: " + serverPort);
    }

}

主启动类

@SpringBootApplication
public class RabbitConsumerMain {

    public static void main(String[] args) {
        SpringApplication.run(RabbitConsumerMain.class, args);
    }

}

3、消息消费者2

引入pom依赖

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

配置文件

group: 就是queue name 。 同一个队列中多个消费者处于竞争关系,多个消费者属于不同的组,会出现重复消费的问题。 g roup也是消息持久化的配置

server:
  port: 8002

spring:
  application:
    name: cloud-stream-consumer2
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
  cloud:
    stream:
      binders:
        defaultRabbit:
          type: rabbit
      bindings:
        input:
          destination: HelloExchange
          content-type: application/json
          default-binder: defaultRabbit
          group: consumer1

eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:7001/eureka/
  instance:
    lease-renewal-interval-in-seconds: 2
    lease-expiration-duration-in-seconds: 5
    instance-id: receiver-8002.com
    prefer-ip-address: true

业务类

@RestController
@Component
@EnableBinding(Sink.class)
public class ReceiveMessageController {

    @Value("${server.port}")
    private String serverPort;

    @StreamListener(Sink.INPUT)
    public void  input(Message<String> message) {
        System.out.println("消费者3号, ============ 接收到的消息: " + message.getPayload() + "\t port: " + serverPort);
    }

}

主启动类

@SpringBootApplication
public class RabbitConsumerMain {

    public static void main(String[] args) {
        SpringApplication.run(RabbitConsumerMain.class, args);
    }

}

测试

同时运行 消息生产者 和两个消费者

访问: http://127.0.0.1:8801/sendMessage

在两个消费者控制台即可看到消费信息