Merge pull request #586 from sivabalachandran/master

Hystrix Spring integration article 2
This commit is contained in:
Alex Theedom 2016-08-08 07:49:53 +01:00 committed by GitHub
commit 440e247176
12 changed files with 257 additions and 25 deletions

View File

@ -6,9 +6,15 @@
<groupId>com.baeldung</groupId> <groupId>com.baeldung</groupId>
<artifactId>hystrix</artifactId> <artifactId>hystrix</artifactId>
<version>1.0</version> <version>1.0</version>
<name>hystrix</name> <name>hystrix</name>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.0.RELEASE</version>
<relativePath></relativePath>
</parent>
<properties> <properties>
<!-- General --> <!-- General -->
@ -32,12 +38,35 @@
<dependencies> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency> <dependency>
<groupId>com.netflix.hystrix</groupId> <groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-core</artifactId> <artifactId>hystrix-core</artifactId>
<version>${hystrix-core.version}</version> <version>${hystrix-core.version}</version>
</dependency> </dependency>
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-metrics-event-stream</artifactId>
<version>1.3.16</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.netflix.hystrix/hystrix-dashboard -->
<!--<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-dashboard</artifactId>
<version>1.4.3</version>
</dependency>-->
<dependency> <dependency>
<groupId>com.netflix.rxjava</groupId> <groupId>com.netflix.rxjava</groupId>
<artifactId>rxjava-core</artifactId> <artifactId>rxjava-core</artifactId>
@ -62,6 +91,10 @@
<build> <build>
<plugins> <plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>

View File

@ -0,0 +1,20 @@
package com.baeldung.hystrix;
import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class AppConfig {
public static void main(String[] args) {
SpringApplication.run(AppConfig.class, args);
}
@Bean
public ServletRegistrationBean adminServletRegistrationBean() {
return new ServletRegistrationBean(new HystrixMetricsStreamServlet(), "/hystrix.stream");
}
}

View File

@ -0,0 +1,81 @@
package com.baeldung.hystrix;
import com.netflix.hystrix.*;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@Component
@Aspect
public class HystrixAspect {
private HystrixCommand.Setter config;
private HystrixCommandProperties.Setter commandProperties;
private HystrixThreadPoolProperties.Setter threadPoolProperties;
@Around("@annotation(com.baeldung.hystrix.HystrixCircuitBreaker)")
public Object circuitBreakerAround(final ProceedingJoinPoint aJoinPoint) {
return new RemoteServiceCommand(config, aJoinPoint).execute();
}
@PostConstruct
private void setup() {
this.config = HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey));
this.config = config.andCommandKey(HystrixCommandKey.Factory.asKey(key));
this.commandProperties = HystrixCommandProperties.Setter();
this.commandProperties.withExecutionTimeoutInMilliseconds(executionTimeout);
this.commandProperties.withCircuitBreakerSleepWindowInMilliseconds(sleepWindow);
this.threadPoolProperties= HystrixThreadPoolProperties.Setter();
this.threadPoolProperties.withMaxQueueSize(maxThreadCount).withCoreSize(coreThreadCount).withMaxQueueSize(queueCount);
this.config.andCommandPropertiesDefaults(commandProperties);
this.config.andThreadPoolPropertiesDefaults(threadPoolProperties);
}
private static class RemoteServiceCommand extends HystrixCommand<String> {
private final ProceedingJoinPoint joinPoint;
RemoteServiceCommand(final Setter config, final ProceedingJoinPoint joinPoint) {
super(config);
this.joinPoint = joinPoint;
}
@Override
protected String run() throws Exception {
try {
return (String) joinPoint.proceed();
} catch (final Throwable th) {
throw new Exception(th);
}
}
}
@Value("${remoteservice.command.execution.timeout}")
private int executionTimeout;
@Value("${remoteservice.command.sleepwindow}")
private int sleepWindow;
@Value("${remoteservice.command.threadpool.maxsize}")
private int maxThreadCount;
@Value("${remoteservice.command.threadpool.coresize}")
private int coreThreadCount;
@Value("${remoteservice.command.task.queue.size}")
private int queueCount;
@Value("${remoteservice.command.group.key}")
private String groupKey;
@Value("${remoteservice.command.key}")
private String key;
}

View File

@ -0,0 +1,11 @@
package com.baeldung.hystrix;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface HystrixCircuitBreaker {
}

View File

@ -0,0 +1,17 @@
package com.baeldung.hystrix;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HystrixController {
@Autowired
private SpringExistingClient client;
@RequestMapping("/")
public String index() throws InterruptedException{
return client.invokeRemoteService();
}
}

View File

@ -1,15 +0,0 @@
package com.baeldung.hystrix;
public class RemoteServiceSimulator {
public String checkSomething(final long timeout) throws InterruptedException {
System.out.print(String.format("Waiting %sms. ", timeout));
// to simulate a real world delay in processing.
Thread.sleep(timeout);
return "Done waiting.";
}
}

View File

@ -1,7 +1,7 @@
package com.baeldung.hystrix; package com.baeldung.hystrix;
class RemoteServiceTestSimulator { public class RemoteServiceTestSimulator {
private long wait; private long wait;

View File

@ -0,0 +1,17 @@
package com.baeldung.hystrix;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component("springClient")
public class SpringExistingClient {
@Value("${remoteservice.timeout}")
private int remoteServiceDelay;
@HystrixCircuitBreaker
public String invokeRemoteService() throws InterruptedException{
return new RemoteServiceTestSimulator(remoteServiceDelay).execute();
}
}

View File

@ -0,0 +1,8 @@
remoteservice.command.group.key=RemoteServiceGroup
remoteservice.command.key=RemoteServiceKey
remoteservice.command.execution.timeout=10000
remoteservice.command.threadpool.coresize=5
remoteservice.command.threadpool.maxsize=10
remoteservice.command.task.queue.size=5
remoteservice.command.sleepwindow=5000
remoteservice.timeout=5000

View File

@ -9,13 +9,15 @@ import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.ExpectedException; import org.junit.rules.ExpectedException;
import java.util.concurrent.ExecutionException;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
public class HystrixTimeoutTest { public class HystrixTimeoutTest {
private static HystrixCommand.Setter config; private HystrixCommand.Setter config;
private static HystrixCommandProperties.Setter commandProperties = HystrixCommandProperties.Setter(); private HystrixCommandProperties.Setter commandProperties ;
@Rule @Rule
@ -23,6 +25,7 @@ public class HystrixTimeoutTest {
@Before @Before
public void setup() { public void setup() {
commandProperties = HystrixCommandProperties.Setter();
config = HystrixCommand config = HystrixCommand
.Setter .Setter
.withGroupKey(HystrixCommandGroupKey.Factory.asKey("RemoteServiceGroup1")); .withGroupKey(HystrixCommandGroupKey.Factory.asKey("RemoteServiceGroup1"));
@ -34,29 +37,86 @@ public class HystrixTimeoutTest {
} }
@Test @Test
public void givenTimeoutEqualTo100_andDefaultSettings_thenReturnSuccess() throws InterruptedException { public void givenServiceTimeoutEqualTo100_andDefaultSettings_thenReturnSuccess() throws InterruptedException {
assertThat(new RemoteServiceTestCommand(config, new RemoteServiceTestSimulator(100)).execute(), equalTo("Success")); assertThat(new RemoteServiceTestCommand(config, new RemoteServiceTestSimulator(100)).execute(), equalTo("Success"));
} }
@Test @Test
public void givenTimeoutEqualTo10000_andDefaultSettings_thenExpectHystrixRuntimeException() throws InterruptedException { public void givenServiceTimeoutEqualTo10000_andDefaultSettings_thenExpectHRE() throws InterruptedException {
exception.expect(HystrixRuntimeException.class); exception.expect(HystrixRuntimeException.class);
new RemoteServiceTestCommand(config, new RemoteServiceTestSimulator(10_000)).execute(); new RemoteServiceTestCommand(config, new RemoteServiceTestSimulator(10_000)).execute();
} }
@Test @Test
public void givenTimeoutEqualTo5000_andExecutionTimeoutEqualTo10000_thenReturnSuccess() throws InterruptedException { public void givenServiceTimeoutEqualTo5000_andExecutionTimeoutEqualTo10000_thenReturnSuccess()
throws InterruptedException {
commandProperties.withExecutionTimeoutInMilliseconds(10_000); commandProperties.withExecutionTimeoutInMilliseconds(10_000);
config.andCommandPropertiesDefaults(commandProperties); config.andCommandPropertiesDefaults(commandProperties);
assertThat(new RemoteServiceTestCommand(config, new RemoteServiceTestSimulator(5_000)).execute(), equalTo("Success")); assertThat(new RemoteServiceTestCommand(config, new RemoteServiceTestSimulator(500)).execute(),
equalTo("Success"));
} }
@Test @Test
public void givenTimeoutEqualTo15000_andExecutionTimeoutEqualTo10000_thenExpectHystrixRuntimeException() throws InterruptedException { public void givenServiceTimeoutEqualTo15000_andExecutionTimeoutEqualTo5000_thenExpectHRE()
throws InterruptedException {
exception.expect(HystrixRuntimeException.class); exception.expect(HystrixRuntimeException.class);
commandProperties.withExecutionTimeoutInMilliseconds(10_000); commandProperties.withExecutionTimeoutInMilliseconds(5_000);
config.andCommandPropertiesDefaults(commandProperties); config.andCommandPropertiesDefaults(commandProperties);
new RemoteServiceTestCommand(config, new RemoteServiceTestSimulator(15_000)).execute(); new RemoteServiceTestCommand(config, new RemoteServiceTestSimulator(15_000)).execute();
} }
@Test
public void givenServiceTimeoutEqual_andExecutionTimeout_andThreadPool_thenReturnSuccess()
throws InterruptedException {
commandProperties.withExecutionTimeoutInMilliseconds(10_000);
config.andCommandPropertiesDefaults(commandProperties);
config.andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter()
.withMaxQueueSize(10)
.withCoreSize(3)
.withQueueSizeRejectionThreshold(10));
assertThat(new RemoteServiceTestCommand(config, new RemoteServiceTestSimulator(500)).execute(),
equalTo("Success"));
}
@Test
public void givenCircuitBreakerSetup_thenReturnSuccess() throws InterruptedException {
commandProperties.withExecutionTimeoutInMilliseconds(1000);
commandProperties.withCircuitBreakerSleepWindowInMilliseconds(4000);
commandProperties.withExecutionIsolationStrategy(
HystrixCommandProperties.ExecutionIsolationStrategy.THREAD);
commandProperties.withCircuitBreakerEnabled(true);
commandProperties.withCircuitBreakerRequestVolumeThreshold(1);
config.andCommandPropertiesDefaults(commandProperties);
config.andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter()
.withMaxQueueSize(1)
.withCoreSize(1)
.withQueueSizeRejectionThreshold(1));
assertThat(this.invokeRemoteService(10000), equalTo(null));
assertThat(this.invokeRemoteService(10000), equalTo(null));
Thread.sleep(5000);
assertThat(new RemoteServiceTestCommand(config, new RemoteServiceTestSimulator(500)).execute(),
equalTo("Success"));
assertThat(new RemoteServiceTestCommand(config, new RemoteServiceTestSimulator(500)).execute(),
equalTo("Success"));
assertThat(new RemoteServiceTestCommand(config, new RemoteServiceTestSimulator(500)).execute(),
equalTo("Success"));
}
public String invokeRemoteService(long timeout) throws InterruptedException{
String response = null;
try{
response = new RemoteServiceTestCommand(config,
new RemoteServiceTestSimulator(timeout)).execute();
}catch(HystrixRuntimeException ex){
System.out.println("ex = " + ex);
}
return response;
}
} }