Spring Cloud学习总结(二)

本文详细介绍了Spring Cloud中的Feign和Gateway的使用,包括Feign的快速入门、负载均衡、Hystrix集成,以及Gateway的路由、过滤器、负载均衡和跨域配置。此外,还提到了Spring Cloud Config的配置管理和Spring Cloud Bus的实时配置更新功能,全面阐述了Spring Cloud微服务架构中的关键组件和最佳实践。
摘要由CSDN通过智能技术生成

说明:由于学习资料是本地视频,尚未找到相关链接,后续找到原视频链接后会及时说明学习视频链接

0. 学习目标

  • 使用Feign进行远程调用
  • 搭建Spring Cloud Gateway网关服务
  • 配置Spring Cloud Gateway路由过滤器
  • 编写Spring Cloud Gateway全局过滤器
  • 搭建Spring Cloud Config配置中心服务
  • 使用Spring Cloud Bus实时更新配置

1. Feign

1.1 简介

在前面的学习中,使用了Ribbon的负载均衡功能,大大简化了远程调用时的代码

String url = "http://user-service/user/" + id;
User user = this.restTemplate.getForObject(url, User.class)

可能以后需要编写类似的大量重复代码,格式基本相同,无非参数不一样。有没有更优雅的方 式,来对这些代码再次优化呢? 可用Feign

Feign的作用:使用Feign实现consumer-demo代码中调用服务

Feign也叫伪装

Feign可以把Rest的请求进行隐藏,伪装成类似SpringMVC的Controller一样。你不用再自己拼接url,拼接参数等等操作,一切都交给Feign去做。

1.2 快速入门

1.导入启动器依赖

​ 在consumer-demo中导入OpenFeign依赖

        <!-- Feign远程服务调用依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

OpenFeign在Feign基础上,支持Spring MVC注解,例如@RequestMapping等

OpenFeign的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。

2.开启Feign功能

​ 在consumer-demo项目的启动类上添加@EnableFeignClients注解开启Feign功能

//整合@SpringBootApplication @EnableDiscoveryClient @EnableCircuitBreaker
@SpringCloudApplication 
@EnableFeignClients //开启Feign功能
public class ConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }

    // 构建一个RestTemplate对象加入到spring容器中进行统一管理
    @Bean
    // 开启负载均衡
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

Feign中已经自动集成了Ribbon负载均衡,因此不需要自己定义RestTemplate进行负载均衡的配置。

3.编写Feign客户端

​ 在consumer-demo中编写UserClient接口,指定user-service路由

// 声明当前类是一个Feign客户端,指定服务名为user-service
@FeignClient("user-service")
public interface UserClient {

    // 和Mybatis的mapper接口类似,会通过动态代理生成这个接口的实现类
    // 拼接http://user-service/user/13
    @GetMapping("/user/{id}")
    User queryById(@PathVariable Long id);
}

首先这是一个接口,Feign会通过动态代理,帮我们生成实现类。这点跟mybatis的mapper很像

@FeignClient ,声明这是一个Feign客户端,同时通过 value 属性指定服务名称

接口中的定义方法,完全采用SpringMVC的注解,Feign会根据注解帮我们生成URL,并访问获取结果

@GetMapping中的**/user**,请不要忘记;因为Feign需要拼接可访问的地址

4.编写处理器ConsumerFeignController(注入Feign客户端并使用)

​ 在consumer-demo中编写ConsumerFeignController接口,通过userClient访问服务

@RestController
@RequestMapping("/cf")
public class ConsumerFeignController {

    @Autowired
    UserClient userClient;

    @GetMapping("/{id}")
    public User queryById(@PathVariable Long id) {
        return userClient.queryById(id);
    }
}

5.测试

在这里插入图片描述

1.3 负载均衡

Feign中已经集成了Ribbon依赖和自动配置

在这里插入图片描述

因此不需要额外引入依赖,也不需要再注册 RestTemplate 对象。

Fegin内置的ribbon默认设置了请求超时时长,默认是1000,我们可以通过手动配置来修改这个超时时长

ribbon:
  ReadTimeout: 2000 # 读取超时时长
  ConnectTimeout: 1000 # 建立链接的超时时长

因为ribbon内部有重试机制,一旦超时,会自动重新发起请求。如果不希望重试,可以添加配置:

ribbon:
  ReadTimeout: 2000 # 读取超时时长
  ConnectTimeout: 1000 # 建立链接的超时时长
  MaxAutoRetries: 0 # 当前服务器的重试次数
  MaxAutoRetriesNextServer: 0 # 重试多少次服务
  OkToRetryOnAllOperations: false # 是否对所有的请求方式都重试

在这里插入图片描述

给UserService的方法设置上线程沉睡时间2秒

@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;

    @GetMapping("/{id}")
    public User queryById(@PathVariable("id") Long id) {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return userService.queryById(id);
    }
}

重新测试http://localhost:8083/cf/13显示访问失败(因为ribbon被取消了重连功能)

1.4 Hystrix集成

Feign默认有对Hystrix的集成

在这里插入图片描述

