GitHub關於hystrix文檔:https://github.com/Netflix/Hystrix/wiki
1. 首先介紹一下hystrix是什麼?
簡單來說,hystrix就是用來容錯(潛在的錯誤、或者致命的錯誤)的。
2. 那麼,不容錯會怎麼樣?
曬下官網的圖:
正常情況下訪問:每個節點都很正常
如果I掛掉了,執行速度很慢或者執行出現異常,類似於RunTimeException
隨着請求不斷的往I上面發送,造成服務不可用
wiki原文:
3. 看來容錯是很有必要的,接下來看點乾貨
這裏有官網的一個列子:https://github.com/Netflix/Hystrix/tree/master/hystrix-examples,可以下下來看看,很
簡單,秒懂!我只是把官網的例子稍作修改。
3.1. 首先在pom.xml裏面加入Hystrix相關代碼
<hystrix.version>1.5.12</hystrix.version>
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-core</artifactId>
<version>${hystrix.version}</version>
</dependency>
3.2. 編寫熔斷代碼
我是基於AOP註解方式實現熔斷方法的
熔斷命令的註解類
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface HystrixCommand {
/**
* 當錯誤次數達到閾值或者執行超時,直接執行下面代碼
* 如果不填fallbackMethod則執行默認熔斷方法
* 如果填寫fallbackMethod則熔斷方法必須在配置註解的同一個類裏面,否則拋出MethodNotFoundException
* [熔斷方法傳參]
* 1. 不傳參則直接執行fallbackMethod熔斷方法
* 2. 傳參則必須和配置註解方法傳參類型保持一致, 否則會執行錯誤
* 參考:HttpHystrixAspect.java
* @return
*/
public String fallbackMethod() default "";
}
AOP環繞通知
import java.lang.reflect.Method;
import javax.el.MethodNotFoundException;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandKey;
import com.netflix.hystrix.HystrixCommandProperties;
import com.netflix.hystrix.HystrixRequestLog;
import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext;
@Component
@Aspect
public class HttpHystrixAspect {
private Logger logger = LoggerFactory.getLogger(getClass());
/**
* 對HTTP請求進行AOP操作
*/
@Around("execution (public String com.xxx.ga.service.impl.*.*(..)) && @annotation(hystrixCommand)")
public Object aroundHttpRequest(ProceedingJoinPoint pjp, com.iboxpay.ga.annotation.HystrixCommand hystrixCommand) throws Exception, Throwable{
Object result = null;
// 執行類名
String targetName = pjp.getTarget().getClass().getSimpleName();
MethodSignature signature = (MethodSignature) pjp.getSignature();
// 執行方法名
String methodName = signature.getMethod().getName();
// 初始化熔斷器上下文
HystrixRequestContext context = HystrixRequestContext.initializeContext();
try {
result = new HttpHystrix(pjp, targetName, methodName, hystrixCommand).execute();
} finally {
logger.info("Request => " + HystrixRequestLog.getCurrentRequest().getExecutedCommandsAsString());
context.shutdown();
}
return result;
}
public class HttpHystrix extends HystrixCommand<Object> {
private final ProceedingJoinPoint pjp;
// 類名
private final String className;
// 方法名
private final String methodName;
// 註解
private final com.iboxpay.ga.annotation.HystrixCommand hystrixCommand;
/**
* @param pjp
* @param serviceId 類名+方法名
*/
protected HttpHystrix(ProceedingJoinPoint pjp, String className, String methodName, com.iboxpay.ga.annotation.HystrixCommand hystrixCommand) {
// Hystrix uses the command group key to group together commands such as for reporting,
// alerting, dashboards, or team/library ownership.
// 同一個groupKey共用同一個線程池
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(className))
.andCommandKey(HystrixCommandKey.Factory.asKey(methodName))
// 超時時間
.andCommandPropertiesDefaults(HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(5000)));
this.pjp = pjp;
this.className = className;
this.methodName = methodName;
this.hystrixCommand = hystrixCommand;
}
@Override
protected Object run() throws Exception {
try {
return pjp.proceed();
} catch (Throwable e) {
throw new Exception(e);
}
}
/**
* 熔斷措施
* 當錯誤次數達到閾值或者執行超時,直接執行下面代碼
*/
@Override
protected Object getFallback() {
logger.info("[{}] 錯誤次數達到閾值或者執行超時, 進行熔斷措施", className + "_" + methodName);
// 熔斷方法名稱
String fallbackMethod = hystrixCommand.fallbackMethod();
// 未聲明瞭熔斷機制,默認熔斷方法
if(StringUtils.isEmpty(fallbackMethod)){
return "返回失敗";
}
Method methods[] = pjp.getTarget().getClass().getMethods();
Method method = null;
for(Method m : methods){
if(m.getName().equals(fallbackMethod)){
method = m;
break;
}
}
// 未在類中找到申明的fallbackMethod方法
if(method == null){
throw new MethodNotFoundException();
}
// 熔斷方法傳入參數
Class<?> clazzs[] = method.getParameterTypes();
// 傳入參數爲空,直接執行方法
if(clazzs.length == 0){
try {
return method.invoke(pjp.getTarget());
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
// 傳入參數不爲空,則傳入AOP攔截方法參數
try {
return method.invoke(pjp.getTarget(), pjp.getArgs());
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
}
}
service方法配置HystrixCommand註解
@Override
@HystrixCommand(fallbackMethod = "hiError")
public String testHystrix(int outtimeRate, int runtimeRate) {
/* 模擬數據操作耗時 */
try {
Thread.sleep((int) (Math.random() * 10) + 2);
} catch (InterruptedException e) {
// do nothing
}
/* 執行失敗比率 */
if (Math.random() > (double) runtimeRate / 100) {
throw new RuntimeException("運行異常");
}
/* 執行超時比率 */
if (Math.random() > (double) outtimeRate / 100) {
try {
Thread.sleep(Integer.parseInt(timeOut) + 5);
} catch (Exception e) {
// do nothing
}
}
return "{'status': 'SUCCESS'}";
}
public String hiError(int n, int m) {
logger.info("熔斷措施:hi,sorry,error!");
return "hi,sorry,error!";
}
這裏省略掉了controller調用service的方法...
至此,Hystrix代碼全部結束,testHystrix方法模擬生產環境運行超時、異常情況。
4. 接下來講一下如何安裝hystrix dashboard監控
dashboard下載地址:http://search.maven.org/remotecontent?filepath=com/netflix/hystrix/hystrix-dashboard/1.5.4/hystrix-dashboard-1.5.4.war
下載後直接丟到tomcat或者jetty裏面運行就可以了。
運行後訪問:http://localhost:7979/hystrix-dashboard/(我的端口號是7979)看到下面界面就說明安裝成功了!
接下來pom.xml文件增加
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-metrics-event-stream</artifactId>
<version>${hystrix.version}</version>
</dependency>
因爲我是基於springboot實現的的,所以直接添加Configuration即可,傳統的web項目在web.xml增加對應的servlet和url-mapping就可以了
@Configuration
public class HystrixConfig {
@Bean
public HystrixMetricsStreamServlet hystrixMetricsStreamServlet() {
return new HystrixMetricsStreamServlet();
}
@Bean
public ServletRegistrationBean registration(HystrixMetricsStreamServlet servlet) {
ServletRegistrationBean registrationBean = new ServletRegistrationBean();
registrationBean.setServlet(servlet);
registrationBean.setEnabled(true); //是否啓用該registrationBean
registrationBean.addUrlMappings("/hystrix.stream");
return registrationBean;
}
}
在Hystrix Dashboard管理頁面輸入路徑
就可以看到監控圖表了