重新定义cloud 第6章 spring cloud hystrix

本文深入解析Hystrix断路器的工作原理,包括其在分布式系统中的作用,如隔离远程系统、防止级联故障、实现恢复能力。介绍了Hystrix的配置、使用场景、异常处理机制以及缓存、请求合并、线程隔离等高级特性。
摘要由CSDN通过智能技术生成

hystrix 概述

  • 是由 Netflix 开源的 分布式系统 容错 处理的开源框架

  • hystrix : 豪猪 , 浑身有刺来保护自己。

  • 是一个延迟和容错库,旨在 隔离 远程系统,服务 和 第三方框架,阻止级联故障,

  • 在复杂的分布式 系统中实现 恢复能力。

  • 服务1 承受不住压力或 发生其他内部错误,导致机器内部资源耗尽,

  • 其他服务 对1 有依赖,导致其他服务 也同样出现 请求堆积,资源占用等问题

  • 整个系统出现大面积的 延迟 和 瘫痪

  • 雪崩效应

  • 需要一种机制 来处理 延迟和故障

  • 保证 整个系统 处于 可用的状态。

设计目标:(官方)

  • 通过客户端库 对延迟和故障 进行保护和控制

  • 停止 级联故障

  • 快速失败 和 迅速 恢复

  • 回退 和 优雅地 降级

  • 实时监控,告警, 和 操作控制

  • hystrix 底层 大量使用 Rxjava

Hystrix 入门案例

引入和启用

		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
		</dependency>
@EnableDiscoveryClient //服务发现的客户端
@SpringBootApplication //boot的启动类

@EnableHystrix //开启hystrix
@EnableFeignClients // 开启feignClients
public class ClientApplication {}

使用

  • fallback降级:方法执行错误,也会降级
@Component
public class UserService implements IUserService{
	
	@Override
	@HystrixCommand(fallbackMethod="defaultUser")
	public String getUser(String username) throws Exception {
		if(username.equals("spring")) {
			return "This is real user";
		}else {
			throw new Exception();
		}
	}
	
	 /**
	  * 出错则调用该方法返回友好错误
	  * @param username
	  * @return
	  */
	 public String defaultUser(String username) {
	    return "The user does not exist in this system";
	 }
}

@RestController
public class TestController {
	@Autowired
	private IUserService userService;
	
	@GetMapping("/getUser")
	public String getUser(@RequestParam String username) throws Exception{
		return userService.getUser(username);
	}
}

测试

http://localhost:8888/getUser?username=spring
This is real user
http://localhost:8888/getUser?username=aaa
The user does not exist in this system

feign中使用断路器

  • feign中 老版本是 默认打开的
  • 新版本默认是关闭的

xml

		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>//网飞 hystrix开始组件
		</dependency>

		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-openfeign</artifactId> //开始 feign组件
		</dependency>

使用

@FeignClient(name = "sc-provider-service", fallback = UserServiceFallback.class)

public interface IUserService {
    
	@RequestMapping(value = "/getUser",method = RequestMethod.GET)
    public String getUser(@RequestParam("username") String username);
    
}

@Component //交给spring管理
public class UserServiceFallback implements IUserService{
	/**
	  * 出错则调用该方法返回友好错误
	  * @param username
	  * @return
	  */
	public String getUser(String username){
		return "The user does not exist in this system, please confirm username";
	}
}

yaml配置和测试

applicaton.yml 开启配置
feign:
  hystrix:
    enabled: true

http://localhost:8888/getUser?username=spring

未开启hystrix:将会报错
ClientException: Load balancer does not have available server for client: sc-provider-service

开启hystrix,将会进入回退方法:
The user does not exist in this system, please confirm username
  • 如果 feign的对象 报错(如扔个异常),也会进入 回退的方法

Hystrix Dashboard

  • 仪表盘
  • 一段时间内 发生的请求情况 来展示的 可视化面板

搭建hystrix

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
		<!-- <dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
		</dependency> -->
feign:
  hystrix:
    enabled: true #开启feign
    
