#286 add hystrix demo back, 最后一轮代码清理。
This commit is contained in:
+4
-4
@@ -11,7 +11,7 @@ import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.experimental.categories.Category;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.client.HttpClientErrorException;
|
||||
import org.springframework.web.client.HttpStatusCodeException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springside.examples.quickstart.data.TaskData;
|
||||
import org.springside.examples.quickstart.entity.Task;
|
||||
@@ -92,7 +92,7 @@ public class TaskRestFT extends BaseFunctionalTestCase {
|
||||
try {
|
||||
restTemplate.getForObject(taskUri, Task.class);
|
||||
fail("Get should fail while feth a deleted task");
|
||||
} catch (HttpClientErrorException e) {
|
||||
} catch (HttpStatusCodeException e) {
|
||||
assertEquals(HttpStatus.NOT_FOUND, e.getStatusCode());
|
||||
}
|
||||
}
|
||||
@@ -105,7 +105,7 @@ public class TaskRestFT extends BaseFunctionalTestCase {
|
||||
try {
|
||||
restTemplate.postForLocation(resoureUrl, titleBlankTask);
|
||||
fail("Create should fail while title is blank");
|
||||
} catch (HttpClientErrorException e) {
|
||||
} catch (HttpStatusCodeException e) {
|
||||
assertEquals(HttpStatus.BAD_REQUEST, e.getStatusCode());
|
||||
Map messages = jsonMapper.fromJson(e.getResponseBodyAsString(), Map.class);
|
||||
assertEquals(1, messages.size());
|
||||
@@ -117,7 +117,7 @@ public class TaskRestFT extends BaseFunctionalTestCase {
|
||||
try {
|
||||
restTemplate.put(resoureUrl + "/1", titleBlankTask);
|
||||
fail("Update should fail while title is blank");
|
||||
} catch (HttpClientErrorException e) {
|
||||
} catch (HttpStatusCodeException e) {
|
||||
assertEquals(HttpStatus.BAD_REQUEST, e.getStatusCode());
|
||||
Map messages = jsonMapper.fromJson(e.getResponseBodyAsString(), Map.class);
|
||||
assertEquals(1, messages.size());
|
||||
|
||||
+3
-3
@@ -19,7 +19,7 @@ import org.springside.modules.utils.Threads;
|
||||
*/
|
||||
@Controller
|
||||
public class DependencyResourceController {
|
||||
public static final int TIMEOUT = 15000;
|
||||
public static final int TIMEOUT = 30000;
|
||||
|
||||
public static String status = "normal";
|
||||
|
||||
@@ -27,7 +27,7 @@ public class DependencyResourceController {
|
||||
private AccountEffectiveService accountService;
|
||||
|
||||
/**
|
||||
* 根据控制器中的状态而演示不同的行为,如正常返回,15秒后返回或直接报错
|
||||
* 根据控制器中的状态而演示不同的行为,如正常返回,30秒后返回或直接报错
|
||||
*/
|
||||
@RequestMapping(value = "/hystrix/resource/{id}", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
@@ -37,7 +37,7 @@ public class DependencyResourceController {
|
||||
return handleRequest(id);
|
||||
}
|
||||
|
||||
// 演示超时,10秒后返回.
|
||||
// 演示超时,30秒后返回.
|
||||
if ("timeout".equals(status)) {
|
||||
Threads.sleep(TIMEOUT);
|
||||
return handleRequest(id);
|
||||
|
||||
+11
-5
@@ -3,13 +3,19 @@ package org.springside.examples.showcase.demos.hystrix.service;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.client.HttpClientErrorException;
|
||||
import org.springframework.web.client.HttpStatusCodeException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springside.examples.showcase.webservice.rest.UserDTO;
|
||||
|
||||
import com.netflix.hystrix.HystrixCommand;
|
||||
import com.netflix.hystrix.exception.HystrixBadRequestException;
|
||||
|
||||
/**
|
||||
* HystrixCommand的实现类, 将访问远程资源的逻辑封装在T run()函数内.
|
||||
* 如需对异常, 超时, 短路等进行自行处理,返回默认值则实现T getFallback()函数.
|
||||
*
|
||||
* @author calvin
|
||||
*/
|
||||
public class GetUserCommand extends HystrixCommand<UserDTO> {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(GetUserCommand.class);
|
||||
@@ -18,7 +24,7 @@ public class GetUserCommand extends HystrixCommand<UserDTO> {
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 构造函数,注入配置,命令用到的资源访问类和命令参数.
|
||||
* 构造函数,注入配置,用到的资源访问类和命令参数.
|
||||
*/
|
||||
protected GetUserCommand(Setter commandConfig, RestTemplate restTemplate, Long id) {
|
||||
super(commandConfig);
|
||||
@@ -34,15 +40,15 @@ public class GetUserCommand extends HystrixCommand<UserDTO> {
|
||||
try {
|
||||
logger.info("Access restful resource");
|
||||
return restTemplate.getForObject("http://localhost:8080/showcase/hystrix/resource/{id}", UserDTO.class, id);
|
||||
} catch (HttpClientErrorException e) {
|
||||
} catch (HttpStatusCodeException e) {
|
||||
throw handleException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理异常,对于客户端本身的异常,抛出HystrixBadRequestException,不计算入短路统计内。
|
||||
* 处理异常,对于客户端自己的异常,抛出HystrixBadRequestException,不算入短路统计内。
|
||||
*/
|
||||
protected Exception handleException(HttpClientErrorException e) {
|
||||
protected Exception handleException(HttpStatusCodeException e) {
|
||||
HttpStatus status = e.getStatusCode();
|
||||
if (status.equals(HttpStatus.BAD_REQUEST)) {
|
||||
throw new HystrixBadRequestException(e.getResponseBodyAsString(), e);
|
||||
|
||||
+29
-35
@@ -1,6 +1,6 @@
|
||||
package org.springside.examples.showcase.demos.hystrix.service;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
@@ -9,7 +9,6 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springside.examples.showcase.webservice.rest.UserDTO;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.netflix.hystrix.HystrixCircuitBreaker;
|
||||
import com.netflix.hystrix.HystrixCommand.Setter;
|
||||
import com.netflix.hystrix.HystrixCommandGroupKey;
|
||||
@@ -29,8 +28,7 @@ import com.netflix.hystrix.util.HystrixRollingNumberEvent;
|
||||
@Service
|
||||
public class UserService {
|
||||
|
||||
private boolean runInNewThread = true;
|
||||
|
||||
private boolean isolateThreadPool = true;
|
||||
private Setter commandConfig;
|
||||
private RestTemplate restTemplate;
|
||||
|
||||
@@ -45,8 +43,8 @@ public class UserService {
|
||||
/**
|
||||
* 演示获取Hystrix的Metrics.
|
||||
*/
|
||||
public Map<String, Object> getHystrixMetrics() {
|
||||
Map<String, Object> metricsMap = Maps.newHashMap();
|
||||
public MetricsMap getHystrixMetrics() {
|
||||
MetricsMap metricsMap = new MetricsMap();
|
||||
HystrixCommandKey key = HystrixCommandKey.Factory.asKey("GetUserCommand");
|
||||
HystrixCommandMetrics metrics = HystrixCommandMetrics.getInstance(key);
|
||||
|
||||
@@ -67,19 +65,6 @@ public class UserService {
|
||||
metricsMap.put("latency50", metrics.getTotalTimePercentile(50));
|
||||
metricsMap.put("latency90", metrics.getTotalTimePercentile(90));
|
||||
metricsMap.put("latency100", metrics.getTotalTimePercentile(100));
|
||||
} else {
|
||||
metricsMap.put("circuitOpen", false);
|
||||
metricsMap.put("totalRequest", 0);
|
||||
metricsMap.put("errorPercentage", 0);
|
||||
metricsMap.put("success", 0);
|
||||
metricsMap.put("timeout", 0);
|
||||
metricsMap.put("failure", 0);
|
||||
metricsMap.put("shortCircuited", 0);
|
||||
metricsMap.put("threadPoolRejected", 0);
|
||||
metricsMap.put("semaphoreRejected", 0);
|
||||
metricsMap.put("latency50", 0);
|
||||
metricsMap.put("latency90", 0);
|
||||
metricsMap.put("latency100", 0);
|
||||
}
|
||||
|
||||
return metricsMap;
|
||||
@@ -89,7 +74,7 @@ public class UserService {
|
||||
* 演示Hystrix Command的各种配置项, 并构造Thread-Safed的RestTemplate.
|
||||
*/
|
||||
@PostConstruct
|
||||
private void init() {
|
||||
public void init() {
|
||||
// 初始化RestTemplate
|
||||
restTemplate = new RestTemplate();
|
||||
|
||||
@@ -100,37 +85,46 @@ public class UserService {
|
||||
HystrixCommandProperties.Setter commandProperties = HystrixCommandProperties.Setter();
|
||||
commandConfig.andCommandPropertiesDefaults(commandProperties);
|
||||
|
||||
// 设置短路计算规则 //
|
||||
commandProperties
|
||||
// 设置短路规则 //
|
||||
// 设置短路后的保护时间 ,默认为5秒,改为20秒方便演示
|
||||
commandProperties.withCircuitBreakerSleepWindowInMilliseconds(20000)
|
||||
// 多少百分比的失败在rolling windows内发生,计算为短路。默认为50%,无改变.
|
||||
.withCircuitBreakerErrorThresholdPercentage(50)
|
||||
// 至少多少请求在rolling window内发生,才开始触发短路的计算,默认为20, 设为3方便演示.
|
||||
.withCircuitBreakerRequestVolumeThreshold(3)
|
||||
// rolling windows 长度,默认为20秒,改为180秒方便演示。同时相应改变桶的数量.
|
||||
.withMetricsRollingStatisticalWindowInMilliseconds(180000)
|
||||
.withMetricsRollingStatisticalWindowBuckets(180);
|
||||
|
||||
// 设置短路后的保护时间 ,默认为5秒,改为20秒方便演示//
|
||||
commandProperties.withCircuitBreakerSleepWindowInMilliseconds(20000);
|
||||
// rolling windows 长度,默认为20秒,改为120秒方便演示。同时相应改变桶的数量.
|
||||
.withMetricsRollingStatisticalWindowInMilliseconds(120000)
|
||||
.withMetricsRollingStatisticalWindowBuckets(120);
|
||||
|
||||
// 设置超时与并发控制 //
|
||||
if (runInNewThread) {
|
||||
if (isolateThreadPool) { // 使用隔离的Hystrix线程池
|
||||
// 线程超时,默认为1秒,设为3秒方便演示.
|
||||
commandProperties.withExecutionIsolationThreadTimeoutInMilliseconds(3000);
|
||||
// 线程池属性, 线程池大小,默认为10,无改变。待执行队列的大小,默认为5,无改变.
|
||||
commandConfig.andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter().withCoreSize(10)
|
||||
.withQueueSizeRejectionThreshold(5));
|
||||
} else {
|
||||
// RestTemplate设置为10秒超时.
|
||||
((SimpleClientHttpRequestFactory) restTemplate.getRequestFactory()).setConnectTimeout(10000);
|
||||
} else { // 使用原有的调用者线程
|
||||
// 依靠RestTemplate本身的超时机制,设为10秒。
|
||||
((SimpleClientHttpRequestFactory) restTemplate.getRequestFactory()).setReadTimeout(10000);
|
||||
|
||||
// 设置使用信号量模式而不是默认的线程池模式,设置并发,默认为10,无改变.
|
||||
// 设置使用原有的调用者线程,设置并发,默认为10,无改变.
|
||||
commandProperties.withExecutionIsolationStrategy(ExecutionIsolationStrategy.SEMAPHORE)
|
||||
.withExecutionIsolationSemaphoreMaxConcurrentRequests(10);
|
||||
}
|
||||
}
|
||||
|
||||
public void setRunInNewThread(boolean runInNewThread) {
|
||||
this.runInNewThread = runInNewThread;
|
||||
public void setIsolateThreadPool(boolean isolateThreadPool) {
|
||||
this.isolateThreadPool = isolateThreadPool;
|
||||
}
|
||||
|
||||
/**
|
||||
* 简易Map,对不存在的统计项返回0.
|
||||
*/
|
||||
public static class MetricsMap extends HashMap {
|
||||
@Override
|
||||
public Object get(Object key) {
|
||||
Object result = super.get(key);
|
||||
return result != null ? result : "0";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+13
-3
@@ -31,7 +31,6 @@ public class HystrixDemoController {
|
||||
model.addAttribute("allStatus", allStatus);
|
||||
model.addAttribute("statusHolder", new StatusHolder(DependencyResourceController.status));
|
||||
model.addAttribute("metrics", userService.getHystrixMetrics());
|
||||
|
||||
return "hystrix/hystrix";
|
||||
}
|
||||
|
||||
@@ -53,6 +52,17 @@ public class HystrixDemoController {
|
||||
return "redirect:/hystrix";
|
||||
}
|
||||
|
||||
/**
|
||||
* 从默认的Hystrix线程池模式切换为使用原调用者线程的模式.
|
||||
*/
|
||||
@RequestMapping(value = "/hystrix/disableIsolateThreadPool")
|
||||
public String disableIsolateThreadPool() {
|
||||
userService.setIsolateThreadPool(false);
|
||||
userService.init();
|
||||
return "redirect:/hystrix";
|
||||
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
allStatus.put("normal", "正常");
|
||||
@@ -62,10 +72,10 @@ public class HystrixDemoController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 给Spring Form Tag使用的类
|
||||
* 给Spring Form Tag使用的类.
|
||||
*/
|
||||
public static class StatusHolder {
|
||||
public String value;
|
||||
private String value;
|
||||
|
||||
public StatusHolder(String value) {
|
||||
this.value = value;
|
||||
|
||||
+6
-7
@@ -8,6 +8,7 @@ import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.context.request.WebRequest;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
|
||||
import org.springside.modules.utils.Exceptions;
|
||||
import org.springside.modules.web.MediaTypes;
|
||||
|
||||
import com.netflix.hystrix.exception.HystrixBadRequestException;
|
||||
@@ -22,7 +23,7 @@ import com.netflix.hystrix.exception.HystrixRuntimeException.FailureType;
|
||||
@ControllerAdvice
|
||||
public class HystrixExceptionHandler extends ResponseEntityExceptionHandler {
|
||||
/**
|
||||
* 处理Hystrix Runtime异常.
|
||||
* 处理Hystrix Runtime异常, 异常分为两类,一类是Command内部抛出异常,一类是Hystrix自身的保护机制
|
||||
*/
|
||||
@ExceptionHandler(value = { HystrixRuntimeException.class })
|
||||
public final ResponseEntity<?> handleException(HystrixRuntimeException e, WebRequest request) {
|
||||
@@ -34,7 +35,7 @@ public class HystrixExceptionHandler extends ResponseEntityExceptionHandler {
|
||||
// 对命令抛出的异常进行特殊处理
|
||||
if (type.equals(FailureType.COMMAND_EXCEPTION)) {
|
||||
status = HttpStatus.INTERNAL_SERVER_ERROR;
|
||||
message = "HystrixRuntimeException:" + e.getCause().getMessage();
|
||||
message = Exceptions.getErrorMessageWithNested(e);
|
||||
}
|
||||
|
||||
logger.error(message, e);
|
||||
@@ -49,13 +50,11 @@ public class HystrixExceptionHandler extends ResponseEntityExceptionHandler {
|
||||
*/
|
||||
@ExceptionHandler(value = { HystrixBadRequestException.class })
|
||||
public final ResponseEntity<?> handleException(HystrixBadRequestException e, WebRequest request) {
|
||||
Exception realException = (Exception) e.getCause();
|
||||
|
||||
logger.error("HystrixClientException:" + realException.getMessage(), realException);
|
||||
String message = Exceptions.getErrorMessageWithNested(e);
|
||||
logger.error(message, e);
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.parseMediaType(MediaTypes.TEXT_PLAIN_UTF_8));
|
||||
return handleExceptionInternal(realException, realException.getMessage(), headers, HttpStatus.BAD_REQUEST,
|
||||
request);
|
||||
return handleExceptionInternal(e, message, headers, HttpStatus.BAD_REQUEST, request);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<%@ 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" %>
|
||||
<%@ taglib prefix="form" uri="http://www.springside.org.cn/tags/form" %>
|
||||
|
||||
@@ -19,7 +18,7 @@
|
||||
$('#hystrixResponse').html('<strong>Status:</strong> '+jqXHR.status+', <strong>Content:</strong> ' + jqXHR.responseText );
|
||||
}).fail( function(jqXHR, textStatus){
|
||||
$('#hystrixResponse').removeClass("alert alert-success").addClass("alert alert-warning");
|
||||
$('#hystrixResponse').html('<strong>Status:</strong> '+jqXHR.status+', <strong>Content:</strong> ' + jqXHR.statusText );
|
||||
$('#hystrixResponse').html('<strong>Status:</strong> '+jqXHR.status+', <strong>Content:</strong> ' + jqXHR.responseText );
|
||||
});
|
||||
}
|
||||
</script>
|
||||
@@ -39,25 +38,26 @@
|
||||
<h2>主要用户故事</h2>
|
||||
<ul>
|
||||
<li>在默认的正常状态,访问Hystrix服务和依赖资源,均返回正常结果。</li>
|
||||
<li>将资源状态切换为"超时",依赖资源需要在15秒后才返回结果访问,此时访问Hystrix服务,3秒后超时,返回503。</li>
|
||||
<li>三次超时后满足短路条件(60秒滚动窗口内起码有3个请求,50%失败),再次访问服务,立即返回503。</li>
|
||||
<li>10秒短路保护期内,所有访问都立即返回503,不会访问依赖资源。</li>
|
||||
<li>保护期过后,会放行一个请求,如果还是超时,则继续保持短路状态。如果成功则重置所有计数器。</li>
|
||||
<li>将资源状态切换为"超时",依赖资源需要在15秒后才返回结果访问,此时访问Hystrix服务,3秒后超时返回503。</li>
|
||||
<li>两次超时后满足短路条件(60秒滚动窗口内起码有3个请求,50%失败),再次访问服务,立即返回503。</li>
|
||||
<li>20秒短路保护期内,所有访问立即返回503,日志内不会显示访问依赖资源。</li>
|
||||
<li>保护期过后,放行一个请求,如果还是超时,则继续保持短路状态。如果成功则重置所有计数器。</li>
|
||||
</ul>
|
||||
|
||||
<h2>其他用户故事</h2>
|
||||
<ul>
|
||||
<li>将资源状态切换为"服务端失败",访问 Hystrix服务,即时返回500错误。</li>
|
||||
<li>将资源状态切换为"错误请求",依赖资源返回400错误,访问Hystrix服务,返回500错误,但错误率变化统计无变化。</li>
|
||||
<li>切换为调用者线程模式。</li>
|
||||
<li>使用JMeter高并发测Hystrix试线程池用光之后的保护.</li>
|
||||
<li>启动后切换为<a href="${ctx}/hystrix/disableIsolateThreadPool">调用者线程模式</a>, 依靠RestTemplate自身的超时机制,延长为10秒。</li>
|
||||
</ul>
|
||||
|
||||
<h2>监控结果 </h2>
|
||||
<form><input type="submit" value="刷新" class="btn btn-success"/></form>
|
||||
<table class="table table-bordered">
|
||||
<tr><td>是否短路:${metrics['circuitOpen']}</td></tr>
|
||||
<tr><td>3分钟窗口内,请求数:${metrics['totalRequest']}, 失败百分比:${metrics['errorPercentage']}%,成功:${metrics['success']}, 超时:${metrics['timeout']},失败:${metrics['failure']},短路:${metrics['shortCircuited']}, 线程池满:${metrics['threadPoolRejected']}</td></tr>
|
||||
<tr><td>50%延时:${metrics['latency50']}ms,90%延时:${metrics['latency90']}ms,100%延时:${metrics['latency100']}ms </td></tr>
|
||||
<tr><td>2分钟窗口内,请求数:${metrics['totalRequest']}, 失败百分比:${metrics['errorPercentage']}%,成功:${metrics['success']}, 超时:${metrics['timeout']},失败:${metrics['failure']},短路:${metrics['shortCircuited']}, 线程池满:${metrics['threadPoolRejected']}</td></tr>
|
||||
<tr><td>最快的50%请求延时:${metrics['latency50']}ms,90%请求延时:${metrics['latency90']}ms,100%请求延时:${metrics['latency100']}ms </td></tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
+2
-2
@@ -4,7 +4,7 @@ import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.springframework.web.client.HttpClientErrorException;
|
||||
import org.springframework.web.client.HttpStatusCodeException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springside.examples.showcase.functional.BaseFunctionalTestCase;
|
||||
import org.springside.examples.showcase.webservice.rest.UserDTO;
|
||||
@@ -33,7 +33,7 @@ public class UserJaxRsFT extends BaseFunctionalTestCase {
|
||||
assertEquals(new Long(1), user.getTeamId());
|
||||
try {
|
||||
user = restTemplate.getForObject(resoureUrl + "/{id}.json", UserDTO.class, 1L);
|
||||
} catch (HttpClientErrorException e) {
|
||||
} catch (HttpStatusCodeException e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
assertEquals("admin", user.getLoginName());
|
||||
|
||||
+5
-4
@@ -19,7 +19,7 @@ import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
||||
import org.springframework.web.client.HttpClientErrorException;
|
||||
import org.springframework.web.client.HttpStatusCodeException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springside.examples.showcase.functional.BaseFunctionalTestCase;
|
||||
import org.springside.examples.showcase.webservice.rest.UserDTO;
|
||||
@@ -56,6 +56,7 @@ public class UserRestFT extends BaseFunctionalTestCase {
|
||||
jdkTemplate = new RestTemplate();
|
||||
// (optional)设置20秒超时
|
||||
((SimpleClientHttpRequestFactory) jdkTemplate.getRequestFactory()).setConnectTimeout(20000);
|
||||
((SimpleClientHttpRequestFactory) jdkTemplate.getRequestFactory()).setReadTimeout(20000);
|
||||
|
||||
// 设置使用HttpClient4.0
|
||||
httpClientRestTemplate = new RestTemplate();
|
||||
@@ -100,7 +101,7 @@ public class UserRestFT extends BaseFunctionalTestCase {
|
||||
HttpEntity<String> xml = jdkTemplate.exchange(resoureUrl + "/{id}.xml", HttpMethod.GET, requestEntity,
|
||||
String.class, 1L);
|
||||
System.out.println("xml output is " + xml.getBody());
|
||||
} catch (HttpClientErrorException e) {
|
||||
} catch (HttpStatusCodeException e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
@@ -122,7 +123,7 @@ public class UserRestFT extends BaseFunctionalTestCase {
|
||||
// 直接取出JSON串
|
||||
String json = httpClientRestTemplate.getForObject(resoureUrl + "/{id}.json", String.class, 1L);
|
||||
System.out.println("json output is " + json);
|
||||
} catch (HttpClientErrorException e) {
|
||||
} catch (HttpStatusCodeException e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
@@ -141,7 +142,7 @@ public class UserRestFT extends BaseFunctionalTestCase {
|
||||
try {
|
||||
jdkTemplate.exchange(resoureUrl + "/{id}.xml", HttpMethod.GET, requestEntity, UserDTO.class, 1L);
|
||||
fail("Get should fail with error username/password");
|
||||
} catch (HttpClientErrorException e) {
|
||||
} catch (HttpStatusCodeException e) {
|
||||
assertEquals(HttpStatus.UNAUTHORIZED, e.getStatusCode());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,16 @@ public class Exceptions {
|
||||
return stringWriter.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取组合本异常信息与底层异常信息的异常描述, 适用于本异常为统一包装异常类,底层异常才是根本原因的情况。
|
||||
*/
|
||||
public static String getErrorMessageWithNested(Exception e) {
|
||||
Throwable nestedException = e.getCause();
|
||||
return new StringBuilder().append(e.getMessage()).append(" nested exception is ")
|
||||
.append(nestedException.getClass().getName()).append(":").append(nestedException.getMessage())
|
||||
.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断异常是否由某些底层的异常引起.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user