强烈建议先去看我的Rocketmq: Rocketmq集群搭建
给大家看看原理
Spring Cloud Bus是用来将分布式系统的节点与轻量级消息系统链接起来的框架,
它整合了Java的事件处理机制和消息中间件的功能。Spring Clud Bus目前支持RabbitMQ和Kafka。
什么是总线?
在微服务架构的系统中,通常会使用轻量级的消息代理来构建一个共用的消息主题,并让系统中所有微服务实例都连接上来。由于该主题中产生的消息会被所有实例监听和消费,所以称它为消息总线。在总线上的各个实例,都可以方便的广播一些需要让其他连接在该主题上的实例都知道的消息。
基本原理
ConfigClient 实例都监听MQ中同一个topic(默认是springcloubus),当一个服务刷新数据的时候,它会把这个信息放入到Topic中,这样其他监听统一topic的服务就能得到通知,然后去更新自身的配置。
这里头比较铁啊,因为用的是springcloud-alibaba,所以自然肯定要用巴巴的rocketmq啊,而且之前还学过,配过集群环境与控制台,那我们就尝试一下Rocketmq嘻嘻~
<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">
<parent>
<artifactId>cloud2020artifactId>
<groupId>com.aiguigu.springcloudgroupId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>cloud-stream-rocketmq-provider8801artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-streamartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-stream-binder-rocketmqartifactId>
<version>0.2.1.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
project>
server:
port: 8801
spring:
cloud:
stream:
rocketmq:
binder:
namesrv-addr: 公网IP1:9876,公网IP2:9876
# 定义name为output的binding
bindings:
output: # 定义name为output的binding
destination: springboot-mq #相当于topic
content-type: application/json
# 定义name为input的binding
# input1:
# destination: test-topic
# content-type: application/json
# group: test-group
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地址
package com.atguigu.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@EnableEurekaClient
@SpringBootApplication
public class StreamMQMain8801 {
public static void main(String[] args) {
SpringApplication.run(StreamMQMain8801.class,args);
}
}
package com.atguigu.springcloud.controller;
import com.atguigu.springcloud.service.IMessageProvider;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
public class SendMessageController {
@Resource
private IMessageProvider messageProvider;
@GetMapping("/sendMessage")
public String sendMessage(){
return messageProvider.send();
}
}
package com.atguigu.springcloud.service;
public interface IMessageProvider {
public String send();
}
package com.atguigu.springcloud.service.impl;
import com.atguigu.springcloud.service.IMessageProvider;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.messaging.Source;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.support.MessageBuilder;
import javax.annotation.Resource;
import java.util.UUID;
@EnableBinding(Source.class)
public class IMessageProviderImpl implements IMessageProvider {
@Resource
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;
}
}
这里测试类,我们用之前Rocketmq章结用的一个consumer来监听这个topic下所有tags
也可以不用一下测试类,因为还要导pom,之后再写一个8802项目作为服务消费者
<dependency>
<groupId>org.apache.rocketmqgroupId>
<artifactId>rocketmq-clientartifactId>
<version>4.4.0version>
dependency>
package com.itheima.mq.rocketmq.base.consumer;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.protocol.heartbeat.MessageModel;
import java.util.List;
/**
* 消息的接受者
*/
public class Consumer {
public static void main(String[] args) throws Exception {
//1.创建消费者Consumer,制定消费者组名
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group1");
//2.指定Nameserver地址
consumer.setNamesrvAddr("一号服务器公网IP:9876;二号服务器公网IP:9876");
//3.订阅主题Topic和Tag
consumer.subscribe("springboot-mq", "*");
consumer.setMessageModel(MessageModel.BROADCASTING);
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
for (MessageExt messageExt : list) {
String rt=new String(messageExt.getBody());
System.out.println(rt);
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
//5.启动消费者consumer
consumer.start();
}
}
这里我们访问服务 http://localhost:8801/sendMessage,这时就会像我们Rocketmq里发一条消息,我们用consumer就可以实时的监听到
这里是打印出了Message所有信息
这里打印出了刚发送的消息
<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">
<parent>
<artifactId>cloud2020artifactId>
<groupId>com.aiguigu.springcloudgroupId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>cloud-stream-rocketmq-consumer8802artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-stream-binder-rocketmqartifactId>
<version>0.2.1.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
project>
server:
port: 8802
spring:
application:
name: rocketmq-consumer
cloud:
stream:
rocketmq:
binder:
namesrv-addr: 一号服务器IP:9876;二号服务器IP:9876
# 定义name为output的binding
bindings:
# output: # 定义name为output的binding
# destination: springboot-mq
# content-type: application/json
# 定义name为input的binding
input:
destination: springboot-mq #消费哪个topic
content-type: application/json
group: group1 #定义消费者组名
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: recive-8802.com #在信息列表时显示主机名称
prefer-ip-address: true #访问的路径变为IP地址
package com.atguigu.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class StreamMQmMain8802 {
public static void main(String[] args) {
SpringApplication.run(StreamMQmMain8802.class,args);
}
}
package com.atguigu.springcloud.controller;
import org.springframework.beans.factory.annotation.Value;
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.Message;
import org.springframework.stereotype.Component;
@Component
@EnableBinding(Sink.class)
public class ReceiveMessageListenerController {
@Value("${server.port}")
private String serverPort;
@StreamListener(Sink.INPUT)
public void input(Message<String> message){
System.out.println("消费者1号,------->接收到的消息: "+message.getPayload()+"\t port: "+serverPort);
}
}
我们把7001注册中心,8801生产者,8802消费者都启动起来,这时我们去调用8801的方法生成随机订单号发给消息队列,8802立马监听到了相同topic下的消息,并把消息打在公屏上
这时候我们rocketmq控制台也有了相对应的曲线与分析
消费者
spring:
cloud:
stream:
default-binder: rocketmq
rocketmq:
binder:
name-server: 192.168.1.179:9876
bindings:
#自定义的名称 对应spring.cloud.stream.bindings.input1
input1:
consumer:
tags: test-tag1 # 订阅的tag,二级分类
orderly: false # 是否按顺序消费
bindings:
#自定义的名称
input1:
destination: test-topic-user # 订阅的topic ,一级分类
content-type: application/json
group: test-input-group1 #group
consumer:
concurrency: 20
maxAttempts: 1
生产者
spring:
cloud:
stream:
default-binder: rocketmq
rocketmq:
binder:
#rocketmq地址
name-server: 192.168.0.78:9876
bindings:
#自定义的名称 对应spring.cloud.stream.bindings.output1
output1:
producer:
group: test-group-user-ouput1
sync: true
#Binding: 包括 Input Binding 和 Output Binding。
#Binding 在消息中间件与应用程序提供的 Provider 和 Consumer 之间提供了一个桥梁,
#实现了开发者只需使用应用程序的 Provider 或 Consumer 生产或消费数据即可,屏蔽了开发者与底层消息中间件的接触。
bindings:
#自定义的名称
output1:
destination: test-topic-user # topic(一级分类)
content-type: application/json
微服务应用放置于同一个group中,就能够保证消息只会被其中一个应用消费一次。
不同的组是可以消费的,同一个组内会发生竞争关系,只有其中 一个可以消费。
就是负载均衡的意思~指定相同组的消费者负载均衡的消费消息