【SpringCloud】OpenFeign 与 Gateway 讲解与部署

OpenFeign 与 Gateway

版本说明:本文以 Spring Boot 3 / Spring Cloud 新版本思路为主。很多旧教程会使用 bootstrap.yml、Ribbon 等写法,学习时可以看,但自己写新项目时更建议优先使用 Spring Cloud LoadBalancer、spring.config.import 等新写法。

6. OpenFeign:声明式服务调用

6.1 OpenFeign 是什么

OpenFeign 是一个声明式 HTTP 客户端。所谓声明式,就是你不用手写完整的 HTTP 请求过程,只需要定义一个接口,Spring Cloud 会帮你生成代理对象去调用远程服务。

没有 Feign 时,你可能要写:

java 复制代码
restTemplate.getForObject("http://user-service/users/1", String.class);

用了 Feign 后,你可以写成接口,服务类只需要实现这个接口功能即可。

java 复制代码
@FeignClient(name = "user-service")
public interface UserClient {

    @GetMapping("/users/{id}")
    String getUser(@PathVariable("id") Long id);
}

在业务代码里像调用本地方法一样调用:

java 复制代码
@Service
public class OrderService {

    private final UserClient userClient;

    public OrderService(UserClient userClient) {
        this.userClient = userClient;
    }

    public String getOrderUser(Long userId) {
        return userClient.getUser(userId);
    }
}

6.2 OpenFeign 调用流程

user-service LoadBalancer 注册中心 Eureka/Nacos OpenFeign 代理对象 order-service user-service LoadBalancer 注册中心 Eureka/Nacos OpenFeign 代理对象 order-service userClient.getUser(1) 根据 user-service 查询实例 返回实例列表 选择一个实例 返回目标地址 发起 HTTP 请求 /users/1 返回用户信息 返回方法结果

OpenFeign 主要负责服务内部之间的远程调用 Gateway 是网关,是外部请求进入系统的统一入口。这两个很容易搞混

6.3 OpenFeign 简单部署流程

第一步:消费者服务添加依赖
xml 复制代码
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
第二步:启动类开启 Feign
java 复制代码
@SpringBootApplication
@EnableFeignClients
public class OrderServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }
}
第三步:定义 Feign 接口
java 复制代码
@FeignClient(name = "user-service")
public interface UserClient {

    @GetMapping("/users/{id}")
    String getUser(@PathVariable("id") Long id);
}
第四步:注入并调用
java 复制代码
@RestController
@RequestMapping("/orders")
public class OrderController {

    private final UserClient userClient;

    public OrderController(UserClient userClient) {
        this.userClient = userClient;
    }

    @GetMapping("/{id}")
    public String getOrder(@PathVariable Long id) {
        String user = userClient.getUser(1L);
        return "order id = " + id + ", " + user;
    }
}

6.4 OpenFeign 常见注意点

  • @FeignClient(name = "user-service") 中的 name 一般写注册中心里的服务名。
  • @PathVariable 建议明确写变量名,例如 @PathVariable("id")
  • OpenFeign 默认适合服务间 HTTP 调用,不适合传输特别大的文件。

7. Gateway:网关服务

7.1 Gateway 是什么

Spring Cloud Gateway 是微服务系统的 API 网关。它通常位于客户端和后端服务之间,作为统一入口。
前端/客户端
Spring Cloud Gateway
user-service
order-service
product-service

Gateway 常见职责:

  • 路由转发:根据路径把请求转发到不同服务;
  • 统一认证:判断用户是否登录、Token 是否有效;
  • 日志记录:记录请求耗时、IP、路径;
  • 跨域处理:统一配置 CORS;
  • 限流降级:保护后端服务;
  • 请求/响应改写:添加请求头、修改路径等。

7.2 Gateway 的三个核心概念

