經過案例瞭解Hystrix的各類基本使用方式

 1 經過一些算術題了解系統發生錯誤的機率

    咱們通常用每秒查詢率(Query Per Second,簡稱QPS)來衡量一個網站的流量,QPS是指一臺服務器在一秒裏能處理的查詢次數,它能夠被用來衡量服務器的性能。java

    假設一個Web應用有20個基於微服務的子模塊,好比某電商系統裏有訂單、合同管理和會員管理等子模塊,該系統的平均QPS是1000,也就是說平均每秒有1000個訪問量,這個數值屬於中等水平,並不高。web

    算術題一,請計算天天的訪問總量?注:通常網站在凌晨1點到上午9點的訪問量比較少,因此計算時按天天16個小時算。spring

    答:1000*60*60*16=57600000=5.76乘以10的8次方。服務器

    算術題二:因爲該系統中有20個子模塊,在處理每次請求時,該模塊有99.9999%的機率不出錯(百萬分之一的出錯機率,這個機率很低了),而任何一個模塊出錯,整個系統就出錯,那麼問題是,每小時該系統出錯的機率是多少?天天(按16小時算)是多少?每個月(按30天算)又是多少?併發

    答:針對每次訪問,一個模塊正常工做的機率是99.9999%,那麼每小時20個模塊都不出錯的機率是99.9999%的(20*3600)次方,大約是93%,換句話說,在一個小時內,該系統出錯的機率是7%。app

    咱們再來算天天的正常工做機率,是93%的16次方,大約是31%,換句話說,天天出錯的機率高達69%。同理咱們能算出,每個月出錯的機率高達95%。異步

    經過這組數據,咱們能看到,規模尚屬中等的網站(至關於尚能正常盈利不虧本的網站)平均每個月就會出現一次故障,對於哪些模塊故障率高於百萬分之一或平均QPS更高的網站,這個出故障週期會更頻繁,因此說,對於互聯網公司而言,服務容錯組件是必配,而不是優化項。async

 

2 準備服務提供者

    這裏咱們將在HystrixServerDemo項目裏,提供兩個供Hystrix調用的服務,其中一個是可用的,而在另一個服務裏,是經過sleep機制,故意讓服務延遲返回,從而形成不可用的後果。函數

    這是一個基本的Spring Boot的服務,以前相似的博文裏咱們已經反覆講述過,因此這裏僅給出實現要點,具體信息請你們本身參照代碼。spring-boot

    要點1,在pom.xml裏引入spring boot的依賴項,關鍵代碼以下。    

1    <dependency>
2        <groupId>org.springframework.boot</groupId> 
3        <artifactId>spring-boot-starter-web</artifactId> 
4        <version>1.5.4.RELEASE</version> 
5    </dependency>

    要點2,在ServerStarter.java裏,開啓服務,代碼以下。      

1    //省略必要的package和import代碼
2    @SpringBootApplication
3    public class ServerStarter{
4        public static void main( String[] args )
5        {      SpringApplication.run(ServerStarter.class, args); }
6    }

    要點3,在控制器Controller.java裏,編寫兩個提供服務的方法,代碼以下。    

1    @RestController
2    public class Controller {        
3        @RequestMapping(value = "/available", method = RequestMethod.GET    )
4        public String availabieService() 
5        { return "This Server works well."; }
6        
7        @RequestMapping(value = "/unavailable", method = RequestMethod.GET    )
8        public String unavailableServicve () {
9            try { Thread.sleep(5000); } 
10            catch (InterruptedException e) 
11            { e.printStackTrace(); }
12            return "This service is unavailable.";
13        }
14    }

    其中在第3行提供了一個可用的服務,在第8行的unavailableServicve的服務裏,是經過第9行的sleep方法,形成「服務延遲返回」的效果。

3 以同步方式調用正常工做的服務

    這裏咱們新建一個HystrixClientDemo項目,在其中開發各類Hystrix調用服務的代碼。

在這個項目裏,咱們將經過Ribbon和Hystrix結合的方式,調用在上部分裏提供的服務,因此在pom.xml文件裏,咱們將引入這兩部分的依賴包,關鍵代碼以下。    

