springcloud feign遠端呼叫熔斷詳解
最近學習springcloud搭建微服務,各個模組單元之間要互相進行呼叫。博主原有是通過httpclient的方式進行呼叫,但是不得不每次都需要暴露url進行呼叫,feign提供本地呼叫的方式,不需要暴露url,t提供類似本地呼叫實現,並且配合hystrix熔斷策略進行使用。
1.maven新增包引用
2.配置檔案需要填寫eureka等相關配置檔案<!--新增feign 提供服務呼叫--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency> <!-- ApacheHttpClient 實現這個類,替換httpclient執行緒池--> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-httpclient</artifactId> <version>9.5.0</version> </dependency> <!-- 新增熔斷器,提供服務熔斷操作 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency>
spring-cloud的版本有Brixton.RELEASE和Dalston.RELEASE,現在建立的新專案應該是引用Dalston.RELEASE這個版本,該版本下預設hystrix是關閉的,所以需要在屬性檔案中進行開啟,如果沒有設定為true,在後面的除錯過程會發現熔斷機制不生效<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies>
#開啟hystrix熔斷機制
feign.hystrix.enabled=true
feign簡單呼叫,如以下例項,假如現在在同一個eureka下有一個專案A需要呼叫專案message-api專案,而message-api專案有一個介面為/example/hello,那麼在A專案做以下配置:
1.建立遠端呼叫客戶端
@FeignClient(value = "message-api")
public interface MessageApiFeignClient {
@RequestMapping(value = "/example/hello",method = RequestMethod.GET)
public String getMessage(@RequestParam("name") String name);
}
本地方法直接呼叫該介面
@RequestMapping(value="/hello3",method = RequestMethod.GET)
public void hello3(String name){
String result = messageApiFeignClient.getMessage("zhangsan");
System.out.println(result);
}
執行起來會發現成功呼叫。
通常feign和hystrix一起配合使用的,什麼是hystrix,hystrix是分散式系統互相呼叫超時處理和容錯的機制。例如著名的雪崩,通常A->B->C互相依次呼叫,但是很可能C專案出現問題,流量過大或者報錯等,導致執行緒一直進行等待,導致拖垮整個鏈路層,執行緒資源耗盡。
工作原理:
1.隔離機制:
執行緒隔離:hystrix為每個依賴呼叫分配一個小的執行緒池,如果執行緒池已滿呼叫將被立即拒絕,預設不採用排隊,加速判定時間
訊號隔離:訊號隔離也可以限制併發訪問,防止阻塞擴散,與執行緒隔離最大不同在於執行依賴程式碼的執行緒依然是請求執行緒(該執行緒需要通過訊號申請,如果客戶端是可信的且可以快速返回,可以使用訊號隔離),springcloud zuul就是採用的是訊號隔離的方式
2.熔斷:
如果某個目標服務呼叫慢或者大量超時,此時,熔斷該服務的呼叫,對於後續呼叫請求,不在繼續呼叫目標服務,直接返回,快速釋放資源。如果目標服務情況好轉則恢復呼叫
以下通過程式碼的方式進行詳細講解
hystrix通過設定fallback和fallbackFactory屬性觸發請求容災降級
1.fallback
首先設定介面服務方一個簡單的方法
@RestController
@RequestMapping("/example")
public class ExampleController {
public static final Logger logger = LoggerFactory.getLogger(ExampleController.class);
@RequestMapping(value = "/hello")
@ResponseBody
public String helloFeign(String name) throws Exception {
logger.info("進入服務端:"+name);
if("zhangsan".equals(name)){
int i = 1/0;
}else if("lisi".equals(name)){
throw new Exception("服務端報錯");
}else{
}
return "hello!"+name;
}
}
在feignclient上新增fallback
@FeignClient(value = "message-api",fallback = MessageApiFailBack.class)
public interface MessageApiFeignClient {
@RequestMapping(value = "/example/hello",method = RequestMethod.GET)
public String getMessage(@RequestParam("name") String name);
}
定義MessageApiFeignClient類
@Component
public class MessageApiFailBack implements MessageApiFeignClient{
@Override
public String getMessage(String name) {
return "出錯啦!";
}
}
呼叫方進行本地呼叫
messageApiFeignClient.getMessage("zhangsan");
執行後服務呼叫方列印:出錯啦!
服務提供方列印:java.lang.ArithmeticException: / by zero
2.fallbackFactory與fallback不同的是可以列印詳細的錯誤資訊
將原來的fallback替換成fallbackFactory
@FeignClient(value = "message-api",fallbackFactory = MessageApiFailFactory.class)
public interface MessageApiFeignClient {
@RequestMapping(value = "/example/hello",method = RequestMethod.GET)
public String getMessage(@RequestParam("name") String name);
}
public interface MessageApiFeignFallBackFactoryClient extends MessageApiFeignClient{
}
@Component
public class MessageApiFailFactory implements FallbackFactory<MessageApiFeignClient> {
public static final Logger logger = LoggerFactory.getLogger(MessageApiFailFactory.class);
@Override
public MessageApiFeignClient create(Throwable throwable) {
logger.info("fallback; reason was: {}",throwable.getMessage());
return new MessageApiFeignFallBackFactoryClient(){
@Override
public String getMessage(String name) {
return "錯誤原因:"+throwable.getMessage();
}
};
}
}
同樣進行本地呼叫
messageApiFeignClient.getMessage("zhangsan");
執行後服務呼叫方列印:錯誤原因:status 500 reading MessageApiFeignClient#getMessage(String); content:
{"timestamp":1508681203932,"status":500,"error":"Internal Server Error","exception":"java.lang.ArithmeticException","message":"/ by zero","path":"/example/hello"}
另外配置檔案下可以設定hystrix服務超時機制
#開啟hystrix請求超時機制 也可以設定成永久不超時hystrix.command.default.execution.timeout.enabled=false
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=60000