spring cloud 分布式基础入门

spring cloud 分布式

一、拆分思想

1、服务拆分原则

  • 什么时候拆?
    • 对于大多数小型项目来说,一般是先采用单体架构,随着用户规模扩大、业务复杂后再逐渐拆分为微服务架构
    • 而对于一些大型项目,在立项之初目的就很明确,为了长远考虑,在架构设计时就直接选择微服务架构
  • 怎么拆?
    • 粒度要小
    • 高内聚
    • 低耦合

2、拆分方式

  • 纵向拆分:所谓纵向拆分,就是按照项目的功能模块来拆分。
  • 横向拆分:而横向拆分,是看各个功能模块之间有没有公共的业务部分,如果有将其抽取出来作为通用服务。

二、服务调用

1、注册中心原理

在微服务远程调用的过程中,包括两个角色:

  • 服务提供者:提供接口供其它微服务访问
  • 服务消费者:调用其它微服务提供的接口

注册中心、服务提供者、服务消费者三者间关系如下:

在这里插入图片描述

流程如下:

  • 服务启动时就会注册自己的服务信息(服务名、IP、端口)到注册中心
  • 调用者可以从注册中心订阅想要的服务,获取服务对应的实例列表(1个服务可能多实例部署)
  • 调用者自己对实例列表负载均衡,挑选一个实例
  • 调用者向该实例发起远程调用

2、Nacos注册中心

  • 我们基于Docker来部署Nacos的注册中心,首先我们要准备MySQL数据库表,用来存储Nacos的数据。

  • 编写nacos/custom.env文件中,有一个MYSQL_SERVICE_HOST也就是mysql地址,需要修改为自己的虚拟机IP地址

    PREFER_HOST_MODE=hostname
    MODE=standalone
    SPRING_DATASOURCE_PLATFORM=mysql
    MYSQL_SERVICE_HOST=192.168.179.137
    MYSQL_SERVICE_DB_NAME=nacos
    MYSQL_SERVICE_PORT=3306
    MYSQL_SERVICE_USER=root
    MYSQL_SERVICE_PASSWORD=123
    MYSQL_SERVICE_DB_PARAM=characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
    
  • 进入root目录,然后执行下面的docker命令

    docker run -d \
    --name nacos \
    --env-file ./nacos/custom.env \
    -p 8848:8848 \
    -p 9848:9848 \
    -p 9849:9849 \
    --restart=always \
    nacos/nacos-server:v2.1.0-slim
    
  • 启动完成后,访问下面地址:http://192.168.150.101:8848/nacos/,注意将192.168.150.101替换为你自己的虚拟机IP地址。

  • 首次访问会跳转到登录页,账号密码都是nacos

3、服务注册

  1. 添加依赖,在单体服务pom.xml中添加依赖:

    <!--nacos 服务注册发现-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    
  2. 配置Nacos,在单体服务application.yml中添加nacos地址配置:

    spring:
      application:
        name: item-service # 服务名称
      cloud:
        nacos:
          server-addr: 192.168.150.101:8848 # nacos地址
    

4、服务发现

  1. 引入依赖

    <!--nacos 服务注册发现-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    
  2. 配置Nacos地址

    spring:
      application:
        name: item-service # 服务名称
      cloud:
        nacos:
          server-addr: 192.168.150.101:8848 # nacos地址
    
  3. 发现并调用服务

    1. 根据微服务名称获取到服务列表
    2. 手写负载均衡,从服务列表挑选一个实例
    3. 获取请求路径,利用RestTemplate发起http请求,得到HTTP的响应。

