Merge pull request #586 from sivabalachandran/master
Hystrix Spring integration article 2
This commit is contained in:
commit
440e247176
|
@ -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>
|
||||||
|
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +1,7 @@
|
||||||
package com.baeldung.hystrix;
|
package com.baeldung.hystrix;
|
||||||
|
|
||||||
|
|
||||||
class RemoteServiceTestSimulator {
|
public class RemoteServiceTestSimulator {
|
||||||
|
|
||||||
private long wait;
|
private long wait;
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue