1. 程式人生 > >Spring Cloud中如何優雅的使用Feign呼叫介面

Spring Cloud中如何優雅的使用Feign呼叫介面

JAVA 專案中介面呼叫怎麼做 ?

  • Httpclient
  • Okhttp
  • Httpurlconnection
  • RestTemplate

上面是最常見的幾種用法,我們今天要介紹的用法比上面的更簡單,方便,它就是 Feign

Feign是一個宣告式的REST客戶端,它的目的就是讓REST呼叫更加簡單。

Feign提供了HTTP請求的模板,通過編寫簡單的介面和插入註解,就可以定義好HTTP請求的引數、格式、地址等資訊。

而Feign則會完全代理HTTP請求,我們只需要像呼叫方法一樣呼叫它就可以完成服務請求及相關處理。

SpringCloud對Feign進行了封裝,使其支援SpringMVC標準註解和HttpMessageConverters。

Feign可以與Eureka和Ribbon組合使用以支援負載均衡。

SpringCloud中使用Feign

當我們搭建好註冊中心Eureka之後,就是需要將自己的服務註冊到Eureka中,然後別的服務可以直接呼叫。

首先呢是服務提供方需要註冊到Eureka中,這邊我們新建一個房產服務fangjia-fsh-house-service

fangjia-fsh-house-service中提供跟房子相關的介面,比如最簡單的獲取房子的基本資訊

/**
 * 獲取房產資訊
 * @param houseId 房產編號
 * @return 
 */
 @GetMapping("/{houseId}"
) public ResponseData hosueInfo(@PathVariable("houseId")Long houseId) { return ResponseData.ok(houseService.getHouseInfo(houseId)); }

另外我們起一個專案來消費房產服務的這個介面,房產置換服務fangjia-fsh-substitution-service

/**
 * 獲取置換資訊
 * @param sid
 * @return
 */
 @GetMapping("/{sid}")
 public ResponseData substitutionInfo
(@PathVariable("sid") Long sid) { return ResponseData.ok(substitutionService.getSubstitutionInfo(sid)); }

在substitutionService中需要消費房產服務的獲取房產資訊介面,一般的做法我們都會通過Httpclient或者最底層的Httpurlconnection來直接呼叫介面,當然這些都需要自己整合或者封裝,在spring裡面已經有了一個很好的封裝,那就是RestTemplate來呼叫介面。

可以直接注入物件,然後呼叫介面,這種方式唯一的弊端就是你需要知道服務提供者的地址,根據指定的地址來進行呼叫

@Autowired
private RestTemplate restTemplate;

@Override
public SubstitutionDto getSubstitutionInfo(Long sid) {
    House house = this.restTemplate.getForObject("http://localhost:8000/hosue/" + id, House.class);
  // .......
}

另一種就是我們今天的主角,簡單的呼叫方式就是使用一個宣告式的REST客戶端Feign來進行介面呼叫

用了Feign之後呼叫介面只需要定義相同的介面即可實現呼叫

使用Feign肯定要引入jar的依賴

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-feign</artifactId>
</dependency>

在啟動類上加@EnableFeignClients註解,如果你的Feign介面定義跟你的啟動類不在一個包名下,還需要制定掃描的包名@EnableFeignClients(basePackages = "com.fangjia.api.client")

這邊建議大家將介面的消費定義,單獨抽一個專案出來,後面打成公共的jar,這樣無論是哪個專案需要呼叫介面,引入公共的介面SDK jar即可,不用重新定義一遍了。

**
 * 房生活房產服務API呼叫客戶端
 *
 * @author yinjihuan
 * @create 2017-10-27 13:55
 **/
@FeignClient(value = "fangjia-fsh-house-service", path = "/house", configuration = FeignConfiguration.class, fallback = HouseRemoteClientHystrix.class)
public interface HouseRemoteClient {

    /**
     * 獲取企業下某使用者的有效房產資訊
     * @param eid   企業編號
     * @param uid   使用者編號
     * @return
     */
    @GetMapping("/list/{eid}/{uid}")
    public HouseListDto hosueList(@PathVariable("eid")Long eid, @PathVariable("uid")String uid);    

    /**
     * 獲取房產詳細資訊
     * @param houseId 房產編號
     * @return
     */
    @GetMapping("/{houseId}")
    public HouseInfoDto hosueInfo(@PathVariable("houseId")Long houseId);

}

@FeignClient裡的value表示你要消費哪個服務的介面,path就是統一的字首,也就是我們HouseController中類上面的@RequestMapping(“/house”)的地址

@FeignClient裡的configuration可以讓你自定義配置資訊來覆蓋Feign的預設配置,
比如配置日誌輸出

日誌的輸出還需要在配置檔案中指定才能生效logging.level.com.fangjia.api.client.fsh.house.HouseRemoteClient=DEBUG

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

@FeignClient裡的fallback可以讓你的介面在熔斷處理時,返回預設的值給呼叫方,這個一般有2種方式:
- 實現Feign的介面,實現所有的預設方法

/**
 * 房產服務呼叫熔斷預設返回處理
 *
 * @author yinjihuan
 * @create 2017-10-29 14:30
 **/
@Component
public class HouseRemoteClientHystrix implements HouseRemoteClient {

    @Override
    public HouseListDto hosueList(Long eid, String uid) {
        return new HouseListDto();
    }

    @Override
    public HouseInfoDto hosueInfo(Long houseId) {
        return new HouseInfoDto();
    }
}

另一種就是@FeignClient裡的fallbackFactory,效果是一樣的

使用的話更簡單了,和普通的Service的類一樣使用,注入進來,然後直接呼叫方法就相當於呼叫遠端介面了

@Autowired
private HouseRemoteClient houseRemoteClient;

HouseInfoDto houseInfoDto = houseRemoteClient.hosueInfo(1L);

普通Java專案中如何使用Feign

通過上面的講解,在SpringCloud中使用Feign顯得那麼的自然,因為整合這件事SpringCloud已經幫我們做好了,這是廣大開發人員的福音。

那如果你們沒有使用SpringCloud來進行開發,我能用Feign來呼叫介面馬,答案是:當然

我們看官方的提個Demo:

定義了一個GitHub的介面呼叫類,上面配置了請求方式以及引數,是通過Feign自帶的註解方式配置的

然後通過Feign.builder()構建一個客戶端,同時可以設定編碼,解碼需要用到的類,以及訪問的目標地址等等資訊,當然也包括日誌的設定,輸出等等。。

interface GitHub {
  @RequestLine("GET /repos/{owner}/{repo}/contributors")
  List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo);
}

static class Contributor {
  String login;
  int contributions;
}

public static void main(String... args) {
  GitHub github = Feign.builder()
                       .decoder(new GsonDecoder())
                       .target(GitHub.class, "https://api.github.com");

  // Fetch and print a list of the contributors to this library.
  List<Contributor> contributors = github.contributors("OpenFeign", "feign");
  for (Contributor contributor : contributors) {
    System.out.println(contributor.login + " (" + contributor.contributions + ")");
  }
}

具體程式碼可以參考我的github: