SpringCloud Stream消息驱动模块

说明

SpringCloud Stream是一个用来为微服务应用构建消息驱动能力的框架。它可以基于SpringBoot来创建独立的、可用于生产的spring应用程序。它通过使用Spring Integration来连接消息代理中间件来实现消息事件驱动。Spring Cloud Stream为一些供应商的消息中间件产品提供了个性化的自动化配置实现,并且引入了发布-订阅、消费组以及消息分区这三个核心概念。简单的说,Spring Cloud Stream本质上就是整合了Spring Boot和Spring Integration,实现了一套轻量级的消息驱动的微服务框架。通过使用Spring Cloud Stream,可以有效地简化开发人员对消息中间件的使用复杂度,让系统开发人员可以有更多的精力关注于核心业务逻辑的处理。

消费组:在很多情况下,消息生产者将消息发送给某个具体的微服务时,只希望被消费一次,我们可以通过设置spring.cloud.stream.bindings.input.group属性为应用指定一个组名,这样这个应用的多个实例在接收到消息的时候,只有一个成员真正接收到消息并进行处理。

消费分区:通过引入消费组的概念,我们已经能解决消息被多次消费的情况,但我们无法跟踪是哪个实例进行了处理,对于某些服务(如监控服务),我们需要跟踪其执行的过程,那么我们可以为消息设置一个特征的ID来标识,使得具有这些特征ID的消息每次都是被同一个消费者接收和处理。

目标

本文的目的在于结合RabbitMQ与Stream来处理消息通信,采取自定义编写Sink(input)和Source(output)来设置多通道消息和消费组、消费分区等操作实现基本的消息驱动的微服务架构。

快速使用

首先给出项目结构

SpringCloud Stream消息驱动模块_第1张图片

1 更新pom.xml配置



	4.0.0

	yunlingfly
	springcloudstream
	0.0.1-SNAPSHOT
	jar

	springcloudstream
	Demo project for Spring Boot

	
		org.springframework.boot
		spring-boot-starter-parent
		1.5.10.RELEASE
		 
	

	
		UTF-8
		UTF-8
		1.8
		Dalston.SR4
	

	
		
			org.springframework.boot
			spring-boot-starter-web
		

		
			org.springframework.cloud
			spring-cloud-starter-stream-rabbit
		

		
			org.springframework.boot
			spring-boot-starter-test
			test
		
	

	
		
			
				org.springframework.cloud
				spring-cloud-dependencies
				${spring-cloud.version}
				pom
				import
			
		
	

	
		
			
				org.springframework.boot
				spring-boot-maven-plugin
			
		
	

2 编写启动类

package yunlingfly.springcloudstream;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@EnableScheduling
public class SpringcloudstreamApplication {

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

3 配置application.yml

info:
  name: 一个springcloudstream
  version: 0.0.1
server:
  port: 8764
spring:
  rabbitmq:
    port: 5672
    username: xxxx
    password: xxxxxx
    host: localhost
  cloud:
    stream:
      # 设置当前消费者的总的实例个数和当前实例的索引
      instance-count: 2
      instance-index: 0
      bindings:
        # 以下是设置输入通道及分组和输出通道及类型
        input:
          destination: somestream
          content-type: application/json
          group: stream1
          #开启消息分区
          consumer:
            partitioned: true
        input2:
          destination: somestream2
          content-type: application/json
          group: stream2
        output:
          destination: somestream
          content-type: application/json
          producer:
            #设置分区键的表达式规则和设置消息分区数量
            partitionKeyExpression: payload
            partitionCount: 2
        output2:
          destination: somestream2
          content-type: application/json

4 编写消息发出者的接口

package yunlingfly.springcloudstream.sender;

import org.springframework.cloud.stream.annotation.Output;
import org.springframework.messaging.MessageChannel;
import org.springframework.stereotype.Component;

@Component
public interface SinkSenderInter {
    String OUTPUT="output";
    String OUTPUT2="output2";

    @Output(OUTPUT)   // 在这里设置输出通道,配置在application.yml里
    MessageChannel output();

    @Output(OUTPUT2)
    MessageChannel output2();
}

5 编写消息发出者的两个实现类(使用@Scheduled周期性发送消息)与使用到的POJO类

SinkSender:

package yunlingfly.springcloudstream.sender;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.scheduling.annotation.Scheduled;
import yunlingfly.springcloudstream.pojo.Person;

@EnableBinding(value = {SinkSenderInter.class})
public class SinkSender {
    @Autowired
    private SinkSenderInter sinkSenderInter;

    @Scheduled(initialDelay = 1000, fixedRate = 6000)    //设置初次执行延迟和执行频率
    public void sendmessage(){
        Person p=new Person();
        p.setMessage("some message");
        p.setName("芸灵fly");
        sinkSenderInter.output().send(MessageBuilder.withPayload(p).build());
    }
}

SinkSender2:

package yunlingfly.springcloudstream.sender;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
@EnableBinding(value = {SinkSenderInter.class})
public class SinkSender2 {
    @Autowired
    private SinkSenderInter sinkSenderInter;

    @Scheduled(initialDelay = 2000, fixedRate = 5000)
    public void sendmessage2(){
        sinkSenderInter.output2().send(MessageBuilder.withPayload("some message test from output2通道").build());
    }
}

Person(POJO):

package yunlingfly.springcloudstream.pojo;

public class Person {
    private String name;
    private String message;

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public String getName() {
        return name;
    }

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

6 编写消息接收者的接口

package yunlingfly.springcloudstream.receiver;

import org.springframework.cloud.stream.annotation.Input;
import org.springframework.messaging.SubscribableChannel;
import org.springframework.stereotype.Component;

@Component
public interface SinkReceiverInter {
    String INPUT="input";
    String INPUT2="input2";

    @Input(INPUT)    //配置在application.yml里了
    SubscribableChannel input();

    @Input(INPUT2)
    SubscribableChannel input2();
}

7 编写消息接收者的两个实现类

SinkReceiver:

package yunlingfly.springcloudstream.receiver;

import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.StreamListener;
import yunlingfly.springcloudstream.pojo.Person;

import java.util.Date;

@EnableBinding(value = {SinkReceiverInter.class})
public class SinkReceiver {
    @StreamListener(SinkReceiverInter.INPUT)
    public void receiver(Person payload){
        System.out.println("时间:"+new Date()+"接收到消息:"+payload.getMessage()+payload.getName());
    }
}

SinkReceiver2:

package yunlingfly.springcloudstream.receiver;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.cloud.stream.messaging.Sink;
import org.springframework.messaging.SubscribableChannel;

import java.util.Date;

@EnableBinding(value = {SinkReceiverInter.class})
public class SinkReceiver2 {

    @StreamListener(SinkReceiverInter.INPUT2)
    public void receiver(Object payload){
        System.out.println("时间:"+new Date()+"接收到消息:"+payload);
    }
}

8 为了方便演示,也使用了自带默认的Source(output)来编写了Controller层的代码

package yunlingfly.springcloudstream.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.messaging.Source;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.MessageChannel;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;


@RestController
@EnableBinding(Source.class)
public class HelloController {
    @Autowired
    @Qualifier("output")
    MessageChannel output;      // 直接使用默认的Source来输出

    @RequestMapping(value = "/stream",method = RequestMethod.GET)
    public String hello(){
        // 发送一条消息
        output.send(MessageBuilder.withPayload("大家好").build());
        return "success";
    }
}

9 (这一步可以不做)如果要看到消费者组和分区的效果,请新建一个和上文一样的pom.xml和启动类,再将Receiver层的代码一样的引入,application.yml修改为以下(主要改了instance-index属性即可)

server:
  port: 8765
spring:
  rabbitmq:
    port: 5672
    username: xxxx
    password: xxxxxxx
    host: localhost
  cloud:
    stream:
      # 设置当前消费者的总的实例个数和当前实例的索引
      instance-count: 2
      instance-index: 1
      bindings:
        input:
          destination: somestream
          content-type: application/json
          group: stream1
          #开启消息分区
          consumer:
            partitioned: true
info:
  name: 另一个springcloudstram
  version: 0.0.1

10 启动项目即可看到效果
 

SpringCloud Stream消息驱动模块_第2张图片

SpringCloud Stream消息驱动模块_第3张图片

(也可以直接通过RabbitMQ网页控制端向队列输出消息)

先点击Queues选择需要输出的队列

SpringCloud Stream消息驱动模块_第4张图片

点击Name那一行后如下图输入PayLoad后点击Publish message即可

SpringCloud Stream消息驱动模块_第5张图片

 

 

 

 

 

 

你可能感兴趣的:(Spring,Cloud)