第四章 服務容錯 - 引入hystrix

来源:http://www.cnblogs.com/java-zhao/archive/2016/06/17/5595157.html
-Advertisement-
Play Games

上一節,描述了服務發現、負載均衡以及服務之間的調用。到這裡,加上第二節的服務註冊,整個微服務的架構就已經搭建出來了,即功能性需求就完成了。從本節開始的記錄其實全部都是非功能性需求。 一、集群容錯 技術選型:hystrix。(就是上圖中熔斷器) 熔斷的作用: 第一個作用: 假設有兩台伺服器server ...


上一節,描述了服務發現、負載均衡以及服務之間的調用。到這裡,加上第二節的服務註冊,整個微服務的架構就已經搭建出來了,即功能性需求就完成了。從本節開始的記錄其實全部都是非功能性需求。

一、集群容錯

技術選型:hystrix。(就是上圖中熔斷器

熔斷的作用

第一個作用:

假設有兩台伺服器server1(假設可以處理的請求閾值是1W請求)和server2,在server1上註冊了三個服務service1、service2、service3,在server2上註冊了一個服務service4,假設service4服務響應緩慢,service1調用service4時,一直在等待響應,那麼在高併發下,很快的server1處很快就會達到請求閾值(server1很快就會耗盡處理線程)之後可能宕機,這時候,不只是service1不再可用,server1上的service2和service3也不可用了。

如果我們引入了hystrix,那麼service1調用service4的時候,當發現service4超時,立即斷掉不再執行,執行getFallback邏輯。這樣的話,server1就不會耗盡處理線程,server1上的其他服務也是可用的。當然,這是在合理的配置了超時時間的情況下,如果超時時間設置的太長的話,還是會出現未引入hystrix之前的情況。

第二個作用:

當被調服務經常失敗,比如說在10min(可配)中之內調用了20次,失敗了15次(可配),那麼我們認為這個服務是失敗的,先關閉該服務,等一會兒後再自動重新啟動該服務!(這是真正的熔斷!)

二、實現

由於代碼變動比較大,我會列出全部關鍵代碼。

1、framework

1.1、pom.xml

  1 <?xml version="1.0" encoding="UTF-8"?>
  2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  4 
  5     <modelVersion>4.0.0</modelVersion>
  6 
  7     <parent>
  8         <groupId>org.springframework.boot</groupId>
  9         <artifactId>spring-boot-starter-parent</artifactId>
 10         <version>1.3.0.RELEASE</version>
 11     </parent>
 12 
 13     <groupId>com.microservice</groupId>
 14     <artifactId>framework</artifactId>
 15     <version>1.0-SNAPSHOT</version>
 16 
 17     <properties>
 18         <java.version>1.8</java.version><!-- 官方推薦 -->
 19     </properties>
 20 
 21     <!-- 引入實際依賴 -->
 22     <dependencies>
 23         <dependency>
 24             <groupId>org.springframework.boot</groupId>
 25             <artifactId>spring-boot-starter-web</artifactId>
 26         </dependency>
 27         <!-- consul-client -->
 28         <dependency>
 29             <groupId>com.orbitz.consul</groupId>
 30             <artifactId>consul-client</artifactId>
 31             <version>0.10.0</version>
 32         </dependency>
 33         <!-- consul需要的包 -->
 34         <dependency>
 35             <groupId>org.glassfish.jersey.core</groupId>
 36             <artifactId>jersey-client</artifactId>
 37             <version>2.22.2</version>
 38         </dependency>
 39         <dependency>
 40             <groupId>com.alibaba</groupId>
 41             <artifactId>fastjson</artifactId>
 42             <version>1.1.15</version>
 43         </dependency>
 44         <!-- 引入監控工具,包含health檢查(用於consul註冊) -->
 45         <dependency>
 46             <groupId>org.springframework.boot</groupId>
 47             <artifactId>spring-boot-starter-actuator</artifactId>
 48         </dependency>
 49         <!-- 引入lombok,簡化pojo -->
 50         <dependency>
 51             <groupId>org.projectlombok</groupId>
 52             <artifactId>lombok</artifactId>
 53             <version>1.16.8</version>
 54         </dependency>
 55         <!-- 引入swagger2 -->
 56         <dependency>
 57             <groupId>io.springfox</groupId>
 58             <artifactId>springfox-swagger2</artifactId>
 59             <version>2.2.2</version>
 60         </dependency>
 61         <dependency>
 62             <groupId>io.springfox</groupId>
 63             <artifactId>springfox-swagger-ui</artifactId>
 64             <version>2.2.2</version>
 65         </dependency>
 66         <!-- retrofit -->
 67         <dependency>
 68             <groupId>com.squareup.retrofit</groupId>
 69             <artifactId>retrofit</artifactId>
 70             <version>1.9.0</version>
 71         </dependency>
 72         <!-- converter-jackson -->
 73         <dependency>
 74             <groupId>com.squareup.retrofit</groupId>
 75             <artifactId>converter-jackson</artifactId>
 76             <version>1.9.0</version>
 77         </dependency>
 78         <!-- okhttp -->
 79         <dependency>
 80             <groupId>com.squareup.okhttp</groupId>
 81             <artifactId>okhttp</artifactId>
 82             <version>2.4.0</version>
 83         </dependency>
 84         <!-- hystrix -->
 85         <dependency>
 86             <groupId>com.netflix.hystrix</groupId>
 87             <artifactId>hystrix-core</artifactId>
 88             <version>1.5.3</version>
 89         </dependency>
 90         <dependency>
 91             <groupId>com.netflix.hystrix</groupId>
 92             <artifactId>hystrix-metrics-event-stream</artifactId>
 93             <version>1.5.3</version>
 94         </dependency>
 95     </dependencies>
 96 
 97     <build>
 98         <plugins>
 99             <plugin>
100                 <groupId>org.springframework.boot</groupId>
101                 <artifactId>spring-boot-maven-plugin</artifactId>
102             </plugin>
103         </plugins>
104     </build>
105 </project>
View Code

1.2、啟動類

 1 package com.microservice;
 2 
 3 import org.springframework.boot.SpringApplication;
 4 import org.springframework.boot.autoconfigure.SpringBootApplication;
 5 
 6 import com.microservice.consul.ConsulRegisterListener;
 7 
 8 import springfox.documentation.swagger2.annotations.EnableSwagger2;
 9 
10 /**
11  * 註意:@SpringBootApplication該註解必須在SpringApplication.run()所在的類上
12  */
13 @SpringBootApplication
14 @EnableSwagger2
15 public class MySpringAplication {
16 
17     public void run(String[] args) {
18         SpringApplication sa = new SpringApplication(MySpringAplication.class);
19         sa.addListeners(new ConsulRegisterListener());
20         sa.run(args);
21     }
22 
23     public static void main(String[] args) {
24     }
25 }
View Code

1.3、服務註冊(consul包)

1.3.1、ConsulProperties

 1 package com.microservice.consul;
 2 
 3 import org.springframework.beans.factory.annotation.Value;
 4 import org.springframework.stereotype.Component;
 5 
 6 import lombok.Getter;
 7 import lombok.Setter;
 8 
 9 @Component
10 @Getter @Setter
11 public class ConsulProperties {
12 
13     @Value("${service.name}")
14     private String servicename;
15     @Value("${service.port:8080}")
16     private int servicePort;
17     @Value("${service.tag:dev}")
18     private String serviceTag;
19     @Value("${health.url}")
20     private String healthUrl;
21     @Value("${health.interval:10}")
22     private int healthInterval;
23     
24 }
View Code

1.3.2、ConsulConfig

 1 package com.microservice.consul;
 2 
 3 import org.springframework.context.annotation.Bean;
 4 import org.springframework.context.annotation.Configuration;
 5 
 6 import com.orbitz.consul.Consul;
 7 
 8 @Configuration
 9 public class ConsulConfig {
10 
11     @Bean
12     public Consul consul(){
13         return Consul.builder().build();
14     }
15 }
View Code

1.3.3、ConsulRegisterListener

 1 package com.microservice.consul;
 2 
 3 import java.net.MalformedURLException;
 4 import java.net.URI;
 5 
 6 import org.springframework.context.ApplicationListener;
 7 import org.springframework.context.event.ContextRefreshedEvent;
 8 
 9 import com.orbitz.consul.AgentClient;
10 import com.orbitz.consul.Consul;
11 
12 /**
13  * 監聽contextrefresh事件
14  */
15 public class ConsulRegisterListener implements ApplicationListener<ContextRefreshedEvent> {
16 
17     @Override
18     public void onApplicationEvent(ContextRefreshedEvent event) {
19         Consul consul = event.getApplicationContext().getBean(Consul.class);
20         ConsulProperties prop = event.getApplicationContext().getBean(ConsulProperties.class);
21 
22         AgentClient agentClient = consul.agentClient();
23         try {
24             agentClient.register(prop.getServicePort(), 
25                                  URI.create(prop.getHealthUrl()).toURL(),
26                                  prop.getHealthInterval(), 
27                                  prop.getServicename(), 
28                                  prop.getServicename(), // serviceId:
29                                  prop.getServiceTag());
30         } catch (MalformedURLException e) {
31             e.printStackTrace();
32         }
33     }
34 
35 }
View Code

1.4、服務發現+負載均衡(loadBalance包)

1.4.1、ServerAddress

 1 package com.microservice.loadBalancer;
 2 
 3 import lombok.AllArgsConstructor;
 4 import lombok.Getter;
 5 import lombok.Setter;
 6 
 7 /**
 8  * 這裡只做簡單的封裝,如果需要複雜的,可以使用java.net.InetAddress類
 9  */
10 @Getter @Setter
11 @AllArgsConstructor
12 public class ServerAddress {
13     private String ip;
14     private int port;
15 }
View Code

1.4.2、MyLoadBalancer

 1 package com.microservice.loadBalancer;
 2 
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 import java.util.Random;
 6 
 7 import org.springframework.beans.factory.annotation.Autowired;
 8 import org.springframework.stereotype.Component;
 9 
10 import com.orbitz.consul.Consul;
11 import com.orbitz.consul.HealthClient;
12 import com.orbitz.consul.model.health.ServiceHealth;
13 
14 /**
15  * 實現思路:
16  * 1、拉取可用服務列表(服務發現)serverList
17  * 2、緩存到本地guava cache中去,以後每隔10min從consulServer拉取一次(這裡這樣做的原因,是因為consul沒有做這樣的事)
18  * 3、使用配置好的路由演算法選出其中1台,執行邏輯
19  */
20 @Component
21 public class MyLoadBalancer {
22     
23     @Autowired
24     private Consul consul;
25     
26     /**
27      * 獲取被調服務的服務列表
28      * @param serviceName 被調服務
29      */
30     public List<ServerAddress> getAvailableServerList(String serviceName){
31         List<ServerAddress> availableServerList = new ArrayList<>();
32         HealthClient healthClient = consul.healthClient();//獲取Health http client
33         List<ServiceHealth> availableServers = healthClient.getHealthyServiceInstances(serviceName).getResponse();//從本地agent查找所有可用節點
34         availableServers.forEach(x->availableServerList.add(new ServerAddress(x.getNode().getAddress(), x.getService().getPort())));
35         return availableServerList;
36     } 
37     
38     /**
39      * 選擇一臺伺服器
40      * 這裡使用隨機演算法,如果需要換演算法,我們可以抽取介面進行編寫
41      */
42     public ServerAddress chooseServer(String serviceName){
43         List<ServerAddress> servers = getAvailableServerList(serviceName);
44         Random random = new Random();
45         int index = random.nextInt(servers.size());
46         return servers.get(index);
47     }
48     
49 }
View Code

以上代碼均與第三節一樣。這的負載均衡之後會用ribbon來做。

1.5、服務通信(retrofit)+集群容錯(hystrix)

註意:這裡我先給出代碼,最後我會好好的說一下調用流程。

1.5.1、RestAdapterConfig

 1 package com.microservice.retrofit;
 2 
 3 import org.springframework.beans.factory.annotation.Autowired;
 4 import org.springframework.stereotype.Component;
 5 
 6 import com.microservice.loadBalancer.MyLoadBalancer;
 7 import com.microservice.loadBalancer.ServerAddress;
 8 
 9 import retrofit.RestAdapter;
10 import retrofit.converter.JacksonConverter;
11 
12 @Component
13 public class RestAdapterConfig {
14 
15     @Autowired
16     private MyLoadBalancer myLoadBalancer;
17 
18     /**
19      * 負載均衡並且創建傳入的API介面實例
20      */
21     public <T> T create(Class<T> tclass, String serviceName) {
22         String commandGroupKey = tclass.getSimpleName();// 獲得簡單類名作為groupKey
23 
24         ServerAddress server = myLoadBalancer.chooseServer(serviceName);// 負載均衡
25         RestAdapter restAdapter = new RestAdapter.Builder()
26                                   .setConverter(new JacksonConverter())
27                                   .setErrorHandler(new MyErrorHandler())
28                                   .setClient(new MyHttpClient(server, commandGroupKey))
29                                   .setEndpoint("/").build();
30         T tclassInstance = restAdapter.create(tclass);
31         return tclassInstance;
32     }
33 }
View Code

說明:這裡我們定義了自己的retrofit.Client和自己的retrofit.ErrorHandler

1.5.2、MyHttpClient(自定義retrofit的Client)

 1 package com.microservice.retrofit;
 2 
 3 import java.io.IOException;
 4 
 5 import com.microservice.hystrix.HttpHystrixCommand;
 6 import com.microservice.loadBalancer.ServerAddress;
 7 import com.netflix.hystrix.HystrixCommand.Setter;
 8 import com.netflix.hystrix.HystrixCommandGroupKey;
 9 import com.netflix.hystrix.HystrixCommandProperties;
10 
11 import retrofit.client.Client;
12 import retrofit.client.Request;
13 import retrofit.client.Response;
14 
15 public class MyHttpClient implements Client {
16     private ServerAddress server;
17     private String commandGroupKey;
18     private int hystrixTimeoutInMillions = 3000;// 這裡暫且將數據硬編碼在這裡(之後會改造)
19 
20     public MyHttpClient(ServerAddress server, String commandGroupKey) {
21         this.server = server;
22         this.commandGroupKey = commandGroupKey;
23     }
24 
25     @Override
26     public Response execute(Request request) throws IOException {
27         Setter setter = Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(commandGroupKey));
28         setter.andCommandPropertiesDefaults(HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(hystrixTimeoutInMillions));
29         return new HttpHystrixCommand(setter, server, request).execute();// 同步執行
30     }
31 }
View Code

說明:在execute()中引入了hystrix

  • 定義了hystrix的commandGroupKey是服務名(eg.myserviceA,被調用服務名
  • 沒有定義commandKey(通常commandKey是服務的一個方法名,例如myserviceA的client的getProvinceByCityName),通常該方法名是被調用服務的client中的被調用方法名
  • 硬編碼了hystrix的超時時間(這裡的硬編碼會通過之後的配置集中管理來處理)

1.5.3、HttpHystrixCommand(hystrix核心類)

 1 package com.microservice.hystrix;
 2 
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 
 6 import org.slf4j.Logger;
 7 import org.slf4j.LoggerFactory;
 8 
 9 import com.microservice.loadBalancer.ServerAddress;
10 	   

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 摘自:http://blog.csdn.net/dyllove98/article/details/9289553 資料庫生成位置可控制(其實主要就是DbContext的構造函數) 1.使用DbContext的構造函數:public DbContext(string nameOrConnection ...
  • 可以轉載,禁止修改。轉載請註明作者以及原文鏈接 註:本文是從貝葉斯分類器的角度來討論判別分析,有關貝葉斯分類器的概念可參考文末延伸閱讀第1-2篇文章。至於Fisher判別分析,未來會連同PCA一同討論。 判別分析也是一種分類器,與邏輯回歸相比,它具有以下優勢: 當類別的區分度高的時候,邏輯回歸的參數 ...
  • 俗話說技多不壓身,當年苦讀《深入理解JVM》還專門整理了筆記,現在就用上了~ 筆記 http://www.cnblogs.com/syjkfind/p/3901774.html 【癥狀】 用戶操作數據導出時總會發生卡頓,後臺占記憶體的定時任務發生時也會。JVM參數就不貼了,比較普通且相對合理。 【思路 ...
  • package com.pro.dao.impl; import java.sql.Date;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;import java.sq ...
  • 自從學了c語言,就一直想做個游戲,今天將之付之行動,第一次寫的特別爛,各種bug;就不貼了。今天網上看了好幾個貪吃蛇,重新寫了一次,做出來的效果還可以。 下麵是詳細的構建過程,本節因為時間限制,先貼出比較重要的控制函數實現,並用它做一個很簡單很簡單很有趣的畫圖程式。 首先,要對貪吃蛇的結構有一個大概 ...
  • java中的單例模式都很熟悉了:簡單地說就是一個類只能有一個實例。在scala中創建單例對象非常簡單,創建類時使用object關鍵字替換class即可。因為單例類無法初始化,所以不能向它的主構造函數傳遞參數。 下麵是一個單例的示例: class Marker(val color: String) {... ...
  • 函數原型: 頭文件: 引入: 字元串由'\0'結尾,所以字元串內部不能包含任何'\0'字元('\0'的ASCII值為0),否則我們將讀不到'\0'後的字元內容。但是,非字元串內部包含零值的情況並不罕見,我們無法利於其它字元串函數來處理這類數據。所以,我們要引入記憶體操作的函數來處理。 memcpy m ...
  • (•̀ᴗ•́)و ̑̑ 單鏈表/雙向鏈表的建立/遍歷/插入/刪除實例 迴圈鏈表的概念 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...