传统项目利用Hystrix实现热点接口的服务隔离
这段时间接了个需求,需要在我目前负责的数据系统上加个接口,主要是实现用户行为的记录。前端对接的项目主要有公司的PC,WAP,WEIXIN,APP等,每个端大概有两台左右的负载。因为目前我的这个项目主要是面向内部,负责数据运营相关的内容,是个单体项目。如果线上各个接入点不做限制,瞬间大量的并发进入必然会导致目前项目的崩溃,其他的功能也无法正常使用。
1、需求分析
通过前期的需求分析,目前线上系统无法进行限流处理,所以最终解决问题还是要从接口入手。
目前我对接口的处理有两种实现方案:
- 可以利用MQ实现消息的错峰,将消息发送到MQ服务器。
- 实现对接口的隔离限流,避免当前接口对其他功能的影响。
其实我认为最好的实现方案就是第一种了,可以保证消息的准确送达,避免并发资源的占用。不过,因为公司条件的限制暂时不能新增中间件,所以只能在现有系统上进行改造,最后只能采用第二种方法了。
2、Hystrix的简单介绍
官方的定义就不说了,这里简单说下我的理解。Hystrix作为断路器主要是实现对服务的容错保护,简单来说就是服务隔离、服务降级、服务熔断,服务限流这几项。
举个常见的例子,当你某宝【抢购】一个产品时,经常会弹出[网络错误,请重试。]的提示,这种时候是真的网络问题吗?显示不是。这种情况下其实是对调用的接口进行了降级处理,当降级的次数或比例达到一定的条件后,断路器就会直接打开,之后的访问就会直接降级,而不会判断是否降级了。达到对服务的容错保护以及给用户友好提示的目的。详细的流程可以看下图。
一般Hystrix的容错保护在微服务中是用在客户端,也就是调用方。而我这次实际上是用在了提供方,主要是前台的项目我无法控制,只能在接口上想方法了。目前我使用Hystrix的主要目的就是实现对服务的隔离和限流,而对降级和熔断反而不是特别的关心,当然实际的使用要结合场景。
因为一般Tomcat默认是一个线程池150个线程,如果单个热点接口的请求过多,就会造成其他功能没有线程可用甚至直接程序崩溃的问题。Hystrix的服务隔离主要有两种,常用的就是线程池隔离的方式,对热点接口建立单独的线程池避免对主程序的影响。另一种是信号量的方式,用的场景不是太多。两者的区别其实就是一个增大系统的开销,一个则直接限制了线程总的并发数,开销更小一些。
Hystrix服务调用逻辑图
3、项目中的应用实现
本文是在传统Spring项目中的应用,Springboot中的相关配置和依赖有稍许的不同。
maven依赖:
- <!-- hystrix -->
- <dependency>
- <groupId>com.netflix.hystrix</groupId>
- <artifactId>hystrix-core</artifactId>
- <version>1.5.9</version>
- </dependency>
- <dependency>
- <groupId>com.netflix.hystrix</groupId>
- <artifactId>hystrix-metrics-event-stream</artifactId>
- <version>1.5.9</version>
- </dependency>
- <dependency>
- <groupId>com.netflix.hystrix</groupId>
- <artifactId>hystrix-javanica</artifactId>
- <version>1.5.9</version>
- </dependency>
在Spring的配置文件中配置Hystrix的切面信息
- <bean id="hystrixAspect" class="com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect"></bean>
- <aop:aspectj-autoproxy />
主要是开启注解的AOP扫描,这里我们可以在这个类的源码中看到实现
可以看到我们主要是通过这个类切面扫描Hystrix的相关注解,以达到接口处理前,提前执行Hystrix相关逻辑的代码。
- /**
- * 提供客户行为接口
- *
- */
- @Controller
- @RequestMapping(value = "/test")
- public class BehaviorController {
- Logger logger = Logger.getLogger(BehaviorController.class);
- @Autowired
- private BehaviorService behaviorService;
- @RequestMapping(value="/addBehavior",method = RequestMethod.POST,produces = "application/json;charset=UTF-8")
- @ResponseBody
- @HystrixCommand(fallbackMethod = "fallback", threadPoolProperties = {
- @HystrixProperty(name = "coreSize", value = "20"), @HystrixProperty(name = "maxQueueSize", value = "100"),
- @HystrixProperty(name = "queueSizeRejectionThreshold", value = "20")},
- commandProperties = {
- @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "30000"),
- @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "20")
- })
- public String addBehavior(@RequestBody String parms) {
- //业务逻辑实现
- return result;
- }
- public String fallback(@RequestBody String parms){
- logger.info("fallback");
- //失败的实现
- return result;
- }
- }
注意:
请求的接口必须为public,fallback为降级的接口逻辑,可以为private,也可以为public。
但是要特别注意fallback方法的返回值和参数必须和请求方法相同。
另外需要说的是,当请求失败、被拒绝、超时或者断路器打开时,都会进入回退方法,但是进入回退方法并不意味着断路器已经被打开。
4、常用参数的介绍
参数 |
描述 |
默认值 |
execution.isolation.strategy |
隔离策略,有THREAD和SEMAPHORE THREAD - 它在单独的线程上执行,并发请求受线程池中的线程数量的限制 |
默认使用THREAD模式,以下几种场景可以使用SEMAPHORE模式: 只想控制并发度 外部的方法已经做了线程隔离 调用的是本地方法或者可靠度非常高、耗时特别小的方法(如medis) |
execution.isolation.thread.timeoutInMilliseconds |
超时时间 |
默认值:1000 在THREAD模式下,达到超时时间,可以中断 在SEMAPHORE模式下,会等待执行完成后,再去判断是否超时 设置标准: 有retry,99meantime+avg meantime 没有retry,99.5meantime |
execution.timeout.enabled |
HystrixCommand.run()执行是否应该有超时。 |
默认值:true |
fallback.isolation.semaphore.maxConcurrentRequests |
设置在使用时允许执行fallback方法的最大并发请求数 |
默认值:10 |
circuitBreaker.requestVolumeThreshold |
设置滚动时间窗中,断路器熔断的最小请求数 |
默认值:20 滚动窗口默认10s,即10s内失败请求达到20个,熔断器即打开 |
coreSize |
设置执行命令线程池的核心线程数。 |
默认值:10 |
maxQueueSize |
设置执行命令线程池的核心线程数。 |
默认值:-1 当设置为-1时,线程池使用SynchronousQueue实现的队列,否则将使用LinkedBlockingQueue实现的队列 |
queueSizeRejectionThreshold |
为队列设置拒绝阈值 |
默认值:5 当设置该参数后,即使队列没有达到最大值也能拒绝请求。 注意:当maxQueueSize属性为-1的时候,该属性不会生效 |
另外需要特别注意的是:fallback的属性maxConcurrentRequests,当请求达到了最大并发数时,后续的请求将会被拒绝并抛出异常(因为它已经没有后续的fallback可以被调用了),异常信息一般为com.netflix.hystrix.exception.HystrixRuntimeException: xxxxxxx fallback execution rejected.
更多参数见官方文档:https://github.com/Netflix/Hystrix/wiki/Configuration
另外附一个网友翻译的文档:https://blog.csdn.net/tongtong_use/article/details/78611225
5、接口测试
并发接口测试的方法很多,可以写代码,也可以用apache batch以及jmeter等工具。以常用的jmeter为例,测试本接口。
- 设置环境变量:
- JMETER_HOME D:\apache-jmeter-3.0
- CLASSPATH %JMETER_HOME%\lib\ext\ApacheJMeter_core.jar;%JMETER_HOME%\lib\jorphan.jar;%JMETER_HOME%\lib/logkit-2.0.jar;
新建线程组:
设置并发参数:
第一个参数为线程数,第二个参数为启动时间,第三个参数为请求次数。以上述配置为例即为1秒内启动50个线程,每个线程请求一次。
添加HTTP请求
这里设置请求的路径,参数等等。
设置请求头信息,因人而异
我的设置:Content-Type:application/json
表格查看结果
查看结果:
这个可以根据机器的性能进行测试,以我的接口为例,当设置并发数为100以内时,基本上不会有降级处理,当并发数大于100时,就会有部分请求进入降级接口了。
6、接口监控
实际项目中,经常需要对我们的接口和项目情况进行监控,Hystrix已经为我们考虑到了。Hystrix提供了近乎实时的监控,Hystrix会实时的,累加的记录所有关于HystrixCommand的执行信息,包括执行了每秒执行了多少请求,多少成功,多少失败等等,更多指标请查看:https://github.com/Netflix/Hystrix/wiki/Metrics-and-Monitoring
在web.xml中添加下述配置:
- <!-- for Hystrix -->
- <servlet>
- <display-name>HystrixMetricsStreamServlet</display-name>
- <servlet-name>HystrixMetricsStreamServlet</servlet-name>
- <servlet-class>com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet</servlet-class>
- </servlet>
- <servlet-mapping>
- <servlet-name>HystrixMetricsStreamServlet</servlet-name>
- <url-pattern>/hystrix.stream</url-pattern>
- </servlet-mapping>
启动应用。访问http://hostname:port/项目名/hystrix.stream,可以看到下面信息
这种是最原生的监控使用方式,大家可以另外集成Hystrix 提供的一个 Dashboard 应用。Dashboard 是一个单独的应用,我们可以独立部署。如果需要监控整个 Hystrix 集群,就需要使用 Turbine 应用。Turbine 也是 Netflix 开源的一个服务。但是使用 Hystrix Stream 和 Turbine 存在一个明显的不足,那就是无法查看历史的监控数据。解决这个问题就需要我们自己来实现了。
监控的相关不是本文的重点,就不多介绍了,大家可以百度下hystrix在微服务项目中监控的实现,这里网上文章很多,基本上和传统项目没有任何区别了,大家可以参考实现。
传统项目利用Hystrix实现热点接口的服务隔离的更多相关文章
- springboot项目利用Swagger2生成在线接口文档
Swagger简介. Swagger2是一款restful接口文档在线生成和在线调试工具.很多项目团队利用Swagger自动生成接口文档,保证接口文档和代码同步更新.在线调试.简单地说,你可以利用这个 ...
- Spring Cloud实战之初级入门(四)— 利用Hystrix实现服务熔断与服务监控
目录 1.环境介绍 2.服务监控 2.1 加入依赖 2.2 修改配置文件 2.3 修改启动文件 2.4 监控服务 2.5 小结 3. 利用hystrix实现消费服务熔断 3.1 加入服务熔断 3.2 ...
- Dubbo 项目与传统项目
1.什么是传统工程 单工程 MVC 架构 控制层通过调用服务层完成业务逻辑处理 业务层调用持久层进程数据操作 2.什么是分布式工程 将传统项目的单工程结构,拆分成多工程 一般会有这几个工程: 父工程: ...
- SpringBoot项目集成Hystrix
Hystrix Hystrix是由Netflix开源的一个服务隔离组件,通过服务隔离来避免由于依赖延迟.异常,引起资源耗尽导致系统不可用的解决方案. 1.什么是服务熔断 服务熔断就是对该服务的调用 ...
- 利用python3 调用zabbix接口完成批量加聚合图形(screens)
在上一篇博客中,我们完成的利用python3 调用zabbix接口批量增加主机,增加主机的item,增加主机的图形! 接下来我们完成批量增加主机的screen 首先我们要增加screen需要哪些参数呢 ...
- Winform项目调用asp.net数据接口
最近一个WPF项目需要改写成android项目,思路是在asp.net项目中编写一个通用接口,便于其它平台下调用数据.刚接触到这些东西的时候完全是一头雾水,最根本的原因是不明白网站中的一个网页,为什么 ...
- 若依项目利用nginx实现负载均衡及保持会话
记录一下若依项目利用nginx实现负载均衡及保持会话的步骤. 此次作为试验性的测试,为了方便在本地window的环境上实现. 具体步骤: 1.安装两个tomcat8,可以下载一个后,另一个复制即可,下 ...
- 关于传统项目打成war包的的分析
技术在不断的革新,以前的项目没有jar管理工具时,都是手动将依赖的jar拷贝到项目之下,然后Build Path,之后Maven出现了,出现了jar包中央仓库,所有的jar包资源集中在这里,免去频繁去 ...
- laravel项目利用twemproxy部署redis集群的完整步骤
Twemproxy是一个代理服务器,可以通过它减少Memcached或Redis服务器所打开的连接数.下面这篇文章主要给大家介绍了关于laravel项目利用twemproxy部署redis集群的相关资 ...
随机推荐
- 在 R 中使用 Python 字符串函数
sprintf( )函数很强大,但并非适用于所有应用场景.例如,如果一些部分在模板中多次出现,那么就需要多次写一样的参数.这通常会使得代码冗长而且难以修改:sprintf("%s, %d y ...
- MQ是什么 RabbitMQ
一.rabbitMQ是什么: RabbitMQ,遵循AMQP协议,由内在高并发的erlanng语言开发,用在实时的对可靠性要求比较高的消息传递上. 学过websocket的来理解rabbitMQ应该是 ...
- UTF-8里包括GB2312
用最易懂的说法就是UTF-8里包括GB2312.UTF-8是国际通用的标准(包括世界所有的语言),而GB2312(只是简体中文)只适合做中文的网站. 假设你想做个中文网页,但是还可以翻成英文的话,就得 ...
- Rails 5 Test Prescriptions 第7章 double stub mock
https://relishapp.com/rspec/rspec-mocks/v/3-7/docs/basics/test-doubles 你有一个问题,如果想为程序添加一个信用卡程序用于自己挣钱. ...
- a标记无效问题
当在<a href=''></a>这个标记中嵌入<td></td> 就会导致部分浏览器无法单击,所以在开发HTML页面的时候,一定不要在 a标记中嵌入 ...
- Lucene Scoring 评分机制
原文出处:http://blog.chenlb.com/2009/08/lucene-scoring-architecture.html Lucene 评分体系/机制(lucene scoring)是 ...
- 单链表是否有环的问题解决与讨论(java实现)
单链表是否有环的问题经常在面试中遇到,一般面试中会要求空间为O(1);再者求若有环,则求环产生时的起始位置. 下面采用java实现. //单链表class ListNode{ int val; Lis ...
- flex布局在垂直居中里,元素超过容器大小后,不能通过滚动条滚动到顶端,这是个flex的bug
The Problem Flexbox makes centering very easy. By simply applying align-items: center and justify-co ...
- java日常知识点积累
java类型中的普通非static方法 示例代码: package com.lvzhi; /** * Created by lvzhi on 2017/9/3 */ public class MyTh ...
- 首次运行tensorflow-gpu 1.0 报错 failed to create cublas handle: CUBLAS_STATUS_NOT_INITIALIZED
发现博客: https://blog.csdn.net/u010752600/article/details/79534910 于是找到解决方法. sudo rm -rf ~/.nv/