@FeignClient 一个module里面多个client 其内部接口的地址不能一样

本文探讨了在Spring Boot应用中如何解决两个不同模块内的接口映射冲突问题,通过修改请求映射路径,成功让服务正常启动运行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在一个module里面,定义了多个client

ZBUpgradeClient 和 PBUpgradeClient

添加了这两个cleint后,服务怎么也运行不起来

他们分别继承于 ZBUpgradeControllerApi 和 PBUpgradeControllerApi

@RequestMapping("/upgrade" )
public interface ZBUpgradeRecordControllerApi {
    @ApiOperation(value = "")
    @GetMapping("/inner/id/map")
    Map<String, Long> innerFindZBNewIdMapByOldIds(@RequestParam("ids") List<String> ids);
}
@RequestMapping("/upgrade" )
public interface PBUpgradeRecordControllerApi {
    @ApiOperation(value = "根据老id 获取新id")
    @GetMapping("/inner/id/map")
    Map<String, Long> innerFindPBNewIdMapByOldIds(@RequestParam("ids") List<String> ids);
}

从这里看,他们的地址是一样的,虽然在不同的module里面。

把 RequestMapping 的地址改了一下,服务就可以运行了

@RequestMapping("/zb/upgrade" )
public interface ZBUpgradeRecordControllerApi {
    @ApiOperation(value = "")
    @GetMapping("/inner/id/map")
    Map<String, Long> innerFindZBNewIdMapByOldIds(@RequestParam("ids") List<String> ids);
}
@RequestMapping("/pb/upgrade" )
public interface PBUpgradeRecordControllerApi {
    @ApiOperation(value = "根据老id 获取新id")
    @GetMapping("/inner/id/map")
    Map<String, Long> innerFindPBNewIdMapByOldIds(@RequestParam("ids") List<String> ids);
}

 

 