概念 作用 举例
Route 路由 定义请求转发到哪里 /api/users/**转发到user-service
Predicate 断言 判断请求是否匹配这条路由 Path、Method、Header、Host
Filter 过滤器 在请求前后做增强处理 鉴权、加请求头、日志、限流

一个请求进入 Gateway 后,大致流程如下:
Service Filter Chain Predicate Gateway Client Service Filter Chain Predicate Gateway Client 发送请求 /api/users/1 判断匹配哪条 Route 匹配 user-service 路由 执行 pre 过滤器 转发到 user-service 返回响应 执行 post 过滤器 返回结果

7.3 Gateway 简单部署流程

第一步:添加依赖
xml 复制代码
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

如果要通过注册中心用服务名转发,还需要接入 Eureka 或 Nacos Discovery。

第二步:配置路由

以 Nacos 为例:

yaml 复制代码
server:
  port: 10010

spring:
  application:
    name: gateway-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      routes:
        - id: user-service-route
          uri: lb://user-service
          predicates:
            - Path=/api/users/**
          filters:
            - StripPrefix=1

        - id: order-service-route
          uri: lb://order-service
          predicates:
            - Path=/api/orders/**
          filters:
            - StripPrefix=1

这里要理解几个点:

  • uri: lb://user-service 表示通过负载均衡调用注册中心里的 user-service
  • Path=/api/users/** 表示路径匹配时才走这条路由。
  • StripPrefix=1 表示转发前去掉第一层路径,例如 /api/users/1 转成 /users/1

请求链路:

text 复制代码
客户端请求:/api/users/1
Gateway 匹配:Path=/api/users/**
StripPrefix=1 后:/users/1
转发目标:lb://user-service/users/1

8. GatewayFilter、GlobalFilter 与自定义 Filter

8.1 GatewayFilter

GatewayFilter路由级过滤器,只对某一条或某几条路由生效。

例如:

yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
        - id: user-service-route
          uri: lb://user-service
          predicates:
            - Path=/api/users/**
          filters:
            - AddRequestHeader=X-Source, gateway
            - StripPrefix=1

这个过滤器只对 user-service-route 生效。它会给转发到后端服务的请求添加请求头:

text 复制代码
X-Source: gateway

常见内置 GatewayFilter:

Filter 作用
AddRequestHeader 添加请求头
AddResponseHeader 添加响应头
StripPrefix 去掉路径前缀
PrefixPath 添加路径前缀
RewritePath 重写路径
RequestRateLimiter 请求限流
Retry 请求失败重试

8.2 GlobalFilter

GlobalFilter全局过滤器,会对所有匹配到路由的请求生效。

它适合处理公共逻辑:

  • 登录校验;
  • Token 解析;
  • 请求日志;
  • 灰度标记;
  • 统一添加请求头;
  • 统计请求耗时。

举一个简单实现栗子

java 复制代码
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String token = exchange.getRequest().getHeaders().getFirst("Authorization");

        if (token == null || token.isBlank()) {
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }

        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return -1;
    }
}

getOrder() 用来控制过滤器顺序。数值越小,优先级越高。

8.3 pre 与 post 过滤逻辑

Gateway 的过滤器通常可以分成两个阶段:

  • pre 阶段:请求转发到后端服务之前执行。
  • post 阶段:后端服务返回响应之后执行。

请求进入 Gateway
pre 过滤器:鉴权/日志/加请求头
转发到后端服务
post 过滤器:统计耗时/改响应头
返回客户端

pre 适合做:鉴权、参数校验、添加请求头、路径改写、限流。

post 适合做:响应日志、统计耗时、添加响应头、统一包装响应。

8.4 自定义 GatewayFilterFactory

如果你想写一个只在某些路由上生效的自定义过滤器,可以写自定义 GatewayFilterFactory

示例:

java 复制代码
@Component
public class CheckHeaderGatewayFilterFactory
        extends AbstractGatewayFilterFactory<CheckHeaderGatewayFilterFactory.Config> {

    public CheckHeaderGatewayFilterFactory() {
        super(Config.class);
    }

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            String value = exchange.getRequest().getHeaders().getFirst(config.headerName);

            if (value == null || value.isBlank()) {
                exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);
                return exchange.getResponse().setComplete();
            }

            return chain.filter(exchange);
        };
    }

    public static class Config {
        private String headerName;

        public String getHeaderName() {
            return headerName;
        }

        public void setHeaderName(String headerName) {
            this.headerName = headerName;
        }
    }
}

配置中使用:

yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
        - id: user-service-route
          uri: lb://user-service
          predicates:
            - Path=/api/users/**
          filters:
            - name: CheckHeader
              args:
                headerName: X-Token

命名规则要注意:

text 复制代码
CheckHeaderGatewayFilterFactory -> 配置中写 CheckHeader

也就是说,类名一般以 GatewayFilterFactory 结尾,配置时去掉这个后缀。

8.5 自定义 GlobalFilter

如果逻辑对所有路由都生效,比如统一日志,可以写 GlobalFilter

java 复制代码
@Component
public class LogGlobalFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        long start = System.currentTimeMillis();
        String path = exchange.getRequest().getURI().getPath();

        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
            long cost = System.currentTimeMillis() - start;
            System.out.println("path=" + path + ", cost=" + cost + "ms");
        }));
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

理解重点:

GatewayFilter 更像"某条路由的局部增强",GlobalFilter 更像"整个网关的全局拦截器"。


9. 请求链路串联

学了这么多概念,在这里做一个简单的知识串联吧~

假设用户访问订单详情:

text 复制代码
GET /api/orders/1001

完整流程如下:
user-service OpenFeign order-service Nacos/Eureka Gateway Client user-service OpenFeign order-service Nacos/Eureka Gateway Client GET /api/orders/1001 GlobalFilter 鉴权、日志 Predicate 匹配 /api/orders/** 根据 order-service 查询实例 返回 order-service 实例列表 负载均衡转发请求 调用 UserClient.getUser(userId) 根据 user-service 查询实例 返回 user-service 实例列表 负载均衡调用 user-service 返回用户信息 返回结果 返回订单详情 post 过滤器记录耗时 返回响应

  • Gateway:负责外部入口、路由、过滤器;
  • Nacos/Eureka:负责服务注册与发现;
  • LoadBalancer:负责从多个实例中选一个;
  • OpenFeign:负责服务内部之间的声明式 HTTP 调用;
  • Nacos Config:虽然不在请求链路中,但负责服务启动和运行时读取配置。

希望对你有所帮助~祝身体健康

相关推荐
她说可以呀1 小时前
JWT令牌检验用户是否登录
java·spring boot·spring·java-ee·maven
庞轩px1 小时前
第三篇:SpringMVC——一个HTTP请求在Spring中经历了什么?
网络协议·spring·http·springmvc·handlermapping·前端控制器
空中海10 小时前
02 ArkTS 语言与工程规范
java·前端·spring
苏渡苇10 小时前
万字长文 | Spring Cloud Alibaba组件之Nacos实战及Nacos客户端服务注册源码解析
spring cloud·微服务·nacos·注册中心·配置中心·sca
亚历克斯神10 小时前
Java 25 模式匹配增强:让代码更简洁优雅
java·spring·微服务
AI精钢11 小时前
修复 AI Gateway 图片 MIME 类型错误:用魔数检测替代扩展名猜测
网络·人工智能·python·gateway·aigc
云烟成雨TD12 小时前
Spring AI Alibaba 1.x 系列【49】状态图运行时引擎:CompiledGraph 源码解析
java·人工智能·spring
Tutankaaa13 小时前
从10队到50队:知识竞赛软件的高并发场景如何设计?
java·经验分享·后端·spring
下次再写13 小时前
微服务架构实战:Spring Boot + Spring Cloud 从入门到精通
java·spring boot·spring cloud·微服务架构·服务注册与发现·分布式系统·api网关