SpringCloud
1、SpringCloud认识
2、分布式微服务初认识
分布式部署方式,每个服务通过springboot开发,app1通过http请求app2
app1
@SpringBootApplication
public class CloudT1Main8081 {
public static void main(String[] args) {
SpringApplication.run(CloudT1Main8081.class,args);
}
}
@RestController
public class TestController {
@PostMapping(value = "/cloudT1/test")
//对象类型要加@RequestBody,否则接收参数为null
public String test(@RequestBody Integer a){
return a + "===>cloudT1String";
}
}
server:
port: 8081
spring:
application:
name: cloud-T1
app2
@SpringBootApplication
public class CloudT2Main8082 {
public static void main(String[] args) {
SpringApplication.run(CloudT2Main8082.class,args);
}
}
@RestController
public class CLoudT2TestController {
@Autowired
private RestTemplate restTemplate;
@PostMapping("/cloudT2/test")
public String Test(Integer a){
return "cloudT2" + restTemplate.postForObject("http://localhost:8081/test",a,String.class);
}
}
//注册RestTemplate
@Configuration
public class MainConfig {
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
server:
port: 8082
spring:
application:
name: cloud-T2
注意点
通过
RestTemplate
实现http访问,RestTemplate由Spring提供,位于org.springframework.web.client
包下。
分布式微服务项目中,每次修改后手动停止启动项目很麻烦,所以要启用自动热部署
详见:idea中SpringBoot项目自动部署----devtools
3、提取公共代码
1、建立子模块作为公共模块,编写公共类,选择maven -clean、maven -install
package com.zmy.springcloud.entrties; public class Person { }
2、在需要用到的项目中、引入自定义jar
<dependency> <groupId>com.zmy.springcloud</groupId> <artifactId>cloud-commons</artifactId> <version>${project.version}</version> </dependency>
4、Eureka服务注册中心
4.1、单机版Eureka
服务端
-
建立子模块
-
导入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
- 编写配置文件
server:
port: 7001
eureka:
instance:
hostname: localhost # eureka服务端的实例名字
client:
# 不注册自己
register-with-eureka: false
# 不检索服务,只维护服务实例
fetch-registry: false
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
- 主启动类
@SpringBootApplication
@EnableEurekaServer
public class CloudEurekaServer7001 {
public static void main(String[] args) {
SpringApplication.run(CloudEurekaServer7001.class,args);
}
}
客户端
- 导入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
- 修改配置文件
# 增加如下配置
eureka:
client:
# 是否注册进eureka注册中心
register-with-eureka: true
# 是否从服务端抓取已有的注册中心,默认true。单点无所谓,集群必须true,否则ribbon无法负载均衡
fetch-registry: true
service-url:
defaultZone: http://localhost:7001/eureka # 入驻地址
- 主启动类
@SpringBootApplication
//增加注解
@EnableEurekaClient
public class CloudT1Main8081 {
public static void main(String[] args) {
SpringApplication.run(CloudT1Main8081.class,args);
}
}
4.2、集群搭建
- 改hosts
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
- eureka1
server:
port: 7001
eureka:
instance:
hostname: eureka7001.com
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://eureka7002.com:7002/eureka/
- eureka2
server:
port: 7002
eureka:
instance:
hostname: eureka7002.com
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
- 服务注册
server:
port: 8082
spring:
application:
name: cloud-T2
# 增加如下配置
eureka:
client:
# 是否注册进eureka注册中心
register-with-eureka: true
# 是否从服务端抓取已有的注册中心,默认true。单点无所谓,集群必须true,否则ribbon无法负载均衡
fetch-registry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka # 入驻地址
server:
port: 8081
spring:
application:
name: cloud-T1
eureka:
client:
# 是否注册进eureka注册中心
register-with-eureka: true
# 是否从服务端抓取已有的注册中心,默认true。单点无所谓,集群必须true,否则ribbon无法负载均衡
fetch-registry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
4.3、服务提供者集群配置
通用返回类型(放在公共代码中)
public class CommonResult<E> implements Serializable {
private int code;
private String message;
E data;
}
消费者
@RestController
public class TestController {
private final String serviceName = "http://CLOUD-T2/";
@Autowired
private RestTemplate restTemplate;
@GetMapping("/T1/{id}")
public CommonResult getUser(@PathVariable("id") int id){
//通过服务名请求接口,具体是哪个端口的服务相应,取决于Ribbon
return restTemplate.getForObject(serviceName + "/T2/" + id,CommonResult.class);
}
}
@Configuration
public class MainConfig {
@Bean
//此处增加@LoadBalanced注解,开启默认的Ribbon负载均衡,默认轮询机制
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
服务提供者
app1 和 app2只有端口不一样
app1
@RestController
public class UserController {
@Autowired
private UserService userService;
@Value("${server.port}")
private String serverPort;
@GetMapping(value = "/T2/{id}")
public CommonResult getUser(@PathVariable("id") int id){
System.out.println(id);
User user = userService.getUser(id);
if(user != null){
return new CommonResult(200,"查询成功,port:" + serverPort,user);
} else{
return new CommonResult(444,"查询失败,port:" + serverPort,null);
}
}
}
server:
# 修改端口,不要重复
port: 8082
spring:
application:
# 服务名保持一致,根据服务名字,负载均衡选择一个
name: cloud-T2
# 增加如下配置
eureka:
client:
# 是否注册进eureka注册中心
register-with-eureka: true
# 是否从服务端抓取已有的注册中心,默认true。单点无所谓,集群必须true,否则ribbon无法负载均衡
fetch-registry: true
service-url:
# 入驻地址
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
4.4、actuator微服务信息完善
导入依赖的jar
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
改yml
eureka:
client:
# 是否注册进eureka注册中心
register-with-eureka: true
# 是否从服务端抓取已有的注册中心,默认true。单点无所谓,集群必须true,否则ribbon无法负载均衡
fetch-registry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
instance:
# 服务的ID
instance-id: T1-8081
# 显示ip和端口号
prefer-ip-address: true
4.5、服务发现Discovery
@SpringBootApplication
@EnableEurekaClient
//启动类增加改注解,(新版不加也可以)
@EnableDiscoveryClient
public class CloudT2Main8082 {
public static void main(String[] args) {
SpringApplication.run(CloudT2Main8082.class,args);
}
}
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping(value = "/T2/discovery")
public Object discovery(){
Map<String,Object> map = new HashMap<String, Object>();
List<String> services = discoveryClient.getServices();
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-T2");
map.put("services",services);
map.put("instances of CLOUD-T2",instances);
return map;
}
5、微服务调用方式
- Ribbon + RestTemplate
- OpenFeign
6、OpenFeign
6.1、OpenFeign入门
使用openfeign需要导包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
编写yml配置文件
server:
port: 8084
spring:
application:
name: cloud-01
eureka:
client:
# 是否注册进eureka注册中心
register-with-eureka: false
# 是否从服务端抓取已有的注册中心,默认true。单点无所谓,集群必须true,否则ribbon无法负载均衡
fetch-registry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
instance:
instance-id: T1-8084
prefer-ip-address: true
编写主启动类
@SpringBootApplication
//激活OpenFeign组件
@EnableFeignClients
public class CloudT1OpenfeignMain8084 {
public static void main(String[] args) {
SpringApplication.run(CloudT1OpenfeignMain8084.class,args);
}
}
编写OpenFeign组件接口,作为service层
@Component
//声明为OpenFeign组件 value为服务名,等价于ip+port。注意:不包含项目启动路径
@FeignClient(value = "CLOUD-T2")
public interface OpenfeignService {
//复制目标微服务接口的controller方法
@GetMapping(value = "/T2/{id}")
CommonResult getUser(@PathVariable("id") int id);
}
//getUser到指定的Eureka服务器上找CLOUD-T2名字的服务,请求路径为/T2/{id}
编写controller层
@RestController
public class TestController {
@Autowired
private OpenfeignService openfeignService;
private final String serviceName = "http://CLOUD-T2/";
@GetMapping("/T1/{id}")
public CommonResult getUser(@PathVariable("id") int id){
return openfeignService.getUser(id);
}
}
效果如下图
6.2、OpenFeign超时控制
使用OpenFeign调用其他微服务时,默认等待1s,1s得不到回复,将会报错
yaml文件中配置超时相关配置
ribbon:
# 读取超时时间
ReadTimeout: 5000
# 连接超时时间
ConnectTimeout: 5000
6.3、OpenFeign日志增强
yaml文件增加配置
logging:
level:
com.zmy.springcloud.service.OpenfeignService: debug
编写配置类
import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FeignConfig {
@Bean
public Logger.Level level(){
return Logger.Level.FULL;
}
}
等级共四种:
结果如下
7、Hystrix服务降级
- 服务降级
- 服务熔断
- 服务限流
7.1、服务提供端服务降级
增加豪猪哥依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
编写yaml
server:
port: 8085
spring:
application:
name: cloud-T2
# 增加如下配置
eureka:
client:
# 是否注册进eureka注册中心
register-with-eureka: true
# 是否从服务端抓取已有的注册中心,默认true。单点无所谓,集群必须true,否则ribbon无法负载均衡
fetch-registry: true
service-url:
# 入驻地址
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
instance:
instance-id: T2-8085
prefer-ip-address: true
主启动类
@SpringBootApplication
@EnableEurekaClient
//启用豪猪哥组件
@EnableCircuitBreaker
public class CloudT2HystrixMain8085 {
public static void main(String[] args) {
SpringApplication.run(CloudT2HystrixMain8085.class,args);
}
}
业务类
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping(value = "/T2/ok")
public String testOk(){
String result = userService.ok();
return result;
}
@GetMapping(value = "/T2/timeout/{time}")
public String timeout(@PathVariable("time") int time){
String result = userService.timeout(time);
return result;
}
}
@Service
public class UserService {
public String ok(){
return "OK!😊";
}
@HystrixCommand(fallbackMethod = "timeoutHandler",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000")
})
public String timeout(int time){
try {
Thread.sleep(time * 1000);
}catch (Exception e){
e.printStackTrace();
}
return "等待了" + time + "s,得到了回复!";
}
public String timeoutHandler(int time){
return "服务超时!";
}
}
7.2、消费端提供服务降级
因为消费端并没有直接导入Hystrix的依赖,而是使用OpenFeign集成的Hystrix,所以需要在配置文件中开启Hystrix的支持
# 开启feign的hystrix
feign:
hystrix:
enabled: true
其它与服务端写法一致
7.3、目前存在的问题
- 每一个方法都对应一个降级方法:代码膨胀
- 业务逻辑和降级方法写在一起:代码混乱
能不能配置一个默认的降级方法?解决代码膨胀
@Service
//配置默认降级方法
@DefaultProperties(defaultFallback = "defaultFallback")
public class UserService {
public String ok(){
return "OK!😊";
}
//未指定降级方法,使用默认
@HystrixCommand
public String timeout(int time){
try {
Thread.sleep(time * 1000);
}catch (Exception e){
e.printStackTrace();
}
return "等待了" + time + "s,得到了回复!";
}
//默认降级方法
public String defaultFallback(int time){
return "defaultFallback!";
}
}
消费端做服务降级:解耦的方式
1、@FeignClient注解中增加fallback
2、编写fallback制定的类,需要实现@FeignClient标注的接口,override方法即为降级方法
FeignClient类
@Component
//FeignClient的接口方法出异常后,统一由fallback指定的OpenfeignFallbackService类同名方法处理
@FeignClient(value = "CLOUD-T2",fallback = OpenfeignFallbackService.class)
public interface OpenfeignService {
@GetMapping(value = "/T2/{id}")
CommonResult getUser(@PathVariable("id") int id);
@HystrixCommand
@GetMapping(value = "/T2/timeout/{time}")
String timeout(@PathVariable("time") int time);
}
降级类
@Component
public class OpenfeignFallbackService implements OpenfeignService{
@Override
public CommonResult getUser(int id) {
return new CommonResult(444,"getUser default fallback method!",null);
}
@Override
public String timeout(int time) {
return "timeout default fallback method!";
}
}
@controller
@RestController
public class TestController {
@Autowired
private OpenfeignService openfeignService;
private final String serviceName = "http://CLOUD-T2/";
@GetMapping("/T1/{id}")
public CommonResult getUser(@PathVariable("id") int id){
return openfeignService.getUser(id);
}
@GetMapping(value = "/T1/timeout/{time}")
public String timeout(@PathVariable("time") int time){
return openfeignService.timeout(time);
}
}
8、Hystrix服务熔断
1、服务雪崩
多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C有调用其他的微服务,这就是所谓的”扇出”,如扇出的链路上某个微服务的调用响应式过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统雪崩,所谓的”雪崩效应”。
2、服务熔断
熔断机制是应对雪崩效应的一种微服务链路保护机制,
当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回”错误”的响应信息。当检测到该节点微服务响应正常后恢复调用链路。
8.1、服务端服务熔断
@Service
@Service
@DefaultProperties(defaultFallback = "defaultFallback")
public class UserService {
//服务熔断
//默认为10s内20次50%错误率
@HystrixCommand(fallbackMethod = "circuitBreakerFallback",commandProperties = {
//在10s内,请求次数达到10次及以上,错误率达到60%,开启断路器,服务降级
//开启服务熔断
@HystrixProperty(name = "circuitBreaker.enabled",value = "true"),
//请求总次数
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),
//请求总时间
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"),
//请求错误率
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"),
})
public String circuitBreaker(Integer id){
if(id < 0){
throw new RuntimeException("id 不能为负数");
}
return "调用成功:" + IdUtil.simpleUUID();
}
public String circuitBreakerFallback(Integer id){
return "id不能为负数:" + id;
}
}
@Controller
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping(value = "/T2/circuitBreaker/{id}")
public String circuitBreaker(@PathVariable("id") Integer id){
return userService.circuitBreaker(id);
}
}
9、GateWay
三大核心概念:
- Route(路由)
- Predicate(断言)
- Filter(过滤器)
9.1、工作流程
9.2、GateWay示例
1、pom
<?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>cloudStudy</artifactId>
<groupId>com.zmy.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-gateway-gateway9527</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</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-starter-web</artifactId>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-actuator</artifactId>-->
<!-- </dependency>-->
</dependencies>
</project>
2、yml
server:
port: 9527
spring:
application:
name: cloud-gateway
eureka:
instance:
hostname: cloud-gateway-service
client:
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://eureka7001.com:7001/eureka
3、业务类
无
4、主启动类
@SpringBootApplication
@EnableEurekaClient
public class CloudGateWayMain9527 {
public static void main(String[] args) {
SpringApplication.run(CloudGateWayMain9527.class,args);
}
}
5、配置网关(yml)
server:
port: 9527
spring:
application:
name: cloud-gateway
# 如下为增加的网关配置
cloud:
gateway:
routes:
- id: T1_1
uri: http://localhost:8082
predicates:
- Path=/T2/** # 断言,相匹配的进行路由
- id: T1_2
uri: http://localhost:8082
predicates:
- Path=/T2/timeout/**
# =========================================================
eureka:
instance:
hostname: cloud-gateway-service
client:
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://eureka7001.com:7001/eureka
6、测试
访问http://localhost:9527/T2/1
9.3、GateWay配置路由的两种方式
- 配置文件方式(见9.2)
- 编码方式:注入RouteLocator的Bean(本节)
@Configuration
public class GateWayConfig {
@Bean
public RouteLocator routeLocator1(RouteLocatorBuilder builder){
return builder.routes()
.route("route1",r -> r.path("/guonei").uri("http://news.baidu.com/guonei"))
.build();
}
@Bean
public RouteLocator routeLocator2(RouteLocatorBuilder builder){
return builder.routes()
.route("route2",r -> r.path("/guoji").uri("http://news.baidu.com/guoji"))
.build();
}
}
9.4、动态路由(使用服务名进行调用,可负载均衡)
1、pom
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2、yaml
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
routes:
- id: T1_1
uri: lb://cloud-T2 # lb://服务名
predicates:
- Path=/T2/** # 断言,相匹配的进行路由
- id: T1_2
uri: lb://cloud-T2
predicates:
- Path=/T2/timeout/**
discovery:
locator:
enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
eureka:
instance:
hostname: cloud-gateway-service
client:
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://eureka7001.com:7001/eureka
效果如下:(若有多个服务,则会负载均衡)
负载均衡效果:
9.5、GateWay常用的断言
- After
,- Before
,- Between
生成时间串的工具类
public class T2 {
public static void main(String[] args) {
ZonedDateTime dateTime = ZonedDateTime.now();
System.out.println(dateTime);
}
}
//打印结果:2020-12-01T17:19:18.420240100+08:00[Asia/Shanghai]
predicates:
- Path=/T2/timeout/**
# 该路径服务满足时间要求才可用,否则404not found
- After=2020-12-01T17:19:18.420240100+08:00[Asia/Shanghai]
- Before=2020-12-01T17:19:18.420240100+08:00[Asia/Shanghai]
- Between=2020-12-01T17:19:18.420240100+08:00[Asia/Shanghai],2020-12-02T17:19:18.420240100+08:00[Asia/Shanghai]
- Cookie
需要两个参数,cookie名+正则表达式
predicates:
- Path=/T2/timeout/**
- Cookie=username,123
crul http://localhost:9527/T1/2 --cookie “username=123”
- Header
predicates:
- Path=/T2/timeout/**
# 请求头要有X-Request-Id属性,并且值为整数,才匹配
- Header=X-Request-Id,\d+
crul http://localhost:9527/T1/2 -H “X-Request-Id:400”
- Host
predicates:
- Path=/T2/timeout/**
# 主机匹配才能成功
- Host=**.abc.com
crul http://localhost:9527/T1/2 -H “Host:www.abc.com”
- Method
predicates:
- Path=/T2/timeout/**
# Get请求才能匹配成功
- Method=GET
- Query
predicates:
- Path=/T2/timeout/**
# 带有查询条件,要有参数名username,并且值还要是整数才能路由
- Query=username,\d+
crul http://localhost:9527/T1/2?username=31
9.6、GateWay过滤器
自定义过滤器
@Component
@Slf4j
public class GateWayFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("网关全局过滤器拦截开始-------------------------");
//如果url为login/**;则直接放行
if(exchange.getRequest().getURI().getPath().equals("/login")){
chain.filter(exchange);
}else{
//请求头中带有Authorization,才放行
String headerToken = exchange.getRequest().getHeaders().getFirst("Authorization");
//检查请求携带的参数的写法
//String username = exchange.getRequest().getQueryParams().getFirst("username");
if (headerToken == null) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
return response.setComplete();
}
}
log.info("网关全局过滤器拦截结束-------------------------");
//放行
return chain.filter(exchange);
}
//加载过滤器的顺序,越小,优先级越高
@Override
public int getOrder() {
return 0;
}
}
10、Config服务配置中心
当我们系统业务扩展到一定程度的时候;免不了会增加很多的配置文件和信息,例如证书文件、接口对接的参数信息、数据库连接信息等;传统的单体式架构系统,SSH、SSM还是Struts等,只能是一个文件一个文件的增加堆积到项目系统中。每次更改配置信息的时候,都要重启服务器,影响线上业务浪费时间等。当配置文件数量达到一定程度的时候,整个项目就会看起来非常臃肿冗余,更甚者可能会拿错配置信息导致程序崩溃等。那么,这时候分布式系统采用的配置中心的优势就突出出来了。由业务拆分的多个模块系统的各配置文件,全部配置在配置中心统一管理;与程序分离,做到动态配置获取配置信息。无需重启服务器即可动态刷新加载配置信息。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kpcOxHxt-1622708965894)(E:\docs\images\Snipaste_2021-06-02_19-05-25.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sLZgpl6C-1622708965896)(E:\docs\images\Snipaste_2021-06-02_19-07-20.png)]
10.1、Spring Cloud Config和GitHub整合使用
10.2、Config配置总控中心(服务端)
- 在github上新建一个仓库
- 克隆到本地,新建配置文件,编写内容并推到远程仓库
- 新建配置中心模块
- POM文件
<?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>cloudStudy</artifactId>
<groupId>com.zmy.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-config-center-3344</artifactId>
<dependencies>
<!--config-server需要添加这个依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-eureka-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>com.zmy.springcloud</groupId>
<artifactId>cloud-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>mysql</groupId>
<artifactId>mysql-connector-java</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: 3344
spring:
application:
name: cloud-config-center
cloud:
config:
server:
git:
# git地址
uri: https://github.com/Zmyk/springcloudconfigtest.git
# git仓库目录
search-paths: springcloudconfigtest
# 分支
label: master
# 增加如下配置
eureka:
client:
service-url:
# 入驻地址
defaultZone: http://localhost:7001/eureka
- 启动类
package com.zmy.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
/**
* @program: cloudStudy
* @description:
* @author: zhangmy
* @create: 2021-06-02 19:38
*/
@SpringBootApplication
@EnableConfigServer
public class SpringConfigApplication3344 {
public static void main(String[] args) {
SpringApplication.run(SpringConfigApplication3344.class,args);
}
}
- 测试
启动7001Eureka,启动3344Config-Server,访问localhost:3344/master/config-test.yml
10.3、Config客户端
- 建模块
- POM
<?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>cloudStudy</artifactId>
<groupId>com.zmy.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-config-client-3355</artifactId>
<dependencies>
<!--客户端导入该依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-eureka-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>com.zmy.springcloud</groupId>
<artifactId>cloud-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>mysql</groupId>
<artifactId>mysql-connector-java</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
建立bootstrap.yml
,系统级配置文件,优先级更高,加载顺序在application.yml
文件之前。
server:
port: 3355
spring:
application:
name: cloud-config-client3355
cloud:
# 拼在一起就是 {uri}/{label}/{name-profile}.yml http://localhost:3344/master/config-dev.yml
config:
# 分支
label: master
# 配置文件名称
name: config
# 配置文件环境
profile: test
# 配置中心地址
uri: http://localhost:3344
# 增加如下配置
eureka:
client:
service-url:
# 入驻地址
defaultZone: http://localhost:7001/eureka
- 主启动
package com.zmy.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
/**
* @program: cloudStudy
* @description:
* @author: zhangmy
* @create: 2021-06-03 11:34
*/
@SpringBootApplication
@EnableEurekaClient
public class CloudConfigClient3355 {
public static void main(String[] args) {
SpringApplication.run(CloudConfigClient3355.class,args);
}
}
- 业务类
package com.zmy.springcloud.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @program: cloudStudy
* @description:
* @author: zhangmy
* @create: 2021-06-03 14:47
*/
@RestController
@RequestMapping("client")
public class ConfigClientController {
@Value("${config.test}")
private String configTest;
@Autowired
private User user;
@RequestMapping("getTest")
public String getTest() {
return configTest;
}
@RequestMapping("getUser")
public User getUser() {
return user;
}
}
package com.zmy.springcloud.propertity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @program: cloudStudy
* @description:
* @author: zhangmy
* @create: 2021-06-03 14:58
*/
@ConfigurationProperties(prefix = "user")
@Component
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private String name;
private Integer age;
private String sex;
}
配置文件中加入user
- 测试
访问http://localhost:3355/client/getTest
访问localhost://3355/client/getUser
10.4、Config客户端动态刷新
实践发现,github上配置文件更新后,3344服务端无需重启即能够得到最新值。但是客户端却不能得到最新值,必须重启客户端才能够得到。
- 首先,config客户端引入监控所需依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
- bootstrap.yml添加配置
# 暴漏监控端点
management:
endpoints:
web:
exposure:
include: "*"
- 开启刷新功能
package com.zmy.springcloud.controller;
import com.zmy.springcloud.propertity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @program: cloudStudy
* @description:
* @author: zhangmy
* @create: 2021-06-03 14:47
*/
@RestController
@RequestMapping("client")
//在业务类上添加该注解,开启刷新功能
@RefreshScope
public class ConfigClientController {
@Value("${config.test}")
private String configTest;
@Autowired
private User user;
@RequestMapping("getTest")
public String getTest() {
return configTest;
}
@RequestMapping("getUser")
public User getUser() {
return user;
}
}
- 发送post请求刷新config客户端
curl -X POST "http://localhost:3355/actuator/refresh"
测试后发现,无论是getTest
还是getUser
,config客户端都能够成功获取到更新后的值。