全网首发:Spring Cloud Gateway设置统一的请求前缀

简介: 全网首发:Spring Cloud Gateway设置统一的请求前缀

前言


最近由于项目中要引入websocket,而原来的zuul网关对websocket支持并不友好,所以将原先的zuul网关切换成了Gateway网关。踩了不少坑,其中的一个问题就是如何给Spring Cloud Gateway添加统一的请求前缀。


在zuul网关中我们可以直接指定server.servlet.context-path属性,但是Gateway网关我们要如何配置呢?


一、Spring Cloud Gateway工作原理说明


这是官网的工作原理示意图:

57.png


Gateway的工作原理:

客户端请求经过HandlerMapping的处理,先进行路由匹配,如果匹配到路由(Router)就交给网关的web处理程序(Gateway Web Handler)来处理,经过一系列的调用过滤器链(肯定有责任链模式)后转发到被代理的服务执行真正的调用逻辑。


结合Gateway内部控制说明:

根据Predicate进行请求路径的匹配。

根据Filter对匹配的请求进行逻辑处理。


二、分析Gateway整合注册中心的原理


首先根据配置属性spring.cloud.gateway.discovery.locator.enabled定位到GatewayDiscoveryClientAutoConfiguration自动化配置类。

查看源码发现,核心是通过initPredicates和initFilters分别初始化定义了一个Predicate和Filter。

其中定义的PathRoutePredicate主要根据serviceId去进行请求的路径匹配,只有路径规则匹配成功后,才会进入Filter的逻辑处理。

initFilters方法中主要是定义了一个RewritePathGatewayFilter,主要是将发到具体服务中的请求的url路径进行重写,去除掉前缀

“/serviceId”

public static List<PredicateDefinition> initPredicates() {
        ArrayList<PredicateDefinition> definitions = new ArrayList();
        PredicateDefinition predicate = new PredicateDefinition();
        predicate.setName(NameUtils.normalizeRoutePredicateName(PathRoutePredicateFactory.class));
        predicate.addArg("pattern", "'/'+serviceId+'/**'");
        definitions.add(predicate);
        return definitions;
    }
    public static List<FilterDefinition> initFilters() {
        ArrayList<FilterDefinition> definitions = new ArrayList();
        FilterDefinition filter = new FilterDefinition();
        filter.setName(NameUtils.normalizeFilterFactoryName(RewritePathGatewayFilterFactory.class));
        String regex = "'/' + serviceId + '/(?<remaining>.*)'";
        String replacement = "'/${remaining}'";
        filter.addArg("regexp", regex);
        filter.addArg("replacement", replacement);
        definitions.add(filter);
        return definitions;
    }
    @Bean
    public DiscoveryLocatorProperties discoveryLocatorProperties() {
        DiscoveryLocatorProperties properties = new DiscoveryLocatorProperties();
        properties.setPredicates(initPredicates());
        properties.setFilters(initFilters());
        return properties;
    }

三、方案确定


重写定义GatewayDiscoveryClientAutoConfiguration自动化配置类,改写原先的Predicate和Filter,添加统一的请求前缀“/api/v1”。

@Slf4j
@Configuration
public class GatewayDiscoveryClientConfig<main> extends GatewayDiscoveryClientAutoConfiguration {
    @Value("${spring.cloud.gateway.api-prefix:/api/v1}")
    private  String prefix;
    @Bean
    @Override
    public DiscoveryLocatorProperties discoveryLocatorProperties() {
        DiscoveryLocatorProperties properties = new DiscoveryLocatorProperties();
        properties.setPredicates(myInitPredicates());
        properties.setFilters(myInitFilters());
        return properties;
    }
    public  List<PredicateDefinition> myInitPredicates() {
        ArrayList<PredicateDefinition> definitions = new ArrayList();
        PredicateDefinition predicate = new PredicateDefinition();
        //定义路由路径的匹配规则
        predicate.setName(NameUtils.normalizeRoutePredicateName(PathRoutePredicateFactory.class));
        String pattern ="'"+prefix+"/'+serviceId+'/**'";
        predicate.addArg("pattern", pattern);
        definitions.add(predicate);
        return definitions;
    }
    public  List<FilterDefinition> myInitFilters() {
        ArrayList<FilterDefinition> definitions = new ArrayList();
        FilterDefinition filter = new FilterDefinition();
        //重新请求路径
        filter.setName(NameUtils.normalizeFilterFactoryName(RewritePathGatewayFilterFactory.class));
        String regex = "'"+prefix+"/' + serviceId + '/(?<remaining>.*)'";
        String replacement = "'/${remaining}'";
        filter.addArg("regexp", regex);
        filter.addArg("replacement", replacement);
        definitions.add(filter);
        return definitions;
    }
}

