1. 程式人生 > >06.Spring Cloud學習筆記之宣告式服務呼叫元件Feign

06.Spring Cloud學習筆記之宣告式服務呼叫元件Feign

前言

Spring Cloud Feign基於Netflix Feign實現,整合了Spring Cloud Ribbon與Spring Cloud Hystrix,除了提供這兩者的強大功能外,還提供了一種宣告式的Web服務客戶端定義方式,我們可以做到使用HTTP請求遠端服務時能與呼叫本地方法一樣的編碼體驗,讓開發者在開發過程中感知不到這是個HTTP請求

快速入門

在pom.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> <parent> <artifactId>spring-cloud-parent</artifactId> <groupId
>
com.roberto.springcloud</groupId> <version>1.0.0-SNAPSHOT</version> </parent> <artifactId>spring-cloud-movie-service</artifactId> <dependencies> <dependency> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency> </dependencies> </project>

在啟動類新增@EnableFeignClients開啟Spring Cloud Feign的支援

package com.roberto.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;

@EnableEurekaClient
@EnableFeignClients
@SpringBootApplication
public class SpringCloudMovieService {
    public static void main(String args[]) {
        SpringApplication.run(SpringCloudMovieService.class, args);
    }
}

定義Service介面,通過@FeignClient註解指定服務名來繫結服務,然後使用SpringMVC的註解來繫結具體服務提供的REST介面

package com.roberto.springcloud.feign;

import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@FeignClient(name = "USER-SERVICE")
public interface UserFeignClient {
    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    String index();
}

此處USER-SERVICE為服務名 且/hello介面返回Hello World

在Controller層注入Feign介面進行呼叫

package com.roberto.springcloud.controller;

import com.roberto.springcloud.feign.UserFeignClient;
import com.roberto.springcloud.feign.UserFeignClient2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MovieController {
    @Autowired
    private UserFeignClient userFeignClient;

    @RequestMapping(value = "/index", method = RequestMethod.GET)
    public String index() {
        return userFeignClient.index();
    }
}

application.yml檔案配置如下

spring:
  application:
    name: movie-service
server:
  port: 9092
eureka:
  client:
    healthcheck:
      enabled: true
    serviceUrl:
      defaultZone: http://roberto:[email protected]:8761/eureka
  instance:
    prefer-ip-address: true

呼叫/index介面檢視結果
Feign結果

Feign引數繫結

新建User實體類

package com.roberto.springcloud.entity;

public class User {
    private String username;
    private String password;

    public User() {

    }

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

修改USER-SERVICE服務的介面

package com.roberto.springcloud.controller;

import com.roberto.springcloud.entity.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;

@RestController
public class UserController {
    @RequestMapping(value = "hello", method = RequestMethod.GET)
    public String hello(@RequestParam String username) {
        return "Hello " + username;
    }

    @RequestMapping(value = "hello2", method = RequestMethod.GET)
    public User hello(@RequestHeader String username, @RequestHeader String password) {
        return new User(username, password);
    }

    @RequestMapping(value = "hello3", method = RequestMethod.POST)
    public User hello(@RequestBody User user) {
        return user;
    }
}

修改UserFeignClient介面

package com.roberto.springcloud.feign;

import com.roberto.springcloud.entity.User;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.*;

@FeignClient(name = "USER-SERVICE")
public interface UserFeignClient {
    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    String hello(@RequestParam("username") String username);

    @RequestMapping(value = "/hello2", method = RequestMethod.GET)
    User hello(@RequestHeader("username") String username, @RequestHeader("password") String password);

