• 02Nacos和Feign及Gateway配置


    一、Nacos配置管理

    1.统一配置管理

    配置更改热更新

     ①在Nacos中添加配置信息:

     ②在弹出表单中填写配置信息:

     2.配置获取的步骤如下

     配置文件bootstrap.yml的优先级比application.yml优先级高。把nacos地址放入bootstrap.yml。

    ①在userservice中引入Nacos的配置管理客户端依赖:

    1. <dependency>
    2. <groupId>com.alibaba.cloudgroupId>
    3. <artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
    4. dependency>

    ②在userservice中的resource目录添加一个bootstrap.yml文件,这个文件是引导文件,优先级高于application.yml:

    1. spring:
    2. application:
    3. name: userservice # 服务名称
    4. profiles:
    5. active: dev # 环境
    6. cloud:
    7. nacos:
    8. server-addr: localhost:8848 # nacos地址
    9. config:
    10. file-extension: yaml # 文件后缀名

    在微服务中添加bootstrap.yml,配置nacos地址、当前环境、服务名称、文件后缀名。这些决定了程序启动时去nacos读取哪个文件。

    ③在user-service中将pattern.dateformat这个属性注入到UserController中做测试

     ④访问http://localhost:8081/user/now获取时间对应格式,配置生效

     

    3.配置自动更新

    Nacos配置更新,无需重启微服务就可以配置更新。

    方式一:在@Value注入的变量所在类上添加注解@RefreshScope

    方式二:

    使用@ConfigurationProperties注解

    ①创建一个配置类PatternProperties

    1. @Component
    2. @Data
    3. @ConfigurationProperties(prefix = "pattern") // 跟配置文件的顶级名一致
    4. public class PatternProperties {
    5. private String dateformat;
    6. }

    ②在需要用的controller进行注入

     

    总结:

    • 通过@Value注解注入,结合@RefreshScope来刷新
    • 通过@ConfigurationProperties注入,自动刷新

    注意事项:

    • 不是所有的配置都适合放到配置中心,维护起来比较麻烦
    • 建议将一些关键参数,需要运行时调整的参数放到nacos配置中心,一般都是自定义配置

    4.多服务共享配置

    启动的时候从nacos读取多个配置文件优先级

    userservice-dev.yaml>userservice.yaml>application.yml

    无论profile如何变化,[spring.application.name].yaml这个文件一定会加载,因此多环境共享配置可以写入这个文件。

    总结:

     

     二、http客户端Feign

    1.RestTemplate方式存在的问题

    RestTemplate远程调用的代码

    String url = "http://userservice/user/" + order.getUserId();
    User user = restTemplate.getForObject(url, User.class);

    ①代码可读性差,编程体验不统一

    ②参数复杂时URL难以维护

    2.定义和使用Feign客户端

    ①在orderservice导入依赖

    1. <dependency>
    2. <groupId>org.springframework.cloudgroupId>
    3. <artifactId>spring-cloud-starter-openfeignartifactId>
    4. dependency>

    ②在启动类添加注解开启Feign功能@EnableFeignClients

     ③编写Feign客户端接口

    1. @FeignClient("userservice") //提供者名
    2. public interface UserClient {
    3. @GetMapping("/user/{id}")
    4. User findById(@PathVariable("id") Long id);
    5. }

    ④使用Feign客户端代替RestTemplate

    总结:

    • 引入依赖
    • 添加@EnableFeignClients注解
    • 编写FeignClient接口
    • 使用FeignClient中定义的方法代替RestTemplate

    3.配置Feign日志的两种方式

    feign.Logger.Level 修改日志级别 包含四种不同的级别:NONE、BASIC、HEADERS、FULL

    方式一

    配置文件方式

    ①全局生效

     ②局部生效

    方式二

    ①声明一个Bean

    1. public class FeignClientConfiguration {
    2. @Bean
    3. public Logger.Level feignLogLevel(){
    4. return Logger.Level.BASIC;
    5. }
    6. }

    ②全局配置,则把它放到@EnableFeignClients这个注解中

    @EnableFeignClients(defaultConfiguration = FeignClientConfiguration.class)

    ③局部配置,则把它放到@FeignClient这个注解

    @FeignClient(value="userservice",configuration = FeignClientConfiguration.class)

    总结

    方式一是配置文件,feign.client.config.xxx.loggerLevel

    • 如果xxx是default则代表全局
    • 如果xxx是服务名称,例如userservice则代表某服务

    方式二是java代码配置Logger.Level这个Bean

    • 如果在@EnableFeignClients注解声明则代表全局
    • 如果在@FeignClient注解中声明则代表某服务

    4.Feign性能优化

    ①Feign连接池的设置

    引入httpClient依赖

     配置连接池

    优化总结

    • 日志级别尽量用basic
    • 使用HttpClient或OKHttp代替URLConnection

            引入feign-httpClient依赖

            配置文件开启httpClient功能,设置连接池参数

    5.Feign的最佳实践

    方式一(继承):给消费者的FeignClient和提供者controller定义统一的父接口。然后实现接口

    缺点

    • 服务紧耦合
    • 父接口参数列表中的映射不会被继承

    方式二(抽取):将FeignClient抽取为独立模块,并且把接口有关的POJO、默认的Feign配置都放到这个模块中,提供给所有消费者使用

    6.抽取FeignClient

    • 首先创建一个module,命名为feign-api,然后引入feign的starter依赖

    • 将order-service中编写的UserClient、User、DefaultFeignConfiguration都复制到feign-api项目中

    • 在order-service中引入feign-api的依赖

    • 修改order-service中的所有与上述三个组件有关的import部分,改成导入feign-api中的包
    • 重启测试

    注意:

    当定义的FeignClient不在SpringBootApplication的扫描包范围时,这些FeignClient无法使用。有两种方式解决:

    方式一:指定FeignClient所在包

    @EnableFeignClients(basePackages = "cn.itcast.feign.clients")

    方式二:指定FeignClient字节码

    @EnableFeignClients(clients = {UserClient.class})

    三、统一网关Gateway

    1.为什么需要网关

    ①对用户请求做身份验证、权限校验。

    ②将用户请求路由到微服务,实现负载均衡

    ③用户请求做限流

    2.网关技术的实现

    SpringCloud实现网关的方式

    ①gateway

    ②zuul

    Zuul是基于Servlet的实现,属于阻塞式编程。而SpringCloudGateway则是基于Spring5中提供的WebFlux,属于响应式编程的实现,具备更好的性能。

    3.搭建网关服务

    ①创建新的module,引入SpringCloudGateway的依赖和nacos的服务发现依赖:

    1. <dependency>
    2. <groupId>com.alibaba.cloudgroupId>
    3. <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
    4. dependency>
    5. <dependency>
    6. <groupId>org.springframework.cloudgroupId>
    7. <artifactId>spring-cloud-starter-gatewayartifactId>
    8. dependency>

    ②编写路由配置及nacos地址

    1. server:
    2. port: 10010
    3. spring:
    4. application:
    5. name: gateway
    6. cloud:
    7. nacos:
    8. server-addr: localhost:8848 #nacos地址
    9. gateway:
    10. routes:
    11. - id: user-service #路由标识,唯一
    12. uri: lb://userservice # 路由的目标地址
    13. predicates: # 判断请求的规则
    14. - Path=/user/** # 路径的规则
    15. - id: order-service #路由标识,唯一
    16. uri: lb://orderservice # 路由的目标地址
    17. predicates: # 判断请求的规则
    18. - Path=/order/** # 路径的规则

    ③测试 http://localhost:10010/order/101

    端口请求访问的是网关,基于路由规则判断,拉取服务列表,进行负载均衡发送请求。

    总结:

    4.路由断言工厂 Route Predicate Factory

    • 在配置文件中写的断言规则只是字符串,这些字符串会被Predicate Factory读取并处理,转变为路由判断的条件
    • Path=/user/**是按照路径匹配,这个规则是由org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory类来处理的

     

    问题

    • PredicateFactory的作用是什么?

            读取用户定义的断言条件,对请求做出判断

    • Path=/user/**是什么含义?

            路径是以/user开头的就认为是符合的

    5.过滤工厂GatewayFilterFactory

    GatewayFilter是网关提供的一种过滤器,对进入网关的请求和微服务的返回的响应做处理

    过滤工厂 

    案例:所有进入userservice的请求添加一个请求头

    请求头:Truth=itcast is freaking awesome!

    ①在gateway的配置文件application.yml添加过滤配置

     ②在user-service的UserController,获取请求头参数

    1. @GetMapping("/prop")
    2. public String prop(@RequestHeader(value = "Truth", required = false) String truth) {
    3. return truth;
    4. }

    ③访问http://localhost:10010/user/prop

    注意:

    所有的路由都生效的过滤器

    总结

    • 过滤器的作用是什么?

            对路由的请求或响应做加工处理,比如添加请求头

            配置在路由下的过滤器只对当前路由的请求生效

    • defaultFilters的作用是什么?

            对所有路由都生效的过滤器

    6.全部过滤器

    实现GlobalFilter接口

    1. public interface GlobalFilter {
    2. Mono filter(ServerWebExchange exchange, GatewayFilterChain chain);
    3. }

    ServerWebExchange:请求上下文,获取request,response信息

    GatewayFilterChain:把请求交给下一个过滤器

    案例:定义全局过滤器,拦截并判断用户身份

    判断请求的参数是否满足下面条件:

    • 参数中是否有authorization,
    • authorization参数值是否为admin

    如果同时满足则放行,否则拦截

    步骤:

    ①在gateway编写自定义全局过滤器

    1. @Order(-1)
    2. @Component
    3. public class AuthorizeFilter implements GlobalFilter {
    4. @Override
    5. public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    6. // 1.获取请求参数
    7. MultiValueMap params = exchange.getRequest().getQueryParams();
    8. // 2.获取参数的authorization
    9. String auth = params.getFirst("authorization");
    10. // 3.判断是否是admin
    11. if ("admin".equals(auth)){
    12. // 放行
    13. return chain.filter(exchange);
    14. }
    15. //4.不放行就禁止
    16. exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
    17. // 5.结束处理
    18. return exchange.getResponse().setComplete();
    19. }
    20. }

    ②发起请求

     总结

     

    7.过滤器执行顺序

    请求进入网关会碰到三类过滤器:DefaultFilter、当前路由的过滤器、GlobalFilter

    ①每一个过滤器都必须指定一个int类型的order值,order值越小,优先级越高,执行顺序越靠前。

    ②当order值一样时,顺序是defaultFilter最先,然后是局部的路由过滤器,最后是全局过滤器

    8.跨域问题处理

    在实际项目中,前后端分成两个不同的项目,各自部署在不同的域名下,这也就会遇到跨域问题了。浏览器禁止请求的发起者与服务端发生跨域ajax请求,请求被浏览器拦截的问题

    解决方案:CORS

     

  • 相关阅读:
    HTML中name和class,id的区别和联系
    1s 创建100G文件,最快的方法是?
    Kubernetes1.16.0重置升级到1.22.2
    SpringAOP详解
    Leetcode2909. 元素和最小的山形三元组 II
    【集合】双列集合
    2.27数据结构
    写不完的数学试卷-----试卷生成器(Qt含源码)
    【在线研讨会】12月12日Softing工业物联网解决方案 助力工业4.0
    22/6/30
  • 原文地址:https://blog.csdn.net/jbkjhji/article/details/133923135