management:
  security:
    enabled: false
  endpoints:
    web:
      exposure:
        include: hystrix.stream #打开端点
@FeignClient(name = "sc-provider-service")
public interface ProviderService {
	
	@RequestMapping(value = "/getDashboard", method = RequestMethod.GET)
    public List<String> getProviderData();

}

搭建Hystrix dashboard 工程

         <dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
		</dependency>

  		 <dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
		</dependency>
@SpringBootApplication
@EnableDiscoveryClient

@EnableHystrixDashboard //开启dashboard
public class HystrixDashboardApplication {
}
management:
  security:
    enabled: false
  endpoints:
    web:
      exposure:
        include: hystrix.stream
测试:
http://192.168.20.92:9000/hystrix #能看到Dashboard界面
访问:
http://192.168.20.92:9091/actuator/hystrix.stream #可以看到 ping data,data里是个对象
在dashboard 输入这个流。
随便访问一个网址,如:http://localhost:9091/getProviderData ,在dashboard界面即可看到这个断路器的状态。
默认集群监控: http://turbine-hostname:port/turbine.stream
指定集群监控: http://turbine-hostname:port/turbine.stream?cluster=[clusterName]
单个应用: http://hystrix-app:port/hystrix.stream
  • http://192.168.20.92:9091/actuator/hystrix.stream F版的spring boot 版本 2.0 ,所有端点前面都加了actuator ,不然会访问不到。

dashboard 监控图

  • 圆圈:
    • 流量的大小 和 流量的健康。 绿色,黄色,橙色,红色
  • 曲线
    • 2分钟内流量的变化,发现流量的浮动趋势。
  • 右边的数字
    • 左边第一列 代表请求的成功,success
    • 熔断数 short-circuited
    • 错误的请求 bad request
    • 超时的请求 timeout
    • 线程池拒绝数 rejected
    • 失败的请求 failure
    • 最近10秒内错误的比率 error %
  • 其他:
    • host cluster 机器和集群的请求频率
    • circuit 熔断器的状态 open/closed
    • hosts median mean 集群下的报告,百分位延迟数
    • thread pools ,线程池的指标,核心线程池指标,队列大小。

提供者配置

xml
    <dependencies>
        <dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
		</dependency> //客户端
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
		</dependency> //hystrix
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency> //openfeign
    </dependencies>
    
yaml
bootstrap.yml

server:
  port: 9091
spring:
  application:
    name: sc-hello-serviceserver:
  port: 9091
spring:
  application:
    name: sc-hello-service
    
    
application.yml

eureka:
  client:
    serviceUrl:
      defaultZone: http://${eureka.host:127.0.0.1}:${eureka.port:8761}/eureka/
  instance:
    prefer-ip-address: true #eureka
    
management:
  security:
    enabled: false #关闭security
  endpoints:
    web:
      exposure:
        include: hystrix.stream #开启hystrix端点
feign:
  hystrix:
    enabled: true #开启hystrix
    
ribbon:
  ConnectTimeout: 6000 #连接超时时间
  ReadTimeout: 6000 #读取超时时间
  MaxAutoRetries: 0 #最大重试
  MaxAutoRetriesNextServer: 0 #自动重试 对于下个server
  
hystrix:
  command: #命令
    default: #默认
      execution: #执行
        timeout: #超时
        isolation: #隔离
          thread: #线程
            timeoutInMilliseconds: 15000 #hystrix命令 默认 执行 超时 隔离 线程 毫秒
            #超时 在 毫秒
isolation 
英 /ˌaɪsəˈleɪʃn/  美 /ˌaɪsəˈleɪʃn/  全球(英国)  
简明 牛津 新牛津  韦氏  柯林斯 例句  百科
n. 隔离;孤立;[] 绝缘;[化学] 离析
@SpringBootApplication
@EnableDiscoveryClient

@EnableHystrix
@EnableFeignClients

public class HelloServiceApplication {}

turbine 聚合 hystrix

  • 聚合整个集群下的 监控状况,然后在 hystrix Dashboard中显示

