【Spring网关】手把手教你基于 Spring Framework 6.2 搭建响应式网关

本文将带你一步步构建一个高性能的响应式 API 网关。该网关支持多服务器适配、URL 规范化、CORS 配置等核心功能,适用于高并发场景。

一、环境准备

前置依赖

  • JDK 17 或更高版本
  • Maven 3.6+
  • 开发工具(IntelliJ IDEA 或 Eclipse)

创建 Maven 项目

第一创建一个空的 Maven 项目,然后在 pom.xml 中添加核心依赖:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>reactive-gateway-demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <spring.version>6.2.0</spring.version>
        <reactor-netty.version>1.2.0</reactor-netty.version>
    </properties>

    <dependencies>
        <!-- Spring 核心依赖 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webflux</artifactId>
            <version>${spring.version}</version>
        </dependency>
        
        <!-- 服务器依赖 -->
        <dependency>
            <groupId>io.projectreactor.netty</groupId>
            <artifactId>reactor-netty-http</artifactId>
            <version>${reactor-netty.version}</version>
        </dependency>
        
        <!-- 日志依赖 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>2.0.16</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.5.12</version>
        </dependency>
    </dependencies>
</project>

二、核心组件实现

1. 创建启动类

创建 GatewayApplication 作为应用入口:

package com.example.gateway;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;

@Configuration
@ComponentScan("com.example.gateway")
public class GatewayApplication {

    public static void main(String[] args) throws Exception {
        // 初始化 Spring 上下文
        AnnotationConfigApplicationContext context = 
            new AnnotationConfigApplicationContext(GatewayApplication.class);
        
        // 构建 HttpHandler
        HttpHandler handler = WebHttpHandlerBuilder.applicationContext(context).build();
        
        // 启动 Reactor Netty 服务器
        startReactorNetty(handler, "localhost", 8080);
        
        // 保持应用运行
        Thread.currentThread().join();
    }
    
    // 启动 Reactor Netty 服务器的方法
    private static void startReactorNetty(HttpHandler handler, String host, int port) {
        ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler);
        HttpServer.create().host(host).port(port).handle(adapter).bindNow();
        System.out.println("Server started on " + host + ":" + port);
    }
}

2. 配置 Web 处理链

创建 WebHandlerConfig 配置请求处理逻辑:

package com.example.gateway.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebHandler;
import org.springframework.web.server.session.DefaultWebSessionManager;
import org.springframework.web.server.session.WebSessionManager;

@Configuration
public class WebHandlerConfig {

    // 核心请求处理逻辑
    @Bean
    public WebHandler webHandler() {
        return exchange -> {
            // 这里可以添加自定义的请求处理逻辑
            return exchange.getResponse().setComplete();
        };
    }

    // 自定义过滤器
    @Bean
    public WebFilter loggingFilter() {
        return (exchange, chain) -> {
            System.out.println("Processing request: " + exchange.getRequest().getPath());
            return chain.filter(exchange)
                .doFinally(signalType -> 
                    System.out.println("Finished processing: " + exchange.getRequest().getPath()));
        };
    }

    // 会话管理配置
    @Bean
    public WebSessionManager webSessionManager() {
        return new DefaultWebSessionManager();
    }
}

3. 配置 URL 规范化

创建 UrlFilterConfig 处理 URL 尾部斜杠问题:

package com.example.gateway.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.web.filter.reactive.UrlHandlerFilter;

@Configuration
public class UrlFilterConfig {

    @Bean
    public UrlHandlerFilter urlHandlerFilter() {
        return UrlHandlerFilter
            // 对 /api/** 路径的带斜杠 URL 执行 308 永久重定向
            .trailingSlashHandler("/api/**").redirect(HttpStatus.PERMANENT_REDIRECT)
            // 对 /admin/** 路径的带斜杠 URL 修改请求路径(内部处理)
            .trailingSlashHandler("/admin/**").mutateRequest()
            .build();
    }
}

4. 配置跨域支持

创建 CorsConfig 解决跨域问题:

package com.example.gateway.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;

@Configuration
public class CorsConfig {

