spring boot版本:2.1.10.RELEASE
spring cloud版本:Greenwich.SR4
stream结构图
stream 解决了什么问题?
Stream 解决了开发人员无感知使用消息中间件的问题。
因为Stream 对消息中间件的进一步封装,可以做到代码层面对中间件的无感知,甚至于动态的切换中间件(例如从RabbitMQ 切换为Kafka)。使得微服务开发的高度解耦,服务可以关注更多自己的业务流程。
Middleware
消息中间件,目前只支持 RabbitMQ 和 Kafka 。
Binder
Binder是应用与消息中间件之间的封装。目前实现了 Kafka 和 Rabbit MQ 的binder。通过 binder,可以很方便的连接中间件,可以动态的改变消息类型(对应于 Kafka 的topic,Rabbit MQ 的 exchanges),这些都可以通过配置文件来实现。
常用注解作用
- @Input:注释标识输入通道,通过该输入通道接收到的消息进入应用程序;
- @Output:注释标识输出通道,发布的消息将通过该通道离开应用程序;
- @StreamListener:监听队列,用于消费者的队列的消息接收;
- @EnableBinding:指信道channel和exchange绑定在一起。
本次示例有两个项目 stream-receiver
(消息接收者服务) 和 stream-sender
(消息发送者服务)。
以下配置以 rabbitmq
中间件为例。
相关依赖
消息发送者和消费者都是该依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
相关配置
#配置eureka注册中心
eureka.client.service-url.defaultZone=http://192.168.xxx.xxx:8761/eureka/,http://192.168.xxx.xxx:8762/eureka/
eureka.instance.prefer-ip-address=true
#rabbitmq配置
spring.rabbitmq.host=192.168.xxx.xxx
spring.rabbitmq.port=5672
spring.rabbitmq.username=admin
spring.rabbitmq.password=admin
spring.rabbitmq.virtual-host=/
stream-receiver 项目
(1)接收类接口
package com.ebook.stream;
import org.springframework.cloud.stream.annotation.Input;
import org.springframework.messaging.SubscribableChannel;
/**
* @author:JZ
* @date:2020/2/2
*/
public interface IReceiveService {
@Input("jz-exchange")
SubscribableChannel receive();
}
(2)接收类接口的实现类
package com.ebook.stream;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.stereotype.Service;
/**
* @author:JZ
* @date:2020/2/2
*/
@Service
@EnableBinding({IReceiveService.class})
public class ReceiveService {
@StreamListener("jz-exchange")
public void onReceive(byte[] msg) {
System.out.println("receive:" + new String(msg));
}
}
stream-sender 项目
(1)发送接口
package com.ebook.stream;
import org.springframework.cloud.stream.annotation.Output;
import org.springframework.messaging.SubscribableChannel;
/**
* @author:JZ
* @date:2020/2/2
*/
public interface ISendService {
//这里的jz-exchange与stream-receiver项目中的一直
@Output("jz-exchange")
SubscribableChannel send();
}
(2)在启动类中添加注解
package com.ebook.stream;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.stream.annotation.EnableBinding;
/**
* @author:JZo
* @date:2020/1/11
*/
@SpringBootApplication
@EnableEurekaClient
@EnableBinding({ISendService.class})//注册成bean
public class StreamSenderApplication {
public static void main(String[] args) {
SpringApplication.run(StreamSenderApplication.class, args);
}
}
(3)运行测试类,发送消息
import com.ebook.stream.ISendService;
import com.ebook.stream.StreamSenderApplication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = StreamSenderApplication.class)
public class StreamTest {
@Autowired
private ISendService send;
@Test
public void send() throws InterruptedException {
//也可以是自定义的实体类
String msg = "jz.............";
Message message = MessageBuilder.withPayload(msg.getBytes()).build();
this.send.send().send(message);
}
}
总结
这样的方式会在rabbitmq中创建一个名为 jz-exchange
的 topic
类型交换器,且路由键采用 模糊匹配 的方式。消息是通过自动生成的 非持久化 队列发送的。
通过以上方式搭建的项目,接收者如果是集群布置,但是每个服务都能接收到发送的消息,而且每个服务都是通过不同的队列发送的。这样就达不到集群部署的目的,可通过 Spring Cloud Stream【消息分组和消息分区】 解决。