xml引入

    <dependencies>
        <dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
		</dependency>  #dashboard
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
		</dependency> #hystrix
        <dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-turbine</artifactId>
		</dependency> #turbine
  		 <dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
		</dependency> #eureka-client
    </dependencies>

main方法开启

@SpringBootApplication
@EnableDiscoveryClient

@EnableTurbine

@EnableHystrixDashboard
public class TurbineApplication {
    public static void main(String[] args) {
        SpringApplication.run(TurbineApplication.class, args);
    }
}

yaml配置

#bootstrap.yml
server:
  port: 9088
spring:
  application:
    name: sc-turbine-dashboard

#application.yml
eureka:
  client:
    serviceUrl:
      defaultZone: http://${eureka.host:127.0.0.1}:${eureka.port:8761}/eureka/
  instance:
    prefer-ip-address: true
management:
  security:
    enabled: false
  endpoints:
    web:
      exposure:
        include: hystrix.stream
turbine:
  appConfig: sc-hello-service,sc-provider-service #需要收集 监控信息的服务名
  clusterNameExpression: "'default'" #设置集群名称

测试

http://localhost:9088/hystrix (dashboard 和 turbine一个项目)
输入:http://localhost:9088/turbine.stream 
之后:访问一下请求:
http://localhost:8099/getHelloService
http://localhost:9091/getProviderData

hystrix异常处理机制

  • hystrix 异常处理,有5种出错 会被 fallback所截获

    • failure 执行失败,抛出异常
    • timeout 超时
    • short_circuited 断路器 打开
    • thread_pool_rejected 线程池拒绝
    • semaphore_rejected 信号量拒绝
  • bad_request 会抛出 hystrix BadRequest Exception ,非法参数

  • 通过例子测试:

    public class PSFallbackBadRequestExpcetion extends HystrixCommand<String>{
    	private static Logger log = LoggerFactory.getLogger(PSFallbackBadRequestExpcetion.class);
    	
        public PSFallbackBadRequestExpcetion() {
            super(HystrixCommandGroupKey.Factory.asKey("GroupBRE"));
        }
        
    	@Override
    	protected String run() throws Exception {
    		 throw new HystrixBadRequestException("HystrixBadRequestException error");
    	}
    	
        @Override
        protected String getFallback() {
            //通过方法 来获取异常
        	System.out.println(getFailedExecutionException().getMessage());
            return "invoke HystrixBadRequestException fallback method:  ";
        }
    	
    	
    }
    
    //如果抛出:throw new Exception("this command will trigger fallback");
    //会被 getFallback() 触发
    
    

hystrix的配置说明

  • https://github.com/Netflix/Hystrix/wiki/Configuration 官方的配置文档

hystrixCommand 命令的超时时间

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 15000

隔离策略,默认为方法名

hystrix:
  command:
    default:
      execution:
        isolation:
          strategy: thread

hystrix command 是否开启超时

hystrix:
  command:
    default:
      execution:
        timeout:
          enabled: true

超时是否中断 执行的操作

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            interruptOnTimeout: true

设置信号量 隔离策略时,设置最大允许的请求数

hystrix:
  command:
    default:
      execution:
        isolation:
          semaphore:
            maxConcurrentRequests: 10

circuit breaker设置 打开 fallback 并 启动 fallback 逻辑的 错误比率

hystrix:
  command:
    default:
      circuitBreaker:
        errorThresholdPercentage: 50

当为线程隔离时,线程池 核心大小

hystrix:
  threadpool:
    default:
      coreSize: 10

隔离模式为线程池 隔离模式时,最大线程池 大小的配置如下,

hystrix:
  threadpool:
    default:
      maximumSize: 10
hystrix:
  threadpool:
    default:
      allowMaximumSizeToDivergeFromCoreSize: true #(默认为 false,上面的生效,1.5.9需要配置)      
  • 真实环境:一般对 超时时间,线程池大小,信号量 修改
  • 超时时间 为 5-10秒
  • 如果配置了ribbon的超时,Ribbon的时间 应该短于 Hystrix超时时间。
  • feign配置15秒,ribbon配置到10秒。

