微服務(wù)實戰(zhàn)SpringCloud之Feign簡介及使用

Feign的目標(biāo)

feign是聲明式的web service客戶端,它讓微服務(wù)之間的調(diào)用變得更簡單了,類似controller調(diào)用service。Spring Cloud集成了Ribbon和Eureka,可在使用Feign時提供負載均衡的http客戶端。

引入Feign

項目中使用了gradle作為依賴管理,maven類似。

dependencies {
    //feign
    implementation('org.springframework.cloud:spring-cloud-starter-openfeign:2.0.2.RELEASE')
    //web
    implementation('org.springframework.boot:spring-boot-starter-web')
    //eureka client
    implementation('org.springframework.cloud:spring-cloud-starter-netflix-eureka-client:2.1.0.M1')
    //test
    testImplementation('org.springframework.boot:spring-boot-starter-test')
}

因為feign底層是使用了ribbon作為負載均衡的客戶端,而ribbon的負載均衡也是依賴于eureka 獲得各個服務(wù)的地址,所以要引入eureka-client。

SpringbootApplication啟動類加上@FeignClient注解,以及@EnableDiscoveryClient。

@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class ProductApplication {

    public static void main(String[] args) {
        SpringApplication.run(ProductApplication.class, args);
    }
}

yaml配置:

server:
  port: 8082

#配置eureka
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka
  instance:
    status-page-url-path: /info
    health-check-url-path: /health

#服務(wù)名稱
spring:
  application:
    name: product
  profiles:
    active: ${boot.profile:dev}
#feign的配置,連接超時及讀取超時配置
feign:
  client:
    config:
      default:
        connectTimeout: 5000
        readTimeout: 5000
        loggerLevel: basic

Feign的使用

@FeignClient(value = "CART")
public interface CartFeignClient {

    @PostMapping("/cart/{productId}")
    Long addCart(@PathVariable("productId")Long productId);
}

上面是最簡單的feign client的使用,聲明完為feign client后,其他spring管理的類,如service就可以直接注入使用了,例如:

//這里直接注入feign client
@Autowired
private CartFeignClient cartFeignClient;

@PostMapping("/toCart/{productId}")
public ResponseEntity addCart(@PathVariable("productId") Long productId){
    Long result = cartFeignClient.addCart(productId);
    return ResponseEntity.ok(result);
}

可以看到,使用feign之后,我們調(diào)用eureka 注冊的其他服務(wù),在代碼中就像各個service之間相互調(diào)用那么簡單。

FeignClient注解的一些屬性

屬性名 默認值 作用 備注
value 空字符串 調(diào)用服務(wù)名稱,和name屬性相同
serviceId 空字符串 服務(wù)id,作用和name屬性相同 已過期
name 空字符串 調(diào)用服務(wù)名稱,和value屬性相同
url 空字符串 全路徑地址或hostname,http或https可選
decode404 false 配置響應(yīng)狀態(tài)碼為404時是否應(yīng)該拋出FeignExceptions
configuration {} 自定義當(dāng)前feign client的一些配置 參考FeignClientsConfiguration
fallback void.class 熔斷機制,調(diào)用失敗時,走的一些回退方法,可以用來拋出異常或給出默認返回數(shù)據(jù)。 底層依賴hystrix,啟動類要加上@EnableHystrix
path 空字符串 自動給所有方法的requestMapping前加上前綴,類似與controller類上的requestMapping
primary true

此外,還有qualifier及fallbackFactory,這里就不再贅述。

Feign自定義處理返回的異常

這里貼上GitHub上openFeign的wiki給出的自定義errorDecoder例子。

public class StashErrorDecoder implements ErrorDecoder {

    @Override
    public Exception decode(String methodKey, Response response) {
        if (response.status() >= 400 && response.status() <= 499) {
            //這里是給出的自定義異常
            return new StashClientException(
                    response.status(),
                    response.reason()
            );
        }
        if (response.status() >= 500 && response.status() <= 599) {
            //這里是給出的自定義異常
            return new StashServerException(
                    response.status(),
                    response.reason()
            );
        }
        //這里是其他狀態(tài)碼處理方法
        return errorStatus(methodKey, response);
    }
}

自定義好異常處理類后,要在@Configuration修飾的配置類中聲明此類。

Feign使用OKhttp發(fā)送request

Feign底層默認是使用jdk中的HttpURLConnection發(fā)送HTTP請求,feign也提供了OKhttp來發(fā)送請求,具體配置如下:

feign:
  client:
    config:
      default:
        connectTimeout: 5000
        readTimeout: 5000
        loggerLevel: basic
  okhttp:
    enabled: true
  hystrix:
    enabled: true

Feign原理簡述

  • 啟動時,程序會進行包掃描,掃描所有包下所有@FeignClient注解的類,并將這些類注入到spring的IOC容器中。當(dāng)定義的Feign中的接口被調(diào)用時,通過JDK的動態(tài)代理來生成RequestTemplate。
  • RequestTemplate中包含請求的所有信息,如請求參數(shù),請求URL等。
  • RequestTemplate聲場Request,然后將Request交給client處理,這個client默認是JDK的HTTPUrlConnection,也可以是OKhttp、Apache的HTTPClient等。
  • 最后client封裝成LoadBaLanceClient,結(jié)合ribbon負載均衡地發(fā)起調(diào)用。

詳細原理請參考源碼解析。

Feign、hystrix與retry的關(guān)系請參考https://xli1224.github.io/2017/09/22/configure-feign/

Feign開啟GZIP壓縮

Spring Cloud Feign支持對請求和響應(yīng)進行GZIP壓縮,以提高通信效率。

application.yml配置信息如下:

feign:
  compression:
    request: #請求
      enabled: true #開啟
      mime-types: text/xml,application/xml,application/json #開啟支持壓縮的MIME TYPE
      min-request-size: 2048 #配置壓縮數(shù)據(jù)大小的下限
    response: #響應(yīng)
      enabled: true #開啟響應(yīng)GZIP壓縮

注意:

由于開啟GZIP壓縮之后,F(xiàn)eign之間的調(diào)用數(shù)據(jù)通過二進制協(xié)議進行傳輸,返回值需要修改為ResponseEntity<byte[]>才可以正常顯示,否則會導(dǎo)致服務(wù)之間的調(diào)用亂碼。

示例如下:

@PostMapping("/order/{productId}")
ResponseEntity<byte[]> addCart(@PathVariable("productId") Long productId);

作用在所有Feign Client上的配置方式

方式一:通過java bean 的方式指定。

@EnableFeignClients注解上有個defaultConfiguration屬性,可以指定默認Feign Client的一些配置。

@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration.class)
@EnableDiscoveryClient
@SpringBootApplication
@EnableCircuitBreaker
public class ProductApplication {

    public static void main(String[] args) {
        SpringApplication.run(ProductApplication.class, args);
    }
}

DefaultFeignConfiguration內(nèi)容:

@Configuration
public class DefaultFeignConfiguration {

    @Bean
    public Retryer feignRetryer() {
        return new Retryer.Default(1000,3000,3);
    }
}

方式二:通過配置文件方式指定。

feign:
  client:
    config:
      default:
        connectTimeout: 5000 #連接超時
        readTimeout: 5000 #讀取超時
        loggerLevel: basic #日志等級

Feign Client開啟日志

日志配置和上述配置相同,也有兩種方式。

方式一:通過java bean的方式指定

@Configuration
public class DefaultFeignConfiguration {
    @Bean
    public Logger.Level feignLoggerLevel(){
        return Logger.Level.BASIC;
    }
}

方式二:通過配置文件指定

logging:
  level:
    com.xt.open.jmall.product.remote.feignclients.CartFeignClient: debug

Feign 的GET的多參數(shù)傳遞

目前,feign不支持GET請求直接傳遞POJO對象的,目前解決方法如下:

  1. 把POJO拆散城一個一個單獨的屬性放在方法參數(shù)中
  2. 把方法參數(shù)編程Map傳遞
  3. 使用GET傳遞@RequestBody,但此方式違反restful風(fēng)格

介紹一個最佳實踐,通過feign的攔截器來實現(xiàn)。

@Component
@Slf4j
public class FeignCustomRequestInteceptor implements RequestInterceptor {

    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public void apply(RequestTemplate template) {
        if (HttpMethod.GET.toString() == template.method() && template.body() != null) {
            //feign 不支持GET方法傳輸POJO 轉(zhuǎn)換成json,再換成query
            try {
                Map<String, Collection<String>> map = objectMapper.readValue(template.bodyTemplate(), new TypeReference<Map<String, Collection<String>>>() {

                });
                template.body(null);
                template.queries(map);
            } catch (IOException e) {
                log.error("cause exception", e);
            }
        }
    }

源碼分析請見文章微服務(wù)實戰(zhàn)SpringCloud之Feign源碼分析

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,578評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,701評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,691評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,974評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 72,694評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,026評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,015評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,193評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,719評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,442評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,668評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,151評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,846評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,255評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,592評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,394評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內(nèi)容