默认情况下是关闭的。需要通过下面的参数来开启:

feign:
  hystrix:
    enabled: true # 开启Feign的熔断功能

Feign中的Fallback配置不像Ribbon中那样简单

1.定义一个类,实现UserFeignClient作为fallback的处理类

// 服务降级处理逻辑编写类
@Component
public class UserClientFallback implements UserClient {

    @Override
    public User queryById(Long id) {
        User user = new User();
        user.setId(id);
        user.setName("用户异常");
        return user;
    }
}

2.在UserFeignClient中添加fallback = UserClientFallback.class指定实现类

// 声明当前类是一个Feign客户端,指定服务名为user-service
@FeignClient(value = "user-service", fallback = UserClientFallback.class)
public interface UserClient {
    // 和Mybatis的mapper接口类似,会通过动态代理生成这个接口的实现类
    // 拼接http://user-service/user/13
    @GetMapping("/user/{id}")
    User queryById(@PathVariable Long id);
}

3.测试

在这里插入图片描述

1.5 请求压缩

Spring Cloud Feign 支持对请求和响应进行GZIP压缩,以减少通信过程中的性能损耗。通过在consumer-demo中的application.yml配置下面的参数即可开启请求与响应的压缩功能:

feign:
  compression:
    request:
      enabled: true # 开启请求压缩
    response:
      enabled: true # 开启响应压缩

也可对请求的数据类型,以及触发压缩的大小下限进行设置:

feign:
  compression:
    request:
      enabled: true # 开启请求压缩
      mime-types: text/html,application/xml,application/json # 设置压缩的数据类型,默认值为这三种压缩的数据类型
      min-request-size: 2048 # 设置触发压缩的大小下限,默认值为2048
    response:
      enabled: true # 开启响应压缩
1.6 日志级别

前面讲过,通过 logging.level.xx=debug 来设置日志级别。然而这个对Fegin客户端而言不会产生效果。

因为**@FeignClient 注解修改的客户端在被代理时,都会创建一个新的Fegin.Logger实例**。

需要额外指定这个日志的级别才可以

1.在consumer-demo的配置文件中设置日志级别为debug

logging:
  level:
    # 项目的包名
    com.demo: debug

2.在consumer-demo编写FeignConfig配置类,定义日志级别

// Feign配置类
@Configuration
public class FeignConfig {

    @Bean
    Logger.Level feignLoggerLevel() {
        //记录所有请求和响应的明细,包括头信息、请求体、元数据
        return Logger.Level.FULL;
    }
}

Feign支持4种级别:

  • NONE:不记录任何日志信息,这是默认值。

  • BASIC:仅记录请求的方法,URL以及响应状态码和执行时间

  • HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息

  • FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据

3.在consumer-demoUserClient接口类上的@FeignClient中添加configuration = FeignConfig.class指定配置类

// 声明当前类是一个Feign客户端,指定服务名为user-service
@FeignClient(value = "user-service", fallback = UserClientFallback.class, configuration = FeignConfig.class)
public interface UserClient {
    // 和Mybatis的mapper接口类似,会通过动态代理生成这个接口的实现类
    // 拼接http://user-service/user/13
    @GetMapping("/user/{id}")
    User queryById(@PathVariable Long id);
}

4.重启项目进行测试

在这里插入图片描述

可以看到访问的日志

2. Spring Cloud Gateway 网关

2.1 简介

为什么需要网关?

  • 有很多的微服务,使用网关只向外暴露一个统一的接口,隔离原本需要暴露接口的微服务,增强服务调用的安全性
  • Spring Cloud Gateway是Spring官网基于Spring5.0、Spring Boot2.0、Project Reactor等技术开发的网关服务

  • Spring Cloud Gateway基于Filter链提供网关基本功能:安全、监控/埋点、限流等

  • Spring Cloud Gateway为微服务架构提供简单、有效且同意的API路由管理方式

  • Spring Cloud Gateway是可替代Netflix Zuul的一套解决方案

Spring Cloud Gateway组件的核心是一系列过滤器,通过这些过滤器可将客户端发送的请求转发(路由)到对应的微服务中。

Spring Cloud Gateway是加在整个微服务最前沿的防火墙和代理器,隐藏微服务节点IP端口信息,从而加强微服务安全性

Spring Cloud Gateway本身也是一个微服务,需要注册到Eureka服务注册中心:

  • 添加依赖
  • 启动类添加注解开启Gateway
  • 配置文件中添加Eureka注册中心地址

网关的核心功能:

  • 过滤
  • 路由
2.2 架构

在这里插入图片描述

不管是来自于客户端(PC或移动端)的请求,还是服务内部调用。一切对服务的请求都可经过网关,然后再由网关来实现鉴权、动态路由等操作。

简而言之,Gateway就是服务的统一入口

