1. 程式人生 > >從零開始學spring cloud(十) -------- hystrix簡單代碼示例

從零開始學spring cloud(十) -------- hystrix簡單代碼示例

nal end 依賴 exc stat http per level start

一、官網文檔閱讀

技術分享圖片

較低級別的服務中的服務故障可能導致級聯故障一直到用戶。 當對特定服務的調用超過circuitBreaker.requestVolumeThreshold(默認值:20個請求)且失敗百分比大於circuit.rolllingStats.timeInMilliseconds定義的滾動窗口中的circuitBreaker.errorThresholdPercentage(默認值:> 50%)時(默認值:10秒) ,電路打開,沒有撥打電話。 在出現錯誤和開路的情況下,開發人員可以提供回退。

二、示例

添加hystrix依賴

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>

修改movie服務的controller

 1 package com.zwjk.cloud.controller;
 2 
 3 import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
 4 import com.zwjk.cloud.entity.User;
 5 import org.springframework.beans.factory.annotation.Autowired;
 6 import org.springframework.beans.factory.annotation.Value;
7 import org.springframework.cloud.client.ServiceInstance; 8 import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; 9 import org.springframework.web.bind.annotation.GetMapping; 10 import org.springframework.web.bind.annotation.PathVariable; 11 import org.springframework.web.bind.annotation.RestController;
12 import org.springframework.web.client.RestTemplate; 13 14 /** 15 * @author : Jixiaohu 16 * @Date : 2019-04-11. 17 * @Time : 9:38. 18 * @Description : 19 */ 20 @RestController 21 public class MovieController { 22 23 @Autowired 24 private RestTemplate restTemplate; 25 26 @Value("${user.userServicePath}") 27 private String userServicePath; 28 29 @GetMapping("/movie/{id}") 30 @HystrixCommand(fallbackMethod = "findByIdFallback") 31 public User findById(@PathVariable Long id) { 32 return this.restTemplate.getForObject(this.userServicePath + id, User.class); 33 } 34 35 36 public User findByIdFallback(Long id) { 37 User user = new User(); 38 user.setId(0L); 39 user.setName("失敗"); 40 return user; 41 } 42 }

這邊需要註意的是,fallbackMethod方法的返回值和入參,必須和原方法一致,下面進行測試:

技術分享圖片

可以看到,這裏返回了正常的結果,下面停掉user服務

技術分享圖片

可以看到,這裏返回的結果,就是fallbackMethod裏面返回的內容,hystrix就起了作用。

下面,繼續看一下

Feign Hystrix Support

參考官網使用示例:

 1 @FeignClient(name = "hello", fallback = HystrixClientFallback.class)
 2 protected interface HystrixClient {
 3     @RequestMapping(method = RequestMethod.GET, value = "/hello")
 4     Hello iFailSometimes();
 5 }
 6 
 7 static class HystrixClientFallback implements HystrixClient {
 8     @Override
 9     public Hello iFailSometimes() {
10         return new Hello("fallback");
11     }
12 }

在配置文件中,增加開啟feign使用斷路器

feign:
  hystrix:
    enabled: true

按照示例,我們在movie服務中,新增UserFeignClientFallback類

 1 package com.zwjk.cloud.fegin;
 2 
 3 import com.zwjk.cloud.entity.User;
 4 import org.springframework.stereotype.Component;
 5 
 6 /**
 7  * @author : Jixiaohu
 8  * @Date : 2019-04-17.
 9  * @Time : 18:20.
10  * @Description :
11  */
12 @Component
13 public class UserFeignClientFallback implements UserFeignClient {
14     @Override
15     public User findById(Long id) {
16         User user = new User();
17         user.setId(0L);
18         return user;
19     }
20 }

在fegin上增加相應的註解:

 1 package com.zwjk.cloud.fegin;
 2 
 3 import com.zwjk.cloud.entity.User;
 4 import org.springframework.cloud.openfeign.FeignClient;
 5 import org.springframework.web.bind.annotation.GetMapping;
 6 import org.springframework.web.bind.annotation.PathVariable;
 7 import org.springframework.web.bind.annotation.PostMapping;
 8 import org.springframework.web.bind.annotation.RequestBody;
 9 
10 /**
11  * @author : Jixiaohu
12  * @Date : 2019-04-12.
13  * @Time : 16:50.
14  * @Description :
15  */
16 @FeignClient(name = "microservice-provider-user", fallback = UserFeignClientFallback.class)
17 public interface UserFeignClient {
18     //@PathVariable得設置value
19     @GetMapping("/simple/{id}")
20     User findById(@PathVariable("id") Long id); //@PathVariable得設置value
21 }

