使用Hystrix构建API Gateway
随着MicroService架构越来越深入人心,如何构建一个高性能,错误容忍的API Gateway成了一个很多人都遇到的问题。Netflix开源了Hystrix来帮助我们实现API Gateway。
这里提供一个Springboot+Hystrix的例子:
https://github.com/qmhu/SpringHystrixSample
关于微服务可以看一下这个系列的文章,讲的非常好:
http://dockone.io/article/394
Hystrix
提供了以下重要功能:
- 同步/异步操作封装
- Fallback
- ThreadPool的隔离
- 请求Cache
- 请求合并
目录
一个例子
在pom.xml里引入springboot和hystrix的依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework</groupId>
<artifactId>gs-spring-boot</artifactId>
<version>0.1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.5.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-core</artifactId>
<version>1.4.23</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.1</version>
</dependency>
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-metrics-event-stream</artifactId>
<version>1.4.23</version>
</dependency>
</dependencies>
<properties>
<java.version>1.8</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
启动springboot
@SpringBootApplication
public class Application {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
定义CommandHttpCall
- 通过继承HystrixCommand定义了一个Command。这里封装了一个HttpCall的Command
- 通过Setter配置了各种Group来做请求配置的隔离
- 配置了熔断器(CircuitBreaker),如果请求达到了熔断的条件会自动触发熔断机制
- 配置了fallback方法,如果熔断机制触发会返回fallback方法的结果
public class CommandHttpCall extends HystrixCommand<String>{
private final String url;
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(CommandHttpCall.class);
public CommandHttpCall(String url) {
super(
Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("hystrix.command.http"))
.andCommandKey(HystrixCommandKey.Factory.asKey("hystrix.command.http"))
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("hystrix.command.http"))
.andCommandPropertiesDefaults(
HystrixCommandProperties.Setter()
.withCircuitBreakerRequestVolumeThreshold(2)
.withCircuitBreakerSleepWindowInMilliseconds(60 * 1000).
withFallbackEnabled(true).
withExecutionIsolationThreadInterruptOnTimeout(true).withExecutionTimeoutInMilliseconds(5000)));
this.url = url;
}
@Override
protected String run() throws Exception {
logger.info("Execution of Command: url={}", url);
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet(url);
try(CloseableHttpResponse response = httpclient.execute(httpGet)) {
HttpEntity entity = (HttpEntity) response.getEntity();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent()));
String total = "";
String line = bufferedReader.readLine();
while (line != null){
total += line;
line = bufferedReader.readLine();
}
return total;
}
}
@Override
protected String getFallback() {
return "failbackFor" + url;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
Future的方式进行异步调用
Hystrix提供了java concurrency包标准的Future接口来获取一个异步调用的结果,
这里首先请求了一个product service,等product service结果返回后再同时调用order service和cart service
@RequestMapping("/future")
public String getFuture() throws InterruptedException {
Future<String> productSyncCall = new CommandHttpCall("http://localhost:8091/product").queue();
try {
String product = productSyncCall.get();
System.out.println("sync get product" + product);
Future<String> orderSyncCall = new CommandHttpCall("http://localhost:8091/order").queue();
Future<String> cartSyncCall = new CommandHttpCall("http://localhost:8091/cart").queue();
System.out.println("sync get order" + orderSyncCall.get());
System.out.println("sync get cart" + cartSyncCall.get());
} catch (ExecutionException e) {
e.printStackTrace();
}
return new CommandHelloWorld("this is content for future").execute();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
ObServe的方式进行异步调用
Hystrix还提供了Rxjava接口Observable的调用方式,这里首先同时调用了product和order service,等结果都complete后再调用cart service
@RequestMapping("/observe")
public String getObserve() throws InterruptedException {
Observable<String> productCall = new CommandHttpCall("http://localhost:8091/product").observe();
Observable<String> orderCall = new CommandHttpCall("http://localhost:8091/order").observe();
Observable<String> cartCall = new CommandHttpCall("http://localhost:8091/cart").observe();
List<Observable<String>> result = new ArrayList<>();
result.add(productCall);
result.add(orderCall);
Observable.merge(result).subscribe(new Observer<String>() {
@Override
public void onCompleted() {
System.out.println("product&order call complete");
cartCall.subscribe(new Observer<String>() {
@Override
public void onCompleted() {
System.out.println("cart call complete");
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(String v) {
System.out.println("onNext: " + v);
}
});
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
}
@Override
public void onNext(String v) {
System.out.println("onNext: " + v);
}
});
return new CommandHelloWorld("this is content for observe").execute();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
Dashboard
在spring boot里注册HystrixMetricsStreamServlet
@Configuration
@PropertySource("classpath:application.properties")
class HystrixConfiguration extends SpringBootServletInitializer {
/**
* to expose stream endpoint
*/
@Bean
public ServletRegistrationBean servletRegistrationBean() {
return new ServletRegistrationBean(new HystrixMetricsStreamServlet(), "/hystrix.stream");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
配置好StreamServlet后就会在/hystrix.stream产生流数据,然后你只要再建一个dashboard并把当前的spring的url加入dashboard就可以看到很fancy的dashboard了。
搭建dashboard这块可以参考:
https://github.com/Netflix/Hystrix/tree/master/hystrix-dashboard
http://blog.csdn.net/MrTitan/article/details/51565074