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)来设置多通道消息和消费组、消费分区等操作实现基本的消息驱动的微服务架构。

快速使用

首先给出项目结构

1 更新pom.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>yunlingfly</groupId>
	<artifactId>springcloudstream</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>springcloudstream</name>
	<description>Demo project for Spring Boot</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.10.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
		<spring-cloud.version>Dalston.SR4</spring-cloud.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

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

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

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 启动项目即可看到效果
 

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

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

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

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值