hystrix其他设置

  • Hystrix 线程调整 和 计算
    • 每秒请求的峰值 * 99% 的延迟百分比(请求的相应时间)+预留缓冲的值
    • 30 * 0.2s=6+预留缓冲的值=10 ,预留了4个线程数

hystrix缓存

  • 初始化请求上下文

    public class CacheContextInterceptor implements HandlerInterceptor {
    	//注入 hystrix上下文
    	private HystrixRequestContext context;
    	
    	@Override
    	public boolean preHandle() {//执行前
    		context = HystrixRequestContext.initializeContext();
    		return true;
    	}
    
    	@Override
    	public void postHandle() {
    	}
     
    	@Override //方法成功执行后
    	public void afterCompletion() {
    		context.shutdown();
    	}
    	
    }
    

使用类 来开启缓存

public class HelloCommand extends HystrixCommand<String>{

    private RestTemplate restTemplate;
    private Integer id;
    
    public HelloCommand(RestTemplate restTemplate, Integer id) {
        super(HystrixCommandGroupKey.Factory.asKey("springCloudCacheGroup"));
        this.id = id;
        this.restTemplate = restTemplate;
    }
    
    @Override
    protected String getFallback() {
        return "fallback";
    }

    @Override
    protected String run() throws Exception {
		String json = restTemplate.getForObject("http://sc-provider-service/getUser/{1}", String.class, id);
		System.out.println(json);
		return json;
    }

    @Override //同一个请求,返回同样的 键值
    protected String getCacheKey() {
        return String.valueOf(id);
    }
    
    //清除缓存
    public static void cleanCache(Long id){
        HystrixRequestCache.getInstance(
            HystrixCommandKey.Factory.asKey("springCloudCacheGroup"), HystrixConcurrencyStrategyDefault.getInstance())
            .clear(String.valueOf(id));
    }

}

使用注解来开启缓存

  • @CacheResult

  • @CacheRemove

    @Component
    public class HelloService implements IHelloService{
    
    	@Autowired
    	private RestTemplate restTemplate;
    	 
    	@CacheResult //加入缓存
    	@HystrixCommand
    	public String hello(Integer id) {
    		String json = restTemplate.getForObject("http://sc-provider-service/getUser/{1}", String.class, id);
    		System.out.println(json);
    		return json;
    	}
    	
    	
    	@CacheResult //使用缓存
    	@HystrixCommand(commandKey = "getUser")
    	public String getUserToCommandKey(@CacheKey Integer id) {
    		String json = restTemplate.getForObject("http://sc-provider-service/getUser/{1}", String.class, id);
    		System.out.println(json);
    		return json;
    	}
    	
    	@CacheRemove(commandKey="getUser") //删除缓存
    	@HystrixCommand
    	public String updateUser(@CacheKey Integer id) {
    		System.out.println("删除getUser缓存");
    		return "update success";
    	}
    }
    

controller

@RestController
public class CacheController {
	private static final Logger logger = LoggerFactory.getLogger(CacheController.class);
	
	@Autowired
	private IHelloService helloService;
	@Autowired
	private RestTemplate restTemplate;
	
	/**
	 * 缓存
	 * @param id
	 * @return
	 */
	@RequestMapping(value = "/getUser/{id}", method = RequestMethod.GET)
	public String getUserId(@PathVariable("id") Integer id) {
		helloService.hello(id);
		helloService.hello(id);
	    return "getUser success";
	}
	
	/**
	 * 缓存更新测试
	 * @param id
	 * @return
	 */
	@RequestMapping(value = "/getUserIdUpdate/{id}", method = RequestMethod.GET)
	public String getUserIdUpdate(@PathVariable("id") Integer id) {
		helloService.hello(id);
		helloService.hello(id);
		helloService.hello(5);
	    return "getUserIdUpdate success";
	}
	