5、服务发现工具OpenFeign

  1. 引入依赖:一个openfeign,一个负载均衡器和性能调优,底层选择okhttp框架(另一个为Apache HttpClient )

      <!--openFeign-->
      <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-openfeign</artifactId>
      </dependency>
      <!--负载均衡器-->
      <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-loadbalancer</artifactId>
      </dependency>
        <!--OK http 的依赖 -->
        <dependency>
          <groupId>io.github.openfeign</groupId>
          <artifactId>feign-okhttp</artifactId>
        </dependency>
    
  2. 开启连接池

    feign:
      okhttp:
        enabled: true # 开启OKHttp功能
    
  3. 启用OpenFeign:在启动类上添加注解@EnableFeignClients并指定扫描包

    @EnableFeignClients(basePackages = "com.hmall.api.client")
    @MapperScan("com.hmall.cart.mapper")
    @SpringBootApplication
    public class CartApplication {
        public static void main(String[] args) {
            SpringApplication.run(CartApplication.class,args);
    }
    
  4. 编写OpenFeign客户端:底层生成代理对象,帮我们完成请求的选择和发送。

    @FeignClient("item-service")
    public interface ItemClient {
    
        @GetMapping("/items")
        List<ItemDTO> queryItemByIds(@RequestParam("ids") Collection<Long> ids);
    }
    
  5. 这里只需要声明接口,无需实现方法。接口中的几个关键信息:

    • @FeignClient("item-service") :声明服务名称
    • @GetMapping :声明请求方式
    • @GetMapping("/items") :声明请求路径
    • @RequestParam("ids") Collection<Long> ids :声明请求参数
    • List<ItemDTO> :返回值类型
  6. 示例:在service注入后,调用ItemClient

    @Service
    @RequiredArgsConstructor
    public class CartServiceImpl extends ServiceImpl<CartMapper, Cart> implements ICartService {
    
        	private final ItemClient itemClient;
    
    		/*省略代码*/
            private void handleCartItems(List<CartVO> vos) {
                Set<Long> itemIds = vos.stream().map(CartVO::getItemId).collect(Collectors.toSet());
                //在service注入后,调用ItemClient
                List<ItemDTO> items = itemClient.queryItemByIds(itemIds);
                if (CollUtils.isEmpty(items)) {
                    return;
                }
                /*省略代码*/
            }
        /*省略代码*/
    }
    

三、路由网关

1、网关

  • 网关可以做安全控制,也就是登录身份校验,校验通过才放行
  • 通过认证后,网关再根据请求判断应该访问哪个微服务,将请求转发过去

2、网关实现方式

  • SpringCloudGateway:基于Spring的WebFlux技术,完全支持响应式编程,吞吐能力更强

3、网关服务编写

  • 网关本身也是一个独立的微服务,因此也需要创建一个模块开发功能。

  • 创建网关微服务

  • 引入SpringCloudGateway、NacosDiscovery依赖

     <dependencies>
            <!--common-->
            <dependency>
                <groupId>com.heima</groupId>
                <artifactId>hm-common</artifactId>
                <version>1.0.0</version>
            </dependency>
            <!--网关-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-gateway</artifactId>
            </dependency>
            <!--nacos discovery-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            </dependency>
            <!--负载均衡-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-loadbalancer</artifactId>
            </dependency>
        </dependencies>
    
  • 编写启动类

  • 配置网关路由

    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/** # 这里是以请求路径作为判断规则
          default-filters:
            - AddRequestHeader=hhhCss, blue
    

    四个属性含义如下:

    • id:路由的唯一标示
    • predicates:路由断言,其实就是匹配条件
    • filters:路由过滤条件。
    • default-filters:全局过滤条件。
    • uri:路由目标地址,lb://代表负载均衡,从注册中心获取目标微服务的实例列表,并且负载均衡选择一个访问。

4、网关登录校验实现

  • 原来的单体springboot项目,登录校验是在拦截器里实现的。但是在spring cloud中,每一个服务在调用时都需要登录校验,也就意味着,需要些多个拦截器,很麻烦。
  • 解决思路:我们可以在网关去进行登录校验,解析token后,将user信息添加到请求头,然后在每个微服务里用拦截器将用户信息提取出来。
  • 这之间存在三个问题:
    • 网关路由是配置的,请求转发是Gateway内部代码,我们如何在转发之前做登录校验?
    • 网关校验JWT之后,如何将用户信息传递给微服务?
    • 微服务之间也会相互调用,这种调用不经过网关,又该如何传递用户信息?
4.1、网关过滤器
  • Gateway内部工作的基本原理

    在这里插入图片描述

  • 如图中所示,最终请求转发是有一个名为NettyRoutingFilter的过滤器来执行的,而且这个过滤器是整个过滤器链中顺序最靠后的一个。如果我们能够定义一个过滤器,在其中实现登录校验逻辑,并且将过滤器执行顺序定义到NettyRoutingFilter之前,这就符合我们的需求了!

  • 网关过滤器链中的过滤器有两种:

    • GatewayFilter:路由过滤器,作用范围比较灵活,可以是任意指定的路由Route.
    • GlobalFilter:全局过滤器,作用范围是所有路由,不可配置。
4.2、网关登录校验
  • 利用自定义GlobalFilter来完成登录校验。
4.2.1、JWT工具
  • 拷贝登录校验需要用到JWT,而且JWT的加密需要秘钥和加密工具。

    • AuthProperties:配置登录校验需要拦截的路径,因为不是所有的路径都需要登录才能访问
    • JwtProperties:定义与JWT工具有关的属性,比如秘钥文件位置
    • SecurityConfig:工具的自动装配
    • JwtTool:JWT工具,其中包含了校验和解析token的功能
    • hmall.jks:秘钥文件
  • 其中AuthPropertiesJwtProperties所需的属性要在application.yaml中配置:

    hm:
      jwt:
        location: classpath:hmall.jks # 秘钥地址
        alias: hmall # 秘钥别名
        password: hmall123 # 秘钥文件密码
        tokenTTL: 30m # 登录有效期
      auth:
        excludePaths: # 无需登录校验的路径
          - /search/**
          - /users/login
          - /items/**
    
4.2.2、登录校验过滤器
@Component
@EnableConfigurationProperties(AuthProperties.class)
@RequiredArgsConstructor
public class AuthGlobalFilter implements GlobalFilter, Ordered {

    private final AuthProperties authProperties;
    private final JwtTool jwtTool;
    private final AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //1、获取request
        ServerHttpRequest request = exchange.getRequest();
        //2、判断是否需要做登录拦截
        if (isExclude(request.getPath().toString())){
            //放行
            return chain.filter(exchange);
        }
        //3、需要就从请求头获取token
        String token = null;
        List<String> authorization = request.getHeaders().get("authorization");
        if (authorization != null && !authorization.isEmpty()){
            token = authorization.get(0);
        }
        //4、校验解析token
        Long userId = null;
        try {
            userId = jwtTool.parseToken(token);
        } catch (UnauthorizedException e){
            //拦截设置响应状态码,为401
            ServerHttpResponse response = exchange.getResponse();
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            response.setComplete();
        }
        //5、传递用户信息
        String userInfo = userId.toString();
        ServerWebExchange swe = exchange.mutate()
                .request(builder -> builder.header("user-info", userInfo))
                .build();
        //6、放行
        return chain.filter(swe);
    }

    private boolean isExclude(String path) {
        List<String> excludePaths = authProperties.getExcludePaths();
        for (String excludePath : excludePaths) {
            if (antPathMatcher.match(excludePath,path)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public int getOrder() {
        return 0;
    }
}
  • 首先要继承 GlobalFilterOrdered 接口

    • GlobalFilter:public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain),用于登录校验的逻辑编写
    • Ordered:public int getOrder() {return 0;},过滤器的执行顺序排序,越小优先级越高。比NettyRoutingFilter高就行,NettyRoutingFilter是转发路由的,所以优先级要在它前面。
  • private final AntPathMatcher antPathMatcher = new AntPathMatcher();

    spring提供的一个路径匹配工具antPathMatcher.match(excludePath,path),用来匹配放行路径

  • 最后调用exchange.mutate()的方法将userId添加到请求头生成新的路由上下文,最后将新的路由上下文传递给下一个过滤器

4.3、微服务获取用户信息
  • 编写微服务拦截器,拦截请求获取用户信息,保存到ThreadLocal后放行
  • 由于每个微服务都需要从请求头拿到用户信息,所以我们吧拦截器配置到common模块,因为所以微服务引入了common模块,所以写一个都可以生效。
  1. 拷贝ThreadLocal工具

  2. 编写拦截器,获取用户信息并保存到UserContext,然后放行即可。

  3. UserInfoInterceptor拦截器代码:

    public class UserInfoInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            // 1.获取请求头中的用户信息
            String userInfo = request.getHeader("user-info");
            // 2.判断是否为空
            if (StrUtil.isNotBlank(userInfo)) {
                // 不为空,保存到ThreadLocal
                    UserContext.setUser(Long.valueOf(userInfo));
            }
            // 3.放行
            return true;
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            // 移除用户
            UserContext.removeUser();
        }
    }
    
  4. 编写SpringMVC的配置类,配置登录拦截器,网关也引入了common包,但是网关是响应式编程没有SpringMvc。所以要加一个条件注解,否则网关会报错。

    @Configuration
    @ConditionalOnClass(DispatcherServlet.class)
    public class MvcConfig implements WebMvcConfigurer {
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new UserInfoInterceptor());
        }
    }
    
  5. 基于SpringBoot的自动装配原理,我们要将其添加到resources目录下的META-INF/spring.factories文件中(新版本是META-INF/spring/下的.import文件)

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
      com.hmall.common.config.MyBatisConfig,\
      com.hmall.common.config.MvcConfig,\
    
4.4、微服务之间OpenFeign传递用户
  • OpenFeign传递用户:由于微服务获取用户信息是通过拦截器在请求头中读取,因此要想实现微服务之间的用户信息传递,就必须在微服务发起调用时把用户信息存入请求头

  • 微服务之间调用是基于OpenFeign来实现的,并不是我们自己发送的请求。

  • 所以要借助Feign中提供的一个拦截器接口:feign.RequestInterceptor

    public interface RequestInterceptor {
    
      /**
       * Called for every request. 
       * Add data using methods on the supplied {@link RequestTemplate}.
       */
      void apply(RequestTemplate template);
    }
    
  • 我们只需要实现这个接口,然后实现apply方法,利用RequestTemplate类来添加请求头,将用户信息保存到请求头中。这样以来,每次OpenFeign发起请求的时候都会调用该方法,传递用户信息。

    @Configuration
    public class RequestInterceptorConfig {
    
        @Bean
        public RequestInterceptor userInfoRequestInterceptor(){
            return new RequestInterceptor() {
                @Override
                public void apply(RequestTemplate requestTemplate) {
                    Long userId = UserContext.getUser();
                    if (userId != null) {
                        requestTemplate.header("user-info", userId.toString());
                    }
                }
            };
        }
    }
    
  • 基于SpringBoot的自动装配原理,我们要将其添加到resources目录下的META-INF/spring.factories文件中(新版本是META-INF/spring/下的.import文件)

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
      com.hmall.api.config.RequestInterceptorConfig
    

四、配置管理

  • 现在依然还有几个问题需要解决:

    • 网关路由在配置文件中写死了,如果变更必须重启微服务
    • 某些业务配置在配置文件中写死了,每次修改都要重启服务
    • 每个微服务都有很多重复的配置,维护成本高
  • 微服务共享的配置可以统一交给Nacos保存和管理,在Nacos控制台修改配置后,Nacos会将配置变更推送给相关的微服务,并且无需重启即可生效,实现配置热更新。

1、共享配置

  • 我们可以把微服务共享的配置抽取到Nacos中统一管理,这样就不需要每个微服务都重复配置了。分为两步:
    • 在Nacos中添加共享配置
    • 微服务拉取配置
1.1、在Nacos中添加共享配置
  1. 进入nacos后台的配置列表

    在这里插入图片描述

  2. 点击加号,进行配置

    在这里插入图片描述

  3. 注意需要改变的相关参数不要写死,例如:

    • 数据库ip:通过${hm.db.host:192.168.150.101}配置了默认值为192.168.150.101,同时允许通过${hm.db.host}来覆盖默认值
    • 数据库端口:通过${hm.db.port:3306}配置了默认值为3306,同时允许通过${hm.db.port}来覆盖默认值
    • 数据库database:可以通过${hm.db.database}来设定,无默认值
1.2、拉取共享配置
  • SpringCloud在初始化上下文的时候会先读取一个名为bootstrap.yaml(或者bootstrap.properties)的文件,如果我们将nacos地址配置到bootstrap.yaml中,那么在项目引导阶段就可以读取nacos中的配置了。

在这里插入图片描述

  1. 引入依赖:

      <!--nacos配置管理-->
      <dependency>
          <groupId>com.alibaba.cloud</groupId>
          <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
      </dependency>
      <!--读取bootstrap文件-->
      <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-bootstrap</artifactId>
      </dependency>
    
  2. 新建bootstrap.yaml,注意这里面已经定义的数据,就不要在后续的application.yaml文件中在配置了。

    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 # 共享日志配置
    
  3. 编写application.yaml

    server:
      port: 8082
    feign:
      okhttp:
        enabled: true # 开启OKHttp连接池支持
    hm:
      swagger:
        title: 购物车服务接口文档
        package: com.hmall.cart.controller
      db:
        database: hm-cart
    

2、配置热更新

  • 非常容易简单理解,用到Nacos的配置热更新能力了,分为两步:
    • 在Nacos中添加配置
    • 在微服务读取配置
2.1、添加配置到Nacos
  • 首先,我们在nacos中添加一个配置文件

  • 注意文件的dataId格式:[服务名]-[spring.active.profile].[文件后缀名]

  • spring.active.profile,可以省略,则所有profile(环境)共享该配置

