背景 :在Spring Cloud Netflix棧中,各個微服務都是以HTTP接口的形式暴露自身服務的,因此在調用遠程服務時就必須使用HTTP客戶端。我們可以使用JDK原生的URLConnection、Apache的Http Client、Netty的異步HTTP Client, Spring的RestTemplate。但是,用起來最方便、最優雅的還是要屬Feign了。Feign是一種聲明式、模板化的HTTP客戶端。
Contronller層通過feignClient調用微服務 獲取所有任務
@Controller @RequestMapping("tsa/task") public class TaskController{ @Autowired TaskFeignClient taskFeignClient; @PostMapping("/getAll") @ResponseBody public List<TaskVO> getAll() { List<TaskVO> all = taskFeignClient.getAll(); return all; } }
@FeignClient用於通知Feign組件對該接口進行代理(不需要編寫接口實現),使用者可直接通過@Autowired注入。
@FeignClient(qualifier = "taskFeignClient", name = "service-tsa",fallback = TaskFeignClientDegraded.class) public interface TaskFeignClient { @PostMapping(value = "taskApiController/getAll") List<TaskVO> getAll(); }
微服務端
@Slf4j @RestController @RequestMapping("taskApiController") public class TaskApiController{ @Autowired private TaskService taskService; @PostMapping("/getAll") public List<TaskVO> getAll() { log.info("--------getAll-----"); List<TaskVO> all = taskService.getAll(); return all; } }
坑1:
com.netflix.hystrix.exception.HystrixRuntimeException: TaskFeignClient#getAll() failed and no fallback available.
at com.netflix.hystrix.AbstractCommand$22.call(AbstractCommand.java:819)
at com.netflix.hystrix.AbstractCommand$22.call(AbstractCommand.java:804)
坑2:
坑3
報錯:RequestParam.value() was empty on parameter 0
如果在FeignClient中的方法有參數傳遞一般要加@RequestParam(“xxx”)注解
錯誤寫法:
@FeignClient(qualifier = "taskFeignClient", name = "service-tsa",fallback = TaskFeignClientDegraded.class) public interface TaskFeignClient { @PostMapping(value = "taskApiController/getAll") List<TaskVO> getAll(String name); } //或者 @FeignClient(qualifier = "taskFeignClient", name = "service-tsa",fallback = TaskFeignClientDegraded.class) public interface TaskFeignClient { @PostMapping(value = "taskApiController/getAll") List<TaskVO> getAll(@RequestParam String name); }
正確寫法:
@PostMapping(value = "taskApiController/getAll") List<TaskVO> getAll(@RequestParam("name") String name);
微服務那邊可以不寫這個注解
參考 https://blog.csdn.net/jxm007love/article/details/80109974
Fiegn Client with Spring Boot: RequestParam.value() was empty on parameter 0
Feign bug when use @RequestParam but not have value
https://www.xttblog.com/ 業余草
坑四:
FeignClient中post傳遞對象和consumes = "application/json",按照坑三的意思,應該這樣寫。
@FeignClient(qualifier = "taskFeignClient", name = "service-tsa",fallback = TaskFeignClientDegraded.class) public interface TaskFeignClient { @PostMapping(value = "taskApiController/getAll") List<TaskVO> getAll(@RequestParam("vo") TaskVO vo); }
很意外報錯
16:00:33.770 [http-apr-8086-exec-1] DEBUG c.b.p.a.s.PrimusCasAuthenticationFilter - proxyTicketRequest = false
16:00:33.770 [http-apr-8086-exec-1] DEBUG c.b.p.a.s.PrimusCasAuthenticationFilter - requiresAuthentication = false
16:00:34.415 [hystrix-service-tsa-2] DEBUG c.b.p.m.b.f.PrimusSoaFeignErrorDecoder - error json:{
"timestamp":1543564834395,
"status":500,
"error":"Internal Server Error",
"exception":"org.springframework.web.method.annotation.MethodArgumentConversionNotSupportedException","message":"Failed to convert value of type 'java.lang.String' to required type 'com.model.tsa.vo.TaskVO';
nested exception is java.lang.IllegalStateException:
Cannot convert value of type 'java.lang.String' to required type 'com.model.tsa.vo.TaskVO':
no matching editors or conversion strategy found","path":"/taskApiController/getAll" }
@FeignClient(qualifier = "taskFeignClient", name = "service-tsa",fallback = TaskFeignClientDegraded.class) public interface TaskFeignClient { @PostMapping(value = "taskApiController/getAll",,consumes = "application/json") List<TaskVO> getAll(TaskVO vo); }
也可以這樣寫
@PostMapping(value = "taskApiController/getAll") List<TaskVO> getAll(@RequestBody TaskVO vo);
此時不用,consumes = "application/json"
@Slf4j @RestController @RequestMapping("taskApiController") public class TaskApiController{ @Autowired private TaskService taskService; @PostMapping("/getAll") public List<TaskVO> getAll(@RequestBody TaskVO vo) { log.info("--------getAll-----"); List<TaskVO> all = taskService.getAll(); return all; } }
我第一次寫這個的時候方法參數里面什么注解都沒加,可以正常跑通,但是傳過去的對象卻為初始值,實際上那是因為對象根本就沒傳
傳遞對象的另一種方法和多參傳遞
@FeignClient("microservice-provider-user") public interface UserFeignClient { @RequestMapping(value = "/get", method = RequestMethod.GET) public User get0(User user); }
然而我們測試時會發現該寫法不正確,我們將會收到類似以下的異常:
{"timestamp":1482676142940,"status":405,"error":"Method Not Allowed","exception":"org.springframework.web.HttpRequestMethodNotSupportedException","message":"Request method 'POST' not supported","path":"/get"}
@FeignClient(name = "microservice-provider-user") public interface UserFeignClient { @RequestMapping(value = "/get", method = RequestMethod.GET) public User get1(@RequestParam("id") Long id, @RequestParam("username") String username); }
這是最為直觀的方式,URL有幾個參數,Feign接口中的方法就有幾個參數。使用@RequestParam注解指定請求的參數是什么。
(2) 方法二
@FeignClient(name = "microservice-provider-user") public interface UserFeignClient { @RequestMapping(value = "/get", method = RequestMethod.GET) public User get2(@RequestParam Map<String, Object> map); }
多參數的URL也可以使用Map去構建
當目標URL參數非常多的時候,可使用這種方式簡化Feign接口的編寫。
POST請求包含多個參數
下面我們來討論如何使用Feign構造包含多個參數的POST請求。實際就是坑四,把參數封裝成對象傳遞過去就可以了。
最后拓展一下
Feign的Encoder、Decoder和ErrorDecoder
@RequestMapping(value = "/group/{groupId}", method = RequestMethod.GET) void update(@PathVariable("groupId") Integer groupId, @RequestParam("groupName") String groupName, DataObject obj);
此時因為聲明的是GET請求沒有請求體,所以obj參數就會被忽略。
Feign的HTTP Client
Feign在默認情況下使用的是JDK原生的URLConnection發送HTTP請求,沒有連接池,但是對每個地址會保持一個長連接,即利用HTTP的persistence connection 。我們可以用Apache的HTTP Client替換Feign原始的http client, 從而獲取連接池、超時時間等與性能息息相關的控制能力。Spring Cloud從Brixtion.SR5版本開始支持這種替換,首先在項目中聲明Apache HTTP Client和feign-httpclient依賴:
<!-- 使用Apache HttpClient替換Feign原生httpclient --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> </dependency> <dependency> <groupId>com.netflix.feign</groupId> <artifactId>feign-httpclient</artifactId> <version>${feign-httpclient}</version> </dependency>
feign.httpclient.enabled=true
通過Feign, 我們能把HTTP遠程調用對開發者完全透明,得到與調用本地方法一致的編碼體驗。這一點與阿里Dubbo中暴露遠程服務的方式類似,區別在於Dubbo是基於私有二進制協議,而Feign本質上還是個HTTP客戶端。如果是在用Spring Cloud Netflix搭建微服務,那么Feign無疑是最佳選擇。
原文鏈接: