SpringCloud第十章,升级篇,服务网关GateWay、服务配置Config和服务总线Bus
一、服务网关GateWay
1、网关概述
为什么存在:
XML
不同的微服务一般会有不同的网络地址,而外部的客户端可能需要调用多个微服务的接口才能完成一个业务的需求,如果让客
户端和多个微服务直接通信,会产生很多问题:
a、客户端多次请求不同的微服务,增加客户端的复杂性
b、存在跨域请求,在一定场景下处理困难
c、认证复杂,每个微服务都需要独立的认证
d、难以重构,随着项目的迭代,可能需要重新划分微服务。
例如:可能会将多个微服务合并成一个或者将一个微服务拆分成多个微服务。这是如果客户端直接与各微服务间通信,增加重构的难度。
e、某些微服务可能使用了防火墙或者浏览器的不友好协议,直接访问会有一定的困难。
什么是服务网关:
xml
网关是介于客户端和服务端之间的中间层。所有的外部请求都会经过网关这一层。也就是说API实现方面更多的考虑业务逻辑。而安全、性能、监控可以交友网关来做。这样既提高了业务的灵活性又不失安全性。
有什么优点:
xml
a、安全,只有网关系统对外进行暴露,微服务可以隐藏在内网,通过防火墙保护。
b、易于监控,可以在网关收集监控数据推送到外部系统进行监控。
c、易与认证,可以在网关上进行认证,然后再将请求转发到后端的微服务,无需再每个微服务上进行认证。
d、减少了客户端和各微服务之间的交互次数。
e、易于统一鉴权。验证用户是否具有访问各微服务的权利。
2、什么是GateWay
是什么:
xml
一句话:gateway是zuul1.x版的替代。
springcloud gateway旨在为微服务架构提供一种简单有效的统一的API路由管理方式。并且基于filter链的方式提供网关的基本功能:安全、监控、熔断、限流、重试等。
能干什么
XML
反向代理、鉴权、流量控制、熔断、日志监控等
gateway和zuul1.x的区别:
XML
zuul1.x是基于servlet之上的一个阻塞式处理模型。
gateway是非阻塞式的。
3、核心概念
xml
a、断言predicate:
参考java8的predicate,开发人员可以匹配http请求中的所有内容。如果请求和路由相匹配则进行路由。
b、过滤Filter:
指的是spring框架中springGateway的实例,可以对请求在路由前或之后进行修改。
c、路由Route:
路由是构建网关的基本模块,它由ID、目标URI、一系列断言predicate和过滤器filter组成。如果断言为true则匹配该路由。
web请求通过一些匹配条件,定位到真正的服务节点,并在这个转发的过程前后进行一系列的精细化控制。
predicate就是我们的匹配条件,而filter,就可以理解为一个无所不能的拦截器,有了这两个元素再加上一个uri就可以实现一个具体的路由了。
4、gateway工作流程
xml
客户端向springcloud gateway发出请求,然后在gateway handler mapping中找到与请求相匹配的路由,将其发送到gateway web handler.
handler再通过指定的过滤器链来将请求发送到实际服务执行业务逻辑,然后返回。
过滤器可能会在发送请求之前pre或者之后post执行业务逻辑。
pre:参数校验、权限校验、流量监控、日志输出、协议转换等。
post:相应内容和响应头的修改,日志输出、流量监控等。
5、案例
5.1、生产者前增加9527网关
新建module : cloud-gateway-9527
XML
<parent>
<artifactId>cloud_2020</artifactId>
<groupId>com.lee.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-gateway-9527</artifactId>
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">
<parent>
<artifactId>cloud_2020</artifactId>
<groupId>com.lee.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-gateway-9527</artifactId>
<dependencies>
<!--新增gateway-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.lee.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</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>
application.yml
yaml
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
routes:
- id: payment_routh #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: http://localhost:8001 #匹配后提供服务的路由地址
predicates:
- Path=/payment/get/** #断言,路径相匹配的进行路由
- id: payment_routh2
uri: http://localhost:8001
predicates:
- Path=/payment/lb/** #断言,路径相匹配的进行路由
eureka:
instance:
hostname: cloud-gateway-service
client:
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://eureka7001.com:7001/eureka
主启动类:
java
@SpringBootApplication
@EnableEurekaClient
public class GateWayMain9527 {
public static void main(String[] args) {
SpringApplication.run(GateWayMain9527.class,args);
}
}
测试:
XML
1、启动eureka7001、provider-8001、gateway-9527
2、访问:http://localhost:8001/payment/get/1
3、访问:http://localhost:9527/payment/get/1
结果都是:
{"code":200,"message":"查询数据成功 serverPort:8001Payment(id=1, serial=001)","data":null}
实现了在8001服务前增加了一个9527。
5.2、优化
yaml
spring:
application:
name: cloud-gateway
cloud:
gateway:
routes:
- id: payment_routh #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: http://localhost:8001 #匹配后提供服务的路由地址
predicates:
- Path=/payment/get/** #断言,路径相匹配的进行路由
- id: payment_routh2
uri: http://localhost:8001
predicates:
- Path=/payment/lb/** #断言,路径相匹配的进行路由
目前我们的配置是http://localhost:8001基于微服务具体地址的。但是payment-provider的微服务在实际生产环境下可能会有多个服务。所以我们要基于provider在注册中心中的微服务名称来进行动态路由。
XML
默认情况下:gateway会根据注册中心的服务列表,以注册中心上的微服务名称创建动态路由进行转发,从而实现动态路由的功能。
修改cloud-gateway-9527 application.yml
yaml
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
routes:
- id: payment_routh #路由的ID,没有固定规则但要求唯一,建议配合服务名
##uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #lb表示启用gateway的负载均衡功能 在eureka中根据微服务名称找到URI
predicates:
- Path=/payment/get/** #断言,路径相匹配的进行路由
- id: payment_routh2
##uri: http://localhost:8001
uri: lb://cloud-payment-service #lb表示启用gateway的负载均衡功能 在eureka中根据微服务名称找到URI
predicates:
- Path=/payment/lb/** #断言,路径相匹配的进行路由
eureka:
instance:
hostname: cloud-gateway-service
client:
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://eureka7001.com:7001/eureka
测试:
XML
1、启动eureka-7001、provider-8001、provider-8002、gateway-9527
2、访问:http://localhost:9527/payment/get/1
结果返回:
{"code":200,"message":"查询数据成功 serverPort:8001Payment(id=1, serial=001)","data":null}
或者
{"code":200,"message":"查询数据成功 serverPort:8002Payment(id=1, serial=001)","data":null}
8001和8002端口微服务轮询使用,证明我们访问9527通过微服务名称可以调用对应的微服务
5.3、Predicate使用
我们在启动cloud-gateway-9527的时候,后台加载了如下:
XML
Springcloud gateway内置了许多route predicate工厂,这些prediacate都与http请求的不同属性相匹配。多个
route predicate可以进行组合使用。
springcloud gateway创建route对象,RoutePredicateFactory创建predicate对象,predicate对象赋值给route。
常见的route predicate:
xml
1、After Route Predicate
例:- After=2020-03-08T10:59:34.102+08:00[Asia/Shanghai]
2、Before Route Predicate
例:- After=2020-03-08T10:59:34.102+08:00[Asia/Shanghai]
3、Between Route Predicate
例:- Between=2020-03-08T10:59:34.102+08:00[Asia/Shanghai] , 2020-03-08T10:59:34.102+08:00[Asia/Shanghai]
4、Cookie Route Predicate
例:- Cookie=username,lee #并且Cookie是username=lee才能访问
5、Header Route Predicate
例:- Header=X-Request-Id, \d+ #请求头中要有X-Request-Id属性并且值为整数的正则表达式
6、Host Route Predicate
例:- Host=**.ngc7293.com
7、Method Route Predicate
例:- Method=GET
8、Path Route Predicate
例:- Path=/payment/get/** #断言,路径相匹配的进行路由
9、Query Route Predicate
例:- Query=username, \d+ #要有参数名称并且是正整数才能路由
总结:
- id: payment_routh2
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service
predicates:
- Path=/payment/lb/** #断言,路径相匹配的进行路由
- After=2020-03-08T10:59:34.102+08:00[Asia/Shanghai]
- Cookie=username,lee #并且Cookie是username=lee才能访问
- Header=X-Request-Id, \d+ #请求头中要有X-Request-Id属性并且值为整数的正则表达式
- Host=**.ngc7293.com
- Method=GET
- Query=username, \d+ #要有参数名称并且是正整数才能路由
说白了,Predicate就是为了实现一组匹配规则,让请求过来找到对应的Route进行处理
5.4、Filter使用
网关经常需要对路由请求进行过滤,进行一些操作,如鉴权之后构造头部之类的,过滤的种类很多,如增加请求头、增加请求 参数 、增加响应头和断路器等等功能,这就用到了Spring Cloud Gateway 的 Filter。
当我们有很多个服务时,比如下图中的user-service、goods-service、sales-service等服务,客户端请求各个服务的Api时,每个服务都需要做相同的事情,比如鉴权、限流、日志输出等。
对于这样重复的工作,可以在微服务的上一层加一个全局的权限控制、限流、日志输出的Api Gateway服务,然后再将请求转发到具体的业务服务层。这个Api Gateway服务就是起到一个服务边界的作用,外接的请求访问系统,必须先通过网关层。
Spring Cloud Gateway 的 Filter 的生命周期不像 Zuul 的那么丰富,它只有两个:"pre" 和 "post"。
XML
PRE: 这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的 HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。
Spring Cloud Gateway 的 Filter 从作用范围可分为另外两种GatewayFilter 与 GlobalFilter。
XML
GatewayFilter:应用到单个路由或者一个分组的路由上。
GlobalFilter:应用到所有的路由上。
5.4.1、GatewayFilter
使用演示:
过滤器工厂会在匹配的请求头加上一对请求头,名称为X-Request-Foo,值为Bar。
所有的/foo/**开始的路径都会命中配置的router。 请求http://www.abc.com/foo/get,会转到http://www.abc.com.org/get\]
xml
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: http://httpbin.org:80/get
filters:
- AddRequestHeader=X-Request-Foo, Bar
- RewritePath=/foo/(?<segment>.*), /$\{segment}
predicates:
- Method=GET
自定义Gateway Filter:
java
@Component
@Slf4j
public class MyLogGatewayFilter implements GatewayFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("-----welcome to MyLogGatewatFilter------"+new Date());
String username = exchange.getRequest().getQueryParams().getFirst("username");
if(StringUtils.isEmpty(username)){
log.info("username is empty!!!");
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}
config:
java
@Configuration
public class GatewayConfig {
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder routeLocatorBuilder){
RouteLocatorBuilder.Builder myLogGatewayFilterBuilder = routeLocatorBuilder.routes().route("myLogGatewayFilter",
r -> r.path("/payment/get/**")
.uri("lb://cloud-payment-service")
.filter(new MyLogGatewayFilter()));
return myLogGatewayFilterBuilder.build();
}
}
测试:
XML
1、启动eureka-7001、payment-provider-8001、payment-provider-8002、gateway-9527
2、访问:http://localhost:9527/payment/get/1
结果:失败
3、访问:http://localhost:9527/payment/get/1?username=lee
结果:
{"code":200,"message":"查询数据成功 serverPort:8001Payment(id=1, serial=001)","data":null}
5.4.2、GlobalFilter
自定义globalFilter
java
@Component
@Slf4j
public class MyGlobalGatewayFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("-----welcome to MyGlobalGatewatFilter------"+new Date());
String password = exchange.getRequest().getQueryParams().getFirst("password");
if(StringUtils.isEmpty(password)){
log.info("password is empty!!!");
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}
测试:
xml
1、启动eureka-7001、payment-provider-8001、payment-provider-8002、gateway-9527
2、访问:http://localhost:9527/payment/get/1
结果:失败
3、访问:http://localhost:9527/payment/get/1?username=lee
结果:失败
4、访问:http://localhost:9527/payment/get/1?username=lee&password=admin123
结果:
{"code":200,"message":"查询数据成功 serverPort:8001Payment(id=1, serial=001)","data":null}
二、分布式配置中心Config
1、Config概述
XML
springcloud config提供集中化的外部配置支持,配置服务器为各个不同微服务应用的所有环境提供了一个中心化的外部配置。
springcloud config是一套集中式的、动态配置管理服务
springcloud config 分为 服务端 和 客户端两部分。
xml
服务端也成为分布式配置中心,他是一个独立的微服务应用,用来连接配置服务器,并为客户端提供获取配置信息,加密解密信息等访问接口。
客户端则通过指定的配置中心来管理应用资源,以及与业务相关的配置内容,并在启动的时候从配置中心获取和加载配置信息。
配置服务器默认采用Git来存储配置信息,这样就有助于对环境配置进行版本管理,并且可以通过Git客户端工具来访问和管理配置内容。
能干什么:
XML
1、集中管理配置文件
2、不同环境不同配置,动态化的配置更新,分环境部署dev test prod beta release
3、运行期间动态调整配置
4、当配置发生变动时,服务不需要重启即可感知
5、将配置信息以rest接口形式暴露
2、案例
2.1、服务端config-server
2.1.1、新建仓库
XML
①、在码云上新建springcloud-config2020仓库
②、获得SSH协议的Git地址
③、本地硬盘目录上新建Git仓库并clone
git clone xxxxxxxxxxxxxxxxxxxxxxxxxxx.git
④、新建一个application.yml(文件内容保存的格式一定要UTF-8)
⑤、将上yml文件推送到gitee上
git status #查看状态
git add .
git commit -m "init file"
git push origin master
application.yml【将作为config-client端的配置文件】
yml
spring:
application:
name: cloud-payment-service #自己的服务名称
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: org.gjt.mm.mysql.Driver
url: jdbc:mysql://localhost:3306/cloud2020?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: admin123
eureka:
client:
register-with-eureka: true #向eureka server注册自己
fetch-registry: true #需要去注册中心获取其他服务的地址
service-url:
#defaultZone: http://localhost:7001/eureka #单机 指向Eureka服务注册中心
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002:7002.com/eureka #集群 执行Eureka服务注册中心
instance:
instance-id: cloud-provider-payment-service-8007
prefer-ip-address: true #是否显示服务IP地址
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.lee.springcloud.entities
##configuration:
##log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #控制台打印SQL
2.1.2、新建配置中心
新建module模块 cloud-config-3344配置中心模块
xml
<parent>
<artifactId>cloud_2020</artifactId>
<groupId>com.lee.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-config-3344</artifactId>
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">
<parent>
<artifactId>cloud_2020</artifactId>
<groupId>com.lee.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-config-3344</artifactId>
<dependencies>
<!--config server-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>com.lee.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.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>
application.yml
yaml
server:
port: 3344
spring:
application:
name: cloud-config
cloud:
config:
server:
git:
uri: https://gitee.com/night_wish/sprincloud-config2020.git #Gitee上对应仓库的名称
username: sssssssss@qq.com #Gitee对应的账号和密码
password: ssssssssssssss
search-paths:
- sprincloud-config2020 #对应的仓库名称
label: master #对应的分支
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka
主启动类:
java
@SpringBootApplication
@EnableConfigServer
public class ConfigMain3344 {
public static void main(String[] args) {
SpringApplication.run(ConfigMain3344.class,args);
}
}
2.1.3、其他配置
XML
修改HOST
127.0.0.1 config3344.com
①①、启动config 测试是否可以从gitee上获取配置内容
http://config3344.com:3344/application-dev.yml
http://config3344.com:3344/application-test.yml
http://config3344.com:3344/application-xxxxx.yml
或
http://config3344.com:3344/application/dev/master
http://config3344.com:3344/application/test/master
http://config3344.com:3344/application/xxxxx/master
2.2、客户端config-client
客户端即微服务,仿照cloud-provider-payment-8001创建cloud-provider-payment-8007
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">
<parent>
<artifactId>cloud_2020</artifactId>
<groupId>com.lee.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<artifactId>cloud-provider-payment-8007</artifactId>
<dependencies>
<!-- SpringCloud Config客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<!--Eureka client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--springboot-->
<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>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</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>
<!--cloud-api-commons公共包-->
<dependency>
<groupId>com.lee.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>
application.yml
yaml
server:
port: 8007
bootstrap.yml
yaml
spring:
cloud:
config:
name: application #需要从gitee上读取的资源名称,注意没有yml后缀名
profile: dev #本次访问的配置项
label: master
uri: http://config3344.com:3344 #本微服务启动后先去找3344号服务,通过SpringCloudConfig获取Gitee的服务地址
创建ConfigClientRestController类
java
@RestController
public class ConfigClientRestController {
@Value("${spring.application.name}")
private String applicationName;
@Value("${eureka.client.service-url.defaultZone}")
private String eurekaServers;
@Value("${server.port}")
private String port;
@Value("${config.info}")
private String info;
@RequestMapping("/config")
public String getConfig()
{
String str = "applicationName: "+applicationName+"\t eurekaServers:"+eurekaServers+"\t port: "+port;
System.out.println("******str: "+ str);
return "applicationName: "+applicationName+"\t eurekaServers:"+eurekaServers+"\t port: "+port;
}
@RequestMapping("/info")
public String getName(){
return "config.info : "+info;
}
}
主启动类:
java
//服务发现
@EnableDiscoveryClient
//表示自己是Eureka的客户端
@EnableEurekaClient
@SpringBootApplication
@MapperScan("com.lee.springcloud.dao")
public class PaymentMain8007 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8007.class,args);
}
}
其余的controller、service、dao、mapper仿照cloud-payment-provider-8001创建
测试:
xml
1、启动eureka-7001、config-3344、cloud-provider-payment-8007
2、访问 http://localhost:8007/info
结果:
config.info : hello-world
证明可以通过config-3344访问 码云上的application.yml配置信息可以读取其内容
3、访问 http://localhost:8007/payment/get/1
结果:
{"code":200,"message":"查询数据成功 serverPort:8007Payment(id=1, serial=001)","data":null}
证明可以通过config-3344访问 码云上的application.yml配置信息可以访问数据库
2.3、客户端只动态刷新的问题
在码云 sprincloud-config2020 上修改application.yml
config:
info: hello-world2 #原来是hello-world
访问: http://localhost:3344/application-dev.yml
XML
结果:
config:
info: hello-world2
。。。。其余省略
访问: http://localhost:8007/info
xml
结果:
config.info : hello-world
重启cloud-provider-payment-8007,访问http://localhost:8007/info
xml
结果:
config.info : hello-world2
解决方案:
cloud-provider-payment-8007 引入actuator监控
pom
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
application.yml
xml
management:
endpoints:
web:
exposure:
include: "*"
controller新增
java
@RefreshScope
运维人员手动发送POST请求刷新8007
xml
curl -X POST "http://localhost:8007/actuator/refresh"
测试:
XML
1、修改码云
config:
info: hello-world3
2、访问http://localhost:3344/application-dev.yml
结果:
config:
info: hello-world2
。。。。其余省略
3、访问http://localhost:8007/info
结果:
config.info : hello-world3
三、消息总线Bus
上述config客户端,假如有多个cloud-provider-payment-800XXX,运维人员每次修改配置文件,都需要手动POST请求,手动刷新非常麻烦。因此能否广播,一次通知处处生效,大范围的刷新。这就引出了消息总线BUS。
1、概述
xml
springcloud bus是用来将分布式系统的节点 与 轻量级消息系统 链接起来的框架。
它整合了java事件处理机制和消息中间件的功能
SpringCloud Bus 目前仅支持RabbitMq和Kafka
springcloud bus能管理和传播分布式系统间的消息,就像一个分布式执行器,可用于广播状态更改、时间推送等。
也可以当做微服务间的通信通道。
XML
什么是总线
在微服务架构的系统中,通常会使用轻量级的消息代理来构建一个公用的消息主题,并让系统中的所有微服务实例都连接上来。由于该主题中产生的消息会被所有实例监听和消费,所以称为消息总线。在总线上的各个实例,都可以方便地广播一些需要让其他链接在该主题行的实例都知道的消息。
基本原来理:
ConfigClient实例都监听MQ中的同一个topic(默认是SpringcloudBus)。当一个服务刷新数据的时候,它会把这个信息放入到topic中,这样其它监听同一个topic的服务就能得到通知,然后去更新自身的配置。
2、安装RabbotMq
xml
①、安装Erlang环境
http://erlang.org/download/otp_win64_21.3.exe
②、安装Rabbit
https://dl.bintray.com/rabbitmq/all/rabbitmq-server/3.7.14/rabbitmq-server-3.7.14.exe
③、启动管理添加可视化插件
cd F:\JavaTools\RabbitMq\rabbitmq_server-3.7.14\sbin
执行 rabbitmq-plugins enable rabbitmq_management
④、启动rabbitmq,并访问http://localhost:15672/
账号:guest 密码:guest
3、创建另一个config-client
再以cloud-provider-payment-8007创建一个config-client
cloud-provider-payment-8008
xml
<parent>
<artifactId>cloud_2020</artifactId>
<groupId>com.lee.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-provider-payment-8008</artifactId>
application.yml
yaml
server:
port: 8008
主启动类
java
//服务发现
@EnableDiscoveryClient
//表示自己是Eureka的客户端
@EnableEurekaClient
@SpringBootApplication
@MapperScan("com.lee.springcloud.dao")
public class PaymentMain8008 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8008.class,args);
}
}
4、设计思想
4.1、利用消息总线触发一个客户端/bus/refresh,而刷新所有客户端的配置
4.2、利用消息总线触发一个服务端ConfigServer的/bus/refresh端点,而刷新所有客户端的配置(更加推荐)
5、动态刷新全局广播案例
5.1、config-server添加消息总线支持
cloud-config-3344
POM新增
xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
yml新增
yaml
server:
port: 3344
spring:
application:
name: cloud-config
cloud:
config:
server:
git:
uri: https://gitee.com/night_wish/sprincloud-config2020.git #Gitee上对应仓库的名称
username: ooooooooo
password: xxxxxxxxx
search-paths:
- sprincloud-config2020
label: master
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka
#新增bus
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
#新增bus
management:
endpoints:
web:
exposure:
include: 'bus-refresh'
controller新增
java
@RefreshScope//新增
@RestController
public class ConfigClientRestController {
//。。。。省略
}
5.2、config-client添加消息总线支持
cloud-provider-payment-8007 和 cloud-provider-payment-8008
POM新增
xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
bootstrap.yml
yaml
spring:
cloud:
config:
name: application #需要从gitee上读取的资源名称,注意没有yml后缀名
profile: dev #本次访问的配置项
label: master
uri: http://config3344.com:3344 #本微服务启动后先去找3344号服务,通过SpringCloudConfig获取Gitee的服务地址
#新增
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
#新增
management:
endpoints:
web:
exposure:
include: "*"
5.3、测试
xml
1、启动eureka-7001 config-3344 cloud-provider-payment-8007 cloud-provider-payment-8008
2、访问:
http://localhost:3344/application-dev.yml
http://localhost:8007/info
http://localhost:8008/info
结果:
config:
info: hello-world3
和
config.info : hello-world3
和
config.info : hello-world3
3、修改码云上springcloud-config2020中application.yml数值hello-world3为hello-world4
4、再次访问以上
结果:
config:
info: hello-world4
和
config.info : hello-world3
和
config.info : hello-world3
5、运维人员发送刷新到config-server
curl -X POST "http://localhost:3344/actuator/bus-refresh"
6、再次访问以上
结果:
config:
info: hello-world4
和
config.info : hello-world4
和
config.info : hello-world4
总结:
我们利用消息总线触发一个服务端ConfigServer(config-3344)的/bus/refresh端点,而刷新所有客户端的配置
6、动态刷新定点通知案例
有时候我们需要数据更新后只刷新某一个或几个config-client,而不是全部的config-client,因此动态刷新定点通知就很重要了。
公式:
xml
http://localhost:配置中心的端口号/actuator/bus-refresh/{destination}
案例测试:
xml
1、启动eureka-7001 config-3344 cloud-provider-payment-8007 cloud-provider-payment-8008
2、访问:
http://localhost:3344/application-dev.yml
http://localhost:8007/info
http://localhost:8008/info
结果:
config:
info: hello-world4
和
config.info : hello-world4
和
config.info : hello-world4
3、修改码云上springcloud-config2020中application.yml数值hello-world4为hello-world5
4、再次访问以上
结果:
config:
info: hello-world5
和
config.info : hello-world4
和
config.info : hello-world4
5、运维人员发送刷新到config-server
curl -X POST "http://localhost:3344/actuator/bus-refresh/cloud-payment-service:8007"
6、再次访问以上
结果:
config:
info: hello-world5
和
config.info : hello-world5
和
config.info : hello-world4