2.3 核心概念
  • 路由(route):由一个ID、一个目的URL、一组断言工厂、一组Filter组成。如果路由断言为真,则说明请求URL和配置路由匹配
  • 断言(Predicate):Spring Cloud Gateway中的断言函数输入类型是Spring5.0框架中的ServerWebExchange,断言函数允许开发者定义匹配来自Http Request中的任何信息(例如请求头和参数)
  • 过滤器(Filter):一个标准的Spring WebFilter。过滤器Filter会对请求和响应进行修改处理。Spring Cloud Gateway中的Filter分为两种类型:
    • Gateway Filter
    • Global Filter
2.4 Gateway入门

目标:搭建网关服务工程,并测试网关服务作用(过滤+路由)

需求:通过网关系统将包含/user的请求路由到http://localhost/user/用户id

1.创建网关工程

​ 在原项目spring-cloud中创建新的modulegateway

2.添加启动器依赖

    <dependencies>
        <!-- 添加Gateway网关依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <!-- 添加Client客户端依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>

3.编写启动引导类和配置文件,在配置文件application.yml中设置路由信息

@SpringBootApplication
@EnableDiscoveryClient // 开启Client服务客户端
public class GatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class, args);
    }
}
server:
  port: 8034

spring:
  application:
    name: gateway
  cloud:
    gateway:
      routes:
        # 路由id,可任意写 注意id前的空格
        - id: user-service
          uri: http://127.0.0.1:8080
          # 路由断言:可匹配映射路径
          predicates:
            # 当访问路径以/user开头的时候将请求转发到uri对应的微服务中 注意Path前的空格
            - Path=/user/**

eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
  instance:
    prefer-ip-address: true

4.测试

在这里插入图片描述

Gateway将http://localhost:8034/user/13转发到http://localhost:8080/user/13

2.5 面向服务的路由

目标:使用在Eureka注册的服务作为路由指向地址

分析:

如果将路由规则中的服务地址写死是明显不合理:

  • 和前面的服务访问一样,访问的时候需要记住服务提供者的地址
  • 并且一个服务有多个实例的话配置麻烦

应该根据服务的名称,去Eureka注册中心查询服务对应的所有实例列表,然后进行动态路由。在Spring Cloud Gateway中可以通过配置动态路由解决

1.修改gateway网关项目中的application.yml,将服务user-service的uri改为lb://user-service

server:
  port: 8034

spring:
  application:
    name: gateway
  cloud:
    gateway:
      routes:
        # 路由id,可任意写
        - id: user-service
#          uri: http://127.0.0.1:8080
          # lb: loadBalance负载均衡,lb表示从eureka中获取具体服务,通过user-service服务名去Eureka注册中心找对应的服务列表
          uri: lb://user-service
          # 路由断言:可匹配映射路径
          predicates:
            # 当访问路径以/user开头的时候将请求转发到uri对应的微服务中
            - Path=/user/**

eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
  instance:
    prefer-ip-address: true

2.测试

在这里插入图片描述
在这里插入图片描述

面向服务的路由:只需要在配置文件中指定路由路径,类似:lb://user-service,其中user-service为注册到eureka的服务id名称

注:lb之后编写的服务名必须要在eureka中注册才能使用

2.6 路由前缀处理

目标:可以对请求到网关服务的地址添加或去除前缀

分析:

假设提供服务的地址为http://127.0.0.1:8080/user/13

  • 添加前缀:对请求地址添加前缀路径之后再作为代理的服务地址

http://127.0.0.1:8034/13 --> http://127.0.0.1:8080/user/13 添加前缀路径/user

  • 去除前缀:对请求地址路径去除一些前缀后再作为代理的服务地址

http://127.0.0.1:8034/user-service/user/13 --> http://127.0.0.1:8080/user/13 去除前缀路径/user-service

2.6.1 添加前缀

1.gatewayapplication.yml中添加filters过滤配置,并在其中添加- PrefixPath前缀

spring:
  application:
    name: gateway
  cloud:
    gateway:
      routes:
        # 路由id,可任意写
        - id: user-service
#          uri: http://127.0.0.1:8080
          # lb: loadBalance负载均衡,lb表示从eureka中获取具体服务,通过user-service服务名去Eureka注册中心找对应的服务列表
          uri: lb://user-service
          # 路由断言:可匹配映射路径
          predicates:
            # 当访问路径以/user开头的时候将请求转发到uri对应的微服务中
#            - Path=/user/**
            - Path=/**
          filters:
            # 添加请求路径的前缀
            - PrefixPath=/user

上述配置的意思:将任何http://127.0.0.1:8034/开头的访问都添加一个/user前缀改为http://127.0.0.1:8034/user/并转发到user-service服务中,根据user-service服务名去eureka注册中心找对应的服务地址列表,并选择一个进行请求访问

过滤器PrefixPath:实现映射路径中地址的添加

2.测试

在这里插入图片描述

2.6.2 去除前缀

1.gatewayapplication.yml中添加filters过滤配置,并在其中添加- StripPrefix前缀

spring:
  application:
    name: gateway
  cloud:
    gateway:
      routes:
        # 路由id,可任意写
        - id: user-service
#          uri: http://127.0.0.1:8080
          # lb: loadBalance负载均衡,lb表示从eureka中获取具体服务,通过user-service服务名去Eureka注册中心找对应的服务列表
          uri: lb://user-service
          # 路由断言:可匹配映射路径
          predicates:
            - Path=/user-service/user/**
          filters:
            # 1表示过滤一个前缀,2表示过滤两个前缀,以此类推
            - StripPrefix=1

上述配置的意思:将任何http://127.0.0.1:8034/user-service/user/开头的请求访问url都去掉user-service改为http://127.0.0.1:8034/user/,并转发到user-service服务中(user-service服务必须在eureka注册中心注册成功,并且可通过user-service服务名称找到这个服务提供地址),根据user-service服务名去eureka注册中心找对应的服务地址列表,并选择一个进行请求访问

过滤器StripPrefix:实现映射路径中地址的去除

2.测试

在这里插入图片描述

小结:

当客户端的请求地址与微服务的服务地址不一致的时候,可通过在网关项目的application.yml中配置路径过滤器实现路径前缀的添加/去除。

2.7 过滤器

目标:Gateway默认过滤器的用法和过滤器类型学习

2.7.1 简介

Gateway网关的一个重要功能,就是实现请求的鉴权。这个鉴权操作往往通过网关提供的过滤器实现(2.6中的路由前缀也是有过滤器实现)

过滤器名称说明
AddRequestHeader对匹配上的请求加上Header
AddRequestParameters对匹配上的请求路由添加参数
AddResponseHeader对从网关返回的相应添加Header
StripPrefix对匹配上的请求路径去除前缀

在这里插入图片描述

2.7.1.1 配置全局默认过滤器

1.在gateway项目的application.yml配置文件中添加default-filters配置全局默认过滤器

spring:
  application:
    name: gateway
  cloud:
    gateway:
      routes:
        # 路由id,可任意写
        - id: user-service
#          uri: http://127.0.0.1:8080
          # lb: loadBalance负载均衡,lb表示从eureka中获取具体服务,通过user-service服务名去Eureka注册中心找对应的服务列表
          uri: lb://user-service
          # 路由断言:可匹配映射路径
          predicates:
            # 当访问路径以/user开头的时候将请求转发到uri对应的微服务中
#            - Path=/user/**
#            - Path=/**
            - Path=/user-service/user/**
          filters:
            # 添加请求路径的前缀
#            - PrefixPath=/user
            # 1表示过滤一个前缀,2表示过滤两个前缀,以此类推
            - StripPrefix=1
      # 默认过滤器,对所有路由都生效
      default-filters:
        # 对从网关返回的相应添加Header
        - AddResponseHeader=X-Response-Default-MyName, spring-cloud-demo

2.测试

访问http://127.0.0.1:8034/user-service/user/13,F12查看发现请求的响应头中有配置的Header

注:如果使用Chrome查看不到,可更换为IE浏览器

在这里插入图片描述

2.7.1.2 过滤器类型

Gateway有两种过滤器:

  • 局部过滤器:通过spring.cloud.gateway.routes.filters配置在具体的路由下,只作用于当前路由上。若配置spring.cloud.gateway.default-filters会对所有路由生效也算是全局过滤器(这些过滤器实现通过实现GatewayFilterFactory接口)
  • 全局过滤器:不需要在配置文件中配置,作用在所有路由上(实现GlobalFilter接口即可)
2.7.2 过滤器生命周期

Spring Cloud Gateway的Filter生命周期类似Spring MVC的拦截器有两个:“pre”和“post”,分别在请求被执行前调用和被执行后调用

在这里插入图片描述

此处prepost可通过过滤器的GatewayFilterChain执行filter方法前后实现

2.7.3 使用场景
  • 请求鉴权:一般GatewayFilterChain执行filter方法前,若发现没有权限则直接返回空
  • 异常处理:一般GatewayFilterChain执行filter方法后,记录异常并返回
  • 服务调用时长统计:GatewayFilterChain执行filter方法前后根据时间统计

小结:

过滤器的用法:在配置文件中指定要使用的过滤器名称

过滤器的类型:局部、全局

适用场景:请求鉴权、异常处理、记录调用时长等

2.8 自定义过滤器
2.8.1 自定义局部过滤器

目标:按照默认过滤器编写并配置一个自定义局部过滤器,该过滤器可通过配置文件中的参数名称获取请求参数值

需求:在过滤器中将http://localhost:8043/user-service/user/13?name=ciery中的参数name的值获取到并输出到控制台

1.配置过滤器

与其他局部过滤器配置一致,在filters中添加配置即可

spring:
  application:
    name: gateway
  cloud:
    gateway:
      routes:
        # 路由id,可任意写
        - id: user-service
          # lb: loadBalance负载均衡,lb表示从eureka中获取具体服务,通过user-service服务名去Eureka注册中心找对应的服务列表
          uri: lb://user-service
          # 路由断言:可匹配映射路径
          predicates:
            - Path=/user-service/user/**
          filters:
            # 1表示过滤一个前缀,2表示过滤两个前缀,以此类推
            - StripPrefix=1
            # 配置自定义局部过滤器
            - MyParam=name

2.编写过滤器

@Component
public class MyParamGatewayFilterFactory extends AbstractGatewayFilterFactory<MyParamGatewayFilterFactory.Config> {

    static final String PARAM_NAME = "param";

    public MyParamGatewayFilterFactory() {
        super(Config.class);
    }

    public List<String> shortcutFieldOrder() {
        return Arrays.asList(PARAM_NAME);
    }

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            // 获取请求参数中param对应的参数名的参数值
            ServerHttpRequest request = exchange.getRequest();
            if (request.getQueryParams().containsKey(config.param)) {
                // 请求中包含param,则打印输出
                request.getQueryParams().get(config.param).forEach(value -> System.out.printf("-----局部过滤器---%s = %s------", config.param, value));
            }
            return chain.filter(exchange);
        };
    }


    public static class Config{
        // 对应在配置过滤器的时候指定的参数名
        private String param;

        public String getParam() {
            return param;
        }

        public void setParam(String param) {
            this.param = param;
        }
    }
}

3.测试

在这里插入图片描述
在这里插入图片描述

请求中携带?name=ciery之后,经过过滤器输出对应的字符串

如果携带其他参数例如?age=23,经过过滤器不拦截,和普通请求一致

参数名可变,即不一定每次都是name;需要可通过配置过滤器的时候做到配置参数名,即修改application.yml中的-MyParam即可,例如修改为age,则请求url中携带age可以输出

自定义过滤器命名应该为:***GatewayFilterFactory

2.8.2 自定义全局过滤器

目标:定义一个全局过滤器检查请求中是否携带有token参数

需求:编写全局过滤器,在过滤器中检查请求地址中是否携带token参数。如果token参数的值存在则放行;如果token的参数值为空或者不存在则设置返回的状态码:未授权也不再执行下去

1.编写全局过滤器

@Component // 过滤器是一个组件,需要使用@Component进行注册
public class MyGlobalFilter implements GlobalFilter, Ordered {
    /**
     * Process the Web request and (optionally) delegate to the next {@code WebFilter}
     * through the given {@link GatewayFilterChain}.
     *
     * @param exchange the current server exchange
     * @param chain    provides a way to delegate to the next filter
     * @return {@code Mono<Void>} to indicate when request processing is complete
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("--------全局过滤器MyGlobalFilter-----------");
        // 获取请求中第一个参数值
        String token = exchange.getRequest().getQueryParams().getFirst("token");
        if (StringUtils.isBlank(token)) {
            // 设置响应状态码为未授权
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }
        // 有token放行
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        // 值越小越先执行
        return 1;
    }
}

2.测试

在这里插入图片描述
在这里插入图片描述

当请求中未加token参数的时候,全局过滤器MyGlobalFilter进行拦截,返回401 Unauthorized错误;当请求中添加token参数的时候(后续也可对token参数的值进行限制,例如登录后生成统一类型的token,之后的请求都需要携带这个token进行访问,否则跳转登录页面等),全局过滤器放行,请求可以得到正常访问并响应。

2.9 负载均衡和熔断处理

Gateway默认集成Ribbon负载均衡和Hystrix熔断机制。但是所有的超时策略都使用的是默认值,例如熔断超时时间只有1s,这就意味着只要1s没有请求到服务就会进行服务降级。所以实际应用中建立手动配置

# 熔断配置
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 6000

# 负载均衡配置
ribbon:
  ConnectTimeout: 1000
  ReadTimeout: 2000
  MaxAutoRetries: 0
  MaxAutoRetriesNextServer: 0
2.10 Gatewey跨域配置

一般网关都是所有微服务的统一入口,必然在调用的时候会出现跨域问题。

跨域:在js请求访问中,如果访问的地址与当前服务器的域名、ip或者端口号不一致则称为跨域请求。如果不解决的话就不能获取对应地址的返回结果

(例如一般的web项目中,如果前后端分离开发,在测试的过程中会因为前后端不是一个ip所以存在跨域问题;当前后端整合之后启动因为ip相同所以跨域问题不存在)

例如:从http://127.0.0.1:8033的js访问http://127.0.0.1:8080的数据,因为端口不同所以也是跨域请求

在访问Spring Cloud Gateway网关服务器的时候如果出现跨域问题的话,可以在网关服务器中通过配置解决,配置允许哪些服务是可以跨域请求的

 spring:
   cloud:
     gateway:
      # 配置跨域请求
      globalcors:
        corsConfigurations:
          '[/**]':
            #allowedOrigins: * # 这种写法或者下面的都可以,*表示全部 
            allowedOrigins: 
            - "http://docs.spring.io" 
            allowedMethods: 
            - GET

上述配置说明:

可允许来自http://docs.spring.io的get请求方式获取服务数据

allowedOrigins:指定允许访问的服务器地址,例如http://localhost:10080也是可以的

‘[/**]’:表示对所有访问到网关服务器的请求地址

2.11 Gateway的高可用

启用多个Gateway服务,自动注册到Eureka形成集群。

如果是服务内部访问,访问Gateway自动负载均衡,没啥问题

但是对于Gateway来讲,更多的是外部访问,例如PC端、移动端等,它们无法通过Eureka进行负载均衡,解决的方法就是使用其他服务网关,对Gateway进行代理,例如Nginx

2.12 Gateway与Feign的区别
  • Gateway:作为整个应用的流量入口,接收所有的请求,例如PC、移动端等,并将不同的请求转发至不同的微服务模块,其作用可视为nginx;大部分情况下用作权限鉴定、服务端流量控制
  • Feign:将当前微服务的部分服务接口暴露出来,且主要用于各个微服务之间的服务调用

Gateway网关一般直接给终端请求使用;Feign一般用在微服务之间调用

3. Spring Cloud Config分布式配置中心

3.1 简介

目标:了解分布式配置中心的作用

在分布式系统中,由于服务数量非常多,配置文件分散在不同的微服务项目中,管理不方便。

为方便配置文件集中管理,需要分布式配置中心组件

在Spring Cloud中,提供了Spring Cloud Config,其支持配置文件放在配置服务的本地,也支持放在远程Git仓库中(例如码云、GitHub等)

在这里插入图片描述

配置中心本质上也是一个微服务,同样需要注册到Eureka服务注册中心

小结:Spring Cloud Config可以通过修改在git仓库中的配置文件实现其它所有微服务的配置文件的修改

3.2 Git配置管理

目标:创建码云的远程公开git参数,搭建配置中心微服务config-server

1.创建git仓库(码云创建)并将user-service项目的application.yml配置加入其中

在这里插入图片描述

2.搭建配置中心config-server(一个Spring Boot项目)

2.1 添加依赖

    <dependencies>
        <!-- 添加eureka服务客户端依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!-- 添加config-server配置中心依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-server</artifactId>
        </dependency>
    </dependencies>

2.2 启动类添加@EnableConfigServer配置开启配置服务中心

@SpringBootApplication
@EnableConfigServer // 开启config server配置服务中心
public class ConfigServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConfigServerApplication.class, args);
    }
}

2.3 配置application.yml,添加config.server.uri.gitgit仓库的链接

server:
  port: 12000

spring:
  application:
    name: config-server
  cloud:
    config:
      server:
        git:
        # 配置git仓库链接
          uri: https://gitee.com/ciery/spring-cloud.git

eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka

3.测试

在这里插入图片描述

启动eureka注册中心和config配置中心,访问http://localhost:12000/user-dev.yml,可查看到码云存储的user-dev.yml文件

当码云中这个user-dev.yml有改动时,刷新上述网址可得到更新后的数据

在这里插入图片描述

在这里插入图片描述

git上配置文件有统一的命名方式:{application}–{profile}.yml或{application}-{profile}.properties

application:应用名称

profile:区分开发环境、测试环境、生产环境等(dev、test、provided)

例如user-dev.yml:用户微服务开发环境下的配置文件

3.3 获取配置中心配置

目标:改造用户微服务user-service,配置文件信息不再由微服务项目提供,而是从配置中心获取

需求:将服务提供工程user-service中的application.yml删除,修改为从配置中心config-server中获取

1.添加启动器依赖

user-service项目中添加config-server的依赖

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
            <version>2.1.1.RELEASE</version>
        </dependency>

2.修改配置文件

​ 删除user-service中原本的application.yml文件,替换为bootstrap.yml文件,并在其中配置仓库中对应的配置文件路径和eureka注册中心地址(同时config-server中原本的关于git地址的配置也得在,才能找到仓库)

spring:
  cloud:
    config:
      # 要与仓库中的配置文件的application保持一致
      name: user
      # 要与仓库中的配置文件的profile保持一致
      profile: dev
      # 要与仓库中的配置文件所属的版本(分支)一致
      label: master
      discovery:
        # 使用配置中心
        enabled: true
        # 配置中心服务名
        service-id: config-server
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka

3.测试

​ 启动eureka-serveruser-serviceconfig-server,可以从eureka注册中心看到user-service服务注册成功,并且可正常访问http://localhost:8080/user/13,即可正常从配置中心config-server中获取user-service的配置文件连接到数据库,获取id=13对应的数据

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

小结:将原来的application.yml删除,添加bootstrap.yml配置文件

同样是Spring Boot的默认配置文件,bootstrap.yml:其内容经常是配置一些项目中固定的配置项

如果是项目经常变动的应该配置到application.yml中,现使用了配置中心则应配置到git仓库中对应的配置文件user-dev.yml

bootstrap.yml文件相当于项目启动时的引导文件,内容相对固定。application.yml文件是微服务的一些常规配置参数,变化比较频繁

感觉上讲,config-server配置中心更是方便微服务项目部署之后,对于某个微服务配置文件的在线修改(如果没有配置中心的话,就必须得在项目中更新配置文件然后重新部署这个服务;如果是配置文件在配置中心的话,直接更新配置文件,调用服务的时候就会从配置中心获取最新的配置信息

4. Spring Cloud Bus服务总线

4.1 Spring Cloud Config中配置中心3.3存在的问题

3.2-3.3中已经完成将微服务中的配置文件集中存储在远程Git仓库,并通过配置中心微服务从Git仓库拉取配置文件当用户微服务启动时会连接配置中心获取配置信息从而启动用户微服务

但是如果更新Git仓库中的配置文件,用户微服务并不会及时接收到新的配置信息并更新,需要一定的等待时间

1.Git仓库中修改user-dev.yml

server:
  port: ${port:8080}

spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/springcloud?useSSL=false
    username: root
    password: root
  application:
    # 应用名(将来会作为服务的id使用)
    name: user-service

mybatis:
  type-aliases-package: com.demo.domain

eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
  instance:
    # 设置服务实例更倾向于使用ip寻址而非host名,
    prefer-ip-address: true
    # 配置服务实例具体的ip地址
    ip-address: 127.0.0.1
    # 续约间隔,默认30s(半分钟)
    lease-renewal-interval-in-seconds: 5
    # 服务失效间隔,默认90s(一分钟半)
    lease-expiration-duration-in-seconds: 5

test:
  name: ciery

2.修改user-service,输出配置文件中的test.name

@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;

    @Value("${test.name}")
    String name;

    @GetMapping("/{id}")
    public User queryById(@PathVariable("id") Long id) {
        System.out.println("-----配置中心中配置文件的test.name=" + name);
        return userService.queryById(id);
    }
}

3.测试

​ 启动注册中心eureka-server、配置中心config-server和用户服务user-service,访问http://localhost:8080/user/13

在这里插入图片描述

第一个输出为更新前的输出,user-service从配置中心获取配置文件,拿到test.name的值为ciery并输出

Git仓库更新user-dev.yml

server:
  port: ${port:8080}

spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/springcloud?useSSL=false
    username: root
    password: root
  application:
    # 应用名(将来会作为服务的id使用)
    name: user-service

mybatis:
  type-aliases-package: com.demo.domain

eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
  instance:
    # 设置服务实例更倾向于使用ip寻址而非host名,
    prefer-ip-address: true
    # 配置服务实例具体的ip地址
    ip-address: 127.0.0.1
    # 续约间隔,默认30s(半分钟)
    lease-renewal-interval-in-seconds: 5
    # 服务失效间隔,默认90s(一分钟半)
    lease-expiration-duration-in-seconds: 5

test:
  name: ciery1

第二个输出为更新后的输出,user-service并没有及时更新配置文件,依旧输出的是ciery而非ciery1

重启user-service项目之后访问可以得到ciery1

结论:对Git仓库中配置文件的修改没有及时更新到用户微服务,只有重启用户服务才会生效

如果想在不重启的情况下及时更新配置,可用Spring Cloud Bus实现配置的自动更新

4.2 Spring Cloud Bus简介

Spring Cloud Bus消息总线是用轻量级的消息代理将分布式的节点连接起来,可用于广播配置文件的更改或服务的监控管理,即消息总线可以为微服务做监控,也可实现应用程序间的相互通信

Spring Cloud Bus可选的消息代理有RabbitMQ和Kafka

在这里插入图片描述

很多微服务注册到Eureka服务中心

配置中心也注册到Eureka服务中心,同时配置项目配置文件对应的git仓库地址

每个微服务都有各自的配置(固定的配置是关于Git仓库中配置文件的定位信息),访问某个微服务,这个服务会去访问配置中心,配置中心根据服务中的配置获取到Git仓库中的配置文件(实际上配置中心会根据微服务中的关于配置文件的命名、位置等信息去Git仓库中拉取相应的配置文件到本地缓存,以供这个微服务使用)

如果这个时候用户在Git仓库中修改了某个微服务的配置文件,配置中心会发起一个消息(说明有一个配置项被修改),所有的微服务(消息的消费者)会监听到RabbitMQ的改变,然后及时进行更新

总结:Spring Cloud Bus可以将git仓库的配置文件更新,在不重启系统的情况下实现及时同步到各个微服务

4.3 Spring Cloud Bus应用

目标:启动RabbitMQ,通过修改Git中的配置文件后,发送Post请求实现及时更新用户微服务中的配置项

需求:在Git仓库中修改user-dev.yml文件,实现不重启user-service的情况下及时更新配置文件

1.启动RabbitMQ

2.修改配置中心config-server

2.1 添加Bus和Rabbit依赖

        <!-- 添加spring-cloud-bus消息总线依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-bus</artifactId>
        </dependency>
        <!-- 添加spring-cloud-bus RabbitMQ依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-stream-binder-rabbit</artifactId>
        </dependency>

2.2 配置文件中添加RabbitMQ和服务总线暴露路径的配置

server:
  port: 12000

spring:
  application:
    name: config-server
  cloud:
    config:
      server:
        git:
          uri: https://gitee.com/ciery/spring-cloud.git
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest

eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka

management:
  endpoints:
    web:
      exposure:
        # 暴露触发消息总线的地址
        include: bus-refresh

3.修改服务提供工程user-service

3.1 添加bus、Rabbit和actuator依赖

        <!-- 添加spring-cloud-bus消息总线依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-bus</artifactId>
        </dependency>
        <!-- 添加spring-cloud-bus RabbitMQ依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-stream-binder-rabbit</artifactId>
        </dependency>
        <!-- 添加actuator监控依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

3.2 配置文件中添加RabbitMQ配置

spring:
  cloud:
    config:
      # 要与仓库中的配置文件的application保持一致
      name: user
      # 要与仓库中的配置文件的profile保持一致
      profile: dev
      # 要与仓库中的配置文件所属的版本(分支)一致
      label: master
      discovery:
        # 使用配置中心
        enabled: true
        # 配置中心服务名
        service-id: config-server
  # rabbitmq的配置信息,如下配置的都是默认值,也可直接不配置(系统使用默认值)
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest

eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka

3.3 UserController中添加@RefreshScope开启刷新配置

@RestController
@RequestMapping("/user")
@RefreshScope // 刷新配置
public class UserController {
    @Autowired
    private UserService userService;

    @Value("${test.name}")
    String name;

    @GetMapping("/{id}")
    public User queryById(@PathVariable("id") Long id) {
        System.out.println("-----配置中心中配置文件的test.name=" + name);
        return userService.queryById(id);
    }
}

4.测试
​ 启动注册中心eureka-server、配置中心config-server、用户服务user-service,访问http://localhsot:8080/user/13获取到正常的结果

​ 修改git仓库中用户服务的配置文件user-dev.yml,使用postman或RESTClient发送POST请求访问http://127.0.0.1:12000/actuator/bus-refresh

​ 重新访问原链接,可以看到配置文件被更新

说明:请求地址http://127.0.0.1:12000/actuator/bus-refresh中 /actuator是固定的,/bus-refresh对应的是配置中心config-server中的application.yml文件的配置项include的内容。

这个POST请求的作用是访问配置中心的消息总线服务,消息总线服务接收到请求后会向消息队列中发送消息,各个微服务会监听消息队列。当微服务接收到队列中的消息后,会重新从配置中心获取最新的配置信息。

5. Spring Cloud体系技术综合应用说明

目标:了解Spring Cloud中的Eureka、Ribbon、Gateway、Config、Bus和Feign等技术的综合应用

在这里插入图片描述

0.根据业务需求开发相应的微服务,例如用户服务和订单服务等

1.这些服务通过注册到Eureka服务注册中心向外界提供服务调用,每个服务都可以做集群部署(一个集群多个相同的服务)

2.Eureka服务注册中心存储服务提供地址列表,并定期检查服务提供者的心跳。若检测到某个微服务哟屋恩替直接剔除

3.服务间可通过Feign进行调用

4.各个微服务的配置文件可以通过配置中心Config Server从Git仓库中拉取对应的配置文件保存在本地缓存中

5.如果Git仓库中微服务的配置文件被更新,会调用post请求的/actuator/bus-refresh进行刷新,实现不重启微服务及时更新自己的配置文件(各个微服务通过监听MQ的变化去更新自己的配置文件)

6.外部请求访问会先到Gateway网关,网关可对其进行权限鉴定、异常处理、日志记录等操作,之后根据配置的微服务id将请求路由到不同的微服务中

7.Gateway默认集成Ribbon,根据访问中的url匹配配置中的微服务id,然后根据id在Eureka注册中心找到对应的服务地址列表,然后负载均衡算法找到一个对应的服务地址以供访问请求

8.当服务出现异常的时候,可以通过Hystrix对服务进行降级处理(线程隔离+服务熔断)

简单总结

  1. Feign

    1. 是一个组件,可实现自动拼接服务地址
    2. 开启Feign
    3. 配置
      1. 负载均衡
      2. Hystrix服务熔断
      3. 请求压缩
      4. 日志级别
  2. Spring Cloud Gateway

    1. 是一个组件,由一系列过滤器组成
    2. 作用:路由和过滤
      1. 路由:配置文件中指定服务名和地址、过滤器、断言
      2. 过滤:可在服务执行之前和之后执行一些非功能性业务
    3. 过滤器:
      1. 局部过滤器:针对配置的路由
      2. 全局过滤器:对所有的路由
    4. 配置
      1. 负载均衡
      2. 服务熔断
    5. 与Feign的区别
      1. Feign:用于服务间调用
      2. Gateway:一般的请求,是所有微服务的入口
  3. Spring Cloud Config

    1. 是一个配置中心服务
    2. 作用:可获取git仓库中的配置文件,给其他微服务使用,从而使各个微服务的配置在git仓库中进行集中式管理
    3. 改造user-service:将配置文件删除,然后从配置中心获取
    4. 问题:git仓库中修改配置项,微服务系统中的配置项没有及时更新,需要重启
  4. Spring Cloud Bus

    1. 可实现修改git仓库中的配置文件并及时同步和不重启微服务系统的情况下更新到配置项
    2. 使用RabbitMQ进行程序间的消息通信
    3. 访问刷新配置中心的一个地址bus-refresh,发送MQ消息。微服务接收消息之后获取最新的配置项
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值