1    <dependencies>    
2        <dependency>
3                <groupId>com.netflix.ribbon</groupId>
4                <artifactId>ribbon-httpclient</artifactId>
5                <version>2.2.0</version>
6        </dependency>
7         <dependency>
8                <groupId>com.netflix.hystrix</groupId>
9                <artifactId>hystrix-core</artifactId>
10                <version>1.5.12</version>
11        </dependency>
12      </dependencies>

    在上述代碼的第2到第6行裏,咱們引入了Ribbon的依賴項,從第7到第11裏,咱們引入了Hystrix的依賴項。

    在NormalHystrixDemo.java裏,咱們將演示經過Hystrix調用正常服務的開發方式,代碼以下。    

1    //省略必要的package和import代碼
2    //繼承HystrixCommand<String>,因此run方法返回String類型對象
3    public class NormalHystrixDemo extends HystrixCommand<String> {
4        //定義訪問服務的兩個對象
5        RestClient client = null;
6        HttpRequest request = null;
7        //在構造函數裏指定命令組的名字 
8        public NormalHystrixDemo() {
9       super(HystrixCommandGroupKey.Factory.asKey("demo"));
10        }
11        //在initRestClient方法裏設置訪問服務的client對象
12        private void initRestClient() {
13            client = (RestClient) ClientFactory.getNamedClient("HelloCommand");
14            try {
15                request = HttpRequest.newBuilder().uri(new URI("/available")).build();
16            } catch (URISyntaxException e) 
17             { e.printStackTrace(); }
18        ConfigurationManager.getConfigInstance().setProperty(    "HelloCommand.ribbon.listOfServers", "localhost:8080");
19        }

    在第12行的initRestClient方法裏,咱們作好了以基於Ribbon的RestClient對象訪問服務的準備工做,具體而言,在第13行裏經過工廠初始化了client對象,在第18行,設置了待訪問的url,在第15行,設置了待訪問的服務名。    

20        protected String run() {
21            System.out.println("In run");
22            HttpResponse response;
23            String result = null;
24            try {
25                response = client.executeWithLoadBalancer(request);
26                System.out.println("Status for URI:" + response.getRequestedURI()+ " is :" + response.getStatus());
27                result = response.getEntity(String.class);
28            } catch (ClientException e) 
29            { e.printStackTrace();} 
30           catch (Exception e) {    e.printStackTrace();    }
31            return "Hystrix Demo,result is: " + result;
32        }

    咱們在第20行定義了返回String類型的run方法, 這裏的返回類型須要和第3行裏本類繼承的HystrixCommand對象的泛型一致。在其中,咱們是經過第25行的代碼調用服務,並在第31行,返回一個包括調用結果的String字符串。    

public static void main(String[] args) {
34            NormalHystrixDemo normalDemo = new NormalHystrixDemo();
35            //初始化調用服務的環境
36            normalDemo.initRestClient();
37            // 睡眠1秒
38            try {Thread.sleep(1000);} 
39            catch (InterruptedException e) 
40            {e.printStackTrace();    }
41            //調用execute方法後,會自動地執行定義在第20行的run方法
42            String result = normalDemo.execute();
43            System.out.println("Call available function, result is:" + result);
44        }
45    }

    在main方法裏,咱們指定了以下的工做流程。

    第一步,在第36行裏,經過調用initRestClient方法完成了初始化的工做。

    第二步,在第42行裏執行了execute方法,這個方法是封裝在HystrixCommand方法裏的,一旦調用,則會觸發第20行的run方法。

    請注意,這裏一旦執行execute方法,則會當即(即以同步的方式)執行run方法,在run方法返回結果以前,代碼是會阻塞在第42行的,即不會繼續日後執行。

    第三步,在第20行的run方法裏,咱們以localhost:8080/available的方式調用了服務端的服務。

    執行本段代碼,會看到以下的打印語句,這些打印語句很好地驗證了上述講述的過程流程。    

1    In run
2    Status for URI:http://localhost:8080/available is :200
3    Call available function, result is:Hystrix Demo,result is: This Server works well.

    