啟動項目,進行測試,

技術分享圖片

同樣的,停掉user服務,再次訪問這個地址:

技術分享圖片

就可以實現熔斷服務

當一個項目中,有個feign時,如何禁用一些feign的hystrix?

查看一下官網文檔

技術分享圖片

通過配置加上feignBuilder,就可以禁用指定fegin的hystrix

查看一下代碼實現:

 1 package com.zwjk.cloud.controller;
 2 
 3 import com.zwjk.cloud.entity.User;
 4 import com.zwjk.cloud.fegin.UserFeignClient2;
 5 import com.zwjk.cloud.fegin.UserFeignClient;
 6 import org.springframework.beans.factory.annotation.Autowired;
 7 import org.springframework.web.bind.annotation.GetMapping;
 8 import org.springframework.web.bind.annotation.PathVariable;
 9 import org.springframework.web.bind.annotation.RestController;
10 
11 /**
12  * @author : Jixiaohu
13  * @Date : 2019-04-11.
14  * @Time : 9:38.
15  * @Description :
16  */
17 @RestController
18 public class MovieController {
19 
20     @Autowired
21     private UserFeignClient userFeignClient;
22 
23     @Autowired
24     private UserFeignClient2 userFeignClient2;
25 
26     @GetMapping("/movie/{id}")
27     public User findById(@PathVariable Long id) {
28         return this.userFeignClient.findById(id);
29     }
30 
31     @GetMapping("/{serviceName}")
32     public String findServiceInfoFromEurekaByServiceName(@PathVariable String serviceName) {
33         return this.userFeignClient2.findServiceInfoFromEurekaByServiceName(serviceName);
34     }
35 
36 
37 }

兩個fegin:

 1 package com.zwjk.cloud.fegin;
 2 
 3 import com.zwjk.cloud.entity.User;
 4 import com.zwjk.config.UserConfiguration;
 5 import feign.Param;
 6 import feign.RequestLine;
 7 import org.springframework.cloud.openfeign.FeignClient;
 8 
 9 /**
10  * @author : Jixiaohu
11  * @Date : 2019-04-12.
12  * @Time : 16:50.
13  * @Description :
14  */
15 @FeignClient(name = "microservice-provider-user", configuration = UserConfiguration.class, fallback =
16     UserFeignClientFallback.class)
17 public interface UserFeignClient {
18     //@PathVariable得設置value
19     @RequestLine("GET /simple/{id}")
20     User findById(@Param("id") Long id); //@PathVariable得設置value
21 
22 }

 1 package com.zwjk.cloud.fegin;
 2 
 3 import com.zwjk.config.UserConfiguration2;
 4 import org.springframework.cloud.openfeign.FeignClient;
 5 import org.springframework.web.bind.annotation.PathVariable;
 6 import org.springframework.web.bind.annotation.RequestMapping;
 7 
 8 /**
 9  * @author : Jixiaohu
10  * @Date : 2019-04-13.
11  * @Time : 11:23.
12  * @Description :
13  */
14 @FeignClient(name = "xxxx", url = "http://192.168.1.114:8761/", configuration = UserConfiguration2.class)
15 public interface UserFeignClient2 {
16     @RequestMapping(value = "/eureka/apps/{serviceName}")
17     public String findServiceInfoFromEurekaByServiceName(@PathVariable("serviceName") String serviceName);
18 }

兩個fallback

 1 package com.zwjk.cloud.fegin;
 2 
 3 import org.springframework.stereotype.Component;
 4 
 5 /**
 6  * @author : Jixiaohu
 7  * @Date : 2019-04-17.
 8  * @Time : 18:20.
 9  * @Description :
10  */
11 @Component
12 public class UserFeignClient2Fallback implements UserFeignClient2 {
13 
14     @Override
15     public String findServiceInfoFromEurekaByServiceName(String serviceName) {
16         return "hahahha";
17     }
18 }
 1 package com.zwjk.cloud.fegin;
 2 
 3 import com.zwjk.cloud.entity.User;
 4 import org.springframework.stereotype.Component;
 5 
 6 /**
 7  * @author : Jixiaohu
 8  * @Date : 2019-04-17.
 9  * @Time : 18:20.
10  * @Description :
11  */
12 @Component
13 public class UserFeignClientFallback implements UserFeignClient {
14     @Override
15     public User findById(Long id) {
16         User user = new User();
17         user.setId(0L);
18         return user;
19     }
20 }