    @Bean
    public CorsWebFilter corsWebFilter() {
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOrigin("https://your-frontend-domain.com");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        config.setMaxAge(3600L); // 预检请求缓存时间

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);

        return new CorsWebFilter(source);
    }
}

5. 配置代理转发头处理

创建 ForwardedHeaderConfig 处理代理服务器传递的头信息:

package com.example.gateway.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.server.adapter.ForwardedHeaderTransformer;

@Configuration
public class ForwardedHeaderConfig {

    @Bean
    public ForwardedHeaderTransformer forwardedHeaderTransformer() {
        ForwardedHeaderTransformer transformer = new ForwardedHeaderTransformer();
        // 如果不需要使用代理头信息,仅需要移除,可以设置为 true
        // transformer.setRemoveOnly(true);
        return transformer;
    }
}

三、扩展功能实现

1. 多服务器支持

如果需要切换到 Tomcat 或 Jetty 服务器,可以添加对应的启动方法:

// 在 GatewayApplication 中添加
private static void startTomcat(HttpHandler handler, String host, int port) throws Exception {
    Servlet servlet = new TomcatHttpHandlerAdapter(handler);
    Tomcat server = new Tomcat();
    File base = new File(System.getProperty("java.io.tmpdir"));
    Context rootContext = server.addContext("", base.getAbsolutePath());
    Tomcat.addServlet(rootContext, "main", servlet);
    rootContext.addServletMappingDecoded("/", "main");
    server.setHostname(host);
    server.setPort(port);
    server.getConnector(); // 触发连接器创建
    server.start();
}

private static void startJetty(HttpHandler handler, String host, int port) throws Exception {
    JettyCoreHttpHandlerAdapter adapter = new JettyCoreHttpHandlerAdapter(handler);
    Server server = new Server();
    server.setHandler(adapter);
    ServerConnector connector = new ServerConnector(server);
    connector.setHost(host);
    connector.setPort(port);
    server.addConnector(connector);
    server.start();
}

然后在 main 方法中根据需要调用对应的启动方法即可。

2. 请求日志追踪

添加请求 ID 日志追踪功能:

package com.example.gateway.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.server.WebFilter;

@Configuration
public class LogConfig {

    private static final Logger logger = LoggerFactory.getLogger(LogConfig.class);

    @Bean
    public WebFilter logIdFilter() {
        return (exchange, chain) -> {
            // 获取请求专属日志前缀
            String logPrefix = exchange.getLogPrefix();
            
            logger.info("{} Handling request: {}", logPrefix, 
                       exchange.getRequest().getURI());
            
            return chain.filter(exchange)
                .doFinally(signalType -> 
                    logger.info("{} Completed request with status: {}", 
                              logPrefix, 
                              exchange.getResponse().getStatusCode()));
        };
    }
}

四、运行与测试

启动应用

直接运行 GatewayApplication 的 main 方法,或使用 Maven 命令:

mvn compile exec:java -Dexec.mainClass="com.example.gateway.GatewayApplication"

应用将在 localhost:8080 启动。

测试功能

  1. URL 规范化测试
  • 访问 http://localhost:8080/api/test/ ,则会被重定向到 http://localhost:8080/api/test(308 状态码)
  • 访问 http://localhost:8080/admin/user/ 会被内部处理为 http://localhost:8080/admin/user
  1. 跨域测试
  • 从配置的前端域名发送请求到网关,应能正常处理跨域请求
  1. 日志测试:
  • 查看控制台输出,应能看到包含请求 ID 的日志信息

五、总结

通过本文的步骤,你已经搭建了一个基于 Spring Framework 6.2 的响应式网关,具备以下功能:

  • 支持 Reactor Netty、Tomcat、Jetty 多种服务器
  • 自动处理 URL 尾部斜杠,保证路由一致性
  • 完善的跨域资源共享配置
  • 支持代理服务器转发头处理
  • 请求级别的日志追踪

你可以根据实际需求,进一步扩展网关功能,如添加路由转发、负载均衡、认证授权等功能。

© 版权声明

相关文章

暂无评论

none
暂无评论...