    @RequestMapping(value = "/hello3", method = RequestMethod.POST)
    User hello(@RequestBody User user);
}

注:在定義各引數繫結時,@RequestParam和@RequestHeader等可以指定引數名稱的註解,他們的value千萬不能省略,在SpringMVC中這些註解會根據引數名作為預設值但在Feign中必須顯示指定,否則會丟擲異常

在Controller中新增測試介面進行測試

package com.roberto.springcloud.controller;

import com.roberto.springcloud.entity.User;
import com.roberto.springcloud.feign.UserFeignClient;
import com.roberto.springcloud.feign.UserFeignClient2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MovieController {
    @Autowired
    private UserFeignClient userFeignClient;

    @RequestMapping(value = "helloTest", method = RequestMethod.GET)
    public String helloTest() {
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append(userFeignClient.hello("RobertoHuang")).append("\n");
        stringBuffer.append(userFeignClient.hello("RobertoHuang","123456")).append("\n");
        stringBuffer.append(userFeignClient.hello(new User("RobertoHuang","123456"))).append("\n");
        return stringBuffer.toString();
    }
}

訪問/helloTest介面檢視輸出結果
訪問/helloTest介面檢視輸出結果

Feign繼承特性

在使用SpringMVC的註解來繫結服務介面時,我們幾乎完全可以從服務提供方的Controller中依靠複製操作,構建出相應的服務客戶端繫結介面,既然存在這麼多複製操作,我們可以把相同的部分進行抽象以減少編碼量,下面我們對上面工程進行改造

新建USER-API工程,在pom.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>

    <parent>
        <artifactId>spring-cloud-parent</artifactId>
        <groupId>com.roberto.springcloud</groupId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>

    <artifactId>spring-cloud-user-api</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
</project>

在USER-API工程中新建User類

package com.roberto.springcloud.entity;

public class User {
    private String usernamne;
    private String password;

    public User() {
    }

    public User(String usernamne, String password) {
        this.usernamne = usernamne;
        this.password = password;
    }

    public String getUsernamne() {
        return usernamne;
    }