4 以異步的方式調用服務

    在上部分的Hystrix案例中,請求是被依次執行,在處理完上個請求以前,後一個請求處於阻塞等待狀態,這種Hystrix同步的處理方式適用於併發量通常的場景。

    但單臺服務器的負載處理能力畢竟是有限的,若是併發量高於(或遠遠高於)這個極限時,那麼咱們就得考慮採用Hystrix基於異步的保護機制,從下圖裏,咱們能看到基於異步處理的效果圖。 

 

    從上圖裏咱們能看到,請求不是被同步地當即執行,而是被放入到一個隊列(queue)中,封裝在HystrixCommand的處理代碼是從queue裏拿出請求,並以基於hystrix保護措施的方式處理該請求。在下面的AsyncHystrixDemo.java裏,咱們將演示hystrix異步執行的方式。   

1    //省略必要的package和import代碼
2    //這裏一樣是繼承HystrixCommand<String>類
3    public class AsyncHystrixDemo extends HystrixCommand<String> {
4        RestClient client = null;
5        HttpRequest request = null;
6        public AsyncHystrixDemo() {
7            // 指定命令組的名字
8    super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
9        }
10        private void initRestClient() {
11            client = (RestClient) ClientFactory.getNamedClient("AsyncHystrix");
12            try {
13                request = HttpRequest.newBuilder().uri(new URI("/available")).build();
14            } 
15            catch (URISyntaxException e) 
16            {    e.printStackTrace();    }
17        ConfigurationManager.getConfigInstance().setProperty(
18                    "AsyncHystrix.ribbon.listOfServers", "localhost:8080");
19        }
20        protected String run() {
21            System.out.println("In run");
22            HttpResponse response;
23            String result = null;
24            try {
25                response = client.executeWithLoadBalancer(request);
26                System.out.println("Status for URI:" + response.getRequestedURI() + " is :" + response.getStatus());
27                result = response.getEntity(String.class);
28            } 
29            catch (ClientException e) {e.printStackTrace(); } 
30            catch (Exception e) { e.printStackTrace();    }
31            return "Hystrix Demo,result is: " + result;
32        }

    在上述代碼的第6行裏,咱們定義了構造函數,在第10行裏,定義了初始化Ribbon環境的initRestClient方法,在第20行裏,定義了執行hytrix業務的run方法。這三個方法和剛纔講到的NormalHystrixDemo類裏很類似,因此就再也不詳細講述。    

33        public static void main(String[] args) {
34            AsyncHystrixDemo asyncDemo = new AsyncHystrixDemo();
35            asyncDemo.initRestClient();
36            try {    Thread.sleep(1000);} 
37            catch (InterruptedException e) 
38            {    e.printStackTrace();    }
39             //上述代碼是初始化環境並sleep 1秒
40            //獲得Future對象  
41            Future<String> future = asyncDemo.queue();
42            String result = null;
43            try {
44                System.out.println("Start Async Call");
45                 //經過get方法以異步的方式調用請求
46                result = future.get();
47            } catch (InterruptedException e) 
48              { e.printStackTrace();} 
49             catch (ExecutionException e) 
50           {     e.printStackTrace();    }
51            System.out.println("Call available function, result is:" + result);
52        }
53    }

    在main函數的34到38行,咱們一樣是初始化了Ribbon環境,這和以前的NormalHystrixDemo類的作法是同樣的。

    在第41行裏,咱們經過queue方法,獲得了一個包含調用請求的Future<String>類型的對象,而在第46行裏,咱們是經過future對象的get方法執行請求。

    這裏有兩個看點,第一,在執行第46行的get方法後,HystrixComman會自動調用定義在第20行的run方法,第二,這裏獲得請求對象是在第41行,而調用請求則在46行,也就是說,並非在請求到達時就當即執行,而是經過異步的方式執行。

    本部分代碼的執行結果和NormalHystrixDemo.java是同樣的,因此就再也不給出了。   

 

本文中的文字和代碼謝絕轉載。

相關文章
相關標籤/搜索