一、命令执行的四种方法:
1、toObservable(异步)
使用该命令,需要进行订阅它返回的可观察对象,才会执行
2、observe(异步)
该命令调用第一个方法,内部已经订阅了toObservable返回的可观察对象
3、queue(异步)
该命令调用第一个方法,内部已经订阅了toObservable返回的可观察对象
4、execute(同步)
该命令调用第三个方法,调用queue的get方法返回结果
二、配置
配置超时时间:
//超时时间,默认是1秒
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds:1000
//代码
public class TimeoutCommand extends HystrixCommand<String>{
//第一种配置方式
public TimeoutCommand(){
//调用父类的构造,设置超时时间为2秒
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))
.andCommandKey(HystrixCommandKey.Factory.asKey("CommandKey"))
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("PoolKey"))
.addCommandPropertiesDefaults(HystrixCommandProperties.Setter()
withExecutionTimeoutInmilliseconds(2000));
/**
*上面命令key介绍
每一个命令,hystrix底层就会分配一个线程池来执行命令,所有的命令会用map来维护这一堆的线程池。
这个map就使用groupKey作为map的key,value就是线程池对象。
groupKey的默认值是使用@HystrixCommand标注的方法所在的类名
commandKey的默认值是@HystrixCommand标注的方法名,即每个方法会被当做一个HystrixCommand
threadPoolKey的默认值是groupKey,而groupKey默认值是@HystrixCommand标注的方法所在类名
命令主名称:Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))
为这个命令起名:andCommandKey(HystrixCommandKey.Factory.asKey("CommandKey"))
线程池名称:andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("PoolKey"))
*/
}
//第二种配置方式,配置全局方式
ConfigurationManager.getConfigInstance().setProperty("hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds",2000);
//TimeoutCommand这里的无参构造就不需要设置.addCommandPropertiesDefaults(HystrixCommandProperties.Setter().withExecutionTimeoutInmilliseconds(2000)了
TimeoutCommand command = new TimeoutCommand();
String result = command.execute();
System.out.print(result);
}
命令的执行流程:
一:断路器是否打开,打开则执行回退
断路器打开条件
1:整个链路达到一定的阀值,默认情况下,10秒内超过20次请求,则符合条件
2:当符合条件后,如果请求的错误百分比大于设置的阈值,断路器打开,默认超过百分之五十
设置属性:
//这里配置10次请求,默认是10s内20次请求
hystrix.command.default.circuitBreaker.requestVolumeThreshold:10
当断路器打开以后,所以的请求默认走fallback方法,当五分钟后,再次尝试请求,如果请求成功,断路器就会关闭,如果仍上失败,断路器仍然打开,走回退逻辑。
二、线程隔离机制
线程池是否满了,满了则执行回退:隔离策略2选一:
Thread:支持异步&超时,线程池默认大小:10
Semaphore:信号量,开销比较小,不支持超时和异步,当一个请求的并发数高于设置的阈值,就不会去执行。
测试Semaphore的线程池设置,可以通过for循环创建Thread线程去调用execute方法测试。
属性配置:
//线程隔离,分为THREAD和SEMAPHORE
//设置为THREAD(hystrix默认使用THREAD隔离策略),线程池大小默认是10,这里设置成3测试
hystrix.threadpool.default.coresize,3
=============================分割线=============================
//设置为SEMAPHORE
hystrix.command.default.execution.isolation.strategy,ExeutionIsolationStrategy.SEMAPHORE
//设置最大线程数:默认是10,这里测试可以设置为2
hystrix.command.default.execution.isolation.semaphore.maxConcurrrentRqeutsts,2
三、缓存策略
一个请求过程中,多个地方调用同一个接口,则使用缓存。缓存可以使用clear清掉
注意:必须是一次请求,才会有缓存策略。
四、整合SpringCloud
1、调用者启动时开启 @EnableCircuitBreaker 开启断路器
2、在调用的方法上,使用:@HystrixCommand(fallback = "getFallBack")
注意,回调方法需要和调用的方法参数保持一致
@HystrixCommand(fallbackMethod = "getOrderInfoFallBack",groupKey = "orderInfoGroup",commandKey = "commandKey",threadPoolKey = "线程池的key"
,commandProperties = {
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "1000")//配置超时时间
},threadPoolProperties = {
@HystrixProperty(name = "coreSize",value = "2")//配置线程池大小
})
@RequestMapping("/getOrderInfo")
public String getOrderInfo(@RequestParam("userName") String userName, @RequestParam("age") Integer age) {
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
return memberServiceFeign.getMemberInfo(userName, age).toString() + Thread.currentThread().getName();
}
3、如果远程调用service的所有方法都需要统一处理,可以在类上加注解,配置内容和@HystrixCommand()配置一样
注意,默认的回退方法是不需要参数的,这里和方法上的@HystrixCommand的回退方法不一样!!!
@DefaultProperties(defaultFallback = "getOrderInfoFallBack")
@Service
public class OrderInfoServiceImpl implements OrderInfoService {
}
4、请求合并 、 缓存
缓存:
4.1:创建拦截类
package io.spring.cloud.service;
import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
/**
* 初始化HystrixRequestContext
当客户端发送请求过来时,进行拦截并对HystrixRequestContext进行初始化
* @Date 2019-06-30 09:10
*/
@WebFilter(urlPatterns = "/*",filterName = "hystrixFilter")
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("**************过滤器拦截*****************");
HystrixRequestContext ctx = HystrixRequestContext.initializeContext();
try {
filterChain.doFilter(servletRequest, servletResponse);
} catch (Exception e) {
} finally {
ctx.shutdown();
}
}
@Override
public void destroy() {
}
}
4.2:开启扫描注解 @ServletComponentScan
@SpringBootApplication
@EnableFeignClients //开启feign的扫描。
@ServletComponentScan //开启扫描
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
4.3:开启缓存
/**
Controller: 一次请求多次调用service
*/
@RequestMapping(value = "/getOrder", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public String getOrder() {
for (int i = 0; i < 3; i++) {
orderService.cache(1);
}
return "hehe";
}
/**
service:
开启缓存@CacheResult,必须和@HystrixCommand注解一起使用才生效,一个请求内,多次调用该方法,参数不变的情况下,该方法只调用一次, 因为有缓存。
注意:!!!!该方法一定要有参数,才会走缓存。如果是无参方法,则不会走缓存
*/
@HystrixCommand(commandKey = "cacheKey")
@CacheResult
public String cache(Integer i) {
System.out.println("&&&&&&&&&&&&&&(***********)))))))))))" +i);
String url = "http://app-member-server/getTest";
return url;
}
4.4删除缓存
//调cache方法使用缓存
//commandKey = "(这里是命令别名,移除缓存应该使用这里的key"
@HystrixCommand(commandKey = "cacheKey")
@CacheResult
public String cache(Integer i) {
System.out.println("&&&&&&&&&&&&&&(***********)))))))))))" +i);
String url = "http://app-member-server/getTest";
return url;
}
//调该方法清除缓存,这里需要和使用缓存的方法参数保持一致!!!
// @CacheRemove(commandKey = "这里是命令别名,使用获取缓存的key,必须相同")
@HystrixCommand
@CacheRemove(commandKey = "cacheKey")
public void removeCache(Integer i){
}
合并请求:
调用多次的服务获取不同的信息,这些服务的url一样,参数不同。对这些请求进行合并。
如有10个请求,10个参数调用同一个服务,我们将其合并成一个请求去调用。
Controller:
@RequestMapping(value = "/collapse", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
public String testCollapse() throws Exception {
Future<Member> member1 = orderService.getMember(1);
Future<Member> member2 = orderService.getMember(2);
Future<Member> member3 = orderService.getMember(3);
Member m1 = member1.get();
Member m2 = member2.get();
Member m3 = member3.get();
return "success";
}
service:
/**
* @param i 参数
* @return
* @HystrixCollapser 请求收集器注解
* batchMethod 收集的参数给谁进行处理
* timerDelayInMilliseconds: 收集多少秒内的请求,这里设置为1秒
* 调该方法其实并不会执行,只会收集请求,所有方法内的日志输出是没有的, 直接走getMembers方法
*/
@HystrixCollapser(batchMethod = "getMembers", collapserProperties = {
@HystrixProperty(name = "timerDelayInMilliseconds", value = "1000")
})
public Future<Member> getMember(Integer i) {
System.out.println("执行单个查询的方法");
return null;
}
//收集的请求最终走这个方法
@HystrixCommand
public List<Member> getMembers(List<Integer> ids) {
List<Member> list = Lists.newArrayList();
for (Integer i : ids) {
System.out.println(i);
Member m = new Member();
m.setId(i);
m.setName("12" + i);
list.add(m);
}
return list;
}
//执行结果:
**************过滤器拦截*****************
3
2
1
**************过滤器拦截*****************
五、SpringCoud整合Hystrix,整合Feign
1、整合feign
配置文件:
#hystrix整合feign的时候,这个开关打开
feign:
hystrix:
enabled: true
hystrix:
command:
HelloFeign#hello(): #这里是 接口#方法,是commandKey,指定具体的接口,如果需要全局配置,这里设置为 default
execution:
isolation:
thread:
timeoutInMilliseconds: 500 #超时时间,500毫秒
circuitBreaker:
requestVolumeThreshold: 3 #10秒内3次请求,如果超过百分之五十失败,则开启短路
常用配置:
Execution相关的属性的配置:
hystrix.command.default.execution.isolation.strategy 隔离策略,默认是Thread, 可选Thread|Semaphore
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds 命令执行超时时间,默认1000ms
hystrix.command.default.execution.timeout.enabled 执行是否启用超时,默认启用true
hystrix.command.default.execution.isolation.thread.interruptOnTimeout发生超时是是否中断,默认true
hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests 最大并发请求数,默认10,该参数当使用ExecutionIsolationStrategy.SEMAPHORE策略时才有效。如果达到最大并发请求数,请求会被拒绝。
2、Hystrix监控面板 hystrix-dashboard
参考:https://www.2cto.com/kf/201807/758784.html
被监控的客户端引入依赖:访问localhost:服务端口号/hystrix.stream可以看到。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
新建dashboard项目,引入依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
</dependencies>
在dashboard启动类上加注解
//在启动类中,添加@EnableHystrixDashboard注解,开启HystrixDashBoard
@EnableHystrixDashboard
@SpringBootApplication
public class DashBoardApplication {
public static void main(String[] args) {
SpringApplication.run(DashBoardApplication.class, args);
}
}
访问DashBoard路径:localhost:端口号/hystrix ,查看监控面板主页
输入被监控的项目路径: http://localhost:8030/hystrix.stream
注意:当第一次监控的时候,被监控的项目必须被访问后才能监测到数据进行展示,否则一直处于loading状态。