Spring Cloud学习|第六篇:消息驱动-Stream

1.Stream简介

Spring Cloud Steam是一个用来为微服务应用构建消息驱动能力的框架,它可以基于Spring Boot创建独立的可用于生产的Spring应用程序。它通过使用Spring Integration来连接消息代理中间件实现消息事件驱动。

[外链图片转存失败(img-QPLgJFTP-1568014059889)(/home/zycao/.config/Typora/typora-user-images/1567994635306.png)]

​ 上图为官方提供的Spring Cloud Stream运行图,图中定义了inputs、outputs分别表示消息的输入通道和输出通道。

Binder:为绑定器用于消息交换,Binder起承上启下作用,通过Binder我们的应用程序就不需要直接与下层消息中间件打交通,所有的封装空全交给Binder,我们只需关心业务逻辑即可

Middleware:表示具体的消息中间件,可以是Rabbitmq或Kafka(目前只支持这两种)

2.入门案例

服务端口作用
consul8500注册中心
spring-cloud-stream-provider8888消息发送端
spring-cloud-stream-consumer8889消息接收端
  • 创建spring-cloud-stream-provider服务

  • 引入依赖

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
    </dependency>
    
  • 书写发送端binder

    public interface MySource {
    
        String OUT_PUT = "send_exchange";
    
        /**
       * @return output channel
       */
        @Output(OUT_PUT)
        MessageChannel sendMsg();
    
    }
    
  • 书写发送消息逻辑

    @Service
    @EnableBinding(MySource.class)
    public class SenderServiceImpl implements SenderService {
    
        @Resource
        private MySource sendChannel;
    
        @Override
        public void sendMsg(String msg) {
            sendChannel.sendMsg().send(MessageBuilder.withPayload(msg).build());
        }
    }
    

    (1)通过@EnableBinding绑定消息通道,即上述所说的定义绑定器

    (2)通过MessageBuilder定义发送消息内容

    (3)调用通道的send()方法发送消息

  • 书写配置文件application.yml

    server:
      port: 8888
    
    spring:
      rabbitmq:
        host: 192.168.1.100
        port: 5672
        username: admin
        password: admin
        virtual-host: /study-rabbitmq
      cloud:
        stream:
          bindings:
            send_exchange:
              destination: test-exchange
    

    (1)spring.rabbitmq:表示配置rabbitmq基本信息,如果不配置,则使用默认配置。默认配置如下:

    spring.rabbitmq.host:localhost
    spring.rabbitmq.port:5672
    spring.rabbitmq.username:guest
    spring.rabbitmq.password:guest
    

    (2)spring.cloud.stream:配置具体的消息通道信息

    send_exchange:表示我们的绑定器中设置的输出通道

    destination:表示消息具体发送到的交换机,对应rabbitmq中的exchange

    完成上述步骤,则我们的消息发送端已经书写完毕,下边继续完成消息接收端服务的书写。

  • 创建服务消费者

  • 引入依赖

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
    </dependency>
    
  • 书写接收端binder

    public interface MySkin {
    
        String INPUT = "send_exchange";
    
        /**
       * @return output channel
       */
        @Input(INPUT)
        SubscribableChannel receiveMsg();
    
    }
    
  • 书写接收端application.yml

    server:
      port: 8889
    
    
    spring:
      rabbitmq:
        host: 192.168.1.100
        port: 5672
        username: admin
        password: adm过in
        virtual-host: /study-rabbitmq
      cloud:
        stream:
          bindings:
            send_exchange:
              destination: test-exchange
    

    配置与发送端相同,完成上述配置,启动两个服务,发送端发送消息,接收端能接收到消息,表示通过Spring Cloud Stream实现消息发送与接收案例已经完成。

3.基本概念

