從靜態(tài)到動態(tài)!Spring Boot 動態(tài)創(chuàng)建配置Feign Client
環(huán)境:SpringBoot3.4.2
1. 簡介
在微服務(wù)架構(gòu)中,F(xiàn)eign堪稱開發(fā)者的得力助手——它是一款聲明式的HTTP客戶端,能讓調(diào)用其他服務(wù)變得像編寫接口一樣簡單。然而,如果你的Feign客戶端配置(如基礎(chǔ)URL、超時時間或認(rèn)證信息)需要動態(tài)化,那該怎么辦呢?
將這類值硬編碼在application.yml文件中,對于靜態(tài)環(huán)境而言尚可接受,但在實際場景中,我們往往需要運行時靈活性:
- 多租戶應(yīng)用中,每個客戶的API端點可能各不相同
- 從數(shù)據(jù)庫或配置服務(wù)器獲取的環(huán)境特定配置
- 根據(jù)業(yè)務(wù)邏輯動態(tài)路由到不同的微服務(wù)
本篇文章我們將通過數(shù)據(jù)庫動態(tài)配置Feign客戶端。
2.實戰(zhàn)案例
2.1 定義Feign基本配置實體
@Entity
@Table(name = "t_feign_config")
public class FeignConfig {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id ;
/**客戶端名稱*/
private String clientName ;
/**該客戶端的baseUrl*/
private String baseUrl ;
/**請求超時配置*/
private Long connectTimeout ;
private Long readTimeout ;
private String apiKey ;
// getters, setters
}該實體對象中我們定義了Feign客戶端所需要的基本信息。準(zhǔn)備如下數(shù)據(jù):
圖片
2.2 定義Repository接口
在該Repository中,我們僅提供了一個方法,通過clientName查詢對應(yīng)的配置詳細(xì)。
public interface FeignConfigRepository extends JpaRepository<FeignConfig, Long> {
/**根據(jù)clientName查詢Feign配置*/
Optional<FeignConfig> findByClientName(String clientName) ;
}2.3 定義Service
@Service
public class FeignConfigService {
private final FeignConfigRepository configRepository ;
public FeignConfigService(FeignConfigRepository configRepository) {
this.configRepository = configRepository;
}
public FeignConfig getClientConfig(String clientName) {
return configRepository.findByClientName(clientName)
.orElseThrow(() -> new RuntimeException("Feign 配置不存在: " + clientName));
}
}2.4 Feign客戶端創(chuàng)建工廠
@Component
public class FeignClientFactory {
private final FeignConfigService configService;
private final ObjectFactory<HttpMessageConverters> messageConverters ;
public FeignClientFactory(FeignConfigService configService,
ObjectFactory<HttpMessageConverters> messageConverters) {
this.configService = configService;
this.messageConverters = messageConverters ;
}
public <T> T getClient(Class<T> clientType, String clientName) {
FeignConfig config = configService.getClientConfig(clientName) ;
Request.Options options = new Request.Options(config.getConnectTimeout(), TimeUnit.MILLISECONDS,
config.getReadTimeout(), TimeUnit.MILLISECONDS, false);
return Feign.builder()
.encoder(new SpringEncoder(this.messageConverters))
.decoder(new SpringDecoder(messageConverters))
.contract(new SpringMvcContract())
.retryer(Retryer.NEVER_RETRY)
.requestInterceptor(template -> template.header("Authorization", "Bearer " + config.getApiKey()))
.options(options)
.target(clientType, config.getBaseUrl()) ;
}
}每次在調(diào)用時都從數(shù)據(jù)庫中獲取最新的配置創(chuàng)建對象。
2.5 測試
@RestController
@RequestMapping("/test")
public class TestController {
private final FeignClientFactory factory;
public TestController(FeignClientFactory factory) {
this.factory = factory;
}
@GetMapping("/user/query")
public UserDTO query() {
return this.factory
.getClient(UserClient.class, "user-client")
.query() ;
}
}調(diào)用的接口
@RestController
@RequestMapping("/users")
public class UserController {
@GetMapping("/query")
public ResponseEntity<?> query() {
return ResponseEntity.ok(new User("pack", "123123", new Date(), "xxgg@qq.com")) ;
}
}執(zhí)行結(jié)果
圖片
以上我們就完成了基于數(shù)據(jù)庫的Feign的動態(tài)配置。
默認(rèn)情況下,spring-cloud-openfeign 已經(jīng)提供了一定程度的動態(tài)配置支持,尤其在以下兩個關(guān)鍵場景中表現(xiàn)尤為突出:
- 動態(tài)配置請求超時時間
- 動態(tài)刷新請求的url
接下來,我們將通過具體代碼示例,演示如何利用 spring-cloud-openfeign 的現(xiàn)有能力實現(xiàn)上述兩種動態(tài)配置功能,幫助你快速掌握這一核心特性。
2.6 動態(tài)配置請求超時時間
首先,準(zhǔn)備三方接口(模擬耗時)。
@GetMapping("/list")
public ResponseEntity<?> list() throws Exception {
List<User> users = List.of(
new User("張三", "111111", new Date(), "zs@gmail.com"),
new User("李四", "222222", new Date(), "ls@gmail.com"),
new User("王五", "333333", new Date(), "ww@gmail.com"),
new User("趙六", "444444", new Date(), "zl@gmail.com")
) ;
// 模擬耗時4s
TimeUnit.SECONDS.sleep(4) ;
return ResponseEntity.ok(users) ;
}其次,定義Feign接口。
@FeignClient(name = "user-client", url = "http://localhost:8080")
public interface UserDefaultClient {
@GetMapping("/users/list")
List<UserDTO> list() ;
}最后,為user-client配置超時時間。
spring:
cloud:
openfeign:
client:
config:
user-client:
read-timeout: 3000設(shè)置讀數(shù)據(jù)超時時間為3s(我們上面接口模擬了4s才會輸出內(nèi)容)。
測試接口
private final UserDefaultClient defaultClient ;
public TestController(UserDefaultClient defaultClient) {
this.defaultClient = defaultClient ;
}
@GetMapping("/user/list2")
public List<UserDTO> list2() {
return this.defaultClient.list() ;
}
圖片

拋出了超時異常。
接下來,我們引入actuator。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>配置actuator。
management:
endpoints:
web:
base-path: /ac
exposure:
include:
- refresh我們再次啟動服務(wù);啟動服務(wù)后,我們直接修改配置如下:
spring:
cloud:
openfeign:
client:
config:
user-client:
read-timeout: 5000接著調(diào)用 /ac/refresh 接口。
圖片
發(fā)現(xiàn)了配置的變化,再次訪問接口。
圖片
動態(tài)刷新配置成功。
2.7 動態(tài)刷新請求URL
首先,修改Feign定義接口如下:
@FeignClient(name = "user-client")
public interface UserDefaultClient {
@GetMapping("/users/list")
List<UserDTO> list() ;
}注意:這里我們并沒有定義url屬性,我們會在配置文件中定義。
其次,配置文件中定義user-client客戶端請求的url。
spring:
cloud:
openfeign:
client:
config:
user-client:
url: http://localhost:8080最后,我們還是訪問上面的接口。
圖片
成功;修改請求的url如下:
spring:
cloud:
openfeign:
client:
config:
user-client:
url: http://localhost:8081將端口改錯;接下來,調(diào)用/ac/refresh接口。
圖片
再次訪問接口。
圖片

連接錯誤了,請求的端口變?yōu)?081。




























