在B站学习的springcloud的学习记录,第5天,最难不过坚持。
本文章只用于学习和分享,欢迎大家的建议,讨论和指点。
目录
Stream消息驱动
Springcloud Stream是一个构建消息驱动的微服务框架
设计思想
- 生产者/消费者之间靠消息媒介传递信息内容——Message
- 消息必须走特定的通道——消息通道MessageChannel
- 消息通道里的消息如何被消费呢,谁负责收发处理——消息通道MessageChannel的子接口SubscribableChannel,由MessageHandler消息处理器所订阅
Stream就是为了让我们不再关注MQ的细节,我们只需要一种适配绑定的方式,自动的给我们在各种MQ内切换
屏蔽底层消息中间件的差异,降低切换成本,统一消息的编程问题
通过cloud Stream中binder对象交互
Input 对应消费者,Output对应生产者。
入门案例
建model,改pom,写yml,主启动类,写业务
消息的生产者模块
改pom
<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/maven-v4_0_0.xsd">
<parent>
<artifactId>cloud2023</artifactId>
<groupId>com.liangliang</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-stream-rabbitmq-provider8801</artifactId>
<packaging>war</packaging>
<name>cloud-stream-rabbitmq-provider8801 Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<!-- rabbitmq-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
<!-- eureka-client 依赖jar包-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--以上两个一定携带出现 包括了图形化、图标等-->
<!-- 一些通用配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>cloud-stream-rabbitmq-provider8801</finalName>
</build>
</project>
写yml
server:
port: 8801
spring:
application:
name: cloud-stream-provider
cloud:
stream:
binders: # 在此处配置要绑定的rabbitmq的服务信息;
defaultRabbit: # 表示定义的名称,用于于binding整合
type: rabbit # 消息组件类型
environment: # 设置rabbitmq的相关的环境配置
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
bindings: # 服务的整合处理
#表示消息的生产者
output: # 这个名字是一个通道的名称
destination: studyExchange # 表示要使用的Exchange名称定义
content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
binder: defaultRabbit # 设置要绑定的消息服务的具体设置
eureka:
client: # 客户端进行Eureka注册的配置
service-url:
defaultZone: http://localhost:7001/eureka
instance:
lease-renewal-interval-in-seconds: 2 # 设置心跳的时间间隔(默认是30秒)
lease-expiration-duration-in-seconds: 5 # 如果现在超过了5秒的间隔(默认是90秒)
instance-id: send-8801.com # 在信息列表时显示主机名称
prefer-ip-address: true # 访问的路径变为IP地址
主启动类
@SpringBootApplication
public class StreamMain8801 {
public static void main(String[] args) {
SpringApplication.run(StreamMain8801.class, args);
}
}
业务
先创建一个接口
public interface IMessageProvider {
public String send();
}
为接口创建一个实现类
导入Binding的Source.class的包时不要导错了,导入这个:
import org.springframework.cloud.stream.messaging.Source;
import org.springframework.cloud.stream.messaging.Source;
@EnableBinding(Source.class) //定义消息的推送管道
public class IMessageProviderImpl implements IMessageProvider {
@Autowired
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 {
@Autowired
private IMessageProvider messageProvider;
@GetMapping(value = "/sendMessage")
public String sendMessage() {
System.out.println("哈哈哈");
return messageProvider.send();
}
}
然后启动rabbitmq,再启动项目,访问localhost:15672
然后当你连续的刷新项目的访问页面后,他会出现波峰
我们可以在后台看到
消息的接收者模块
建model,改pom,写yml
这些和8801端口的一样
除了在yml文件中将端口改成8802,和将bindings下的output修改成input,消息的接收者
server:
port: 8803
spring:
application:
name: cloud-stream-consumer
cloud:
stream:
binders: # 在此处配置要绑定的rabbitmq的服务信息;
defaultRabbit: # 表示定义的名称,用于于binding整合
type: rabbit # 消息组件类型
environment: # 设置rabbitmq的相关的环境配置
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
bindings: # 服务的整合处理
#表示消息的接收者
input: # 这个名字是一个通道的名称
destination: studyExchange # 表示要使用的Exchange名称定义
content-type: application/json # 设置消息类型,本次为对象json,如果是文本则设置“text/plain”
binder: defaultRabbit # 设置要绑定的消息服务的具体设置
group: GGB
eureka:
client: # 客户端进行Eureka注册的配置
service-url:
defaultZone: http://localhost:7001/eureka
instance:
lease-renewal-interval-in-seconds: 2 # 设置心跳的时间间隔(默认是30秒)
lease-expiration-duration-in-seconds: 5 # 如果现在超过了5秒的间隔(默认是90秒)
instance-id: receive-8803.com # 在信息列表时显示主机名称
prefer-ip-address: true # 访问的路径变为IP地址
主启动
@SpringBootApplication
public class StreamMQMain8802 {
public static void main(String[] args) {
SpringApplication.run(StreamMQMain8802.class,args);
}
}
业务——控制类
@Component
@EnableBinding(Sink.class)
public class ReceviceMessageListenController {
@Value("${server.port}")
private String serverPort;
@StreamListener(Sink.INPUT)
public void input(Message<String> message){
System.out.println("消息一号----->接收到的消息:"+message.getPayload()+"\t port:"+serverPort);
}
}
这次使用了@StreamListener的新注解,用来监听信息,当8801有消息的时候,进入页面或者刷新页面,8802都能接受到消息。如下图可以看到。
8802的后台
8801的后台
可以看到他们的uuid是一样的相对应的
分组消费和持久化
避免重复消费的问题,当有两个接收者(两个消息接受者模块),新建一个8803,与8802model一样,端口不同,运行起来后,8801发送消息,这样的话8802和8803都会接收到消息,因为现在还没有做到分组,这样就是出现重复消息的问题,这样是不行的。
要解决这个问题,就通过分组配置group来将微服务分组。
分组的原理:微服务应用放置于同一个group中,就能够保证消息只会被其中一个应用消费一次。不同的组是可以消费的,同一个组内会发生竞争关系,只有其中一个可以消费。
不配置的话,springcloud会默认这两个微服务是不同的组
做法:自定义配置分组,自定义配置分为同一个组,解决重复消费的问题。
修改yml文件,添加代码,
在8002端口的yml文件,input下添加 group: GGA
然后我们重新的启动项目,我们可以在rabbit MQ中看到服务分成了两个组。
但是这样还没解决问题,我们可以通过轮询分组,将两个微服务归并到一个组中,所以端口8803的yml文件也需要改,改成和8802端口的一样。
然后再访问8801发消息,我们可以看到8802和8803的后台是轮询的方式接收消息。