通过上述入门案例,我们对Spring Cloud Stream有了初步的认识,下边具体讲解一下Stream其它基本概念。

  • 消费组

    简单理解消费者,见名知意就是将服务进行分组。分组后,在同一组的服务实例只会有一个服务实例可以接收到消息,通过在消费端配置如下配置进行消息分组。

    spring.cloud.bindings.send_exchange.group=group-a
    

    通过该配置,则所有起动的group-a分组的服务实例中只有一个服务实例可以获取到消息。当启动多个服务分组时,每个服务分组中只会有一个服务实例可以获取到消息,通过此即完成了消息分组。

  • 消息分区

    可以通过设置分区,让每一次服务消费都为指定分区服务消费,配置如下

    (1)服务提供方

    spring.cloud.bindings.send_exchange.producer.partitionCount:2
    spring.cloud.bindings.send_exchange.producer.partitionKeyExpression:headers['partitionKey']
    

    partitionCount:表示分区总数

    partitionKeyExpression:表示设置分区表达式,通过表达式进行设置值,在此通过headers设置值,则在发送消息时,对headers进行设置

    @GetMapping("/sendMessage")
    public void sendMessage(@RequestParam("msg") String msg) {
        System.out.println("发送消息:"+msg);
        mySource.sendMsg().send(MessageBuilder.withPayload(msg).setHeader("partitionKey",1).build());
    }
    

    通过上述设置,则只有设置分区为1的服务实例能收到消息。

    (2)服务消费方

    spring.cloud.bindings.send_exchange.consumer.partitioned:true
    spring.cloud.bindings.send_exchange.consumer.instanceCount:2
    spring.cloud.bindings.send_exchange.consumer.instanceIndex:1
    

    partitioned=true:表示开启分区

    instanceCount:表示服务分区总数

    instanceIndex:表示当前服务分区值,从0开始,-1表示所有分区

  • 开启绑定功能

    通过@EnableBinding注解来绑定消息通道,查看其注解详情如下:

    @Target({ ElementType.TYPE, ElementType.ANNOTATION_TYPE })
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @Configuration
    @Import({ BindingBeansRegistrar.class, BinderFactoryConfiguration.class })
    @EnableIntegration
    public @interface EnableBinding {
    
    	Class<?>[] value() default {};
    
    }
    

    通过上边可以看出该注解就是一个Spring boot的配置,BindingBeansRegistrar作用即扫描@EnableBinding配置的value值,然后将其加入Spring容器的beanMap中。因此我们在自定义channel时,未将自定义的接口加入spring中,但是依然能使用。

  • 绑定消息通道

    Stream默认定义了三个消息通道,分别时SinkSourceProcessor,三个消息通道源码如下:

    public interface Sink {
    
    	/**
    	 * Input channel name.
    	 */
    	String INPUT = "input";
    
    	/**
    	 * @return input channel.
    	 */
    	@Input(Sink.INPUT)
    	SubscribableChannel input();
    
    }
    
    public interface Source {
    
    	/**
    	 * Name of the output channel.
    	 */
    	String OUTPUT = "output";
    
    	/**
    	 * @return output channel
    	 */
    	@Output(Source.OUTPUT)
    	MessageChannel output();
    
    }
    
    public interface Processor extends Source, Sink {
    
    }
    

    三个消息通道通过@Input@Output来定义是消息输入还是输出,只需在配置中定义好输入、输出消息目的地即可实现消息的传递。

  • 注入消息

    在定义消息通道后,我们可以将消息通道中的消息注入到需使用的地方,有两种方式,可以直接接口,通过接口调用。也可以通过别名方式注入,分别用法如下。

    (1)定义通道信息

    public interface MySource {
    
      String OUT_PUT = "send_exchange";
      @Output(OUT_PUT)
      MessageChannel sendMsg();
    
      String OUT_PUT1 = "send_1";
    
      @Output(OUT_PUT1)
      MessageChannel sendMsg1();
    }
    

    (2)使用方式一

    @Service
    @EnableBinding({MySource.class, Processor.class})
    public class SenderServiceImpl implements SenderService {
    
      @Resource
      private MySource sendChannel;
    
      @Override
      public void sendMsg1(String msg) {
        sendChannel.sendMsg().send(MessageBuilder.withPayload(msg).setHeader("partitionKey",1).build());
      }
    
      @Override
      public void sendMsg2(String msg) {
        sendChannel.sendMsg1().send(MessageBuilder.withPayload(msg).setHeader("partitionKey",1).build());
      }
    

    (3)使用方式二

    @Service
    @EnableBinding({MySource.class, Processor.class})
    public class SenderServiceImpl implements SenderService {
    
      @Qualifier("send_exchange")
      private MessageChannel output1;
    
      @Qualifier("send_1")
      private MessageChannel output2;
    
    
      @Override
      public void sendMsg(String msg) {
        output1.send(MessageBuilder.withPayload(msg).setHeader("partitionKey",1).build());
      }
    
      @Override
      public void sendMsg2(String msg) {
        output2.send(MessageBuilder.withPayload(msg).setHeader("partitionKey",1).build());
      }
    

7.参考资料

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值