diff --git a/examples/quickstart/src/main/java/org/springside/examples/quickstart/rest/RestExceptionHandler.java b/examples/quickstart/src/main/java/org/springside/examples/quickstart/rest/RestExceptionHandler.java index 82f69d5d..90f32941 100644 --- a/examples/quickstart/src/main/java/org/springside/examples/quickstart/rest/RestExceptionHandler.java +++ b/examples/quickstart/src/main/java/org/springside/examples/quickstart/rest/RestExceptionHandler.java @@ -2,7 +2,9 @@ package org.springside.examples.quickstart.rest; import javax.validation.ConstraintViolationException; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; @@ -15,7 +17,9 @@ public class RestExceptionHandler extends ResponseEntityExceptionHandler { @ExceptionHandler(value = { ConstraintViolationException.class }) public final ResponseEntity handleException(ConstraintViolationException ex, WebRequest request) { - return new ResponseEntity(BeanValidators.extractPropertyAndMessage(ex.getConstraintViolations()), - HttpStatus.BAD_REQUEST); + String body = BeanValidators.extractPropertyAndMessage(ex.getConstraintViolations()).toString(); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.TEXT_PLAIN); + return handleExceptionInternal(ex, body, null, HttpStatus.BAD_REQUEST, request); } } diff --git a/examples/showcase/src/main/java/org/springside/examples/showcase/demos/hystrix/resource/ResourceController.java b/examples/showcase/src/main/java/org/springside/examples/showcase/demos/hystrix/resource/ResourceController.java index 378c1550..0fef87fe 100644 --- a/examples/showcase/src/main/java/org/springside/examples/showcase/demos/hystrix/resource/ResourceController.java +++ b/examples/showcase/src/main/java/org/springside/examples/showcase/demos/hystrix/resource/ResourceController.java @@ -1,5 +1,6 @@ package org.springside.examples.showcase.demos.hystrix.resource; +import org.hibernate.service.spi.ServiceException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; @@ -18,7 +19,7 @@ import org.springside.modules.utils.Threads; */ @Controller public class ResourceController { - public static final int TIMEOUT = 20000; + public static final int TIMEOUT = 10000; @Autowired private AccountEffectiveService accountService; @@ -39,7 +40,7 @@ public class ResourceController { } if ("fail".equals(HystrixController.status)) { - throw new RuntimeException("Server Exception"); + throw new ServiceException("Server Exception"); } return null; diff --git a/examples/showcase/src/main/java/org/springside/examples/showcase/demos/hystrix/service/GetUserCommand.java b/examples/showcase/src/main/java/org/springside/examples/showcase/demos/hystrix/service/GetUserCommand.java index ff01a7a9..6800b592 100644 --- a/examples/showcase/src/main/java/org/springside/examples/showcase/demos/hystrix/service/GetUserCommand.java +++ b/examples/showcase/src/main/java/org/springside/examples/showcase/demos/hystrix/service/GetUserCommand.java @@ -5,6 +5,8 @@ import org.springside.examples.showcase.webservice.rest.UserDTO; import com.netflix.hystrix.HystrixCommand; import com.netflix.hystrix.HystrixCommandGroupKey; +import com.netflix.hystrix.HystrixCommandKey; +import com.netflix.hystrix.HystrixCommandProperties; public class GetUserCommand extends HystrixCommand { @@ -13,7 +15,20 @@ public class GetUserCommand extends HystrixCommand { private RestTemplate restTemplate = new RestTemplate(); protected GetUserCommand(Long id) { - super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup")); + + super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup")) + .andCommandKey(HystrixCommandKey.Factory.asKey("GetUserCommand")) + .andCommandPropertiesDefaults(HystrixCommandProperties.Setter() + // 线程超时,默认为1秒,设为3秒, + .withExecutionIsolationThreadTimeoutInMilliseconds(3000) + // 至少多少请求在rolling window内发生,开始触发短路的计算,默认为20, 设为3 + .withCircuitBreakerRequestVolumeThreshold(3) + // 多少百分比的fail在rolling windows内发生,计算为短路。默认为50% + .withCircuitBreakerErrorThresholdPercentage(50) + // 被置成短路后,多久才会重新尝试访问资源。默认为5秒,设为10秒 + .withCircuitBreakerSleepWindowInMilliseconds(10000) + // rolling windows 长度,默认为10秒,设为20秒。 + .withMetricsRollingStatisticalWindowInMilliseconds(20000))); this.id = id; } diff --git a/examples/showcase/src/main/java/org/springside/examples/showcase/demos/hystrix/service/ServiceController.java b/examples/showcase/src/main/java/org/springside/examples/showcase/demos/hystrix/service/ServiceController.java index 7855779a..009441c8 100644 --- a/examples/showcase/src/main/java/org/springside/examples/showcase/demos/hystrix/service/ServiceController.java +++ b/examples/showcase/src/main/java/org/springside/examples/showcase/demos/hystrix/service/ServiceController.java @@ -1,10 +1,14 @@ package org.springside.examples.showcase.demos.hystrix.service; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +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.webservice.rest.RestException; import org.springside.examples.showcase.webservice.rest.UserDTO; /** @@ -12,10 +16,18 @@ import org.springside.examples.showcase.webservice.rest.UserDTO; */ @Controller public class ServiceController { + private static Logger logger = LoggerFactory.getLogger(ServiceController.class); + @RequestMapping(value = "/hystrix/service/{id}", method = RequestMethod.GET) @ResponseBody public UserDTO getUser(@PathVariable("id") Long id) { GetUserCommand command = new GetUserCommand(id); - return command.execute(); + + try { + return command.execute(); + } catch (Exception e) { + logger.error(e.getMessage(), e); + throw new RestException(HttpStatus.SERVICE_UNAVAILABLE); + } } } diff --git a/examples/showcase/src/main/java/org/springside/examples/showcase/webservice/rest/RestException.java b/examples/showcase/src/main/java/org/springside/examples/showcase/webservice/rest/RestException.java new file mode 100644 index 00000000..0a8b0b13 --- /dev/null +++ b/examples/showcase/src/main/java/org/springside/examples/showcase/webservice/rest/RestException.java @@ -0,0 +1,20 @@ +package org.springside.examples.showcase.webservice.rest; + +import org.springframework.http.HttpStatus; + +public class RestException extends RuntimeException { + + public HttpStatus status; + + public RestException() { + } + + public RestException(HttpStatus status) { + this.status = status; + } + + public RestException(HttpStatus status, String message) { + super(message); + this.status = status; + } +} diff --git a/examples/showcase/src/main/java/org/springside/examples/showcase/webservice/rest/RestExceptionHandler.java b/examples/showcase/src/main/java/org/springside/examples/showcase/webservice/rest/RestExceptionHandler.java new file mode 100644 index 00000000..0b34c5c6 --- /dev/null +++ b/examples/showcase/src/main/java/org/springside/examples/showcase/webservice/rest/RestExceptionHandler.java @@ -0,0 +1,37 @@ +package org.springside.examples.showcase.webservice.rest; + +import javax.validation.ConstraintViolationException; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +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.beanvalidator.BeanValidators; + +@ControllerAdvice +public class RestExceptionHandler extends ResponseEntityExceptionHandler { + + @ExceptionHandler(value = { ConstraintViolationException.class }) + public final ResponseEntity handleException(ConstraintViolationException ex, WebRequest request) { + String body = BeanValidators.extractPropertyAndMessage(ex.getConstraintViolations()).toString(); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.TEXT_PLAIN); + return handleExceptionInternal(ex, body, headers, HttpStatus.BAD_REQUEST, request); + } + + @ExceptionHandler(value = { RestException.class }) + public final ResponseEntity handleException(RestException ex, WebRequest request) { + String body = ex.getMessage(); + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.TEXT_PLAIN); + + HttpStatus status = (ex.status != null) ? ex.status : HttpStatus.INTERNAL_SERVER_ERROR; + + return handleExceptionInternal(ex, body, headers, status, request); + } +} diff --git a/examples/showcase/src/main/webapp/WEB-INF/views/story/hystrix.jsp b/examples/showcase/src/main/webapp/WEB-INF/views/story/hystrix.jsp index 185a9fd1..902b31da 100644 --- a/examples/showcase/src/main/webapp/WEB-INF/views/story/hystrix.jsp +++ b/examples/showcase/src/main/webapp/WEB-INF/views/story/hystrix.jsp @@ -15,6 +15,8 @@

Hystrix 演示

+ +

Netflix Hystrix 是一个延迟与容错类库,通过独立访问远程系统、服务和第三方库的节点,在复杂的分布式系统里停止雪崩及提供恢复能力。

当前资源状态为<%= HystrixController.status %>

- + +

访问Hystrix服务

+ ${ctx}/hystrix/service/1 \ No newline at end of file