hystrix对应的中文名字是“豪猪”,豪猪周身长满了刺,能保护自己不受天敌的伤害,代表了一种防御机制, 所以开发团队取了这个名字. (⊙o⊙)…
为什么要用断路器? 其实这个跟保险丝一样, 是为了避免分布式系统中的”雪崩效应”,或者我觉得也是多米诺骨牌效应.
比如A调用服务B,服务B调用服务C…如果C因为某些原因挂掉了,或者网络原因掉不到C,这时候B这个方法也挂掉了,A这边也跟着挂….这就是雪崩效应,其实跟spring管理实例一样,当实例很多,又互相依赖的时候, 通常我们需要靠框架来帮我们进行管理.
当某个服务挂掉之后,通过hystrix,我们可以跟定义变量的默认值一样,给方法定义一个Fallback
方法来执行
这样我们就有了服务提供者和注册中心
service-ribbon的端口号是8805
使用熔断器很简单,首先在 service-ribbon中 引入依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-hystrixartifactId>
dependency>
在启动app上打上注解,开启hystrix
package com.zgd.springcloud.eurekaClient;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
/**
* eureka client 客户端
*/
@EnableDiscoveryClient
@EnableEurekaClient
//开启hystrix熔断
@EnableHystrix
@SpringBootApplication
public class RibbonApp {
public static void main(String[] args) {
SpringApplication.run(RibbonApp.class, args);
}
/**
* 使用restTemplate消费方法
* @return
* @LoadBalanced 负载均衡
*/
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
}
在回到我们的service层, 首先定义一个error方法,在熔断的时候执行这个方法
/**
* 熔断执行的方法
* @param name
* @return
*/
public String hiError(String name){
return name + ",client1出现问题,进行了熔断";
}
然后我们在service方法上打上注解@HystrixCommand
指定fallback方法
/**
* 通过restTemplate采用rest直接访问eureka-client-01的路由调用方法
* @param name
* @return
*/
@HystrixCommand(fallbackMethod = "hiError")//指定熔断时调用的方法
public String hiService(String name) {
//eureka-client-01工程的applicationName
String client01Name = "client01";
//eureka-client-01工程的ProducerController的hello方法的路由
String prefix = "hello";
//访问eureka-client-01的路由,远程调用eureka-client-01的方法
return restTemplate.getForObject("http://" + client01Name + "/" + prefix + "?name=" + name, String.class);
}
然后我们启动service-ribbon, 访问 http://localhost:8085/getHi?name=zgd
这个时候是没问题的
然后我们关闭eureka-client-01 ,按理来说会报错,但是访问连接显示
可见我们的fallback正常调用了
feign同样的相对来说简单一些
首先配置文件增加配置feign.hystrix.enable = true
开启熔断器
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
spring:
application:
name: service-feign
server:
port: 8086
feign:
hystrix:
enabled: true
我们之前是只写了一个接口,并没有写具体实现类, 然后我们实现一下FeignService接口,写一个业务的具体实现类,
package com.zgd.springcloud.eurekaClient.imp;
import com.zgd.springcloud.eurekaClient.interf.FeignService;
import org.springframework.stereotype.Component;
@Component
public class FeignServiceImpl implements FeignService {
@Override
public String hiService(String name) {
return name+",这里被熔断了";
}
}
其中覆写的方法就是我们的fallback方法
然后在接口上标明我们的实现类是fallback类
package com.zgd.springcloud.eurekaClient.interf;
import com.zgd.springcloud.eurekaClient.imp.FeignServiceImpl;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
/**
* @FeignClient value是指定的服务提供者在eurekaServer中的服务名,eureka-client-01的applicationName就是client01
*/
@FeignClient(value = "client01",fallback = FeignServiceImpl.class)
@Component
public interface FeignService{
/**
* 这里还是访问的eureka-client-01的
* com.zgd.springcloud.eurekaClient.controller.ProducerController
* 的hello方法的路由
* @param name
* @return
*/
@RequestMapping(value = "/hello",method = RequestMethod.GET)
String hiService(@RequestParam(value = "name") String name);
}
这样就ok了, 不启动client ,直接启动service-feign, 访问http://localhost:8086/getHi?name=zgd
熔断成功.
所以ribbon和feign之间,主要的一个区别就是ribbon是在方法级上定义熔断后调用的fallback,feign是在类级上,直接创建一个实现类,去调用实现类的方法
@HystrixCommand
有两种隔离机制,THREAD(默认,推荐)和SEMAPHORE
顾名思义,thread隔离机制,跟线程池相关,Hystrix的线程池配置如下:
线程数默认值10,适用于大部分情况(有时可以设置得更小),如果需要设置得更大,那有个基本得公式可以follow:
requests per second at peak when healthy × 99th percentile latency in seconds + some breathing room
每秒最大支撑的请求数 (99%平均响应时间 + 缓存值)
比如:每秒能处理1000个请求,99%的请求响应时间是60ms,那么公式是:
1000 (0.060+0.012)
基本得原则时保持线程池尽可能小,他主要是为了释放压力,防止资源被阻塞。
当一切都是正常的时候,线程池一般仅会有1到2个线程激活来提供服务
hystrix.threadpool.default.coreSize
并发执行的最大线程数,默认10
hystrix.threadpool.default.maxQueueSize
BlockingQueue的最大队列数,当设为-1,会使用SynchronousQueue,值为正时使用LinkedBlcokingQueue。该设置只会在初始化时有效,之后不能修改threadpool的queue size,除非reinitialising thread executor。默认-1。
hystrix.threadpool.default.queueSizeRejectionThreshold
即使maxQueueSize没有达到,达到queueSizeRejectionThreshold该值后,请求也会被拒绝。因为maxQueueSize不能被动态修改,这个参数将允许我们动态设置该值。if maxQueueSize == -1,该字段将不起作用
hystrix.threadpool.default.keepAliveTimeMinutes
如果corePoolSize和maxPoolSize设成一样(默认实现)该设置无效。如果通过plugin(https://github.com/Netflix/Hystrix/wiki/Plugins)使用自定义实现,该设置才有用,默认1.
hystrix.threadpool.default.metrics.rollingStats.timeInMilliseconds
线程池统计指标的时间,默认10000
hystrix.threadpool.default.metrics.rollingStats.numBuckets
将rolling window划分为n个buckets,默认10
//name一般都是上面一串中default后面的
@HystrixCommand(fallbackMethod = "hiError"
,threadPoolProperties = {
@HystrixProperty(name = "coreSize",value = "100")
}
)
如果在高并发情况下,大量请求超过了线程池处理能力, 将会熔断, 采用jmeter发出1000条并发请求,结果如下:
Execution相关的属性的配置:
hystrix.command.default.execution.isolation.strategy
隔离策略,默认是Thread, 可选Thread|Semaphore
thread 通过线程数量来限制并发请求数,可以提供额外的保护,但有一定的延迟。一般用于网络调用
semaphore 通过semaphore count来限制并发请求数,适用于无网络的高并发请求
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策略时才有效。
如果达到最大并发请求数,请求会被拒绝。理论上选择semaphore size的原则和选择thread size一致,但选用semaphore时每次执行的单元要比较小且执行速度快(ms级别),否则的话应该用thread。
semaphore应该占整个容器(tomcat)的线程池的一小部分。
@HystrixCommand(fallbackMethod = "hiError"
, commandProperties = {
//隔离策略,默认推荐是THREAD,还有就是SEMAPHORE(信号)
@HystrixProperty(name = "execution.isolation.strategy", value = "SEMAPHORE"),
//熔断的最大并发请求,只有在隔离策略是SEMAPHORE时才生效
@HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests", value = "1000")
}
)
https://github.com/zzzgd/SpringCloudDemo/tree/master/spring-cloud-part2