Spring Cloud Feign實例講解學習
前面博文搭建了一個Eureka+Ribbon+Hystrix的框架,雖然可以基本滿足服務之間的調用,但是代碼看起來實在丑陋,每次客戶端都要寫一個restTemplate,為了讓調用更美觀,可讀性更強,現在我們開始學習使用Feign。
Feign包含了Ribbon和Hystrix,這個在實戰(zhàn)中才慢慢體會到它的意義,所謂的包含并不是Feign的jar包包含有Ribbon和Hystrix的jar包這種物理上的包含,而是Feign的功能包含了其他兩者的功能這種邏輯上的包含。簡言之:Feign能干Ribbon和Hystrix的事情,但是要用Ribbon和Hystrix自帶的注解必須要引入相應的jar包才可以。
案例一:
Eureka注冊中心:https://github.com/yejingtao/forblog/tree/master/demo-eureka-register
服務提供方:https://github.com/yejingtao/forblog/tree/master/demo-feign-freeservice
服務調用方:https://github.com/yejingtao/forblog/tree/master/demo-feign-freeconsumer
服務提供方就是個簡單的EurekaClient端+web應用,提供以下方法
@RestController
@RequestMapping("/feign-service")
public class HelloServiceContorller {
private Logger logger = LoggerFactory.getLogger(this.getClass());
private void sleep(String methodName) {
int sleepMinTime = new Random().nextInt(3000);
logger.info("helloService "+methodName+" sleepMinTime: "+sleepMinTime);
try {
Thread.sleep(sleepMinTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@RequestMapping(value="/serviceGet",method=RequestMethod.GET)
public String helloService(@RequestParam String name) {
sleep("get");
return "HelloServiceImpl name :"+name;
}
@RequestMapping(value="/serviceHead", method=RequestMethod.HEAD)
public String helloService(@RequestHeader String name,
@RequestHeader String password) {
sleep("header");
return "helloServiceHead name :"+name +" password:"+password;
}
@RequestMapping(value="/servicePost", method=RequestMethod.POST)
public String helloService(@RequestBody UserDemo userDemo) {
sleep("post");
return userDemo.toString();
}
}
需要注意的以下注解不可以省略。
@RequestParam:Annotation which indicates that amethod parameter should be bound to a web request parameter
@RequestBody:Annotation indicating a methodparameter should be bound to the body of the web request.
@RequestHeader:Annotation which indicates that amethod parameter should be bound to a web request header.
如果缺少了以上注解,服務運行起來以后雖然不會報錯,但是獲取不到入參。
服務調用方項目:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
這里只依賴了Feign,沒有依賴Ribbon和Hystrix。
application.yml:
server: port: 9051 spring: application: name: demo-feign-freeconsumer eureka: client: serviceUrl: defaultZone: http://peer1:1111/eureka/,http://peer2:1112/eureka/ feign: hystrix: enabled: true #Ribbon 超時時間設置 #ribbon: # ConnectTimeout: 500 # ReadTimeout: 3000
hystrix這個配置坑了我好久我用的Spring Cloud是Dalston版本SR1,比網上其他材料的版本要新,因為在新版本中Feign對Hystrix的支持默認是關閉的,所以要通過配置手動打開feign.hystrix.enabled=true,這樣服務降級等功能才有效果。
Application啟動程序
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class DemoFeignApplication {
public static void main(String[] args) {
SpringApplication.run(DemoFeignApplication.class, args);
}
}
注意這里還有個坑,我這里用的是@SpringBootApplication+@EnableEurekaClient,而不是用的@SpringCloudApplication,因為后者包含了@EnableCircuitBreaker,而@EnableCircuitBreaker又是屬于Hystrix包里的內容,我的pom里并沒有引入Hystrix。所以這一點Spring Cloud做的還是有不足的地方,直接用@SpringCloudApplication編譯不會報錯,但是啟動不了。當然這里的主角還是@EnableFeignClients這個注解。
核心客戶端代碼
@FeignClient(name="demo-feign-freeservice",fallback=DemoFeignFallback.class)
public interface DemoFeignService{
@RequestMapping(value="/feign-service/serviceGet",method=RequestMethod.GET)
String helloService(@RequestParam("name") String name);
@RequestMapping(value="/feign-service/serviceHead", method=RequestMethod.HEAD)
String helloService(@RequestHeader("name") String name,
@RequestHeader("password") String password);
@RequestMapping(value="/feign-service/servicePost", method=RequestMethod.POST)
String helloService(@RequestBody UserDemo userDemo);
}
@FeignClient注解定義了該接口是一個Feign客戶端,name指定了注冊到Eureka上的服務名,fallback是服務降級后的接口實現類。
@RequestMapping里指定了請求的相對url和http請求方式,與服務端一一對應。入參里的@RequestParam、
@RequestBody、@RequestHeader注解比起服務端多了value屬性,這里不能省略,需要顯式的告知Feign客戶端參數要如何對應。
降級服務代碼:
@Component
public class DemoFeignFallback implements DemoFeignService{
@Override
public String helloService(String name) {
return "get error";
}
@Override
public String helloService(String name,String password) {
return "head error";
}
@Override
public String helloService(UserDemo userDemo) {
return "post error";
}
}
發(fā)現這里的入參里我故意去掉了@RequestParam、@RequestBody、@RequestHeader注解,因為這幾個注解本質上的意義就在于Feign在做微服務調用的時候對http傳遞參數用的,但服務降級根本不會做http請求了,所以此處可以省略。
Controller代碼:
@RestController
public class DemoFeignController {
@Autowired
private DemoFeignService demoFeignService;
@RequestMapping(value="/test", method=RequestMethod.GET)
public String demoServiceTest() {
StringBuffer sb = new StringBuffer();
sb.append(demoFeignService.helloService("yuanyuan"));
sb.append("\n");
sb.append(demoFeignService.helloService("yjt","xixihaha"));
sb.append("\n");
sb.append(demoFeignService.helloService(new UserDemo("yejingtao","123456")));
return sb.toString();
}
}
我們來看效果:


我們服務都沒超時,3個方法全部正常,但是head請求沒有拿到返回值,這個是因為head方式http請求的特性決定的,head不返回response的body體,一般用來做連通性測試來用。
再看一組:
運氣不好head和post請求方法處理時間超過了2000ms,服務降級,實現被fallback處理類取代。


在案例一中我們總有種感覺,服務提供方和服務調用方存在重復的代碼,是否可以進行優(yōu)化?請看案例二。
案例二:
Eureka注冊中心:https://github.com/yejingtao/forblog/tree/master/demo-eureka-register
接口API:https://github.com/yejingtao/forblog/tree/master/demo-feign-serviceapi
服務提供方:https://github.com/yejingtao/forblog/tree/master/demo-feign-serviceimpl
服務調用方:https://github.com/yejingtao/forblog/tree/master/demo-feign-apiconsumer
案例二最大的變動是將服務能力單獨寫到一個API的project中,調用方和提供方pom都依賴這個API。
API:
public interface HelloService {
@RequestMapping(value="/feign-service/serviceGet",method=RequestMethod.GET)
String helloService(@RequestParam("name") String name);
@RequestMapping(value="/feign-service/serviceHead", method=RequestMethod.HEAD)
String helloService(@RequestHeader("name") String name,
@RequestHeader("password") String password);
@RequestMapping(value="/feign-service/servicePost", method=RequestMethod.POST)
String helloService(@RequestBody UserDemo userDemo);
}
服務提供方:
@RestController
public class HelloServiceContorller implements HelloService{
private Logger logger = LoggerFactory.getLogger(this.getClass());
private void sleep(String methodName) {
int sleepMinTime = new Random().nextInt(3000);
logger.info("helloService "+methodName+" sleepMinTime: "+sleepMinTime);
try {
Thread.sleep(sleepMinTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public String helloService(@RequestParam("name") String name) {
sleep("get");
return "HelloServiceImpl name :"+name;
}
@Override
public String helloService(@RequestHeader("name") String name,
@RequestHeader("password") String password) {
sleep("header");
return "helloServiceHead name :"+name +" password:"+password;
}
@Override
public String helloService(@RequestBody UserDemo userDemo) {
sleep("post");
return userDemo.toString();
}
}
服務調用方:
@FeignClient(name="demo-feign-serviceimpl", fallback=FeignServiceFallback.class)
public interface FeignService extends HelloService{
}
其它代碼基本不變,效果也一樣。
兩種風格各有優(yōu)缺點:freestyle的更自由,服務端新增方法不會影響客戶端代碼,缺點是服務能力不同步服務能力的變動會引起異常;API格式服務端客戶端服務能力同步,但是接口的變動需要修改兩邊的代碼,需要構建的時候就要考慮清楚。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
- Spring Cloud Feign簡單使用詳解
- 使用Spring Cloud Feign作為HTTP客戶端調用遠程HTTP服務的方法(推薦)
- 詳解spring cloud Feign使用中遇到的問題總結
- spring cloud feign不支持@RequestBody+ RequestMethod.GET報錯的解決方法
- 詳解springcloud Feign的Hystrix支持
- SpringCloud之Feign示例詳解
- 使用Spring Cloud Feign上傳文件的示例
- spring cloud 之 Feign 使用HTTP請求遠程服務的實現方法
- Spring Cloud中關于Feign的常見問題總結
- 解決Spring Cloud中Feign/Ribbon第一次請求失敗的方法
相關文章
詳解Springboot @Cacheable 注解(指定緩存位置)
這篇文章主要介紹了詳解Springboot @Cacheable 注解(指定緩存位置),使用? @Cacheable ?注解就可以將運行結果緩存,以后查詢相同的數據,直接從緩存中取,不需要調用方法,需要的朋友可以參考下2023-09-09
詳解IDEA中SpringBoot整合Servlet三大組件的過程
這篇文章主要介紹了詳解IDEA中SpringBoot整合Servlet三大組件的過程,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-11-11