四、完整项目实战


1、maven依赖

spring-boot-starter-parent版本2.3.4.RELEASE

spring cloud版本Hoxton.SR8

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR8</spring-cloud.version>
    </properties>
<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>
</dependencies>


2、属性配置

server.port=8020
spring.application.name=gateway
spring.cloud.gateway.api-prefix=/api/v1
#配置网关的默认路由
spring.cloud.gateway.enabled=true
spring.cloud.gateway.discovery.locator.enabled=true
spring.cloud.gateway.discovery.locator.lower-case-service-id=true
#注册中心
eureka.client.service-url.defaultZone=http://register:8001/eureka/
eureka.instance.prefer-ip-address=true
eureka.instance.hostname=${spring.cloud.client.ip-address}
eureka.instance.instance-id=${eureka.instance.hostname}:${server.port}


3、代码

3.1 启动类代码

注意:

既然要自定义GatewayDiscovery配置类,就要在启动类中exclude对应的自动化配置类GatewayDiscoveryClientAutoConfiguration

@SpringBootApplication(exclude = {
        GatewayDiscoveryClientAutoConfiguration.class
})
@EnableDiscoveryClient
@Slf4j
public class WxswjGatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(WxswjGatewayApplication.class, args);
        log.info("网关服务启动成功!");
    }
}


3.2 自定义网关注册中心配置

/**
 * @program: wxswj
 * @description: 自定义网关注册中心配置
 * @create: 2020-10-14 20:11
 **/
@Slf4j
@Configuration
public class GatewayDiscoveryClientConfig<main> extends GatewayDiscoveryClientAutoConfiguration {
    @Value("${spring.cloud.gateway.api-prefix:/api/v1}")
    private  String prefix;
    @Bean
    @Override
    public DiscoveryLocatorProperties discoveryLocatorProperties() {
        DiscoveryLocatorProperties properties = new DiscoveryLocatorProperties();
        properties.setPredicates(myInitPredicates());
        properties.setFilters(myInitFilters());
        return properties;
    }
    public  List<PredicateDefinition> myInitPredicates() {
        ArrayList<PredicateDefinition> definitions = new ArrayList();
        PredicateDefinition predicate = new PredicateDefinition();
        //定义路由路径的匹配规则
        predicate.setName(NameUtils.normalizeRoutePredicateName(PathRoutePredicateFactory.class));
        String pattern ="'"+prefix+"/'+serviceId+'/**'";
        predicate.addArg("pattern", pattern);
        definitions.add(predicate);
        return definitions;
    }
    public  List<FilterDefinition> myInitFilters() {
        ArrayList<FilterDefinition> definitions = new ArrayList();
        FilterDefinition filter = new FilterDefinition();
        //重新请求路径
        filter.setName(NameUtils.normalizeFilterFactoryName(RewritePathGatewayFilterFactory.class));
        String regex = "'"+prefix+"/' + serviceId + '/(?<remaining>.*)'";
        String replacement = "'/${remaining}'";
        filter.addArg("regexp", regex);
        filter.addArg("replacement", replacement);
        definitions.add(filter);
        return definitions;
    }
}


项目源码地址


总结


本文首先介绍了Spring Cloud Gateway的工作原理,然后通过跟踪源码解读了gateway和注册中心集成的实现原理,推断出Spring Cloud Gateway设置统一的请求前缀的实现方案。


最后给出了完整的实现代码。

目录
相关文章
|
2月前
|
Java Spring
spring多线程实现+合理设置最大线程数和核心线程数
本文介绍了手动设置线程池时的最大线程数和核心线程数配置方法,建议根据CPU核数及程序类型(CPU密集型或IO密集型)来合理设定。对于IO密集型,核心线程数设为CPU核数的两倍;CPU密集型则设为CPU核数加一。此外,还讨论了`maxPoolSize`、`keepAliveTime`、`allowCoreThreadTimeout`和`queueCapacity`等参数的设置策略,以确保线程池高效稳定运行。
278 10
spring多线程实现+合理设置最大线程数和核心线程数
|
1月前
|
存储 Java API
简单两步,Spring Boot 写死的定时任务也能动态设置:技术干货分享
【10月更文挑战第4天】在Spring Boot开发中,定时任务通常通过@Scheduled注解来实现,这种方式简单直接,但存在一个显著的限制:任务的执行时间或频率在编译时就已经确定,无法在运行时动态调整。然而,在实际工作中,我们往往需要根据业务需求或外部条件的变化来动态调整定时任务的执行计划。本文将分享一个简单两步的解决方案,让你的Spring Boot应用中的定时任务也能动态设置,从而满足更灵活的业务需求。
100 4
|
2月前
|
负载均衡 Java Nacos
SpringCloud基础2——Nacos配置、Feign、Gateway
nacos配置管理、Feign远程调用、Gateway服务网关
SpringCloud基础2——Nacos配置、Feign、Gateway
|
2月前
|
Java 开发者 Spring
Spring Cloud Gateway 中,过滤器的分类有哪些?
Spring Cloud Gateway 中,过滤器的分类有哪些?
63 3
|
2月前
|
负载均衡 Java 网络架构
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
117 5
|
1月前
|
负载均衡 Java API
【Spring Cloud生态】Spring Cloud Gateway基本配置
【Spring Cloud生态】Spring Cloud Gateway基本配置
41 0
|
2月前
|
安全 Java 开发者
强大!Spring Cloud Gateway新特性及高级开发技巧
在微服务架构日益盛行的今天,网关作为微服务架构中的关键组件,承担着路由、安全、监控、限流等多重职责。Spring Cloud Gateway作为新一代的微服务网关,凭借其基于Spring Framework 5、Project Reactor和Spring Boot 2.0的强大技术栈,正逐步成为业界的主流选择。本文将深入探讨Spring Cloud Gateway的新特性及高级开发技巧,助力开发者更好地掌握这一强大的网关工具。
243 6
|
3月前
|
Java Spring 开发者
解锁 Spring Boot 自动化配置的黑科技:带你走进一键配置的高效开发新时代,再也不怕繁琐设置!
【8月更文挑战第31天】Spring Boot 的自动化配置机制极大简化了开发流程,使开发者能专注业务逻辑。通过 `@SpringBootApplication` 注解组合,特别是 `@EnableAutoConfiguration`,Spring Boot 可自动激活所需配置。例如,添加 JPA 依赖后,只需在 `application.properties` 配置数据库信息,即可自动完成 JPA 和数据源设置。这一机制基于多种条件注解(如 `@ConditionalOnClass`)实现智能配置。深入理解该机制有助于提升开发效率并更好地解决问题。
72 0
|
3月前
|
负载均衡 Java API
深度解析SpringCloud微服务跨域联动:RestTemplate如何驾驭HTTP请求,打造无缝远程通信桥梁
【8月更文挑战第3天】踏入Spring Cloud的微服务世界,服务间的通信至关重要。RestTemplate作为Spring框架的同步客户端工具,以其简便性成为HTTP通信的首选。本文将介绍如何在Spring Cloud环境中运用RestTemplate实现跨服务调用,从配置到实战代码,再到注意事项如错误处理、服务发现与负载均衡策略,帮助你构建高效稳定的微服务系统。
93 2
|
3月前
|
Java 应用服务中间件 nginx
【Azure Spring Apps】Spring App部署上云遇见 502 Bad Gateway nginx
【Azure Spring Apps】Spring App部署上云遇见 502 Bad Gateway nginx
下一篇
无影云桌面