Hystrix-超时机制和断路器模式

  • Post author:
  • Post category:其他



一 超时机制、断路器模式简介


1.1 背景





假设服务提供者响应非常缓慢,那么消费者对提供者的请求线程就会被等待,知道服务返回,在高并发高负载的场景下,如果不做任何处理,这种问题很有可能造成所有处理用户请求的线程的资源耗竭,而不能响应用户进一步请求。


如果服务消费者又是另外服务的提供者,那么有可能产生级联反应,导致其它的下一级服务消费者不可用,最终产生雪崩效应。



1.2 解决方案


1.2.1 超时机制


通过网络请求其他服务时,都设置超时。正常情况下,一个远程调用,即使毫秒就返回了。


当依赖的服务不可用的时候,或者因为网络问题,响应时间会变得很长(几十秒)。而通常情况下,一次远程调用对应了一个线程或者进程,如果响应太慢,那这个线程、进程就会得不到释放。而线程和进程是系统资源,如果大量线程、进程都不被释放,越积越多,服务资源就会被耗尽。所以我们必须设置超时请求。



1.2.2 断路器模式


类似于电短路的时候,跳闸。电流量过大,就会自动断开电路,避免电路升温,烧断电路或者电器。


当依赖的服务有大量超时的时候,再让新的请求去访问已经没有太大意义,只会无谓的消耗现有资源。譬如我们设置了超时时间为1秒,如果短时间有大量请求在1秒内得不到响应,往往意味着异常。此时就没有必要让更多的请求去访问这个依赖了,我们应该使用断路器避免资源浪费。


断路器可以实现快速失败,如果在一段时间内侦测到许多的类似的错误,就会强迫其以后的多个调用快速失败,不再请求所依赖的服务,从而防止应用程序不断的尝试执行可能失败的操作,这样应用程序可以继续执行而不用等待修正错误。断路器模式也可以使得应用程序能够诊断错误是否已经修正,如果已经修正,应用程序会再次尝试调用操作。


断路器模式就类似于那些容易导致错误的操作的一种代理。这种代理能够记录最近调用发生几次错误,然后决定使用允许操作继续,否则直接返回错误。





二 Hystrix传播Security Context或者SpringScope


如果你想将本地线程上下文传播到注解HystrixCommand中,默认的声明是不会起到这个作用的,因为它是在一个线程中启动的。你可以选择让Hystrix使用同一个线程,通过一些配置或直接写在注解上,通过使用isolation strategy属性。


比如:


@HystrixCommand(fallbackMethod =”stubMyService”,


commandProperties = {


@HystrixProperty(name=”execution.isolation.strategy”,value=”SEMAPHORE”)


}


)


同样的方式适用于如果你用@SessionScope 或者 @RequestScope。你应该知道什么时候去做这件事因为有些运行时异常报找不到scoped上下文。



你也可以选择设置hystrix.shareSecurityContext 属性为true。这样做会自动配置一个Hystrix 并发策略插件钩子,然后会将SecurityContext从你当前主线程


传输到一个使用Hystrix Command注解的地方。




三 Hysteria Health Indicator 及Metric System


3.1health indicator 健康指标


断路器的状态同样暴露在/health端点上。


{


“hystrix”: {


“openCircuitBreakers”: [


“StoreIntegration::getStoresByLocationLink”


],


“status”: “CIRCUIT_OPEN”


},


“status”: “UP”


}


3.2Hystrix Metrics Stream


使用Hystrix metricsstream需要引入依赖spring-boot-starter-actuator。这会暴露/hystrix.stream作为一个管理端点。


<dependency>


<groupId>org.springframework.boot</groupId>


<artifactId>spring-boot-starter-actuator</artifactId>


</dependency>


四 Feign对Hystrix的支持


我们上面所说的熔断机制都是基于在方法上添加@HystrixCommand注解,然后通过属性fallbackMethod实现回退的。然而Feign是以接口形式工作的,他没有方法体,那么前面所述的方式是否依然适合Feign呢?


那么Feign要如何整合Hystrix呢?而且要如何实现Feign的回退呢?


Spring Cloud默认已经为Feign整合了Hystrix,只要Hystrix在项目的classpath中,Feign默认就会用断路器包裹的所有方法。


4.1 为Feign添加回退


首先添加fallback属性为FeignClient,并且指定一个class,这个class实现了接口



@FeignClient

(name=

“microservice-provider-user”

,configuration=

Configuration1

.

class

,


fallback=HystrixClientFallback.

class

)



public


interface

UserFeignClient {




@RequestLine

(

“GET /user/{id}”

)



public

User findById(

@Param

(

“id”

) Long

id

);


}



创建实现了Feign接口的类,并且实现fallback方法:



@Component



public


class


HystrixClientFallback


implements

UserFeignClient {




@Override



public

UserfindById(Long

id

) {


User

user

=

new

User();



user

.setId(-1L);



user

.setName(




默认用户




);



return


user

;


}


}


4.2 禁用单个FeignClient对Hystrix的支持


4.2.1 如果是某些Feign客户端想禁用Hystrix



@FeignClient

(name=

“microservice-provider-user”

,configuration=Configuration1.

class

,


fallback=HystrixClientFallback.

class

)



public


interface

UserFeignClient {




@RequestLine

(

“GET /user/{id}”

)



public

User findById(

@Param

(

“id”

) Long

id

);


}



@Configuration



public


class

Configuration1 {



@Bean



@Scope

(

“prototype”

)



public

Feign.BuilderfeignBuilder() {



return

Feign.

builder

();


}


}



4.2.2 如果是希望全局禁用


在application.yml中添加feign.hystrix.enabled=false即可



4.3 Feign使用fallbackFactory的属性打印fallback异常


如果有时候我们需要知道发生了什么异常,或者造成回退的原因是什么,我们应该怎么做呢?


我们可以在FeignClient注解上属性加上fallbackFactory属性


首先,创建一个类



public


class

HystrixClientFallbackFactory



implements

FallbackFactory<UserFeignClient>{




private


static


final

Logger


logger


= LoggerFactory.

getLogger


(HystrixClientFallbackFactory.

class

);



@Override



public

UserFeignClientcreate(Throwable

cause

) {



/**



*


—-


日志最好放在各个



fallback



方法中,而不要直接放在


create


方法中



*


—-


否则在启动的时候,就会打印日志



*/



return


new

UserFeignClient(){



@Override



public

UserfindById(Long

id

) {


HystrixClientFallbackFactory.


logger


.info


(

“fallback,causedby: ”

+

cause

);


User

user

=

new

User();



user

.setId(-1L);



user

.setName(




默认用户




);



return


user

;


}


};


}


}


然后:在Feign客户端的注解@FeignClient上加上属性


fallbackFactory



@FeignClient

(name=

“microservice-provider-user”

,configuration=Configuration1.

class

,



fallbackFactory

=HystrixClientFallbackFactory.

class

)



public


interface

UserFeignClient {




@RequestLine

(

“GET /user/{id}”

)



public

User findById(

@Param

(

“id”

) Long

id

);


}



Fallbackfactory属性还有其他用途, 我们可以让不同的异常返回不同的回退结果,从而使得Feign的回退更加灵活。例如:



public


class

HystrixClientFallbackFactory



implements

FallbackFactory<UserFeignClient>{




private


static


final

Logger


logger


= LoggerFactory.

getLogger


(HystrixClientFallbackFactory.

class

);



@Override



public

UserFeignClientcreate(Throwable

cause

) {



/**



*


—-


日志最好放在各个



fallback



方法中,而不要直接放在


create


方法中



*


—-


否则在启动的时候,就会打印日志



*/



return


new

UserFeignClient(){



@Override



public

UserfindById(Long

id

) {


HystrixClientFallbackFactory.


logger


.info


(

“fallback,causedby: ”

+

cause

);


User

user

=

new

User();



if

(

cause


instanceof

IllegalArgumentException) {



user

.setId(-1L);


}

else

{



user

.setId(-2L);


}



user

.setName(




默认用户




);



return


user

;


}


};


}


}


五Hystrix的监控


5.1 指标化监控


处理实现容错外,Hystrix还提供了近乎实时的监控。HystrixCommand和HystrixObservableCommand在执行时,会生成执行结果和运行指标,比如每秒执行的请求数,成功数等,这些监控数据在对分析应用时很有用。


Spring-cloud-starter-hystrix已包含该模块,在此基础上,只需为项目添加spring-boot-starter-actuator,就可以使用/hystrix.stream端点获得Hystrix的监控信息了。


然后就可以访问了/hystrix.stream



对于Feign项目的Hystrix监控,我们需要在启动类上加上@Enable-


CircuitBreaker,这样就可以使用/hystrix.stream



5.2 可视化监控(HystrixDashboard)


前面的方式我们很难通过肉眼迅速看出当前系统运行状态,因为是以文字演示的,使用Hystrix 仪表盘,我们一眼就可以看出当前的运行状态,让监控数据可视化,图形化。


首先:添加依赖



<


dependency


>



<


groupId


>

org.springframework.cloud

</


groupId


>



<


artifactId


>

spring-cloud-starter-

hystrix



dashboard


</


artifactId


>



</


dependency


>


其次: 在启动类上添加@EnableHystrixDashboard,我么修改端口为8030



@SpringBootApplication



@EnableHystrixDashboard



public


class

MovieServiceHystrixDashBoardRunner {



@Bean



@LoadBalanced



public

RestTemplaterestTemplate(){



return


new

RestTemplate();


}




public


static


void

main(String[]

args

) {


SpringApplication.

run

(MovieServiceHystrixDashBoardRunner.

class

,

args

);


}


}



server:



port:

8030


最后:这样一个简单的Hystrix Board就完成了,我们知道,我们并没有把Hystrix Dashboard注册到Eureka Server上,访问localhost:8030/


Hystrix





六 Turbine


前面我们已经知道,/hystrix.stream端点用于监控单个微服务实例,但是微服务架构体系中一般会包含若干个微服务,在生产环境中每一个微服务都可能会集群部署的,监控单个实例的话,就需要在Hystrix Dashboard上切换想要监控的地址,这显示是很不方便的,怎么办呢?


6.1Turbine简介


Turbine是一个聚合Hystrix监控数据的工具,他可以将相关/hystrix.stream端点的数据聚合到一个组合的/turbine.stream中,从而让集群监控的更加方便。





6.2 使用turbine监控多个微服务


# 添加依赖



<


dependency


>



<


groupId


>

org.springframework.cloud

</


groupId


>



<


artifactId


>

spring-cloud-starter-

hystrix


</


artifactId


>



</


dependency


>



<


dependency


>



<


groupId


>

org.springframework.cloud

</


groupId


>



<


artifactId


>

spring-cloud-starter-

turbine


</


artifactId


>



</


dependency


>


# 启动类添加注解@EnableTurbine



@SpringBootApplication



@EnableTurbine



public


class

TurbineHystrixApplication {



public


static


void

main(String[]

args

) {


SpringApplication.

run

(TurbineHystrixApplication.

class

,

args

);


}


}


# 修改application.yml配置文件





turbine.aggregator.clusterConfig: 集群名字,多个逗号分割


turbine.appConfig: 微服务名称,多个逗号分割


# 访问


localhost:8031/turbine.stream?cluster=MICROSERVICE-CONSUMER-FEIGN-WITH-HYSTRIX


或者


localhost:8031/turbine.stream?cluster=MICROSERVICE-CONSUMER-RIBBON-WITH-HYSTRIX



如果启动了 Hystrix Dashboard,那么可以在Dashboard可视化展示数据




版权声明:本文为zhanglh046原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。