看一下兩個配置文件

 1 package com.zwjk.config;
 2 
 3 import feign.Contract;
 4 import feign.Logger;
 5 import org.springframework.context.annotation.Bean;
 6 import org.springframework.context.annotation.Configuration;
 7 
 8 /**
 9  * @author : Jixiaohu
10  * @Date : 2019-04-13.
11  * @Time : 10:20.
12  * @Description :
13  */
14 @Configuration
15 public class UserConfiguration {
16 
17     @Bean
18     public Contract feignContract() {
19         return new feign.Contract.Default();
20     }
21 
22     @Bean
23     Logger.Level feignLoggerLevel() {
24         return Logger.Level.FULL;
25     }
26 }
 1 package com.zwjk.config;
 2 
 3 import feign.Feign;
 4 import feign.auth.BasicAuthRequestInterceptor;
 5 import org.springframework.context.annotation.Bean;
 6 import org.springframework.context.annotation.Configuration;
 7 import org.springframework.context.annotation.Scope;
 8 
 9 /**
10  * @author : Jixiaohu
11  * @Date : 2019-04-13.
12  * @Time : 11:25.
13  * @Description :
14  */
15 @Configuration
16 public class UserConfiguration2 {
17     @Bean
18     public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
19         return new BasicAuthRequestInterceptor("user", "password123");
20     }
21 
22     @Configuration
23     public class FooConfiguration {
24         @Bean
25         @Scope("prototype")
26         public Feign.Builder feignBuilder() {
27             return Feign.builder();
28         }
29     }
30 }

啟動服務,進行測試

技術分享圖片

技術分享圖片

下面,我們停掉eureka,和user服務,在一次查看返回結果

啟用hystrix的返回結果如下:

技術分享圖片

沒有啟用hystrix的返回結果,提示500錯誤

技術分享圖片

從中,可以看出,hytrix的熔斷,可以有很細的粒度。

如果想要查看fallback回退的原因,可以使用可以使用@FeignClient中的fallbackFactory屬性。

看一下如何實現?

參考一下官網文檔:

技術分享圖片

先寫一個hystrixFactory:

 1 package com.zwjk.cloud.fegin;
 2 
 3 import com.zwjk.cloud.entity.User;
 4 import feign.hystrix.FallbackFactory;
 5 import org.slf4j.Logger;
 6 import org.slf4j.LoggerFactory;
 7 import org.springframework.stereotype.Component;
 8 
 9 /**
10  * @author : Jixiaohu
11  * @Date : 2019-04-17.
12  * @Time : 19:37.
13  * @Description :
14  */
15 @Component
16 public class UserFeginClientFactory implements FallbackFactory<UserFeignClient> {
17 
18     private static final Logger Log = LoggerFactory.getLogger(UserFeginClientFactory.class);
19 
20     @Override
21     public UserFeignClient create(Throwable cause) {
22         UserFeginClientFactory.Log.info("fallback:reason was : {}", cause.getMessage());
23         return id -> {
24             User user = new User();
25             user.setId(-1L);
26             user.setName("haha");
27             return user;
28         };
29     }
30 }

修改一下原feignClient

 1 package com.zwjk.cloud.fegin;
 2 
 3 import com.zwjk.cloud.entity.User;
 4 import org.springframework.cloud.openfeign.FeignClient;
 5 import org.springframework.web.bind.annotation.GetMapping;
 6 import org.springframework.web.bind.annotation.PathVariable;
 7 import org.springframework.web.bind.annotation.PostMapping;
 8 import org.springframework.web.bind.annotation.RequestBody;
 9 
10 /**
11  * @author : Jixiaohu
12  * @Date : 2019-04-12.
13  * @Time : 16:50.
14  * @Description :
15  */
16 @FeignClient(name = "microservice-provider-user", fallbackFactory =
17     UserFeginClientFactory.class)
18 public interface UserFeignClient {
19     //@PathVariable得設置value
20     @GetMapping("/simple/{id}")
21     User findById(@PathVariable("id") Long id); //@PathVariable得設置value
22 }

啟動user服務,movie服務,先正常訪問:

然後關閉user服務,會發現控制臺開始打印日誌:

技術分享圖片

這是為什麽呢?因為此時,斷路器並沒有打開,實際的原因是TimeoutException

技術分享圖片

當斷路器打開時,就會答應異常的信息。

從零開始學spring cloud(十) -------- hystrix簡單代碼示例