SpringCloud

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客户端都能够成功获取到更新后的值。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值