本項目的筆記和資料的Download,請點擊這一句話自行獲取。html
day01-springboot(理論篇) ;day01-springboot(實踐篇)git
day02-springcloud(理論篇一) ;day02-springcloud(理論篇二) ;day02-springcloud(理論篇三) ;day02-springcloud(理論篇四) ;github
day03-springcloud(Hystix,Feign) ;day03-springcloud(Zuul網關)
spring
Hystix,即熔斷器。json
主頁:https://github.com/Netflix/Hystrix/springboot
Hystix是一個延遲和容錯庫,用於隔離訪問遠程服務、第三方庫,防止出現級聯失敗。mybatis
正常工做的狀況下,客戶端請求調用服務API接口:app
當有服務出現異常時,直接進行失敗回滾,服務降級處理: 負載均衡
當服務繁忙時,若是服務出現異常,不是粗暴的直接報錯,而是返回一個友好的提示,雖然拒絕了用戶的訪問,可是會返回一個結果。dom
這就比如去買魚,日常超市買魚會額外贈送殺魚的服務。等到逢年過節,超時繁忙時,可能就不提供殺魚服務了,這就是服務的降級。
系統特別繁忙時,一些次要服務暫時中斷,優先保證主要服務的暢通,一切資源優先讓給主要服務來使用,在雙11、618時,京東天貓都會採用這樣的策略。
首先在user-consumer中引入Hystix依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
咱們改造user-consumer,添加一個用來訪問的user服務的DAO,而且聲明一個失敗時的回滾處理函數:
@Component public class UserDao { @Autowired private RestTemplate restTemplate; private static final Logger logger = LoggerFactory.getLogger(UserDao.class); @HystrixCommand(fallbackMethod = "queryUserByIdFallback") public User queryUserById(Long id){ long begin = System.currentTimeMillis(); String url = "http://user-service/user/" + id; User user = this.restTemplate.getForObject(url, User.class); long end = System.currentTimeMillis(); // 記錄訪問用時: logger.info("訪問用時:{}", end - begin); return user; } public User queryUserByIdFallback(Long id){ User user = new User(); user.setId(id); user.setName("用戶信息查詢出現異常!"); return user; } }
@HystrixCommand(fallbackMethod="queryUserByIdFallback")
:聲明一個失敗回滾處理函數queryUserByIdFallback,當queryUserById執行超時(默認是1000毫秒),就會執行fallback函數,返回錯誤提示。在原來的業務邏輯中調用這個DAO:
@Service public class UserService { @Autowired private UserDao userDao; public List<User> queryUserByIds(List<Long> ids) { List<User> users = new ArrayList<>(); ids.forEach(id -> { // 咱們測試屢次查詢, users.add(this.userDao.queryUserById(id)); }); return users; } }
改造服務提供者,隨機休眠一段時間,以觸發熔斷:
@Service public class UserService { @Autowired private UserMapper userMapper; public User queryById(Long id) throws InterruptedException { // 爲了演示超時現象,咱們在這裏然線程休眠,時間隨機 0~2000毫秒 Thread.sleep(new Random().nextInt(2000)); return this.userMapper.selectByPrimaryKey(id); } }
而後運行並查看日誌:
id爲九、十、11的訪問時間分別是:
id爲12的訪問時間:
所以,只有12是正常訪問,其它都會觸發熔斷,咱們來查看結果:
雖然熔斷實現了,可是咱們的重試機制彷佛沒有生效,是這樣嗎?
其實這裏是由於咱們的Ribbon超時時間設置的是1000ms:
而Hystix的超時時間默認也是1000ms,所以重試機制沒有被觸發,而是先觸發了熔斷。
因此,Ribbon的超時時間必定要小於Hystix的超時時間。
咱們能夠經過hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds
來設置Hystrix超時時間。
hystrix: command: default: execution: isolation: thread: timeoutInMillisecond: 6000 # 設置hystrix的超時時間爲6000ms
在前面的學習中,咱們使用了Ribbon的負載均衡功能,大大簡化了遠程調用時的代碼:
String baseUrl = "http://user-service/user/"; User user = this.restTemplate.getForObject(baseUrl + id, User.class)
若是就學到這裏,你可能之後須要編寫相似的大量重複代碼,格式基本相同,無非參數不同。有沒有更優雅的方式,來對這些代碼再次優化呢?
這就是咱們接下來要學的Feign的功能了。
有道詞典的英文解釋:
爲何叫假裝?
Feign能夠把Rest的請求進行隱藏,假裝成相似SpringMVC的Controller同樣。你不用再本身拼接url,拼接參數等等操做,一切都交給Feign去作。
項目主頁:https://github.com/OpenFeign/feign
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
@FeignClient("user-service") public interface UserFeignClient { @GetMapping("/user/{id}") User queryUserById(@PathVariable("id") Long id); }
@FeignClient
,聲明這是一個Feign客戶端,相似@Mapper
註解。同時經過value
屬性指定服務名稱改造原來的調用邏輯,再也不調用UserDao:
@Service public class UserService { @Autowired private UserFeignClient userFeignClient; public List<User> queryUserByIds(List<Long> ids) { List<User> users = new ArrayList<>(); ids.forEach(id -> { // 咱們測試屢次查詢, users.add(this.userFeignClient.queryUserById(id)); }); return users; } }
咱們在啓動類上,添加註解,開啓Feign功能
@SpringBootApplication @EnableDiscoveryClient @EnableHystrix @EnableFeignClients // 開啓Feign功能 public class UserConsumerDemoApplication { public static void main(String[] args) { SpringApplication.run(UserConsumerDemoApplication.class, args); } }
訪問接口:
Feign中自己已經集成了Ribbon依賴和自動配置:
所以咱們不須要額外引入依賴,也不須要再註冊RestTemplate
對象。
另外,咱們能夠像上節課中講的那樣去配置Ribbon,能夠經過ribbon.xx
來進行全局配置。也能夠經過服務名.ribbon.xx
來對指定服務配置:
user-service: ribbon: ConnectTimeout: 250 # 鏈接超時時間(ms) ReadTimeout: 1000 # 通訊超時時間(ms) OkToRetryOnAllOperations: true # 是否對全部操做重試 MaxAutoRetriesNextServer: 1 # 同一服務不一樣實例的重試次數 MaxAutoRetries: 1 # 同一實例的重試次數
Feign默認也有對Hystix的集成:
只不過,默認狀況下是關閉的。咱們須要經過下面的參數來開啓:
feign: hystrix: enabled: true # 開啓Feign的熔斷功能
可是,Feign中的Fallback配置不像Ribbon中那樣簡單了。
1)首先,咱們要定義一個類,實現剛纔編寫的UserFeignClient(接口),做爲fallback的處理類。
@Component public class UserFeignClientFallback implements UserFeignClient { @Override public User queryUserById(Long id) { User user = new User(); user.setId(id); user.setName("用戶查詢出現異常!"); return user; } }
2)而後在UserFeignClient中,指定剛纔編寫的實現類
@FeignClient(value = "user-service", fallback = UserFeignClientFallback.class) public interface UserFeignClient { @GetMapping("/user/{id}") User queryUserById(@PathVariable("id") Long id); }
3)重啓測試:
咱們關閉user-service服務,而後在頁面訪問:
Spring Cloud Feign 支持對請求和響應進行GZIP壓縮,以減小通訊過程當中的性能損耗。經過下面的參數便可開啓請求與響應的壓縮功能:
feign: compression: request: enabled: true # 開啓請求壓縮 response: enabled: true # 開啓響應壓縮
同時,咱們也能夠對請求的數據類型,以及觸發壓縮的大小下限進行設置:
feign: compression: request: enabled: true # 開啓請求壓縮 mime-types: text/html,application/xml,application/json # 設置壓縮的數據類型 min-request-size: 2048 # 設置觸發壓縮的大小下限
注:上面的數據類型、壓縮大小下限均爲默認值。
前面講過,經過logging.level.xx=debug
來設置日誌級別。然而這個對Fegin客戶端而言不會產生效果。由於@FeignClient
註解修改的客戶端在被代理時,都會建立一個新的Fegin.Logger實例。咱們須要額外指定這個日誌的級別才能夠。
1)設置com.leyou包下的日誌級別都爲debug
logging: level: com.leyou: debug
2)編寫配置類,定義日誌級別
@Configuration public class FeignConfig { @Bean Logger.Level feignLoggerLevel(){ return Logger.Level.FULL; } }
這裏指定的Level級別是FULL,Feign支持4種級別:
3)在FeignClient中指定配置類:
@FeignClient(value = "user-service", fallback = UserFeignClientFallback.class, configuration = FeignConfig.class) public interface UserFeignClient { @GetMapping("/user/{id}") User queryUserById(@PathVariable("id") Long id); }
4)重啓項目,便可看到每次訪問的日誌:
=============================================
參考資料:
Spring Cloud升級最新Finchley版本的全部坑
end