通过案例了解Hystrix的各种基本使用方式

1 通过一些算术题了解系统发生错误的概率

我们一般用每秒查询率(Query Per Second,简称QPS)来衡量一个网站的流量,QPS是指一台服务器在一秒里能处理的查询次数,它可以被用来衡量服务器的性能。

假设一个Web应用有20个基于微服务的子模块,比如某电商系统里有订单、合同管理和会员管理等子模块,该系统的平均QPS是1000,也就是说平均每秒有1000个访问量,这个数值属于中等水平,并不高。

算术题一,请计算每天的访问总量?注:一般网站在凌晨1点到上午9点的访问量比较少,所以计算时按每天16个小时算。

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

算术题二:由于该系统中有20个子模块,在处理每次请求时,该模块有99.9999%的概率不出错(百万分之一的出错概率,这个概率很低了),而任何一个模块出错,整个系统就出错,那么问题是,每小时该系统出错的概率是多少?每天(按16小时算)是多少?每月(按30天算)又是多少?

答:针对每次访问,一个模块正常工作的概率是99.9999%,那么每小时20个模块都不出错的概率是99.9999%的(20*3600)次方,大约是93%,换句话说,在一个小时内,该系统出错的概率是7%。

我们再来算每天的正常工作概率,是93%的16次方,大约是31%,换句话说,每天出错的概率高达69%。同理我们能算出,每月出错的概率高达95%。

通过这组数据,我们能看到,规模尚属中等的网站(相当于尚能正常盈利不亏本的网站)平均每月就会出现一次故障,对于哪些模块故障率高于百万分之一或平均QPS更高的网站,这个出故障周期会更频繁,所以说,对于互联网公司而言,服务容错组件是必配,而不是优化项。

2 准备服务提供者

这里我们将在HystrixServerDemo项目里,提供两个供Hystrix调用的服务,其中一个是可用的,而在另外一个服务里,是通过sleep机制,故意让服务延迟返回,从而造成不可用的后果。

这是一个基本的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是一样的,所以就不再给出了。

本文中的文字和代码谢绝转载。

原文地址:https://www.cnblogs.com/JavaArchitect/p/9399209.html

时间: 2024-10-10 04:26:13

通过案例了解Hystrix的各种基本使用方式的相关文章

项目案例模板之jdbc两种连接方式

项目案例模板之jdbc两种连接方式 第一种连接方式 JDBCUtils.java package jdbc; ? import org.junit.jupiter.api.Test; ? import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; ? public class JDBCUtils { public static Connection connection; pri

DBA_Oracle冷备份案例脚本本法(案例)(数据库最基本备份方式)

2014-08-10 BaoXinjian 一.摘要 1. 冷备份 数据库在关闭状态下完成所有物理系统文件拷贝的过程,也称脱机备份 适合于非归档模式下,数据库处于一致性状态 2. 步骤 首先在运行的库中得到数据库运行的所有的物理文件位置,然后在计划内关闭数据库(shutdown) 再执行拷贝物理文家到备份路径或备份设备 备份完成后立即启动数据库让其提供正常的服务 3. 总结 - 优点 冷备模式下概念易于理解,即将需要备份的文件复制到安全的位置 操作比较简单,不需要太多的干预 容易恢复到某个时间点

0166 DOM 之 节点操作: 删除节点,删除留言案例,复制(克隆)节点,动态生成表格案例,创建元素的三种方式,innerHTML和createElement效率对比

1.1.1 删除节点 node.removeChild(child) // 此处的node指 父节点 node.removeChild() 方法: 从 node节点中删除一个子节点,返回删除的节点. <button>删除</button> <ul> <li>熊大</li> <li>熊二</li> <li>光头强</li> </ul> <script> // 1.获取元素 va

案例 同城双核心机房的专线接入方式

某公司在一个城市拥有2个核心机房,用于容灾备份,两机房间采用同城裸光纤互联,与运营商间分别租用MPLS L3 VPN,其实是分别连接至不同的PE(双PE),但都属于AS4809.分支机构也租用MPLS L3VPN,接入AS4809. 机房1和机房2之间EBGP连接,2机房都向AS4809宣告路由,分支结构接收到机房1和机房2的路由表,业务流程: 深圳访问机房1:65522à4809à64560 深圳访问机房2:65522à4809à64554 如果核心机房1与对应PE的链路断开,核心机房2同时向

Hystrix针对不可用服务的保护机制以及引入缓存

之前我写过一篇博文,通过案例了解Hystrix的各种基本使用方式,在这篇文章里,我们是通过Hystrix调用正常工作的服务,也就是说,Hytrix的保护机制并没有起作用,这里我们将在HystrixProtectDemo.java里演示调用不可用的服务时,hystrix启动保护机制的流程.这个类是基于NormalHystrixDemo.java改写的,只是在其中增加了getFallback方法,代码如下. 1 //省略必要的package和import代码 2 public class Hystr

JAVA实用案例之文件导出(JasperReport踩坑实录)

写在最前面 想想来新公司也快五个月了,恍惚一瞬间. 翻了翻博客,因为太忙,也有将近五个多月没认真总结过了. 正好趁着今天老婆出门团建的机会,记录下最近这段时间遇到的大坑-JasperReport. 六月份的时候写过一篇利用poi文件导入导出的小Demo,JAVA实用案例之文件导入导出(POI方式). 虽然简单,但是企业应用的原理基本上也就是这样,只不过是封装的更好些,不像我之前写的那样每个Cell都需要定义,其实poi的方式也是我目前最推崇的方式之一了.主要原因是jxl不支持xlsx,Jaspe

网站爬取-案例四:知乎抓取(COOKIE登录抓取个人中心)(第一卷)

有很多网站是需要先登录,才可以浏览的,所以我们这个案例主要讲解如何以登陆的方式抓取这类的页面 第一:http本身是一种无状态的协议 这样两个请求没有任何关系,像淘宝这样的网站需要记录用户的每次请求,来看看有状态的请求 看一下COOKIE本地存储 用户名密码可以存到本地,所以安全性不高,这样就出现了SESSION机制,根据用户名和密码生成SESSIONID,根据SESSIONID请求取出用户要的内容 登陆时产生,退出时清空 看下登陆时 三个字段为ID,加密字段,失效日期,看下登录后的控制台 说到这

Java案例整理

1.随机点名器案例 1.1      案例介绍 随机点名器,即在全班同学中随机的找出一名同学,打印这名同学的个人信息. 此案例在我们昨天课程学习中,已经介绍,现在我们要做的是对原有的案例进行升级,使用新的技术来实现. 我们来完成随机点名器,它具备以下3个内容: l  存储所有同学姓名 l  总览全班同学姓名 l  随机点名其中一人,打印到控制台 1.2      案例需求分析 全班同学中随机的找出一名同学,打印这名同学的个人信息. 我们对本案例进行分析,得出如下分析结果: 1.存储全班同学信息(

Kaggle十大案例精讲课程

Kaggle十大案例精讲课程网盘地址:https://pan.baidu.com/s/1WyCtvJmIda7WJHeQHduXbw 提取码: dpny 课程特色:1.精选kaggle初级案例,实战入门讲解.一步一脚印,十大案例精讲,案例知识点与代码交互讲解,让你快速突破R语言2.实战案例是最好的数据能力提升方式,本课程提供案例的数据集.代码以及老师讲解的PPT.3.kaggle竞赛案例包含大量日常工作中的数据处理的方法,以及数据可视化的方法,助你突破数据工作的效率,对于数据处理.可视化.建模和