SpringCloud 使用 OpenFeign 聲明式服務(wù)調(diào)用
Feign 組件最初由 Netflix 公司提供,由于不支持 SpringMVC 注解,所以 SpringCloud 對(duì)其封裝并進(jìn)行支持,因此產(chǎn)生了 OpenFeign 組件。Feign 是一個(gè)聲明式的 REST 客戶端,它采用基于接口的注解方式,具有代碼簡(jiǎn)潔、使用方便的優(yōu)勢(shì)。
本篇博客仍然使用最新的 SpringCloud 版本 2021.0.3 進(jìn)行 Demo 制作和演示。在客戶端調(diào)用服務(wù)端接口時(shí),對(duì)比 OpenFeign 和 RestTemplate 兩種實(shí)現(xiàn)方式,在本篇博客的最后會(huì)提供源代碼的下載。
一、搭建工程
采用 Maven 搭建 springcloud_feign 父工程,下面包含 3 個(gè)子工程:

對(duì)于 springcloud_feign 父工程 pom 文件內(nèi)容如下所示:
<?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.jobs</groupId>
? ?<artifactId>springcloud_feign</artifactId>
? ?<packaging>pom</packaging>
? ?<version>1.0-SNAPSHOT</version>
? ?<modules>
? ? ? ?<module>eureka_app</module>
? ? ? ?<module>provider_app</module>
? ? ? ?<module>consumer_app</module>
? ?</modules>
? ?<properties>
? ? ? ?<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
? ? ? ?<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
? ? ? ?<java.version>1.8</java.version>
? ?</properties>
? ?<!--spring boot-->
? ?<parent>
? ? ? ?<groupId>org.springframework.boot</groupId>
? ? ? ?<artifactId>spring-boot-starter-parent</artifactId>
? ? ? ?<version>2.6.11</version>
? ? ? ?<relativePath/>
? ?</parent>
? ?<!--Spring Cloud-->
? ?<dependencyManagement>
? ? ? ?<dependencies>
? ? ? ? ? ?<dependency>
? ? ? ? ? ? ? ?<groupId>org.springframework.cloud</groupId>
? ? ? ? ? ? ? ?<artifactId>spring-cloud-dependencies</artifactId>
? ? ? ? ? ? ? ?<version>2021.0.3</version>
? ? ? ? ? ? ? ?<type>pom</type>
? ? ? ? ? ? ? ?<scope>import</scope>
? ? ? ? ? ?</dependency>
? ? ? ?</dependencies>
? ?</dependencyManagement>
</project>
注意:這里使用 SpringBoot 的版本是 2.6.11 ,使用的 SpringCloud 版本是 2021.0.3
有關(guān)【Eureka 注冊(cè)中心】和【服務(wù)提供者】的搭建過程,這里就省略了,跟上篇博客一模一樣。
二、消費(fèi)者的搭建
本篇博客的服務(wù)消費(fèi)者搭建的結(jié)果如下所示:

在 pom 文件中除了要引入 spring-cloud-starter-netflix-eureka-client 的依賴之外,
還需要引入 spring-cloud-starter-openfeign 的依賴,如下所示:
<?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>springcloud_feign</artifactId>
? ? ? ?<groupId>com.jobs</groupId>
? ? ? ?<version>1.0-SNAPSHOT</version>
? ?</parent>
? ?<modelVersion>4.0.0</modelVersion>
? ?<artifactId>consumer_app</artifactId>
? ?<dependencies>
? ? ? ?<!--spring boot web-->
? ? ? ?<dependency>
? ? ? ? ? ?<groupId>org.springframework.boot</groupId>
? ? ? ? ? ?<artifactId>spring-boot-starter-web</artifactId>
? ? ? ?</dependency>
? ? ? ?<!--eureka-client-->
? ? ? ?<dependency>
? ? ? ? ? ?<groupId>org.springframework.cloud</groupId>
? ? ? ? ? ?<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
? ? ? ?</dependency>
? ? ? ?<!--引入 openfeign 的依賴-->
? ? ? ?<dependency>
? ? ? ? ? ?<groupId>org.springframework.cloud</groupId>
? ? ? ? ? ?<artifactId>spring-cloud-starter-openfeign</artifactId>
? ? ? ?</dependency>
? ?</dependencies>
</project>
由于 SpringCloud 從 2020.0.1 版本后,移除了 Ribbon 組件,所以 openfeign 也不再依賴 Ribbon
要想使用 openfeign 聲明式調(diào)用,需要經(jīng)歷如下 4 個(gè)步驟:
(1)在 SpringBoot 的啟動(dòng)類上,增加 @EnableFeignClients 注解
package com.jobs.consumer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
//啟用 Feign 客戶端功能
@EnableFeignClients
@SpringBootApplication
public class ConsumerApp {
? ?public static void main(String[] args) {
? ? ? ?SpringApplication.run(ConsumerApp.class,args);
? ?}
}
(2)定義 feign 聲明式接口,添加注解 @FeignClient,設(shè)置 value 值為【服務(wù)提供者的】應(yīng)用名稱
(3)定義接口方法,方法的參數(shù)列表和返回值,需要跟服務(wù)提供者保持一致,方法名稱無所謂
package com.jobs.consumer.feignclient;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.Map;
@FeignClient("PROVIDER-APP")
public interface ProviderAppClient {
? ?@RequestMapping("/provider/getdata/{id}")
? ?Map GetProviderData(@PathVariable("id") int id);
}
(4)在調(diào)用服務(wù)接口的地方,注入該 feign 接口對(duì)象,調(diào)用接口方法完成遠(yuǎn)程調(diào)用
package com.jobs.consumer.controller;
import com.jobs.consumer.feignclient.ProviderAppClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.Map;
@RequestMapping("/consumer")
@RestController
public class ConsumerController {
? ?@Autowired
? ?private RestTemplate restTemplate;
? ?//注入該 feign 接口對(duì)象
? ?@Autowired
? ?private ProviderAppClient providerAppClient;
? ?@RequestMapping("/getdata1/{id}")
? ?public Map GetData1(@PathVariable("id") int id) {
? ? ? ?//采用 restTemplate 調(diào)用 PROVIDER-APP 的接口
? ? ? ?String url = "http://PROVIDER-APP/provider/getdata/" + id;
? ? ? ?Map result = restTemplate.getForObject(url, Map.class);
? ? ? ?result.put("remark","采用 restTemplate 調(diào)用返回的結(jié)果");
? ? ? ?return result;
? ?}
? ?@RequestMapping("/getdata2/{id}")
? ?public Map GetData2(@PathVariable("id") int id) {
? ? ? ?//采用 feign 的接口中的方法,實(shí)現(xiàn)對(duì) PROVIDER-APP 服務(wù)接口的調(diào)用
? ? ? ?Map result = providerAppClient.GetProviderData(id);
? ? ? ?result.put("remark","采用 feign 聲明式接口調(diào)用返回的結(jié)果");
? ? ? ?return result;
? ?}
}
以上代碼,同時(shí)列出 RestTemplate 和 feign 接口的方式調(diào)用服務(wù)提供者的接口,進(jìn)行對(duì)比,后者代碼簡(jiǎn)潔很多。
三、Feign 的客戶端超時(shí)配置
在服務(wù)消費(fèi)者的 application.yml 配置文件中,可以對(duì) feign 客戶端調(diào)用服務(wù)端接口的超時(shí),進(jìn)行默認(rèn)配置,也可以分別針對(duì)每個(gè)服務(wù)提供者進(jìn)行獨(dú)立的超時(shí)配置,下面列出服務(wù)消費(fèi)者的配置文件內(nèi)容,具體如下所示:
server:
?port: 8100
eureka:
?instance:
? ?# 配置主機(jī)名
? ?hostname: consumer-service
? ?# 顯示 ip 地址,代替顯示主機(jī)名
? ?prefer-ip-address: true
? ?# 所注冊(cè)服務(wù)實(shí)例名稱的顯示形式
? ?instance-id: ${eureka.instance.hostname}:${server.port}
? ?# 每隔 3 秒發(fā)一次心跳包
? ?lease-renewal-interval-in-seconds: 3
? ?# 如果 15 秒沒有發(fā)送心跳包,就讓 eureka 把自己從服務(wù)列表中移除
? ?lease-expiration-duration-in-seconds: 15
?client:
? ?service-url:
? ? ?# 將當(dāng)前 springboot 服務(wù)注冊(cè)到 eureka 中
? ? ?defaultZone: http://localhost:8761/eureka
? ?# 是否將自己的路徑注冊(cè)到 eureka 上
? ?register-with-eureka: true
? ?# 是否需要從 eureka 中抓取路徑
? ?fetch-registry: true
# consumer 使用的 application 名稱
spring:
?application:
? ?name: consumer-App
# 這里可以設(shè)置 feign 客戶端連接和獲取接口數(shù)據(jù)的超時(shí)時(shí)間
feign:
?client:
? ?config:
? ? ?# 通過 default 可以設(shè)置 feign 默認(rèn)超時(shí)時(shí)間
? ? ?default:
? ? ? ?# 連接超時(shí)時(shí)間(毫秒)
? ? ? ?ConnectTimeout: 1000
? ? ? ?# 等待業(yè)務(wù)處理返回結(jié)果的超時(shí)時(shí)間(毫秒)
? ? ? ?ReadTimeout: 1000
? ? ?# 這里可以針對(duì)具體的【服務(wù)提供者】應(yīng)用名稱下的所有接口,設(shè)置超時(shí)時(shí)間
? ? ?PROVIDER-APP:
? ? ? ?ConnectTimeout: 1000
? ? ? ?ReadTimeout: 3000
在上面的配置文件內(nèi)容的最下面,
通過 default 下的 ConnectTimeout 和 ReadTimeout 進(jìn)行默認(rèn)連接超時(shí)時(shí)間和默認(rèn)業(yè)務(wù)處理等待超時(shí)時(shí)間的配置。
由于本篇博客的 Demo 只有一個(gè)服務(wù)提供者 PROVIDER-APP ,所以單獨(dú)針對(duì)其也進(jìn)行了客戶端訪問超時(shí)配置。
在服務(wù)提供者 PROVIDER-APP 提供的接口中,增加 Sleep 代碼,可以進(jìn)行相關(guān)的測(cè)試驗(yàn)證。
package com.jobs.provider.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
@RequestMapping("/provider")
@RestController
public class ProviderController {
? ?@Autowired
? ?private Environment env;
? ?@RequestMapping("/getdata/{id}")
? ?public Map GetData(@PathVariable("id") int id) {
? ? ? ?//在此模擬耗時(shí)處理過程,測(cè)試驗(yàn)證 feign 客戶端超時(shí)訪問的配置
? ? ? ?try {
? ? ? ? ? ?Thread.sleep(2000);
? ? ? ?} catch (InterruptedException e) {
? ? ? ? ? ?e.printStackTrace();
? ? ? ?}
? ? ? ?//獲取當(dāng)前服務(wù)提供者的 hostname 和 port
? ? ? ?String providerName = env.getProperty("eureka.instance.hostname")
? ? ? ? ? ? ? ?+ ":" + env.getProperty("server.port");
? ? ? ?Map result = new HashMap();
? ? ? ?result.put("status", 0);
? ? ? ?result.put("msg", "success");
? ? ? ?result.put("provider", providerName);
? ? ? ?result.put("get_id_value", id);
? ? ? ?result.put("version", UUID.randomUUID().toString());
? ? ? ?return result;
? ?}
}
Ok,到此為止,有關(guān) SpringCloud 使用 OpenFeign 聲明式服務(wù)調(diào)用,已經(jīng)介紹完畢。本篇博客的 Demo 經(jīng)過詳細(xì)測(cè)試無誤,博客中只介紹了 openfeign 實(shí)現(xiàn)的重要細(xì)節(jié),省略了與之前博客相同的一些細(xì)節(jié),詳細(xì)請(qǐng)下載查看 Demo 源代碼。
本篇博客代碼的源代碼下載地址:https://files.cnblogs.com/files/blogs/699532/springcloud_feign.zip
鏈接:https://www.dianjilingqu.com/514810.html