1. 程式人生 > >SpringCloud學習筆記(3):使用Feign實現宣告式服務呼叫

SpringCloud學習筆記(3):使用Feign實現宣告式服務呼叫

簡介

Feign是一個宣告式的Web Service客戶端,它簡化了Web服務客戶端的編寫操作,相對於Ribbon+RestTemplate的方式,開發者只需通過簡單的介面和註解來呼叫HTTP API。它支援Spring MVC註解和JAX-RS註解,還支援可插拔式的編碼器和解碼器。整合了Eureka,Ribbon和Hystrix,具有可插拔、基於註解、負載均衡、服務熔斷等一系列便捷功能。

專案介紹

  1. sc-parent,父模組(請參照SpringCloud學習筆記(1):Eureka註冊中心)
  2. sc-eureka,註冊中心(請參照SpringCloud學習筆記(1):Eureka註冊中心)
  3. sc-provider,提供者(請參照SpringCloud學習筆記(1):Eureka註冊中心)
  4. sc-consumer-feign,基於Feign宣告式呼叫的消費者

基於Feign宣告式呼叫的消費者

1.在父模組下建立子模組專案sc-consumer-feign,pom.xml:

<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>
    <groupId>com.cf</groupId>
    <artifactId>sc-parent</artifactId>
    <version>0.0.1-SNAPSHOT</version>
  </parent>
  <artifactId>sc-consumer-feign</artifactId>
  
  <dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
  </dependencies>
</project>

2.建立啟動類feign.FeignApplication:

package feign;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

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

3.建立Feign宣告式介面:feign.inter.BookService

package feign.inter;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;

@FeignClient("sc-provider")
public interface BookService {
    
    @GetMapping("/book/list")
    public String getBookList();
}

4.建立呼叫提供者服務的Controller:

package provider.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping("/book")
@RestController
public class BookController {
    @GetMapping("/list")
    public String getBookList(){
        return "[\"Java入門到放棄\",\"C++入門到放棄\",\"Python入門到放棄\",\"C入門到放棄\"]";
    }
}

5.建立application.yml:

server:
  port: 8084

spring:
  application:
    name: sc-consumer-feign
    
eureka:
  client:
    registerWithEureka: false
    serviceUrl:
      defaultZone: http://localhost:8080/eureka/

6.依次啟動註冊中心sc-eureka、提供者sc-provider、消費者sc-consumer-feign,並訪問http://localhost:8084/feign/getBookList:

Feign基於Ribbon實現,也具有Ribbon負載均衡的特性,可以將呼叫的提供者服務換成sc-provider-random(請參照SpringCloud學習筆記(2):使用Ribbon負載均衡)來測試。

帶引數的請求

上面例子沒有涉及到引數的傳遞,接下來測試下如何使用Feign構造帶引數的請求,首先對提供者和消費者做如下更改:

//提供者Controller添加了兩個引數,並列印到控制檯。
@RequestMapping("/book")
@RestController
public class BookController {
    @GetMapping("/list")
    public String getBookList(String param1, Integer param2){
        System.out.println(param1 + ":" + param2);
        return "[\"Java入門到放棄\",\"C++入門到放棄\",\"Python入門到放棄\",\"C入門到放棄\"]";
    }
}


//消費者Feign介面和Controller新增引數
@FeignClient("sc-provider")
public interface BookService {
    @GetMapping("/book/list")
    public String getBookList(String param1, Integer param2);
}

@RequestMapping("/feign")
@RestController
public class FeignController {
    @Autowired
    private BookService bookService;
    
    @GetMapping("/getBookList")
    public String getBookList(){
        return bookService.getBookList("Java", 520);
    }
}

依次啟動註冊中心sc-eureka、提供者sc-provider、消費者sc-consumer-feign,啟動消費者sc-consumer-feign時會啟動失敗:
java.lang.IllegalStateException: Method has too many Body parameters: public abstract java.lang.String feign.inter.BookService.getBookList(java.lang.String,java.lang.Integer)

解決方法1

更改Feign介面,為引數新增@RequestParam註解:

@FeignClient("sc-provider")
public interface BookService {
    @GetMapping("/book/list")   
    public String getBookList(@RequestParam("param1") String param1, @RequestParam("param2") Integer param2);
}

解決方法2

將引數封裝到Map裡,更改消費者Feign介面和Controller:

@FeignClient("sc-provider")
public interface BookService {
    @GetMapping("/book/list")
    public String getBookList(@RequestParam Map<String, Object> paramMap);
}

@RequestMapping("/feign")
@RestController
public class FeignController {
    @Autowired
    private BookService bookService;
    
    @GetMapping("/getBookList")
    public String getBookList(){
        Map<String,Object> paramMap = new HashMap<String, Object>();
        paramMap.put("param1", "Java");
        paramMap.put("param2", 520);
        return bookService.getBookList(paramMap);
    }
}

在引數較多的情況下,該方式可以簡化Feign介面的編寫。

自定義型別的引數

OpenFeign的@QueryMap註解支援將自定義型別用於GET引數對映,由於@QueryMap和Spring不相容,Spring Cloud OpenFeign提供了一個等價的@SpringQueryMap註解,可以用於自定義型別和Map型別的引數對映。下面將使用自定義型別Params作為引數,使用@SpringQueryMap註解來處理自定義型別的引數對映。

1.分別在提供者和消費者中建立類Params(可以建一個公共模組,然後在提供者和消費者中新增依賴):

public class Params {
    private String param1;
    
    private Integer param2;

    public String getParam1() {
        return param1;
    }

    public void setParam1(String param1) {
        this.param1 = param1;
    }

    public Integer getParam2() {
        return param2;
    }

    public void setParam2(Integer param2) {
        this.param2 = param2;
    }
    
    @Override
    public String toString() {
        return "Params [param1=" + param1 + ", param2=" + param2 + "]";
    }

    public Params(String param1, Integer param2) {
        this.param1 = param1;
        this.param2 = param2;
    }

    public Params() {}
}

2.更改提供者和消費者相關類

//提供者
@RequestMapping("/book")
@RestController
public class BookController {
    @GetMapping("/list")
    public String getBookList(Params params){
        System.out.println(params.toString());
        return "[\"Java入門到放棄\",\"C++入門到放棄\",\"Python入門到放棄\",\"C入門到放棄\"]";
    }
}

//消費者
@FeignClient("sc-provider")
public interface BookService {
    @GetMapping("/book/list")
    public String getBookList(@SpringQueryMap Params params);
}

@RequestMapping("/feign")
@RestController
public class FeignController {
    @Autowired
    private BookService bookService;
    
    @GetMapping("/getBookList")
    public String getBookList(){    
        Params params = new Params("Java", 520);
        return bookService.getBookList(params);
    }
}

3.依次啟動註冊中心sc-eureka、提供者sc-provider、消費者sc-consumer-feign,並訪問http://localhost:8084/feign/getBookList,提供者控制檯輸出:

Params [param1=Java, param2=520]