1. 程式人生 > >springcloud-feign元件實現宣告式的呼叫

springcloud-feign元件實現宣告式的呼叫

11.使用feign實現宣告式的呼叫

使用RestTemplate+ribbon已經可以完成對服務端負載均衡的呼叫,為什麼還要使用feign?

   @RequestMapping("/hi")
    public String hi(String name) {
        String restTemplateForObject = restTemplate.getForObject("http://HI-SERVICE/test/test?name=" + name, String.class);
        return restTemplateForObject;
    }

上述程式碼採用url拼接引數的形式傳送請求,如果要傳送多個請求引數那麼程式碼就會變得很低效並且難以維護。例如

http://localhost:8762/hi/hi?name=dd&password=123456&age=18

如果使用字串拼接的方式,那麼程式碼可以編排為:

  @RequestMapping("/hi")
    public String hi(String name, String password, Integer age) {
        Map<Object, Object> map = new HashMap<>();
        map.put("name", name);
        map.put("password", password);
        map.put("age", age);
String restTemplateForObject = restTemplate.getForObject("http://HI-SERVICE/test/test?name={name}&password={password}&age={age}", String.class, map);
        return restTemplateForObject;
    }

在這裡url僅僅包含三個引數,如果url為10個引數那麼程式碼會變的更加難以維護。

1.feign簡介

Feign是一個宣告式的偽Http客戶端,它使得寫Http客戶端變得更簡單。使用Feign,只需要建立一個介面並注入。它具有可插拔的註解特性(可以使用springmvc的註解),可使用Feign 註解和JAX-RS註解。Feign支援可插拔的編碼器和解碼器。Feign預設集成了Ribbon,並和Eureka結合,預設實現了負載均衡的效果並且springcloud為feign添加了springmvc註解的支援。

2.為消費者整合feign

1.引入feign的相關依賴
       <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-feign</artifactId>
        </dependency>
2.入口類開啟feign支援
@EnableDiscoveryClient
@SpringBootApplication
@EnableFeignClients
public class HiApplication {
    public static void main(String[] args) {
        SpringApplication.run(HiApplication.class, args);
    }
}
3.建立feign介面
@FeignClient(serviceId = "EUREKA-PROVIDER")
public interface feignTest {
    @RequestMapping("/test/test")
    public String sayHi(String name);
}
建議:feign介面與服務者被呼叫的方法名最好保持一致,呼叫清晰明瞭。
4.控制器
@RequestMapping("/hi")
    public String hi(String name) {
        final String s = feignTest.sayHi(name);
        System.out.println(s);
        return s;
    }
5.服務者控制器
@RestController
@RequestMapping("/test")
public class TestController {

    @RequestMapping("/test1")
    public String sayHi(String name){
        return "xixixi 8763  :"+name;
    }
}
結果:
單引數呼叫是沒有任何問題的。

3.feign日誌

很多的場景下,需要了解feign處理請求的具體細節,如何滿足這種需求呢?

feign對日誌的處理非常靈活可為每個feign客戶端指定日誌記錄策略,每個客戶端都會建立一個logger預設情況下logger的名稱是feign的全限定名需要注意的是,feign日誌的列印只會DEBUG級別做出響應。

我們可以為feign客戶端配置各自的logger.lever物件,告訴feign記錄那些日誌logger.lever有以下的幾種值

  • NONE 不記錄任何日誌
  • BASIC 僅僅記錄請求方法,url,響應狀態程式碼及執行時間
  • HEAdERS 記錄Basic級別的基礎上,記錄請求和響應的header
  • FULL 記錄請求和響應的header,body和元資料
1.java配置核心日誌類
@Configuration
public class FeignLogConf {
    @Bean
    public Logger.Level feignConfiguration() {
        return Logger.Level.FULL;
    }
}
2.配置feign客戶端的日誌級別
logging.level.com.nyist.feign.FeignLogConf=debug

4.feign構造多引數請求

多引數情況下的feign呼叫會直接報錯,比如兩個String引數feign是無法識別的,必須要引數繫結,我想這就是可插拔註解:RequestParam是springmvc的。

1.錯誤方式
feign的注意事項
1.多引數傳輸的時候 必須要在feign介面上進行引數的繫結 
    public String testFeign(@RequestParam("name") String name, @RequestParam("password") String password, @RequestParam("age") Integer age);
2.以物件格式為引數進行資料傳輸時 必須設定feign的請求形式為post
3.在服務方接收物件引數時需在形參上加入@RequestBody的註解
public interface feignPost {
    @RequestMapping(value = "/test/test1", method = RequestMethod.GET)
    public User sayHi(User user);
}

錯誤日誌資訊

feign.FeignException: status 405 reading feignPost#sayHi(User); content:
{"timestamp":1546852876890,"status":405,"error":"Method Not Allowed","exception":"org.springframework.web.HttpRequestMethodNotSupportedException","message":"Request method 'POST' not supported","path":"/test/test1"}

由異常資訊可知,儘管指定了get的方法,feign依然會使用post傳送請求(物件傳輸時)。正確的方式如下:

2.get
    @RequestMapping("/test/test",method = RequestMethod.GET)
    public String sayHi(@RequestParam("name") String name,@RequestParam("password")String password);
3.post

feign配置

@FeignClient(serviceId = "HI-SERVICE")
public interface feignPost {
    @RequestMapping(value = "/test/test1", method = RequestMethod.POST)
    public User sayHi(User user);
}

但換個電腦測試時,GET請求是正常的。。。。

服務者

  @RequestMapping("/test1")
    public User test1(@RequestBody User user) {
        return user;
    }