1. 引言
之前我们展示过如何用 Ratpack 构建高性能响应式应用。本文将探讨如何在 Ratpack 应用中集成 Netflix Hystrix。
Netflix Hystrix 通过隔离访问点来控制分布式服务间的交互,防止级联故障并提供容错降级选项。它能帮助我们构建更健壮的应用。快速回顾可参考我们的Hystrix 入门指南。
我们将利用这些特性增强 Ratpack 应用。⚠️ 注意:ratpack-hystrix 模块已被移除(参考),本文代码不再维护。
2. Maven 依赖
在 pom.xml
中添加 ratpack-hystrix
依赖:
<dependency>
<groupId>io.ratpack</groupId>
<artifactId>ratpack-hystrix</artifactId>
<version>1.4.6</version>
</dependency>
最新版本可在这里查看。该模块已包含 ratpack-core
和 hystrix-core
。
为使用 Ratpack 响应式特性,还需添加 ratpack-rx
:
<dependency>
<groupId>io.ratpack</groupId>
<artifactId>ratpack-rx</artifactId>
<version>1.4.6</version>
</dependency>
最新版本可在这里查看。
3. 使用 Hystrix 命令提供服务
使用 Hystrix 时,底层服务通常被包装在 HystrixCommand
或 HystrixObservableCommand
中。Hystrix 支持同步、异步和响应式三种执行方式,其中响应式是唯一非阻塞且官方推荐的方式。
以下示例将构建从 GitHub REST API 获取用户信息的接口。
3.1. 响应式命令执行
首先构建响应式后端服务:
public class HystrixReactiveHttpCommand extends HystrixObservableCommand<String> {
//...
@Override
protected Observable<String> construct() {
return RxRatpack.observe(httpClient
.get(uri, r -> r.headers(h -> h.add("User-Agent", "Baeldung HttpClient")))
.map(res -> res.getBody().getText()));
}
@Override
protected Observable<String> resumeWithFallback() {
return Observable.just("eugenp's reactive fallback profile");
}
}
这里使用 Ratpack 响应式 HttpClient
发送 GET 请求。HystrixReactiveHttpCommand
可作为响应式处理器:
chain.get("rx", ctx ->
new HystrixReactiveHttpCommand(
ctx.get(HttpClient.class), eugenGithubProfileUri, timeout)
.toObservable()
.subscribe(ctx::render));
接口测试如下:
@Test
public void whenFetchReactive_thenGotEugenProfile() {
assertThat(appUnderTest.getHttpClient().getText("rx"),
containsString("www.baeldung.com"));
}
3.2. 异步命令执行
HystrixCommand
的异步执行会将命令加入线程池并返回 Future
:
chain.get("async", ctx -> ctx.render(
new HystrixAsyncHttpCommand(eugenGithubProfileUri, timeout)
.queue()
.get()));
HystrixAsyncHttpCommand
实现:
public class HystrixAsyncHttpCommand extends HystrixCommand<String> {
//...
@Override
protected String run() throws Exception {
return EntityUtils.toString(HttpClientBuilder.create()
.setDefaultRequestConfig(requestConfig)
.setDefaultHeaders(Collections.singleton(
new BasicHeader("User-Agent", "Baeldung Blocking HttpClient")))
.build().execute(new HttpGet(uri)).getEntity());
}
@Override
protected String getFallback() {
return "eugenp's async fallback profile";
}
}
这里使用阻塞式 HttpClient
(而非非阻塞式),目的是让 Hystrix 控制命令执行超时,避免手动处理 Future
响应。这样还能启用 Hystrix 的降级和缓存功能。
异步执行同样能获得预期结果:
@Test
public void whenFetchAsync_thenGotEugenProfile() {
assertThat(appUnderTest.getHttpClient().getText("async"),
containsString("www.baeldung.com"));
}
3.3. 同步命令执行
同步执行直接在当前线程运行命令:
chain.get("sync", ctx -> ctx.render(
new HystrixSyncHttpCommand(eugenGithubProfileUri, timeout).execute()));
HystrixSyncHttpCommand
实现与 HystrixAsyncHttpCommand
几乎相同,仅降级结果不同。未触发降级时,行为与响应式/异步执行一致:
@Test
public void whenFetchSync_thenGotEugenProfile() {
assertThat(appUnderTest.getHttpClient().getText("sync"),
containsString("www.baeldung.com"));
}
4. 监控指标
通过注册 Guice 模块 HystrixModule
到 Ratpack 注册中心,可流式传输请求级指标,并通过 GET 接口暴露事件流:
serverSpec.registry(
Guice.registry(spec -> spec.module(new HystrixModule().sse())))
.handlers(c -> c.get("hystrix", new HystrixMetricsEventStreamHandler()));
HystrixMetricsEventStreamHandler
以 text/event-stream
格式流式传输 Hystrix 指标,便于在 Hystrix Dashboard 中监控。
可部署独立 Hystrix Dashboard,将事件流地址加入监控列表查看应用性能:
多次请求后,可在 Dashboard 中看到 Hystrix 命令相关指标。
4.1. 实现原理
HystrixModule
通过 HystrixPlugin 注册 Hystrix 并发策略,用 Ratpack 注册中心管理请求上下文。这消除了每次请求前初始化 Hystrix 上下文的需要:
public class HystrixModule extends ConfigurableModule<HystrixModule.Config> {
//...
@Override
protected void configure() {
try {
HystrixPlugins.getInstance().registerConcurrencyStrategy(
new HystrixRegistryBackedConcurrencyStrategy());
} catch (IllegalStateException e) {
//...
}
}
//...
}
5. 总结
本文展示了如何在 Ratpack 中集成 Hystrix,以及如何将应用指标推送到 Hystrix Dashboard 以便监控性能。
完整实现代码请参考 GitHub 项目。