SpringCloud-整体学习(一)SpringCloud简介+版本选择
SpringCloud-整体学习(二)项目初始构建-加公共部分提取
SpringCloud-整体学习(三)Eureka、zookeeper、Consul(注册中心)
SpringCloud-整体学习(四)Ribbon(负载均衡+手写轮询算法)
SpringCloud-整体学习(五)OpenFeign(服务调用)
SpringCloud-整体学习(六)Hystrix(服务降级)
SpringCloud-整体学习(七)GateWay(服务网关)
SpringCloud-整体学习(八)Config、Bus、Stream(服务配置和消息交互)
SpringCloud-整体学习(九)Sleuth(分布式请求链路追踪)
SpringCloud-整体学习(十)SpringCloudAlibaba(注册中心+配置中心)
SpringCloud-整体学习(十一) Sentinel(服务降级)
SpringCloud-整体学习(十二) Seata(分布式事务)
git :
https://github.com/lucine-maker/cloud2020
gitee:
https://gitee.com/lucine_li_tao/springcloud
Config分布式配置中心介绍
在微服务架构中项目模块众多、有需要区分不同的环境
这样application.yml就会变得难以维护 —Config就是用来解决这个问题的。
文档:
https://docs.spring.io/spring-cloud-config/docs/2.2.6.RELEASE/reference/html/
Config配置总控中心搭建
新建cloud-config-center-3344
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
yml
server:
port: 3344
spring:
application:
name: cloud-config-center
cloud:
config:
server:
git:
#uri: git@github.com:lucine-maker/springcloud-config.git #Github上的git仓库名字
uri: https://github.com/lucine-maker/springcloud-config.git
##搜索目录.这个目录指的是github上的目录
search-paths:
- springcloud-config
##读取分支
label: master
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
@EnableConfigServer
hosts 中加入 模拟映射
127.0.0.1 config-3344.com
效果:
配置命名规则:
坑:
启动3344:
Authentication is required but no CredentialsProvider has been registered
(因为设置的库是私有库)
解决:
Config客户端配置与测试
新建cloud-config-client-3355
这里采用 :bootstrap.yml (优先级高于application.yml)
pom:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
yml
server:
port: 3355
spring:
application:
name: config-client
cloud:
#Config客户端配置
config:
label: master #分支名称
name: config #配置文件名称
profile: dev #读取后缀名称 上诉3个综合就是 master分支上 config-dev.yml
uri: http://localhost:3344
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
controller
// 因为config仓库以rest形式暴露,所以所有客户端都可以通过config服务端访问到github上对应的文件信息
@Value("${config.info}")
private String configInfo;
@Value("${server.port}")
private String serverPort;
@GetMapping("/configInfo")
public String getConfigInfo() {
return "serverPort: " + serverPort + "\t\n\n configInfo" + configInfo;
}
效果:
但是目前还有问题,就是当你github已经变更了信息,但是,3355不会立即显示(重启后可以)。
3344可以因为是直接连接的github
坑:
pom (以为只在3344中删除server)
问题
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
Config动态刷新之手动版
解决上面说的不能读取到更新后的数据
pom(必须有)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
在controller 加入@RefreshScope注解在类上
yml:
##暴露监控端点
management:
endpoints:
web:
exposure:
include: "*"
但是也是不能马上生效,需要进行一个请求的激活。执行下面的命令。
curl -X POST “http://localhost:3355/actuator/refresh”
效果:
此时,基本可以满足需求了,不用重启服务只要,发一个post请求。
但是如果集群很多 。。 就可能要发5—100个,还是很繁琐需要写脚本发送
Bus消息总线是什么
可以现实:
分布式自动刷新配置功能
SpringCloud Bus 配合 SpringCloud Config 实现自动动态刷新
Bus支持RabbitMQ、Kafka
下面的图是 在第三步的时候 推送给A->由A在继续向后面传递
下面的是发送给ConfigServer - 然后每个Service 在拉取信息。
Bus动态刷新全局广播的设计思想和选型
rabbitMQ 安装就不详细记录了。
新建cloud-config-center-3366
除了端口和3355都一致。
1、打破了微服务的职责单一性,因为微服务本身是业务模块,它本不应该承担配置的职责
2、破坏了微服务各节点的对等性。
3、有一定的局限性。例如:在微服务迁徙时,他的网络地址常常会发生变化,此时如果想要做的自动刷新,就会增加更多的修改。(不利于维护)
Bus动态刷新全局广播配置实现
在3344、3355、3366
pom中加入Bus
<!--添加消息总线RabbitMQ支持-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
yml 中加入RabbitMQ(yml用bootstrap)
注意在spring的yml下。
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
然后启动7001 - 3344 – 3355 --3366
然后去github更改信息
执行下面的命令
curl -X POST “http://localhost:3344/actuator/bus-refresh”
然后3355、3366就自动更新了。(一次更改、广播通知,处处生效)
Bus动态刷新定点通知
curl -X POST “http://localhost:3344/actuator/bus-refresh/{服务名:端口}”
curl -X POST “http://localhost:3344/actuator/bus-refresh/config-client:3355”
Stream为什么被引入
比方说我们用到了RabbitMQ和Kafka,由于这两个消息中间件的架构上的不同,像RabbitMQ有exchange,kafka有Topic,partitions分区,这些中间件的差异性导致我们实际项目开发给我们造成了一定的困扰,我们如果用了两个消息队列的其中一种,
后面的业务需求,我想往另外一种消息队列进行迁移,这时候无疑就是一个灾难性的,一大堆东西都要重新推倒重新做,因为它跟我们的系统耦合了,这时候springcloud Stream给我们提供了一种解耦合的方式。
一句话:
屏蔽底层的消息中间件,降低切换成本,统一消息编程模型
Stream是什么及Binder介绍
我的理解:用来交互消息中间件的(binder、binding)。
官网文档:
https://docs.spring.io/spring-cloud-stream/docs/3.1.0-SNAPSHOT/reference/html/
中文:
http://m.wang1314.com/doc/webapp/topic/20971999.html
Stream的设计思想
标准的MQ:
Pub —Message—> QUEUE -----Message---->Sub
消息通道MessageChannel的子接口SubscribableChannel,由MessageHandler
消息处理器所订阅
引用了之后
Stream编码常用注解简介
Stream消息驱动之生产者
新建cloud-stream-rabbitmq-provider8801
pom:
<dependencies>
<!--stream-rabbit-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
<!--eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>com.lt.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<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.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<!--如果没写版本,从父层面找,找到了就直接用,全局统一-->
</dependency>
<!--mysql-connector-java-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--jdbc-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</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>
</dependencies>
</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:
service-url:
defaultZone: http://eureka7001.com: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
service:
public interface IMessageProvider {
public String send();
}
iml:
@EnableBinding(Source.class)
public class MessageProvider 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;
}
}
controller
@RestController
public class SendMessageController {
@Autowired
private IMessageProvider messageProvider;
@GetMapping(value = "/sendMessage")
public String sendMessage(){
return messageProvider.send();
}
}
效果:
当请求8801sendMessage接口的时候在rabbitMQ中 又起伏
Stream消息驱动之消费者
新建cloud-stream-rabbitmq-consumer8802
pom、启动类和8801一致
yml (基本一致)
server:
port: 8802
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 # 设置要绑定的消息服务的具体设置
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka
instance:
lease-renewal-interval-in-seconds: 2 # 设置心跳的间隔时间,默认30
lease-expiration-duration-in-seconds: 5 # 超过5秒间隔,默认90
instance-id: send-8802.com # 主机名
prefer-ip-address: true # 显示ip
controller:
@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);
}
}
效果:
Stream之消息重复消费
新建cloud-stream-rabbitmq-consumer8803
基本和8802一致(就不记录了)
目前环境:
此时8801发送一个消息 会有重复消费现象
Stream之group解决消息重复消费
故障问题:一个订单重复消费
导致原因:默认的分组是不一致的,组流水号不一致,被认为是不同组,可以消费
解决方案:
自定义分组- 到同一个组中(发生竞争关系-就会只有一个可以消费)
修改yml
就完成了。
Stream之消息持久化
一句话 :group 分组属性,加上可以防止消息丢失