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>
|
||||
<artifactId>hystrix</artifactId>
|
||||
<version>1.0</version>
|
||||
|
||||
<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>
|
||||
|
||||
<!-- General -->
|
||||
|
@ -32,12 +38,35 @@
|
|||
|
||||
<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>
|
||||
<groupId>com.netflix.hystrix</groupId>
|
||||
<artifactId>hystrix-core</artifactId>
|
||||
<version>${hystrix-core.version}</version>
|
||||
</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>
|
||||
<groupId>com.netflix.rxjava</groupId>
|
||||
<artifactId>rxjava-core</artifactId>
|
||||
|
@ -62,6 +91,10 @@
|
|||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<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;
|
||||
|
||||
|
||||
class RemoteServiceTestSimulator {
|
||||
public class RemoteServiceTestSimulator {
|
||||
|
||||
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.rules.ExpectedException;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
public class HystrixTimeoutTest {
|
||||
|
||||
private static HystrixCommand.Setter config;
|
||||
private static HystrixCommandProperties.Setter commandProperties = HystrixCommandProperties.Setter();
|
||||
private HystrixCommand.Setter config;
|
||||
private HystrixCommandProperties.Setter commandProperties ;
|
||||
|
||||
|
||||
@Rule
|
||||
|
@ -23,6 +25,7 @@ public class HystrixTimeoutTest {
|
|||
|
||||
@Before
|
||||
public void setup() {
|
||||
commandProperties = HystrixCommandProperties.Setter();
|
||||
config = HystrixCommand
|
||||
.Setter
|
||||
.withGroupKey(HystrixCommandGroupKey.Factory.asKey("RemoteServiceGroup1"));
|
||||
|
@ -34,29 +37,86 @@ public class HystrixTimeoutTest {
|
|||
}
|
||||
|
||||
@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"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenTimeoutEqualTo10000_andDefaultSettings_thenExpectHystrixRuntimeException() throws InterruptedException {
|
||||
public void givenServiceTimeoutEqualTo10000_andDefaultSettings_thenExpectHRE() throws InterruptedException {
|
||||
exception.expect(HystrixRuntimeException.class);
|
||||
new RemoteServiceTestCommand(config, new RemoteServiceTestSimulator(10_000)).execute();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenTimeoutEqualTo5000_andExecutionTimeoutEqualTo10000_thenReturnSuccess() throws InterruptedException {
|
||||
public void givenServiceTimeoutEqualTo5000_andExecutionTimeoutEqualTo10000_thenReturnSuccess()
|
||||
throws InterruptedException {
|
||||
commandProperties.withExecutionTimeoutInMilliseconds(10_000);
|
||||
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
|
||||
public void givenTimeoutEqualTo15000_andExecutionTimeoutEqualTo10000_thenExpectHystrixRuntimeException() throws InterruptedException {
|
||||
public void givenServiceTimeoutEqualTo15000_andExecutionTimeoutEqualTo5000_thenExpectHRE()
|
||||
throws InterruptedException {
|
||||
exception.expect(HystrixRuntimeException.class);
|
||||
commandProperties.withExecutionTimeoutInMilliseconds(10_000);
|
||||
commandProperties.withExecutionTimeoutInMilliseconds(5_000);
|
||||
config.andCommandPropertiesDefaults(commandProperties);
|
||||
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