	/**
	 * 继承类方式
	 * @param id
	 * @return
	 */
	@RequestMapping(value = "/getUserIdByExtendCommand/{id}", method = RequestMethod.GET)
	public String getUserIdByExtendCommand(@PathVariable("id") Integer id) {
		HelloCommand one = new HelloCommand(restTemplate,id);
		one.execute();
		logger.info("from cache:   " + one.isResponseFromCache());
		HelloCommand two = new HelloCommand(restTemplate,id);
		two.execute();
		logger.info("from cache:   " + two.isResponseFromCache());
	    return "getUserIdByExtendCommand success";
	}
	
	/**
	 * 缓存和清除缓存
	 * @param id
	 * @return
	 */
	@RequestMapping(value = "/getAndUpdateUser/{id}", method = RequestMethod.GET)
	public String getAndUpdateUser(@PathVariable("id") Integer id) {
		//调用接口并缓存数据
		helloService.getUserToCommandKey(id);
		helloService.getUserToCommandKey(id);
		//清除缓存
		helloService.updateUser(id);
		//再调用接口
		helloService.getUserToCommandKey(id);
		helloService.getUserToCommandKey(id);
		return "getAndUpdateUser success";
	}
	
}
  • 大量重复的接口,可以利用缓存机制 有效地 减轻 请求的压力
    • 开启@EnableHystrix
    • 初始化 HystrixRequestContext
    • 注意:要用@HystrixCommand

Hystrix Request Collapser

  • 多个请求 调用 单个后端依赖做的 一种优化和节约 网络开销的方法

  • 只 需要一个 线程 和 一个连接的 开销

  • 实现 Hystrix 上下文的初始化 和 关闭

    public class HystrixContextInterceptor implements HandlerInterceptor {
    	
    	private HystrixRequestContext context;
    	
    	@Override
    	public boolean preHandle(HttpServletRequest request, HttpServletResponse respone, Object arg2) throws Exception {
    		context = HystrixRequestContext.initializeContext();
    		return true;
    	}
    
    	@Override
    	public void postHandle()
    			throws Exception {
    	}
    
    	@Override
    	public void afterCompletion(){
    		context.shutdown();
    	}
    	
    }
    

使用

@Component
public class CollapsingService implements ICollapsingService{
	 
	 @HystrixCollapser(batchMethod = "collapsingList", collapserProperties = {
			 @HystrixProperty(name="timerDelayInMilliseconds", value = "1000")
	    })
	public Future<Animal> collapsing(Integer id) {
		return null;
	}
	 
	 @HystrixCollapser(batchMethod = "collapsingListGlobal",scope = Scope.GLOBAL, collapserProperties = {
			 @HystrixProperty(name="timerDelayInMilliseconds", value = "10000")
	    })
	public Future<Animal> collapsingGlobal(Integer id) {
		return null;
	}
	
	@HystrixCollapser(batchMethod = "collapsingList", collapserProperties = {
			 @HystrixProperty(name="timerDelayInMilliseconds", value = "1000")
	    })
	public Animal collapsingSyn(Integer id) {
		// TODO Auto-generated method stub
		return null;
	}
	
	@HystrixCommand
	public List<Animal> collapsingList(List<Integer> animalParam) {
		System.out.println("collapsingList当前线程" + Thread.currentThread().getName());
		System.out.println("当前请求参数个数:" + animalParam.size());
		List<Animal> animalList = new ArrayList<Animal>();
        for (Integer animalNumber : animalParam) {
        	Animal animal = new Animal();
        	animal.setName("Cat - " + animalNumber);
        	animal.setSex("male");
        	animal.setAge(animalNumber);
        	animalList.add(animal);
        }
        return animalList;
	}
	
	
	@HystrixCommand
	public List<Animal> collapsingListGlobal(List<Integer> animalParam) {
		System.out.println("collapsingListGlobal当前线程" + Thread.currentThread().getName());
		System.out.println("当前请求参数个数:" + animalParam.size());
		List<Animal> animalList = new ArrayList<Animal>();
        for (Integer animalNumber : animalParam) {
        	Animal animal = new Animal();
        	animal.setName("Dog - " + animalNumber);
        	animal.setSex("male");
        	animal.setAge(animalNumber);
        	animalList.add(animal);
        }
        return animalList;
	}
	
}
  • 	 @HystrixCollapser(batchMethod = "collapsingList", collapserProperties = {
      			 @HystrixProperty(name="timerDelayInMilliseconds", value = "1000")
      	    })
      	public Future<Animal> collapsing(Integer id) {
      		return null;
      	}
    
  • 实现 Future异步返回值 的方法,配置请求合并的注解,外部通过调用这 方法 来实现 请求的 合并。

  • 这个方法必须是Future异步返回值的

  • HystrixCollapser 开启请求合并,实际运行collapsingList

  • timerDelayInMilliseconds 合并 1s 内的请求,默认10ms

  • public List collapsingList

  • feign 不支持 合并请求

  • 如何对所有线程 请求中的多次服务 进行合并呢

    • 修改 @HystrixCollapser注解的 Scope属性,配置成Global
    • scope = Scope.GLOBAL 。默认是 Request
  • 当 某个时间 内有大量 或 并发的相同请求时,适合 用请求合并。

action

@RestController
public class CollapsingController {
	
	@Autowired
	private ICollapsingService collapsingService;
	
	/**
	 * 请求聚合/合并
	 * @return
	 * @throws ExecutionException
	 * @throws InterruptedException
	 */
	@RequestMapping("/getAnimal")
	public String getAnimal() throws Exception {
	    Future<Animal> user = collapsingService.collapsing(1);
	    Future<Animal> user2 = collapsingService.collapsing(2);
	    System.out.println(user.get().getName());
	    System.out.println(user2.get().getName());
	    return "Success";
	}
	
	/**
	 * 返回值必须是Future,否则不会进行合并/聚合
	 * @return
	 * @throws ExecutionException
	 * @throws InterruptedException
	 */
	@RequestMapping("/getAnimalSyn")
	public String getAnimalSyn() throws ExecutionException, InterruptedException {
	    Animal user = collapsingService.collapsingSyn(1);
	    Animal user2 = collapsingService.collapsingSyn(2);
	    System.out.println(user.getName());
	    System.out.println(user2.getName());
	    return "Success";
	}
	
	
	/**
	 * 请求聚合/合并,整个应用的
	 * @return
	 * @throws ExecutionException
	 * @throws InterruptedException
	 */
	@RequestMapping("/getAnimalGolbal")
	public String getAnimalGolbal() throws Exception {
	    Future<Animal> user = collapsingService.collapsingGlobal(1);
	    Future<Animal> user2 = collapsingService.collapsingGlobal(2);
	    System.out.println(user.get().getName());
	    System.out.println(user2.get().getName());
	    return "Success";
	}
	
}

Hystrix 线程传递及 并发隔离

  • 提供了两种隔离方式
    • 信号量
      • 请求的时候 会获取到一个信号量,成功拿到,继续进行请求,请求在一个线程中执行完毕
    • 线程隔离
      • 把请求放入线程池中执行,有可能产生线程的变化,导致线程1的上下文,在线程2拿不到。

新建 请求接口 和 本地线程持有对象

  • 建立 ThreadLocal ,把当前请求的上下文 数据放入 本地线程变量,方便使用 及 销毁

    public class HystrixThreadLocal {
        public static ThreadLocal<String> threadLocal = new ThreadLocal<>();
    }
    

定义controller

@RestController
public class ThreadContextController {
  //得到日志对象。
  private static final Logger log = LoggerFactory.getLogger(ThreadContextController.class);
     
  @Autowired//注入service
  private IThreadContextService threadContextService;
  
  //接口的使用
  @RequestMapping(value = "/getUser/{id}", method = RequestMethod.GET)
  public String getUser(@PathVariable("id") Integer id) {
  	//第一种测试,放入上下文对象
  	HystrixThreadLocal.threadLocal.set("userId : "+ id);
  	//第二种测试,利用RequestContextHolder放入对象测试
  	RequestContextHolder.currentRequestAttributes().setAttribute("userId", "userId : "+ id, RequestAttributes.SCOPE_REQUEST);
  	//打印线程的ID
  	log.info("ThreadContextController, Current thread: " + Thread.currentThread().getId());
  	//打印 ThreadLocal 的get
      log.info("ThreadContextController, Thread local: " + HystrixThreadLocal.threadLocal.get());
      //打印 request域属性
      log.info("ThreadContextController, RequestContextHolder: " + RequestContextHolder.currentRequestAttributes().getAttribute("userId", RequestAttributes.SCOPE_REQUEST));
      //调用
      String user = threadContextService.getUser(id);
      return user;
  }
}

定义service

public interface IThreadContextService {
	public String getUser(Integer id);
}

@Component
public class ThreadContextService implements IThreadContextService{
	private static final Logger log = LoggerFactory.getLogger(ThreadContextController.class);
	
   @HystrixCommand
	public String getUser(Integer id) {
		log.info("ThreadContextService, Current thread : " + Thread.currentThread().getId());
		log.info("ThreadContextService, ThreadContext object : " + HystrixThreadLocal.threadLocal.get());//获得到ThreadLocal
		log.info("ThreadContextService, RequestContextHolder : " + RequestContextHolder.currentRequestAttributes().getAttribute("userId", RequestAttributes.SCOPE_REQUEST).toString());//得到请求域中的对象
		return "Success";
	}
}
  • http://localhost:3333/getUser/55555

线程隔离模式下,获取用户信息

  • 如果在 getUser接口 @HystrixCommand ,会发现 当达到后台服务的时候,线程ID变了。
  • 线程的隔离已经生效,是重新启动的线程进行请求的,父子线程数据传递的问题

修改隔离策略

  • 使用信号量隔离,hystrix默认是线程隔离,(不推荐)
hystrix:
  command:
    default:
      execution:
        isolation:
          strategy: thread

使用 HystrixConcurrencyStrategy

  • 实现 wrapCallable方法
  • warpCallable提供在请求执行前 包装的机会,注入自己定义的动作,比如复制线程的状态。
public class SpringCloudHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {
    private HystrixConcurrencyStrategy delegateHystrixConcurrencyStrategy;
    
    @Override
    public <T> Callable<T> wrapCallable(Callable<T> callable) {
        return new HystrixThreadCallable<>(callable, RequestContextHolder.getRequestAttributes(),HystrixThreadLocal.threadLocal.get());
    }//传递request域和 自定义的 HystrixThreadLocal 
}
public class HystrixThreadCallable<S> implements Callable<S>{
	 private final RequestAttributes requestAttributes;  
	 private final Callable<S> delegate;
	 private String params;
	  
     public HystrixThreadCallable(Callable<S> callable, RequestAttributes requestAttributes,String params) {  
         this.delegate = callable; 
         this.requestAttributes = requestAttributes;  
         this.params = params;
     }

     @Override  
     public S call() throws Exception {  
         try {
             RequestContextHolder.setRequestAttributes(requestAttributes);
             HystrixThreadLocal.threadLocal.set(params);
             return delegate.call();  
         } finally {
             RequestContextHolder.resetRequestAttributes();
             HystrixThreadLocal.threadLocal.remove();
         }  
     }  
       
}

  • 计算service上写 这个注解,也可传递

       @HystrixCommand
    	public String getUser(Integer id) {}
    

