Spring cloud 之熔斷機制

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"作者:Damon","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"博客:","attrs":{}},{"type":"link","attrs":{"href":"http://www.damon8.cn","title":"","type":null},"content":[{"type":"text","text":"http://www.damon8.cn","attrs":{}}]}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"程序猿Damon | 微服務 | 容器化 | 自動化","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"前面講過","attrs":{}},{"type":"link","attrs":{"href":"https://xie.infoq.cn/article/a98e4ecd0d90810dbeba34b58","title":null,"type":null},"content":[{"type":"text","text":"Spring cloud 之多種方式限流","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1e6bb8","name":"user"}},{"type":"strong"}]},{"type":"text","text":"處理請求頻繁的壓力。大家都知道,多個微服務之間調用的時候,假設微服務 A 調用微服務 B 和微服務 C,微服務 B 和微服務 C 有調用其他的微服務,這就是所謂的 扇出,若扇出的鏈路上某個微服務的請求時間過長或者不可用,對微服務 A 的調用就會佔用越來越多的時間以及更多資源,進而引起系統雪崩,即”雪崩效應”。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個時候,需要一個機制來保證當某個微服務出現異常時(請求反應慢或宕機),其整個流程還是闊以友好滴進行下去。即向調用方返回一個符合預期的、可處理的備選響應(FallBack),而不是長時間的等待或者拋出調用方無法處理的異常,這樣就可以保證調用方的線程不會被長時間、無厘頭滴佔用,從而避免了故障在分佈式系統中的蔓延,乃至雪崩。我們把這個機制,或者這種處理方式叫作“熔斷器”。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"熔斷機制是應對雪崩效應的一種微服務鏈路保護機制,當整個鏈路的某個微服務異常時,會進行服務的降級,進而熔斷該節點微服務的調用,快速返回“合理”的響應信息。當檢測到該節點微服務正常後恢復調用鏈路,在Spring cloud 框架機制通過 Hystrix 實現,Hystrix 會監控微服務見調用的狀況,當失敗的調用到一個閾值,默認是5秒內20次調用失敗就會啓動熔斷機制,熔斷機制的註解是 @HystrixCommand。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最近研究了一下 Spring cloud 的熔斷機制,特分享一些代碼,以及實戰中的坑。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在Spring cloud 中,假設有幾個微服務:用戶管理服務、訂單服務、鑑權中心、物流服務等。這時,訂單服務中,某個接口請求用戶管理服務,這個時候如果需要熔斷機制,該怎麼處理呢?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先,訂單服務引入依賴:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"\n\n            org.springframework.cloud\n            spring-cloud-starter-netflix-ribbon\n        \n\n        \n            org.springframework.cloud\n            spring-cloud-starter-netflix-hystrix\n        \n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個時候,訂單服務啓動類中需要引用熔斷註解 @EnableCircuitBreaker,使其生效:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"/**\n * @author Damon\n * @date 2020年1月13日 下午3:23:06\n *\n */\n\n@EnableOAuth2Sso\n@Configuration\n@EnableAutoConfiguration\n@ComponentScan(basePackages = {\"com.damon\"})\n@EnableDiscoveryClient\n@EnableCircuitBreaker\npublic class OrderApp {\n\n    public static void main(String[] args) {\n        SpringApplication.run(OrderApp.class, args);\n    }\n\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏,不要忘記註解 @EnableDiscoveryClient 來相互暴露服務。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最後需要在調用用戶管理服務的函數中,加入註解 @HystrixCommand:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"@HystrixCommand(fallbackMethod = \"admin_service_fallBack\", commandProperties = {\n      @HystrixProperty(name = \"execution.isolation.thread.timeoutInMilliseconds\", value = \"5000\") })//隔離策略:execution.isolation.strategy =SEMAPHORE or THREAD(不配置默認)\n  @Override\n  public Response getUserInfo(HttpServletRequest req, HttpServletResponse res) {\n\n    ResponseEntity forEntity = restTemplate.getForEntity(envConfig.getAdmin_web_url() + \"/api/user/getUserInfo\", String.class);\n    HttpHeaders headers = new HttpHeaders();\n    MediaType type = MediaType.parseMediaType(\"application/json; charset=UTF-8\");\n    headers.setContentType(type);\n    headers.add(\"Accept\", MediaType.APPLICATION_JSON.toString());\n    headers.add(\"Authorization\", \"bearer \" + StrUtil.subAfter(req.getHeader(\"Authorization\"), \"bearer \", false));\n    HttpEntity formEntity = new HttpEntity(null, headers);\n    String body = \"\";\n    try {\n      ResponseEntity responseEntity = restTemplate.exchange(\"http://admin-web-service/api/user/getUserInfo\",\n          HttpMethod.GET, formEntity, String.class);\n      if (responseEntity.getStatusCodeValue() == 200) {\n        logger.debug(String.format(\"request getUserInfo return: {}\", JSON.toJSON(responseEntity.getBody())));\n        return Response.ok(responseEntity.getStatusCodeValue(), 0, \"success\", JSON.toJSON(responseEntity.getBody()));\n      }\n    } catch (Exception e) {\n      logger.error(\"loadJobDetail error\");\n      logger.error(e.getMessage(), e);\n    }\n    return null;\n  }\n\n  /**\n   * 熔斷時調用的方法\n   *\n   * 參數要與被請求的方法的參數一致\n   *\n   * @return\n   */\n  private Response admin_service_fallBack(HttpServletRequest req, HttpServletResponse res) {\n    String token = StrUtil.subAfter(req.getHeader(\"Authorization\"), \"bearer \", false);\n    logger.info(\"admin_service_fallBack token: {}\", token);\n    return Response.ok(200, -2, \"用戶服務掛啦!\", null);\n  }\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其中上面代碼需要注意的是:註解中 fallbackMethod 的值指定了熔斷後的處理函數,這個函數的參數與當前的調用方法的參數需要保持一致,否則報錯:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"com.netflix.hystrix.contrib.javanica.exception.FallbackDefinitionException:fallback method wasn't found.\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最後,配置 Hystrix 相關的參數配置yaml:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"hystrix.command.BackendCall.execution.isolation.thread.timeoutInMilliseconds: 5000\nhystrix.threadpool.BackendCallThread.coreSize: 5\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其中第一個配置在調用函數中其實也可以配置:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"@HystrixCommand(fallbackMethod = \"admin_service_fallBack\", commandProperties = {\n      @HystrixProperty(name = \"execution.isolation.thread.timeoutInMilliseconds\", value = \"3000\") })\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏配置的3000毫秒生效後,如果配置文件中也配置了,則會被覆蓋。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果不加@HystrixCommand中的commandProperties=@HystrixProperty註解配置,下面的FallBack函數admin_service_fallBack()是一個線程;@HystrixCommand()是一個隔離線程。若加上commandProperties=@HystrixProperty註解配置後,將2個線程合併到一個線程裏。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這樣到此爲止,調用方就結束配置了,至於被調用方,相關配置與源碼在","attrs":{}},{"type":"link","attrs":{"href":"http://mp.weixin.qq.com/s?__biz=MzA5NTE1MjY0NQ==&mid=2648831713&idx=1&sn=c2b2000cdb8a2ac351ee54aa784b6b84&chksm=8856e98bbf21609d2b0e513173e040dc6e4259559d61d81b41d906511f508fe1cba2d4c452a7&scene=21#wechat_redirect","title":null,"type":null},"content":[{"type":"text","text":"Spring Cloud Kubernetes之實戰二服務註冊與發現","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1e6bb8","name":"user"}},{"type":"strong"}]},{"type":"text","text":" 一文中,講過被調用服務的相關,這裏的http://admin-web-service 爲被調用服務,則在其服務啓動類中需要註解 @EnableDiscoveryClient:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"package com.damon;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.EnableAutoConfiguration;\nimport org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;\nimport org.springframework.boot.context.properties.EnableConfigurationProperties;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.context.annotation.ComponentScan;\nimport org.springframework.context.annotation.Configuration;\n\nimport com.damon.config.EnvConfig;\n\n/**\n * @author Damon\n * @date 2020年1月13日 下午3:23:06\n *\n */\n\n@EnableOAuth2Sso\n@Configuration\n@EnableAutoConfiguration\n@ComponentScan(basePackages = {\"com.damon\"})\n@EnableConfigurationProperties(EnvConfig.class)\n@EnableDiscoveryClient\npublic class AdminApp {\n\n    public static void main(String[] args) {\n        SpringApplication.run(AdminApp.class, args);\n    }\n\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"另外,配置 RestTemplate 的 Bean 中加上註解 @LoadBalanced 需要作 LB,這樣利用服務名來根據 LB 規則找到對應的其中一個服務,這樣比較明顯看出 LB 的效果:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"package com.damon.config;\n\nimport javax.annotation.Resource;\n\nimport org.springframework.cloud.client.loadbalancer.LoadBalanced;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.env.Environment;\nimport org.springframework.http.client.SimpleClientHttpRequestFactory;\nimport org.springframework.web.client.RestTemplate;\n\n/**\n * @author Damon\n * @date 2018年2月2日 下午7:15:53\n */\n@Configuration\npublic class BeansConfig {\n  @Resource\n  private Environment env;\n\n  @LoadBalanced//就不能用ip等形式來請求其他服務\n  @Bean\n  public RestTemplate restTemplate() {\n    SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();\n    requestFactory.setReadTimeout(env.getProperty(\"client.http.request.readTimeout\", Integer.class, 15000));\n    requestFactory.setConnectTimeout(env.getProperty(\"client.http.request.connectTimeout\", Integer.class, 3000));\n    RestTemplate rt = new RestTemplate(requestFactory);\n    return rt;\n  }\n\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最後如果沒問題了,可以先暫停用戶管理服務,然後運行訂單服務時,返回熔斷結果:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"{\"message\":{\"code\":-2,\"message\":\"用戶服務掛啦!\",\"status\":200}}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"OK,Spring cloud 熔斷實戰就結束了!","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"結束福利","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"開源實戰利用 k8s 作微服務的架構設計代碼:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"https://gitee.com/damon_one/spring-cloud-k8s\nhttps://gitee.com/damon_one/spring-cloud-oauth2\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"歡迎大家 star,多多指教。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"關於作者","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"  ","attrs":{}},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"筆名:Damon,技術愛好者,長期從事 Java 開發、Spring Cloud 的微服務架構設計,以及結合 Docker、K8s 做微服務容器化,自動化部署等一站式項目部署、落地。目前主要從事基於 K8s 雲原生架構研發的工作。Golang 語言開發,長期研究邊緣計算框架 KubeEdge、調度框架 Volcano 等。公衆號 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"程序猿Damon","attrs":{}}],"marks":[{"type":"italic"}],"attrs":{}},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":" 發起人。個人微信 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MrNull008","attrs":{}}],"marks":[{"type":"italic"}],"attrs":{}},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":",個人網站:","attrs":{}},{"type":"link","attrs":{"href":"http://www.damon8.cn","title":"","type":null},"content":[{"type":"text","text":"Damon | Micro-Service | Containerization | DevOps","attrs":{}}],"marks":[{"type":"italic"},{"type":"color","attrs":{"color":"#1e6bb8","name":"user"}},{"type":"strong"}]},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":",歡迎來撩。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"歡迎關注:","attrs":{}},{"type":"link","attrs":{"href":"https://www.infoq.cn/profile/1905020/following/user","title":"InfoQ","type":null},"content":[{"type":"text","text":"InfoQ","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1e6bb8","name":"user"}},{"type":"strong"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"歡迎關注:","attrs":{}},{"type":"link","attrs":{"href":"https://cloud.tencent.com/developer/column/invite/533b1afb","title":"","type":null},"content":[{"type":"text","text":"騰訊自媒體專欄","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1e6bb8","name":"user"}},{"type":"strong"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"精彩推薦","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://www.damon8.cn/2021/05/06/micro-cloud-native/","title":"","type":null},"content":[{"type":"text","text":"實戰 | 雲原生時代的微服務架構","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://www.damon8.cn/2020/07/24/micro-service01/","title":"","type":null},"content":[{"type":"text","text":"淺談微服務安全架構設計","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://www.damon8.cn/2020/07/27/mq-01/","title":"","type":null},"content":[{"type":"text","text":"消息中間件那點事兒","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://xie.infoq.cn/article/fe449751ec689f4d4f97923f1","title":"","type":null},"content":[{"type":"text","text":"Spring Cloud 與 K8s 在微服務層面的不同","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://xie.infoq.cn/article/637299b17ab089a5ad2a2ac8a","title":"","type":null},"content":[{"type":"text","text":"如何利用k8s拉取私有倉庫鏡像","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://xie.infoq.cn/article/a69ef3df1b8de21c906a3a369","title":"","type":null},"content":[{"type":"text","text":"個站建立基礎教程","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://www.damon8.cn/2020/07/23/core-java02/","title":"","type":null},"content":[{"type":"text","text":"淺談 Java 集合 | 底層源碼解析","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://www.damon8.cn/2020/07/23/mysql-norm/","title":"","type":null},"content":[{"type":"text","text":"大佬整理的mysql規範,分享給大家","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://www.damon8.cn/2020/07/14/ci-cd/","title":"","type":null},"content":[{"type":"text","text":"微服務自動化部署CI/CD","attrs":{}}]}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"歡迎關注","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/38/385163791aa296c4cc23be10afa5631d.jpeg","alt":null,"title":"","style":[{"key":"width","value":"25%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/86/860561234baac42a49dac2ea24239882.jpeg","alt":null,"title":"","style":[{"key":"width","value":"25%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/31/31b11ee840215b57487aa0269453e572.jpeg","alt":null,"title":"","style":[{"key":"width","value":"25%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章