    public void setUsernamne(String usernamne) {
        this.usernamne = usernamne;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{" +
                "usernamne='" + usernamne + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

在USER-API工程中新建UserServiceApi類

package com.roberto.springcloud.api;

import com.roberto.springcloud.entity.User;
import org.springframework.web.bind.annotation.*;

public interface UserServiceApi {
    @RequestMapping(value = "hello", method = RequestMethod.GET)
    String hello(@RequestParam("username") String username);

    @RequestMapping(value = "hello2", method = RequestMethod.GET)
    User hello(@RequestHeader("username") String username, @RequestHeader("password") String password);

    @RequestMapping(value = "hello3", method = RequestMethod.POST)
    User hello(@RequestBody User user);
}

在USER-SERVICE的pom檔案新增USER-API的依賴

 <dependency>
    <groupId>com.roberto.springcloud</groupId>
    <artifactId>spring-cloud-user-api</artifactId>
    <version>1.0.0-SNAPSHOT</version>
</dependency>

修改USER-SERVICE的UserController

package com.roberto.springcloud.controller;

import com.roberto.springcloud.api.UserServiceApi;
import com.roberto.springcloud.entity.User;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController implements UserServiceApi {
    @Override
    public String hello(@RequestParam String username) {
        return "Hello " + username;
    }

    @Override
    public User hello(@RequestHeader String username, @RequestHeader String password) {
        return new User(username, password);
    }

    @Override
    public User hello(@RequestBody User user) {
        return user;
    }
}

在服務呼叫方的pom檔案新增USER-API的依賴

 <dependency>
    <groupId>com.roberto.springcloud</groupId>
    <artifactId>spring-cloud-user-api</artifactId>
    <version>1.0.0-SNAPSHOT</version>
</dependency>

修改服務呼叫者的UserFeignClient

package com.roberto.springcloud.feign;

import com.roberto.springcloud.api.UserServiceApi;
import org.springframework.cloud.netflix.feign.FeignClient;

@FeignClient(name = "USER-SERVICE")
public interface UserFeignClient extends UserServiceApi{

}

訪問/helloTest介面檢視輸出結果
訪問/helloTest介面檢視輸出結果

Feign日誌配置

Spring Cloud Feign在構建被@FeignClient註解修飾的服務客戶端時,會為每一個客戶端建立一個feign.Logger例項,我們可以配置修改日誌列印級別,在application.yml檔案中新增如下配置

logging:
  level:
    com.roberto.springcloud.feign: debug

如果只配置瞭如上內容是無法打印出Feign的日誌的,因為Feign客戶端預設的Logger.Level物件定義為NONE級別,該級別不會記錄任何Feign呼叫過程中的資訊,所以我們需要調整它的級別,針對全域性日誌級別,在主類或配置類中直接加入Logger.Level的Bean建立,具體如下:

@Bean
public Logger.Level feignLoggerLevel(){
    return Logger.Level.FULL;
}

測試結果如下
Feign日誌測試

Feign的Logger級別主要提供了下面4類,可根據實際情況調整使用

NONE:不記錄任何資訊
BASIC:僅記錄請求方法、URL以及響應狀態和時間
HEADERS:除了記錄BASIC級別的資訊外,還會記錄請求和響應的頭資訊
FULL:記錄所有請求與響應的明細,包括頭資訊、請求體、元資料等

Feign請求壓縮

Spring Cloud Feign支援對請求與響應進行GZIP壓縮,以減少通訊過程中的效能損耗,我們只需要通過如下兩個引數進行設定,就能開啟請求與響應壓縮的功能

feign.compression.request.enabled=true
feign.compression.response.enabled=true

同時我們還能對請求壓縮做一些更細緻的設定,比如下面的配置內容指定了壓縮的請求資料型別,並設定了請求壓縮的大小下限,只有超過這個大小的請求才會對其進行壓縮

feign.compression.request.enabled=true
feign.compression.request.mime-types=text/xml,application/xml,application/json
feign.compression.request.min-request-size=2048

上述配置的mime-types與min-request-size均為預設值

相關推薦

06.Spring Cloud學習筆記宣告服務呼叫元件Feign

前言 Spring Cloud Feign基於Netflix Feign實現,整合了Spring Cloud Ribbon與Spring Cloud Hystrix,除了提供這兩者的強大功能外,還提供了一種宣告式的Web服務客戶端定義方式,我們可以做到使用HT

Spring Cloud學習筆記Eureka Server註冊中心

Eureka Server提供服務註冊服務,各個節點啟動後,會在Eureka Server中進行註冊,這樣EurekaServer中的服務登錄檔中將會儲存所有可用服務節點的資訊,服務節點的資訊可以在介面中直觀的看到。我們簡單實現一下Eureka Server。 1.新建一個Maven專案

Spring Cloud學習筆記Eureka框架的原理

Eureka 服務發現與註冊:我們在呼叫微服務的時候,如果我們的微服務部署了多份,我們應該如何去呼叫?這裡就涉及到了服務發現與註冊。服務發現就是程式如何通過一個標誌來獲取服務列表,並且這個服務列表是能夠隨著服務的狀態而動態變更的。 Spring Cloud提供了多種註冊中心的支援:如Eur

Spring Cloud學習筆記微服務實現(一)(Spring Boot+IDEA)

我們先使用Spring Boot實現一個微服務,業務非常簡單: 1.商品微服務,通過商品id查詢商品的微服務 2.訂單微服務,通過訂單id查詢訂單資料,同時需要呼叫商品微服務查詢出訂單詳情資料對應的商品資料。 說明: 1.對於商品微服務而言,商品微服務是服務的提供者,訂單微服務是服務的消費

Spring Cloud學習筆記微服務實現(二)(Spring Boot+Spring Cloud+IDEA)

在【Spring Cloud學習筆記之微服務實現(一)】中,我們實現了微服務,但是在實際的專案中,我們需要實現動態訪問微服務,在此之前,已經介紹了Spring Cloud和Eureka,並且實現了eureka註冊中心。現在我們實現一下動態呼叫。 注:註冊中心的服務在此期間保持啟動狀態。

03.Spring Cloud學習筆記服務註冊與服務發現元件Eureka

前言 從本篇部落格開始將正式進入Spring Cloud的實戰部分,因為博主用了很長時間的Dubbo,並且Spring Cloud和Dubbo都是微服務框架,它們有很多相似之處,所以可能在部落格中提及進行類比,如果沒有接觸過Dubbo的朋友直接略過該部分內容即

幹貨分享微服務spring-cloud(5.聲明服務調用feign

ace request pre sha 通過 san rest process white Spring cloud feign基於Netflix feign實現,整合了spring cloud ribbon與spring cloud hystrix,除了提供這兩者的強大功

Spring Cloud學習筆記 【篇一:分布配置中心 Spring Colud Config】

16px gin war imp web項目 tps conf name request 一、簡介 Spring Cloud Config提供了在分布式系統的外部配置的客戶端支持。通過配置服務(Config Server)來為所有的環境和應用提供外部配置的集中管理。這些概念

Sprng Cloud學習筆記Spring Cloud簡介

Spring Cloud Spring Cloud是一系列框架的有序集合(Spring Cloud並不是一個專案,它是一套專案的組合)。它利用Spring Boot的開發便利性巧妙地簡化了分散式系統基礎設施的開發,如服務發現註冊、配置中心、訊息匯流排、負載均衡、斷路器、資料監控等,都可以

Spring Cloud學習筆記(三)——服務發現與消費使用Ribbon

服務消費一般使用ribbon和feign兩種方式。而feign實際上也是以ribbon為基礎的。有多個服務提供者例項的情況下ribbon可以實現負載均衡。 1.pom檔案:這裡與服務提供者不同的是需要引入ribbon包。 <dependency>

Spring Cloud學習筆記(四)——服務發現與消費使用Feign

Feign 是一個宣告web服務客戶端,這便得編寫web服務客戶端更容易,使用Feign 建立一個介面並對它進行註解,它具有可插拔的註解支援包括Feign註解與JAX-RS註解,Feign還支援可插拔的編碼器與解碼器,Spring Cloud 增加了對 Spring MVC的

Spring Cloud 宣告REST客戶端 Feign元件

Feign是一個宣告式的Web服務客戶端。能很簡單的呼叫其他服務的API。並且實現負載均衡。 1、建立Feign模組 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http:/

spring cloud學習筆記3(同步呼叫、非同步呼叫、響應呼叫)

1.同步呼叫  同步呼叫,在學習筆記2就是同步呼叫 2.非同步呼叫 修改service package com.study.cloud.consumer.services; import com.netflix.hystrix.contrib.javanica.annot

Spring Cloud Eureka 分散式開發服務註冊中心、負載均衡、宣告服務呼叫實現

介紹 本示例主要介紹 Spring Cloud 系列中的 Eureka,使你能快速上手負載均衡、宣告式服務、服務註冊中心等 Eureka Server Eureka 是 Netflix 的子模組,它是一個基於 REST 的服務,用於定位服務,以實現雲端中間層服務發現和故障轉移。 服務註冊和發現對於微服務架

我的第一個spring boot程序(spring boot 學習筆記二)

獲取json 了解 訪問 static 依賴 過程 public 獲取數據 gap 第一個spring boot程序 寫在前面:鑒於spring註解以及springMVC的配置有大量細節和知識點,在學習理解之後,我們將直接進入spring boot的學習,在後續學習中用到註

Spring Cloud學習筆記-003

開發 spring pre 學習筆記 bubuko lse 參數 穩定 觸發 Spring Cloud學習筆記-003 服務提供者:向註冊中心註冊服務 1. 新建maven工程,骨架選擇quickstart,工程名稱:demo-member 2. 加入相關依賴

Spring Cloud學習筆記-004

服務 微服務架構 裏的 url 文件 啟動 spa 實現 ron 高可用註冊中心   在微服務架構這樣的分布式環境中,需要充分考慮發生故障的情況,所以在生產環境中必須對各個組件進行高可用部署,對於微服務如此,對於服務註冊中心也一樣。如果一直使用單節點的服務註冊中心

Spring Cloud學習筆記-007

通過 cati tar source 復雜 members quest 項目 ati 聲明式服務調用:Spring Cloud Feign   Feign基於Netflix Feign實現,整合了Spring Cloud Ribbon和Spring Cloud H

Spring Cloud學習筆記-011

設備 配置文件 enable 環境 共享 app 啟用 .config localhost 分布式配置中心:安全保護   由於配置中心存儲的內容比較敏感,做一定的安全處理是必需的。為配置中心實現安全保護的方式有很多,比如物理網絡限制、OAuth2授權等。由於微服務

Spring Cloud學習筆記-012

目錄 註冊中心 image yml 接口 面向 基礎 collect 16px 分布式服務跟蹤:Spring Cloud Sleuth   隨著業務的發展,系統規模也會變得越來越大,各微服務間的調用關系也變得越來越錯綜復雜。通常一個由客戶端發起的請求在後端系統中會