并发策略

  • 上面 registerConcurrencyStrategy 方法只能被调用一次,不然会报错,无法与其他并发策略一起。

  • 把其他并发策略注入进去,达到并存,

    public class SpringCloudHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {
        private HystrixConcurrencyStrategy delegateHystrixConcurrencyStrategy;
        
        @Override
        public <T> Callable<T> wrapCallable(Callable<T> callable) {
            return new HystrixThreadCallable<>(callable, RequestContextHolder.getRequestAttributes(),HystrixThreadLocal.threadLocal.get());
        }
        
        public SpringCloudHystrixConcurrencyStrategy() {
        	init();
        }
        
        private void init() {
       	 try {
                this.delegateHystrixConcurrencyStrategy = HystrixPlugins.getInstance().getConcurrencyStrategy();
                if (this.delegateHystrixConcurrencyStrategy instanceof SpringCloudHystrixConcurrencyStrategy) {
                    return;
                }
    
                HystrixCommandExecutionHook commandExecutionHook = HystrixPlugins.getInstance().getCommandExecutionHook();
                HystrixEventNotifier eventNotifier = HystrixPlugins.getInstance().getEventNotifier();
                HystrixMetricsPublisher metricsPublisher = HystrixPlugins.getInstance().getMetricsPublisher();
                HystrixPropertiesStrategy propertiesStrategy = HystrixPlugins.getInstance().getPropertiesStrategy();
    
                HystrixPlugins.reset();
                HystrixPlugins.getInstance().registerConcurrencyStrategy(this);
                HystrixPlugins.getInstance().registerCommandExecutionHook(commandExecutionHook);
                HystrixPlugins.getInstance().registerEventNotifier(eventNotifier);
                HystrixPlugins.getInstance().registerMetricsPublisher(metricsPublisher);
                HystrixPlugins.getInstance().registerPropertiesStrategy(propertiesStrategy);
            }
            catch (Exception e) {
                throw e;
            }
       }
        
        
        @Override
    	public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,
    			HystrixProperty<Integer> corePoolSize,
    			HystrixProperty<Integer> maximumPoolSize,
    			HystrixProperty<Integer> keepAliveTime, TimeUnit unit,
    			BlockingQueue<Runnable> workQueue) {
    		return this.delegateHystrixConcurrencyStrategy.getThreadPool(threadPoolKey, corePoolSize, maximumPoolSize,
    				keepAliveTime, unit, workQueue);
    	}
    
    	@Override
    	public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,
    			HystrixThreadPoolProperties threadPoolProperties) {
    		return this.delegateHystrixConcurrencyStrategy.getThreadPool(threadPoolKey, threadPoolProperties);
    	}
    
    	@Override
    	public BlockingQueue<Runnable> getBlockingQueue(int maxQueueSize) {
    		return this.delegateHystrixConcurrencyStrategy.getBlockingQueue(maxQueueSize);
    	}
    
    	@Override
    	public <T> HystrixRequestVariable<T> getRequestVariable(
    			HystrixRequestVariableLifecycle<T> rv) {
    		return this.delegateHystrixConcurrencyStrategy.getRequestVariable(rv);
    	}
    
    }
    

Hystrix 命令注解

  • HystrixCommand

    • 封装执行的代码,具有故障延迟容错,断路器,统计,是阻塞命令。和 Observable共用的。
    • 默认是阻塞式,提供同步和异步两种方式。
    • 方法时run
    • 一个实例只能发送一条数据出去
  • HystrixObservableCommand ,

    • 是一个非阻塞的调用模式
    • 方法时 construct
    • 可以发送多条数据
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface HystrixCommand {
    String groupKey() default "";//唯一标识服务分组的名称,内部根据这个键值来统计,仪表盘等,
    //默认的线程划分 是根据这命令组的名称来进行的。

    String commandKey() default "";//全局唯一的标识符,默认是方法名

    String threadPoolKey() default "";//对服务的线程池信息进行设置,监控,缓存等用途。
    
    String defaultFallback() default ""; //默认的fallback方法

    String fallbackMethod() default "";//指定的处理回退逻辑的方法
    
    Class<? extends Throwable>[] ignoreExceptions() default {};//HystrixBadRequestException 不会触发 fallback ,不希望哪些异常被回退,而是直接抛出。

    HystrixProperty[] commandProperties() default {};//配置一些命名的属性,如隔离策略

    HystrixProperty[] threadPoolProperties() default {};//配置线程池相关的属性


    ObservableExecutionMode observableExecutionMode() default ObservableExecutionMode.EAGER;

    HystrixException[] raiseHystrixExceptions() default {};


}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值