在这里插入图片描述

2.2、在微服务读取配置
  • 使用属性类,来读取数据即可。(一定要是属性类读取)

    @Data
    @Component
    @ConfigurationProperties(prefix = "hm.cart")
    public class CartProperties {
        private Integer maxAmount;
    }
    
  • 最后在需要使用的地方,将属性类注入,即可完成配置文件热部署。

五、动态路由

  • 监听Nacos的配置变更,然后手动把最新的路由更新到路由表中。这里有两个难点:
    • 如何监听Nacos配置变更?
    • 如何把路由信息更新到路由表?

1、监听Nacos配置变更

  1. NacosConfigAutoConfiguration中自动创建好了NacosConfigManager,而NacosConfigManager中是负责管理NacosConfigService

  2. 因此,只要我们拿到NacosConfigManager就等于拿到了ConfigService

  3. 编写监听器。虽然官方提供的SDK是ConfigService中的addListener,不过项目第一次启动时不仅仅需要添加监听器,也需要读取配置,因此建议使用的API是这个:

    String getConfigAndSignListener(
        String dataId, // 配置文件id
        String group, // 配置组,走默认
        long timeoutMs, // 读取配置的超时时间
        Listener listener // 监听器
    ) throws NacosException;
    
  4. 既可以配置监听器,并且会根据dataId和group读取配置并返回。我们就可以在项目启动时先更新一次路由,后续随着配置变更通知到监听器,完成路由更新。

2、更新路由信息

  • 更新路由要用到org.springframework.cloud.gateway.route.RouteDefinitionWriter这个接口:

    package org.springframework.cloud.gateway.route;
    
    import reactor.core.publisher.Mono;
    
    /**
     * @author Spencer Gibb
     */
    public interface RouteDefinitionWriter {
            /**
         * 更新路由到路由表,如果路由id重复,则会覆盖旧的路由
         */
            Mono<Void> save(Mono<RouteDefinition> route);
            /**
         * 根据路由id删除某个路由
         */
            Mono<Void> delete(Mono<String> routeId);
    }
    
    
  • 这里更新的路由,也就是RouteDefinition,之前我们见过,包含下列常见字段:

    • id:路由id
    • predicates:路由匹配规则
    • filters:路由过滤器
    • uri:路由目的地
  • 将来我们保存到Nacos的配置也要符合这个对象结构,将来我们以JSON来保存,以便配置读取转换成RouteDefinition,格式如下:

    {
      "id": "item",
      "predicates": [{
        "name": "Path",
        "args": {"_genkey_0":"/items/**", "_genkey_1":"/search/**"}
      }],
      "filters": [],
      "uri": "lb://item-service"
    }
    

