Spring Cloud Feign執行個體講解學習,feign執行個體講解

來源:互聯網
上載者:User

Spring Cloud Feign執行個體講解學習,feign執行個體講解

前面博文搭建了一個Eureka+Ribbon+Hystrix的架構,雖然可以基本滿足服務之間的調用,但是代碼看起來實在醜陋,每次用戶端都要寫一個restTemplate,為了讓調用更美觀,可讀性更強,現在我們開始學習使用Feign。

Feign包含了Ribbon和Hystrix,這個在實戰中才慢慢體會到它的意義,所謂的包含並不是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";   } } 

發現這裡的入參裡我故意去掉了@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處理類取代。

在案例一中我們總有種感覺,服務提供者和服務調用方存在重複的代碼,是否可以進行最佳化?請看案例二。

案例二:

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{  } 

其它代碼基本不變,效果也一樣。

兩種風格各有優缺點:freestyle的更自由,服務端新增方法不會影響用戶端代碼,缺點是服務能力不同步服務能力的變動會引起異常;API格式服務端用戶端服務能力同步,但是介面的變動需要修改兩邊的代碼,需要構建的時候就要考慮清楚。

以上就是本文的全部內容,希望對大家的學習有所協助,也希望大家多多支援幫客之家。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.