add hystrix demo back, and improve the usage to follow the layers

This commit is contained in:
Calvin
2013-10-16 14:59:18 +08:00
parent 476de45fc0
commit b33687f03c
6 changed files with 128 additions and 27 deletions
@@ -22,7 +22,7 @@ import org.springside.modules.utils.Threads;
public class DependencyResourceController {
public static final int TIMEOUT = 15000;
private String status = "normal";
public static String status = "normal";
@Autowired
private AccountEffectiveService accountService;
@@ -16,11 +16,9 @@ public class GetUserCommand extends HystrixCommand<UserDTO> {
private Long id;
protected GetUserCommand(Setter config, RestTemplate restTemplate, Long id) {
// 配置Command
super(config);
// 传入
this.restTemplate = restTemplate;
// 传入请求参数
this.id = id;
}
@@ -5,6 +5,7 @@ import javax.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springside.examples.showcase.webservice.rest.UserDTO;
@@ -15,17 +16,24 @@ import com.netflix.hystrix.HystrixCommandProperties;
import com.netflix.hystrix.HystrixCommandProperties.ExecutionIsolationStrategy;
import com.netflix.hystrix.HystrixThreadPoolProperties;
/**
* 使用Hystrix 封装的Service。原来直接调用restTemplate改为调用Hystrix Command的封装.
*
* @author calvin
*/
@Service
public class UserService {
private static Logger logger = LoggerFactory.getLogger(UserService.class);
private Boolean runInNewThread = true;
private Setter config;
private RestTemplate restTemplate = new RestTemplate();
private boolean runInNewThread = true;
private Setter commandConfig;
private RestTemplate restTemplate;
public UserDTO getUser(Long id) throws Exception {
GetUserCommand command = new GetUserCommand(config, restTemplate, id);
GetUserCommand command = new GetUserCommand(commandConfig, restTemplate, id);
try {
return command.execute();
@@ -36,7 +44,7 @@ public class UserService {
}
/**
* 演示Command的各种配置项,构造RestTemplate.
* 演示Command的各种配置项, 并构造Thread-Safed的RestTemplate.
*/
@PostConstruct
private void init() {
@@ -44,41 +52,39 @@ public class UserService {
restTemplate = new RestTemplate();
// 设置Command名称 //
config = Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup")).andCommandKey(
commandConfig = Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup")).andCommandKey(
HystrixCommandKey.Factory.asKey("GetUserCommand"));
HystrixCommandProperties.Setter commandProperties = HystrixCommandProperties.Setter();
commandConfig.andCommandPropertiesDefaults(commandProperties);
// 设置短路计算规则 //
config.andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
commandProperties
// 多少百分比的失败在rolling windows内发生,计算为短路。默认为50%,无改变。
.withCircuitBreakerErrorThresholdPercentage(50)
// 至少多少请求在rolling window内发生,才开始触发短路的计算,默认为20, 设为3方便演示。
.withCircuitBreakerRequestVolumeThreshold(3)
// rolling windows 长度,默认为20秒,改为60秒方便演示。同时相应改变桶的数量。
.withMetricsRollingStatisticalWindowInMilliseconds(60000)
.withMetricsRollingStatisticalWindowBuckets(60));
// rolling windows 长度,默认为20秒,改为180秒方便演示。同时相应改变桶的数量。
.withMetricsRollingStatisticalWindowInMilliseconds(180000)
.withMetricsRollingStatisticalWindowBuckets(180);
// 设置短路后的保护时间 //
config.andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
.withCircuitBreakerSleepWindowInMilliseconds(20000));
// 设置短路后的保护时间 ,默认为5秒,改为20秒方便演示//
commandProperties.withCircuitBreakerSleepWindowInMilliseconds(20000);
// 设置超时与并发控制 //
if (runInNewThread) {
// 使用Hystrix线程池的异步执行方式
config.andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
// 线程超时,默认为1秒,设为2秒方便演示
.withExecutionIsolationThreadTimeoutInMilliseconds(2000))
commandProperties.withExecutionIsolationThreadTimeoutInMilliseconds(2000);
// 线程池属性, 线程池大小,默认为10,无改变。待执行队列的大小,默认为5,无改变。
.andThreadPoolPropertiesDefaults(
HystrixThreadPoolProperties.Setter().withCoreSize(10).withQueueSizeRejectionThreshold(5));
commandConfig.andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter().withCoreSize(10)
.withQueueSizeRejectionThreshold(5));
} else {
// 使用Invoker原有线程的方式, 设置使用信号量模式而不是默认的线程池模式,设置并发,默认为10,无改变
config.andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
.withExecutionIsolationStrategy(ExecutionIsolationStrategy.SEMAPHORE)
.withExecutionIsolationSemaphoreMaxConcurrentRequests(10));
// RestTemplate设置为10秒超时
((SimpleClientHttpRequestFactory) restTemplate.getRequestFactory()).setConnectTimeout(10000);
// 设置使用信号量模式而不是默认的线程池模式,设置并发,默认为10,无改变
commandProperties.withExecutionIsolationStrategy(ExecutionIsolationStrategy.SEMAPHORE)
.withExecutionIsolationSemaphoreMaxConcurrentRequests(10);
}
}
@@ -0,0 +1,36 @@
package org.springside.examples.showcase.demos.hystrix.web;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springside.examples.showcase.demos.hystrix.service.UserService;
import org.springside.examples.showcase.webservice.rest.RestException;
import org.springside.examples.showcase.webservice.rest.UserDTO;
@Controller
public class HystrixDemoController {
private static Logger logger = LoggerFactory.getLogger(HystrixDemoController.class);
@Autowired
private UserService userService;
@RequestMapping(value = "/hystrix/user/{id}", method = RequestMethod.GET)
@ResponseBody
public UserDTO getUser(@PathVariable("id") Long id) {
try {
return userService.getUser(id);
} catch (Exception e) {
logger.error(e.getMessage(), e);
throw new RestException(HttpStatus.SERVICE_UNAVAILABLE);
}
}
}
@@ -11,6 +11,7 @@
<div class="submenu">
<a id="web-tab" href="${ctx}/story/web">Web演示</a>
<a id="webservice-tab"href="${ctx}/story/webservice">WebService演示</a>
<a id="hystrix-tab" href="${ctx}/story/hystrix">JMX演示</a>
<a id="jmx-tab" href="${ctx}/story/jmx">JMX演示</a>
<a id="log-tab" href="${ctx}/story/log">日志高级演示</a>
<a id="monitor-tab" href="${ctx}/story/monitor">性能监控演示</a>
@@ -0,0 +1,60 @@
<%@ page contentType="text/html;charset=UTF-8" %>
<%@ page import="org.springside.examples.showcase.demos.hystrix.dependency.DependencyResourceController,com.netflix.hystrix.*,com.netflix.hystrix.HystrixCommandMetrics.HealthCounts,com.netflix.hystrix.util.*" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><%! %>
<c:set var="ctx" value="${pageContext.request.contextPath}"/>
<html>
<head>
<title>Web Service高级演示</title>
<script>
$(document).ready(function() {
$("#hystrix-tab").addClass("active");
});
</script>
</head>
<body>
<h1>Hystrix 演示</h1>
<p><a href="https://github.com/Netflix/Hystrix" target="_blank">Netflix Hystrix</a> 是一个延迟与容错类库,通过独立访问远程系统、服务和第三方库的节点,在复杂的分布式系统里停止雪崩及提供恢复能力。</p>
<h2>演示操作</h2>
<ul>
<li>依赖资源当前状态为<%= DependencyResourceController.status %>:切换为<a href="${ctx}/hystrix/resource/status/normal">正常</a>、<a href="${ctx}/hystrix/resource/status/timeout">超时</a>、<a href="${ctx}/hystrix/resource/status/fail">失败</a>。</li>
<li>访问服务:<a href="${ctx}/hystrix/user/1" target="_blank">Hystrix服务</a>、<a href="${ctx}/hystrix/resource/1" target="_blank">依赖资源</a></li>
</ul>
<h2>主要用户故事</h2>
<ul>
<li> 在默认的正常状态,访问Hystrix服务和依赖资源,均返回正常结果。</li>
<li> 将资源状态切换为"超时",访问依赖资源,需要在15秒后才返回结果。</li>
<li> 访问Hystrix服务,2秒后超时,返回503。</li>
<li> 三次超时后满足短路条件(60秒滚动窗口内起码有3个请求,50%失败),再次刷新服务,立即返回503。</li>
<li> 10秒短路保护期内,所有访问都立即返回503,不会访问依赖资源。</li>
<li> 保护期过后,会放行一个请求,如果还是超时,则继续保持短路状态。如果成功则重置所有计数器。</li>
</ul>
<h2>其他用户故事</h2>
<ul>
<li> 将资源状态切换为"失败",访问依赖资源,立即返回500错误,访问Hystrix服务,即时返回503错误。</li>
<li> 默认使用Hystrix线程池模式,可修改代码使用调用线程池模式。</li>
</ul>
<h2>监控结果 </h2>
<form><input type="submit" value="刷新"/></form>
<%
HystrixCommandKey key = HystrixCommandKey.Factory.asKey("GetUserCommand");
HystrixCommandMetrics metrics = HystrixCommandMetrics.getInstance(key);
if (metrics!=null){
HealthCounts counts = metrics.getHealthCounts();
HystrixCircuitBreaker circuitBreaker = HystrixCircuitBreaker.Factory.getInstance(key);
%>
<ul>
<li>是否短路:<%= circuitBreaker.isOpen() %></li>
<li>3分钟窗口内,请求:<%= counts.getTotalRequests()%>, 成功:<%= metrics.getRollingCount(HystrixRollingNumberEvent.SUCCESS) %>, 超时:<%= metrics.getRollingCount(HystrixRollingNumberEvent.TIMEOUT) %>,失败:<%= metrics.getRollingCount(HystrixRollingNumberEvent.FAILURE) %>,短路:<%= metrics.getRollingCount(HystrixRollingNumberEvent.SHORT_CIRCUITED) %>, 失败百分比:<%= counts.getErrorPercentage() %>%
<li>50%延时:<%= metrics.getTotalTimePercentile(50) +"ms"%>,90%延时:<%= metrics.getTotalTimePercentile(90) +"ms"%>,100%延时:<%= metrics.getTotalTimePercentile(100) +"ms"%></li>
</ul>
<%} %>
</body>
</html>