3、实现动态路由

  1. 首先, 我们在网关gateway引入依赖

    <!--统一配置管理-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    </dependency>
    <!--加载bootstrap-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-bootstrap</artifactId>
    </dependency>
    
  2. 在网关gatewayresources目录创建bootstrap.yaml文件,拉取配置

    spring:
      application:
        name: gateway
      cloud:
        nacos:
          server-addr: 192.168.150.101 #nacos地址
          config:
            file-extension: yaml
            shared-configs:
              - dataId: shared-log.yaml # 共享日志配置
    
  3. 然后,在gateway中定义配置监听器:

    @Slf4j
    @Component
    @RequiredArgsConstructor
    public class DynamicRouteLoader {
    
        private final RouteDefinitionWriter writer;
        private final NacosConfigManager nacosConfigManager;
    
        // 路由配置文件的id和分组
        private final String dataId = "gateway-routes.json";
        private final String group = "DEFAULT_GROUP";
        // 保存更新过的路由id
        private final Set<String> routeIds = new HashSet<>();
    
        @PostConstruct
        public void initRouteConfigListener() throws NacosException {
            // 1.注册监听器并首次拉取配置
            String configInfo = nacosConfigManager.getConfigService()
                    .getConfigAndSignListener(dataId, group, 5000, new Listener() {
                        @Override
                        public Executor getExecutor() {
                            return null;
                        }
    
                        @Override
                        public void receiveConfigInfo(String configInfo) {
                            updateConfigInfo(configInfo);
                        }
                    });
            // 2.首次启动时,更新一次配置
            updateConfigInfo(configInfo);
        }
    
        private void updateConfigInfo(String configInfo) {
            log.debug("监听到路由配置变更,{}", configInfo);
            // 1.反序列化
            List<RouteDefinition> routeDefinitions = JSONUtil.toList(configInfo, RouteDefinition.class);
            // 2.更新前先清空旧路由
            // 2.1.清除旧路由
            for (String routeId : routeIds) {
                writer.delete(Mono.just(routeId)).subscribe();
            }
            routeIds.clear();
            // 2.2.判断是否有新的路由要更新
            if (CollUtils.isEmpty(routeDefinitions)) {
                // 无新路由配置,直接结束
                return;
            }
            // 3.更新路由
            routeDefinitions.forEach(routeDefinition -> {
                // 3.1.更新路由
                writer.save(Mono.just(routeDefinition)).subscribe();
                // 3.2.记录路由id,方便将来删除
                routeIds.add(routeDefinition.getId());
            });
        }
    }
    
  4. 我们直接在Nacos控制台添加路由,路由文件名为gateway-routes.json,类型为json

    在这里插入图片描述

  5. 配置内容如下:

    [
        {
            "id": "item",
            "predicates": [{
                "name": "Path",
                "args": {"_genkey_0":"/items/**", "_genkey_1":"/search/**"}
            }],
            "filters": [],
            "uri": "lb://item-service"
        },
        {
            "id": "cart",
            "predicates": [{
                "name": "Path",
                "args": {"_genkey_0":"/carts/**"}
            }],
            "filters": [],
            "uri": "lb://cart-service"
        },
        {
            "id": "user",
            "predicates": [{
                "name": "Path",
                "args": {"_genkey_0":"/users/**", "_genkey_1":"/addresses/**"}
            }],
            "filters": [],
            "uri": "lb://user-service"
        },
        {
            "id": "trade",
            "predicates": [{
                "name": "Path",
                "args": {"_genkey_0":"/orders/**"}
            }],
            "filters": [],
            "uri": "lb://trade-service"
        },
        {
            "id": "pay",
            "predicates": [{
                "name": "Path",
                "args": {"_genkey_0":"/pay-orders/**"}
            }],
            "filters": [],
            "uri": "lb://pay-service"
        }
    ]
    
  6. 无需重启网关,稍等几秒钟后,再次访问地址。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值