<think>我们使用Spring Cloud Alibaba和OpenFeign来实现一个远程调用案例。这个案例将包含两个服务:一个服务提供者(provider)和一个服务消费者(consumer)。服务消费者通过OpenFeign调用服务提供者的接口。 步骤: 1. 创建父工程(可选)管理依赖。 2. 创建服务提供者模块,提供一个简单的REST接口。 3. 创建服务消费者模块,使用OpenFeign声明式调用服务提供者的接口。 4. 使用Nacos作为服务注册中心,两个服务都注册到Nacos。 注意:这里我们使用Spring Cloud Alibaba的依赖管理。 详细步骤和代码: ### 1. 创建父工程(可选) 父工程管理依赖,pom.xml如下: ```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>spring-cloud-alibaba-demo</artifactId> <version>1.0-SNAPSHOT</version> <packaging>pom</packaging> <modules> <module>provider-service</module> <module>consumer-service</module> </modules> <properties> <java.version>1.8</java.version> <spring-cloud.version>Hoxton.SR9</spring-cloud.version> <spring-cloud-alibaba.version>2.2.6.RELEASE</spring-cloud-alibaba.version> </properties> <dependencyManagement> <dependencies> <!-- Spring Cloud Hoxton --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <!-- Spring Cloud Alibaba --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>${spring-cloud-alibaba.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> <scope>provided</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> ``` ### 2. 创建服务提供者模块(provider-service) #### 2.1 添加依赖(provider-service/pom.xml) ```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"> <parent> <artifactId>spring-cloud-alibaba-demo</artifactId> <groupId>com.example</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>provider-service</artifactId> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Nacos服务注册发现 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> </dependencies> </project> ``` #### 2.2 配置文件(application.yml) ```yaml server: port: 8081 spring: application: name: provider-service cloud: nacos: discovery: server-addr: 127.0.0.1:8848 # Nacos服务地址 ``` #### 2.3 启动类 ```java package com.example.provider; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @SpringBootApplication @EnableDiscoveryClient public class ProviderApplication { public static void main(String[] args) { SpringApplication.run(ProviderApplication.class, args); } } ``` #### 2.4 提供REST接口 ```java package com.example.provider.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; @RestController public class ProviderController { @GetMapping("/hello/{name}") public String hello(@PathVariable String name) { return "Hello, " + name + "! This is from provider service."; } } ``` ### 3. 创建服务消费者模块(consumer-service) #### 3.1 添加依赖(consumer-service/pom.xml) ```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"> <parent> <artifactId>spring-cloud-alibaba-demo</artifactId> <groupId>com.example</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>consumer-service</artifactId> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Nacos服务注册发现 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!-- OpenFeign --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> </dependencies> </project> ``` #### 3.2 配置文件(application.yml) ```yaml server: port: 8082 spring: application: name: consumer-service cloud: nacos: discovery: server-addr: 127.0.0.1:8848 # Nacos服务地址 ``` #### 3.3 启动类 ```java package com.example.consumer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableDiscoveryClient @EnableFeignClients // 开启Feign客户端 public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } ``` #### 3.4 创建Feign客户端接口 ```java package com.example.consumer.feign; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @FeignClient(name = "provider-service") // 指定服务提供者的应用名称 public interface ProviderFeignClient { @GetMapping("/hello/{name}") // 对应服务提供者的接口 String hello(@PathVariable("name") String name); } ``` #### 3.5 消费者Controller ```java package com.example.consumer.controller; import com.example.consumer.feign.ProviderFeignClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; @RestController public class ConsumerController { @Autowired private ProviderFeignClient providerFeignClient; @GetMapping("/call/{name}") public String callHello(@PathVariable String name) { // 通过Feign客户端调用提供者的接口 return providerFeignClient.hello(name); } } ``` ### 4. 测试 1. 启动Nacos服务器(默认端口8848)。 2. 启动服务提供者(provider-service)。 3. 启动服务消费者(consumer-service)。 4. 访问消费者接口:`http://localhost:8082/call/World`,应该会返回:`Hello, World! This is from provider service.` ### 解释 - **服务提供者**:在Nacos中注册自己,并提供一个简单的REST接口。 - **服务消费者**:通过OpenFeign声明一个接口,该接口会通过动态代理的方式实现远程调用。消费者通过该接口调用提供者的服务,而无需手动处理HTTP请求。 - **Nacos**:作为服务注册中心,提供服务发现功能。消费者通过服务名(provider-service)找到提供者的地址。 ### 注意事项 1. 确保Nacos服务器已启动。 2. 服务提供者和消费者都要注册到同一个Nacos服务器。 3. Feign客户端接口的方法签名(包括注解、路径、参数)需要服务提供者的接口一致。 ### 扩展:负载均衡 OpenFeign默认集成了Ribbon,所以天然支持负载均衡。可以启动多个服务提供者实例,消费者会自动轮询调用。 §§相关问题§§: 1. Spring Cloud OpenFeign的工作原理是什么? 2. 如何在OpenFeign中配置超时时间? 3. 如何为OpenFeign添加请求拦截器? 4. Spring Cloud Alibaba中除了Nacos,还有哪些组件?</think># Spring Cloud Alibaba OpenFeign 远程调用案例详解 下面我将展示一个完整的 Spring Cloud Alibaba 使用 OpenFeign 实现远程调用的案例。这个案例包含服务提供者、服务消费者和注册中心(Nacos)三个部分。 ## 完整案例架构 ``` +----------------+ +-------------+ +----------------+ | 服务消费者 | | 注册中心 | | 服务提供者 | | (consumer) |<---->| (Nacos) |<---->| (provider) | | OpenFeign客户端| | | | REST API | +----------------+ +-------------+ +----------------+ ``` ## 1. 环境准备 ### 依赖管理 (pom.xml) ```xml <!-- 父工程管理依赖版本 --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.0</version> <relativePath/> </parent> <properties> <java.version>1.8</java.version> <spring-cloud.version>2021.0.3</spring-cloud.version> <spring-cloud-alibaba.version>2021.0.4.0</spring-cloud-alibaba.version> </properties> <dependencyManagement> <dependencies> <!-- Spring Cloud 依赖 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <!-- Spring Cloud Alibaba 依赖 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>${spring-cloud-alibaba.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> ``` ## 2. 服务提供者 (provider) ### 依赖 (provider/pom.xml) ```xml <dependencies> <!-- Web 支持 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Nacos 服务注册 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> </dependencies> ``` ### 配置文件 (provider/src/main/resources/application.yml) ```yaml server: port: 8081 # 服务端口 spring: application: name: user-service # 服务名称 cloud: nacos: discovery: server-addr: localhost:8848 # Nacos 地址 # 自定义配置 user: default-name: "Guest User" ``` ### 服务提供者启动类 ```java package com.example.provider; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @SpringBootApplication @EnableDiscoveryClient // 启用服务注册发现 public class ProviderApplication { public static void main(String[] args) { SpringApplication.run(ProviderApplication.class, args); } } ``` ### 服务接口实现 ```java package com.example.provider.controller; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; @RestController public class UserController { @Value("${user.default-name}") private String defaultName; // 获取用户信息接口 @GetMapping("/user/{id}") public User getUser(@PathVariable Long id) { // 模拟从数据库获取用户 return new User(id, id % 2 == 0 ? "Alice" : "Bob", "user" + id + "@example.com"); } // 获取默认用户接口 @GetMapping("/user/default") public User getDefaultUser() { return new User(0L, defaultName, "default@example.com"); } // 用户信息类 public static class User { private Long id; private String name; private String email; // 构造方法、getter和setter省略(实际开发中应包含) } } ``` ## 3. 服务消费者 (consumer) ### 依赖 (consumer/pom.xml) ```xml <dependencies> <!-- Web 支持 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Nacos 服务注册 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!-- OpenFeign 支持 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> </dependencies> ``` ### 配置文件 (consumer/src/main/resources/application.yml) ```yaml server: port: 8080 # 服务端口 spring: application: name: order-service # 服务名称 cloud: nacos: discovery: server-addr: localhost:8848 # Nacos 地址 # OpenFeign 配置 feign: client: config: default: # 全局配置 connect-timeout: 5000 # 连接超时时间(ms) read-timeout: 5000 # 读取超时时间(ms) logger-level: basic # 日志级别 ``` ### 服务消费者启动类 ```java package com.example.consumer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableDiscoveryClient // 启用服务注册发现 @EnableFeignClients // 启用OpenFeign客户端 public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } ``` ### OpenFeign 客户端接口 ```java package com.example.consumer.feign; import com.example.consumer.feign.UserFeignClient.User; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; // 声明Feign客户端,指定要调用的服务名称 @FeignClient(name = "user-service", path = "/user") public interface UserFeignClient { // 定义提供者相同的接口方法 @GetMapping("/{id}") User getUserById(@PathVariable("id") Long id); @GetMapping("/default") User getDefaultUser(); // 用户信息类(提供者保持一致) class User { private Long id; private String name; private String email; // 必须有无参构造函数 public User() {} // getter和setter方法(实际开发中应包含) } } ``` ### 服务消费者控制器 ```java package com.example.consumer.controller; import com.example.consumer.feign.UserFeignClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; @RestController public class OrderController { private final UserFeignClient userFeignClient; @Autowired public OrderController(UserFeignClient userFeignClient) { this.userFeignClient = userFeignClient; } // 创建订单接口,需要获取用户信息 @GetMapping("/order/{userId}") public String createOrder(@PathVariable Long userId) { // 通过Feign客户端调用用户服务 UserFeignClient.User user = userFeignClient.getUserById(userId); // 模拟创建订单 return String.format("Order created for user: %s (ID: %d, Email: %s)", user.getName(), user.getId(), user.getEmail()); } // 获取默认用户的订单 @GetMapping("/order/default") public String createDefaultOrder() { UserFeignClient.User user = userFeignClient.getDefaultUser(); return String.format("Default order created for user: %s", user.getName()); } } ``` ## 4. 测试流程 ### 步骤1: 启动Nacos注册中心 ```bash # 下载并启动Nacos sh startup.sh -m standalone ``` ### 步骤2: 启动服务提供者 ```bash cd provider mvn spring-boot:run ``` ### 步骤3: 启动服务消费者 ```bash cd consumer mvn spring-boot:run ``` ### 步骤4: 测试远程调用 1. 访问消费者接口获取用户订单: ``` http://localhost:8080/order/1 ``` 响应示例: ```json "Order created for user: Bob (ID: 1, Email: user1@example.com)" ``` 2. 访问默认用户订单: ``` http://localhost:8080/order/default ``` 响应示例: ```json "Default order created for user: Guest User" ``` ## OpenFeign 高级特性 ### 1. 自定义请求拦截器 ```java package com.example.consumer.config; import feign.RequestInterceptor; import feign.RequestTemplate; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class FeignConfig { // 添加认证头 @Bean public RequestInterceptor authInterceptor() { return template -> template.header("Authorization", "Bearer token123"); } // 添加请求ID @Bean public RequestInterceptor requestIdInterceptor() { return template -> template.header("X-Request-ID", UUID.randomUUID().toString()); } } ``` ### 2. 错误处理 ```java package com.example.consumer.feign; import com.example.consumer.feign.UserFeignClient.User; import feign.FeignException; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @FeignClient( name = "user-service", path = "/user", fallback = UserFeignClientFallback.class // 指定降级类 ) public interface UserFeignClient { // ... 接口方法 ... } // 降级实现类 @Component class UserFeignClientFallback implements UserFeignClient { @Override public User getUserById(Long id) { // 返回降级数据 return new User(id, "Fallback User", "fallback@example.com"); } @Override public User getDefaultUser() { return new User(0L, "Fallback Default", "default@fallback.com"); } } ``` ### 3. 日志配置 ```java package com.example.consumer.config; import feign.Logger; import org.springframework.context.annotation.Bean; @Configuration public class FeignLogConfig { // 配置Feign日志级别 @Bean Logger.Level feignLoggerLevel() { // 可选值: NONE, BASIC, HEADERS, FULL return Logger.Level.FULL; } } ``` ## 核心原理解释 1. **服务注册发现**: - 服务提供者启动时向 Nacos 注册自己的服务信息 - 服务消费者通过 Nacos 获取服务提供者的地址列表 2. **OpenFeign 工作流程**: - 消费者定义接口并使用 `@FeignClient` 注解 - Spring 启动时创建接口的 JDK 动态代理实现 - 调用接口方法时,代理对象将方法转换为 HTTP 请求 - 通过负载均衡器(如 Ribbon)选择服务实例 - 发送 HTTP 请求到选中的服务实例 3. **负载均衡**: - OpenFeign 默认集成 Ribbon 实现客户端负载均衡 - 支持轮询、随机、加权等负载均衡策略 4. **容错处理**: - 通过 `fallback` 或 `fallbackFactory` 实现服务降级 - 可 Sentinel 集成实现熔断、限流等高级功能 ## 常见问题解决 1. **404 错误**: - 确保服务提供者的接口路径 Feign 客户端定义一致 - 检查服务名称是否正确注册到 Nacos 2. **超时问题**: - 在配置文件中调整超时时间: ```yaml feign: client: config: default: connect-timeout: 5000 read-timeout: 10000 ``` 3. **序列化问题**: - 确保双方使用相同的序列化框架(默认是 Jackson) - 检查对象是否有无参构造函数和 getter/setter 方法 4. **请求头丢失**: - 使用拦截器添加必要的请求头 - 确保 `@RequestHeader` 注解正确使用 ## 总结 本案例展示了 Spring Cloud Alibaba 中使用 OpenFeign 实现服务间通信的完整流程: 1. 通过 Nacos 实现服务注册发现 2. 使用 OpenFeign 声明式 HTTP 客户端简化远程调用 3. 实现基本的负载均衡和容错处理 4. 展示了高级特性如拦截器、日志配置和错误处理 OpenFeign 的优势在于: - 声明式编程,减少模板代码 - Spring MVC 注解无缝集成 - 内置负载均衡和容错机制 - 易于扩展和定制
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值