简介
在Hystrix配置中我们简单的了解了Hystrix常用的一些配置,这一篇我们简单的通过一些实例来加深对这些配置的理解。
并且通过ab做一点简单的并发测试,来看一下Hystrix对系统在流量比较高的时候的影响。
模拟服务
import java.util.concurrent.TimeUnit;
public class ServiceMockUtil {
public static String mock() {
double random = Math.random();
System.out.println(random);
if(Double.compare(0.2,random) >= 0) {
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
return "interrupted";
}
return "5";
}else if(Double.compare(0.2,random) < 0 && Double.compare(0.8,random)>0){
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
return "interrupted";
}
return "1";
}
return "im";
}
public static String mockSixtyFiveSecond() {
double random = Math.random();
System.out.println(random);
if(Double.compare(0.5,random) >= 0) {
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
return "interrupted";
}
return "5";
}
return "im";
}
}
先来一个模拟的服务的类,用来模拟服务的调用时间,通过随机的方式让休眠一些时间。
HystrixCommand
import cn.freemethod.service.ServiceMockUtil;
import com.netflix.hystrix.*;
public class LoginHystrixCommand extends HystrixCommand<String>{
private static Setter getSetter() {
return Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("groupOne"))
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey(Thread.currentThread().getId() + "login"))
.andCommandPropertiesDefaults(
HystrixCommandProperties.Setter()
.withCircuitBreakerRequestVolumeThreshold(1)
.withCircuitBreakerSleepWindowInMilliseconds(30000)
.withCircuitBreakerErrorThresholdPercentage(1)
// .withCircuitBreakerForceOpen(true)
// .withCircuitBreakerForceClosed(true)
.withExecutionTimeoutEnabled(true)
.withExecutionTimeoutInMilliseconds(3000))
.andThreadPoolPropertiesDefaults(
HystrixThreadPoolProperties.Setter()
.withCoreSize(10));
}
public LoginHystrixCommand() {
super(getSetter());
}
@Override
protected String run() throws Exception {
// return ServiceMockUtil.mock();
return ServiceMockUtil.mockSixtyFiveSecond();
}
@Override
protected String getFallback() {
return "hys failure";
}
}
上面就模拟了一个登录的服务继承了HystrixCommand类。最好每一个方法都是一个Command,把业务封装在run中。
在构造函数中需要一个Setter来配置Hystrix,可以调整这些参数来改变Command的行为。
上面我们为了方便都是通过字面量的方式写在了程序之中。最常用的一些配置都包含在上面了,可以根据自己的理解,测试一下是否正确。
为了方便我们做一下简单的测试,这里我们来一个Controller类:
import cn.freemethod.hys.LoginHystrixCommand;
import cn.freemethod.service.ServiceMockUtil;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class HystrixController {
@RequestMapping("/normal")
@ResponseBody
public String normal(){
// return ServiceMockUtil.mock();
return ServiceMockUtil.mockSixtyFiveSecond();
}
@RequestMapping("/hys")
@ResponseBody
public String hys(){
LoginHystrixCommand command = new LoginHystrixCommand();
return command.execute();
}
@RequestMapping("/gologin")
public String goLogin(){
return "dologin";
}
}
其他配置文件稍后附录,这里先看使用ab简单的模拟测试一下:
ab的参数-n表示请求的总数量,-c表示并发数量,所以上面模拟的是10000次请求,并发为200,我们可以从执行时间,平均请求执行时间,失败数量等来对比使用Hystrix和没有Hystrix的区别。
从上面我们可以看到使用Hystrix的失败的请求数很高,那是因为我们的熔断时间窗口设置的是30秒,整个10000请求执行的时间才16秒不到。
所以我们可以调整一下下面的三个参数:
HystrixCommandProperties.Setter()
.withCircuitBreakerRequestVolumeThreshold(10)
.withCircuitBreakerSleepWindowInMilliseconds(1000)
.withCircuitBreakerErrorThresholdPercentage(30)
上面的调整,表示并发数要达到10才有可能触发熔断,熔断时间1秒,然后重新接受部分流量开始统计,并且要错误率要达到30%之上才会触发熔断。
我们来看一下调整之后的测试结果:
其他配置文件
spring-application.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="cn.freemethod">
<context:exclude-filter expression="org.springframework.stereotype.Controller" type="annotation" />
</context:component-scan>
</beans>
dispatcher-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<mvc:annotation-driven />
<context:component-scan base-package="cn.freemethod.controller">
<context:include-filter expression="org.springframework.stereotype.Controller" type="annotation" />
</context:component-scan>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="contentType" value="text/html; charset=utf-8"/>
<property name="prefix" value="/WEB-INF/view/"/>
<property name="suffix" value=".jsp" />
</bean>
</beans>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<context-param>
<param-name>webAppRootKey</param-name>
<param-value>root</param-value>
</context-param>
<!-- Sping配置文件 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-application.xml</param-value>
</context-param>
<!-- Spring 容器启动监听器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 字符集 过滤器 -->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Spring view分发器 -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/dispatcher-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<!--<url-pattern>*.html</url-pattern>-->
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
其他jsp文件随便创建一个就可以了。