通过案例了解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是一样的,所以就不再给出了。
本文中的文字和代码谢绝转载。
通过案例了解Hystrix的各种基本使用方式的更多相关文章
- 项目案例模板之jdbc两种连接方式
项目案例模板之jdbc两种连接方式 第一种连接方式 JDBCUtils.java package jdbc; import org.junit.jupiter.api.Test; import ...
- JAVA实用案例之文件导入导出(POI方式)
1.介绍 java实现文件的导入导出数据库,目前在大部分系统中是比较常见的功能了,今天写个小demo来理解其原理,没接触过的同学也可以看看参考下. 目前我所接触过的导入导出技术主要有POI和iRepo ...
- Spring Cloud微服务系列文,Hystrix与Eureka的整合
和Ribbon等组件一样,在项目中,Hystrix一般不会单独出现,而是会和Eureka等组件配套出现.在Hystrix和Eureka整合后的框架里,一般会用到Hystrix的断路器以及合并请求等特性 ...
- 架构师系列文:通过Spring Cloud组件Hystrix合并请求
在前文里,我们讲述了通过Hystrix进行容错处理的方式,这里我们将讲述通过Hystrix合并请求的方式 哪怕一个URL请求调用的功能再简单,Web应用服务都至少会开启一个线程来提供服务,换句话说,有 ...
- 架构师入门:Spring Cloud系列,Hystrix与Eureka的整合
和Ribbon等组件一样,在项目中,Hystrix一般不会单独出现,而是会和Eureka等组件配套出现.在Hystrix和Eureka整合后的框架里,一般会用到Hystrix的断路器以及合并请求等特性 ...
- Hystrix针对不可用服务的保护机制以及引入缓存
之前我写过一篇博文,通过案例了解Hystrix的各种基本使用方式,在这篇文章里,我们是通过Hystrix调用正常工作的服务,也就是说,Hytrix的保护机制并没有起作用,这里我们将在HystrixPr ...
- Spring Cloud微服务系列文,服务调用框架Feign
之前博文的案例中,我们是通过RestTemplate来调用服务,而Feign框架则在此基础上做了一层封装,比如,可以通过注解等方式来绑定参数,或者以声明的方式来指定请求返回类型是JSON. 这种 ...
- Spring Cloud系列文,Feign整合Ribbon和Hysrix
在本博客之前的Spring Cloud系列里,我们讲述了Feign的基本用法,这里我们将讲述下Feign整合Ribbon实现负载均衡以及整合Hystrix实现断路保护效果的方式. 1 准备Eureka ...
- JAVA实用案例之水印开发
写在最前面 上周零零碎碎花了一周的时间研究水印的开发,现在终于写了个入门级的Demo,做下笔记同时分享出来供大家参考. Demo是在我上次写的 JAVA实用案例之文件导入导出(POI方式) 框架基础上 ...
随机推荐
- 安装pwntools
使用手册 http://pwntools.readthedocs.io/en/stable/ 环境 全新的ubuntu 14.04 64位 py2.7 实测ubuntu 18.04 64位同样适用 过 ...
- Quartz+JAVA+Servlet实现任务调度系统(简洁)
前言 该系统使用场景: 在12306上买了一张火车票,30分钟内需要支付(需要添加一个倒计时),30分钟还没有支付就请求取消订单的接口(自动根据url请求),如果支付了收到了支付的回调通知后,就删除计 ...
- scrapy的持久化相关
终端指令的持久化存储 保证爬虫文件的parse方法中有可迭代类型对象(通常为列表or字典)的返回,该返回值可以通过终端指令的形式写入指定格式的文件中进行持久化操作. 需求是:将糗百首页中段子的内容和标 ...
- 启动两个tomcat服务,以及使用nginx代理实现访问
1.shoudowm.bat\startup.bat\catalina.bat, 将CATALINA_HOME修改为CATALINA_HOME_2 2.server.xml <Server po ...
- Mysql 常见问题
------------------------------------------------ ------------------------------------------------ -- ...
- AJAX-同源策略 跨域访问
## 同源策略 概述: 同源策略是浏览器的一种安全策略,视为同源是指域名,协议,端口完全相同.只有同源的地址才可以通过AJAX方式请求.同源或者不同源说的是两个地址的关系,不同源地址之间请求我们称之为 ...
- 【原创】.Net WebForm Calendar 日历控件常用方法
微软官方地址 https://msdn.microsoft.com/en-us/library/add3s294.aspx 1.设置日历控件单个日期Table Cell样式 颜色/外观/边距 prot ...
- LeetCode笔记:39. Combination Sum
题目描述 给定一个无重复的正整数数组 candidates 和一个正整数 target, 求所有和为 target 的 candidates 中数的组合中.其中相同数的不同顺序组合算做同一种组合,ca ...
- service注入失败
每一个service都需要一个注解
- SQL Server 2017 新功能分享
本篇文章是我在MVP直通车分享的关于SQL Server 2017的新功能,现在ppt分享如下,可以点击这里下载.