为什么需要网关?

我们知道,单体项目,在我们拆分成微服务之后,一个服务,我可能部署了好几份,比如都是A服务,我部署了三份,分别是8081,8081;8082。我怎么知道我现在是要访问哪一个?所以需要一个网关,来分配不同的路由,去找到对应的微服务。(就是网络的关口,负责请求的路由转发,身份验证)

如何设置网关?

四步:

  1. 创建新模块
  2. 引入网关依赖(spring-cloud-starter-gateway(网关),spring-cloud-starter-alibaba-nacos-discovery(nacos discovery),spring-cloud-starter-loadbalancer(负载均衡))
  3. 编写启动类
  4. 配置路由规则
    server:
      port: 8080
    spring:
      application:
        name: gateway
      cloud:
        nacos:
          server-addr: 192.168.150.101:8848
        gateway:
          routes:
            - id: item # 路由规则id,自定义,唯一
              uri: lb://item-service # 路由的目标服务,lb代表负载均衡,会从注册中心拉取服务列表
              predicates: # 路由断言,判断当前请求是否符合当前规则,符合则路由到目标服务
                - Path=/items/**,/search/** # 这里是以请求路径作为判断规则
            - id: cart
              uri: lb://cart-service
              predicates:
                - Path=/carts/**
            - id: user
              uri: lb://user-service
              predicates:
                - Path=/users/**,/addresses/**
            - id: trade
              uri: lb://trade-service
              predicates:
                - Path=/orders/**
            - id: pay
              uri: lb://pay-service
              predicates:
                - Path=/pay-orders/**
    
    

问题:

单体架构时我们只需要完成一次用户登录、身份校验,就可以在所有业务中获取到用户信息。而微服务拆分后,每个微服务都独立部署,不再共享数据。也就意味着每个微服务都需要做登录校验,这显然不可取。既然网关是所有微服务的入口,一切请求都需要先经过网关。我们完全可以把登录校验的工作放到网关去做

网关过滤器链(可以去了解下网关的路由转发链,过滤器链,为什么实现了这个就可以在转发前进行身份验证)

@Component
public class PrintAnyGlobalFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 编写过滤器逻辑
        System.out.println("未登录,无法访问");
        // 放行
        // JWT校验
        // return chain.filter(exchange);

        // 拦截
        ServerHttpResponse response = exchange.getResponse();
        response.setRawStatusCode(401);
        return response.setComplete();
    }

    @Override
    public int getOrder() {
        // 过滤器执行顺序,值越小,优先级越高
        return 0;
    }
}

网关校验了之后,我们如何获取用户信息呢?

因为单体项目中,JWT校验后,直接存入theadlocal,后续需要用直接用就可以,因为都在同一个线程,共享这部分数据。但微服务中,网关服务,和它需要转发给的服务不是一个线程,而theadlocal是线程的独立空间,不同的线程无法共享。

做法:

  • 保存用户到请求头。exchange中的mutate方法就可以做到。

  • 然后再一个公共服务中,编写SpringMVC的配置类,配置登录拦截,获取用户信息存入theadlocal。但是要将配置类添加到 spring.factories文件(为什么?
  • 组件扫描的局限性

    默认情况下,Spring Boot 的 @ComponentScan功能(由 @SpringBootApplication注解触发)只会扫描主应用程序类所在包及其子包下的组件(如 @Component, @Configuration, @Service等)。您的微服务主应用可能位于 com.hmall.user,而公共配置类在 com.hmall.common.config,这确实不在默认扫描范围内,因此配置类不会生效。这是因为类的物理位置在不同JAR包,与是否是微服务本身无关。即使是单体应用,如果引入的第三方JAR包里有 @Configuration类,默认也不会被扫描到。

  • 自动装配的超越

    Spring Boot 的自动装配机制(@EnableAutoConfiguration)正是为了突破上述限制而设计的。它通过一个固定的约定文件 META-INF/spring.factories来工作。当应用启动时,Spring Boot 会扫描所有依赖的 JAR 包中的这个文件,并加载其中声明的配置类,完全不受主应用程序包路径的限制。这正是您需要将配置类添加到 spring.factories文件的原因——它主动“告知”Spring Boot:“这里有一个配置类,请加载它!”)。

  • (这里还要注意,网关服务一般也引用了这个公共模块,但网关模块不是springboot的,没有自动装配,也就没法扫描,而且网关服务也不需要这个拦截器生效。所以要加一个条件注解,只再spring项目中去启动这个拦截器@ConditionalOnClass(DispatcherServlet.class))

微服务不通过网关,自己通过openfeign请求另一个微服务,我们另一个微服务如何获取用户信息呢?

Feign中提供的一个拦截器接口:feign.RequestInterceptor我们只需要实现这个接口,然后实现apply方法,利用RequestTemplate类来添加请求头,将用户信息保存到请求头中。这样以来,每次OpenFeign发起请求的时候都会调用该方法,传递用户信息。

配置管理:

添加配置:配置管理->配置列表中点击+新建一个配置

拉取共享配置:读取Nacos配置是SpringCloud上下文(ApplicationContext)初始化时处理的,发生在项目的引导阶段。然后才会初始化SpringBoot上下文,去读取application.yaml。也就是说引导阶段,application.yaml文件尚未读取,根本不知道nacos 地址,该如何去加载nacos中的配置文件呢?SpringCloud在初始化上下文的时候会先读取一个名为bootstrap.yaml(或者bootstrap.properties)的文件,如果我们将nacos地址配置到bootstrap.yaml中,那么在项目引导阶段就可以读取nacos中的配置了

spring:
  application:
    name: cart-service # 服务名称
  profiles:
    active: dev
  cloud:
    nacos:
      server-addr: 192.168.150.101 # nacos地址
      config:
        file-extension: yaml # 文件后缀名
        shared-configs: # 共享配置
          - dataId: shared-jdbc.yaml # 共享mybatis配置
          - dataId: shared-log.yaml # 共享日志配置
          - dataId: shared-swagger.yaml # 共享日志配置

配置热更新:文件的dataId格式:[服务名]-[spring.active.profile].[后缀名],用配置类读取,然后要用的地方引用配置类就可以。

更多推荐