Spring,Reactor和ElasticSearch:从回调到反应流

Spring 5(以及Boot 2,在数周之内到货)是一次革命。 不是“ XML上的注释 ”或“ Java上的注释类 ”的革命。 这是一个真正的革命性框架,可以编写全新的应用程序类别。 近年来,我对此框架感到有些恐惧。 “ Spring Cloud是简化了Spring Boot使用的框架,Spring简化了Spring使用的框架,是简化了企业开发的框架。” start.spring.io (也称为“ 开始…点弹簧…点I…O ”)列出了120个可以添加到服务中的不同模块(!)。 这些天的春天变成了一个庞大的伞式项目,我可以想象为什么有些人(仍然!)偏爱Java EE(或这些天叫什么)。

但是Spring 5带来了革命性的革命。 它不再只是阻止servlet API和各种Web框架的包装器。 在Project Reactor之上的Spring 5允许编写高性能,超快速和可伸缩的服务器,完全避免了servlet堆栈。 该死的,CLASSPATH上没有Jetty甚至servlet API! 在Spring 5 Web-flux的核心,我们将找到Netty ,这是一个用于编写异步客户端和服务器的低级框架。 最终,Spring成为反应框架家族的一等公民。 Java开发人员可以实现快速服务,而不必离开自己的舒适区,也可以使用https://doc.akka.io/docs/akka-http/current/https://www.playframework.com/ 。 Spring 5是用于构建高度可扩展且具有弹性的应用程序的完全被动的现代工具。 尽管如此,诸如控制器,Bean,依赖注入之类的基本原理都是相同的。 而且,升级路径很顺利,我们可以逐步添加功能,而不是学习全新的外来框架。 足够多的谈话,让我们写一些代码。

在本文中,我们将编写一个简单的无头应用程序,该应用程序可以在ElasticSearch中大量索引文档。 我们的目标是即使服务器速度变慢,也只需要几个线程即可实现数千个并发连接。 但是,与Spring Data MongoDB不同, Spring Data ElasticSearch本身不支持非阻塞存储库。 好吧,后者似乎不再维护了,当前版本已经3年了。 许多文章定位Spring 5 +的MongoDB其仓库返回无阻塞流( FluxFlowable的RxJava)。 这一点会更高级。

ElasticSearch 6 Java API使用RESTful接口,并使用非阻塞HTTP客户端实现。 不幸的是,它使用回调而不是像CompletableFuture这样的理智的东西。 因此,让我们自己构建客户端适配器。

使用Fluxes和Monos的ElasticSearch客户端

本文的源代码可在react reactive-elastic-search分支上的github.com/nurkiewicz/elastic-flux上找到。

我们想通过返回FluxMono来构建一个支持Project Reactor的ElasticSearch Java客户端。 当然,如果基础流是完全异步的并且不消耗线程,则将获得最大的好处。 幸运的是,Java API就是这样。 首先,让我们将ElasticSearch的客户端设置为Spring Bean:

import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
 
@Bean
RestHighLevelClient restHighLevelClient() {
    return new RestHighLevelClient(
            RestClient
                    .builder(new HttpHost("localhost", 9200))
                    .setRequestConfigCallback(config -> config
                            .setConnectTimeout(5_000)
                            .setConnectionRequestTimeout(5_000)
                            .setSocketTimeout(5_000)
                    )
                    .setMaxRetryTimeoutMillis(5_000));
}

在现实生活中,我们显然会参数化大多数这些东西。 我们将为简单的JSON文档建立索引,目前它们的内容并不重要:

@Value
class Doc {
    private final String username;
    private final String json;
}

我们将编写的代码包装RestHighLevelClient并通过返回Mono<IndexResponse>使它更高级Mono非常类似于CompletableFuture但有两个例外:

  • 这很懒–只要您不订阅,就不会开始计算
  • CompletableFuture不同, Mono可以正常完成而不会发出任何值

第二个区别总是对我有些误导。 在RxJava 2.x中,有两种不同的类型: Single (总是带有值或错误来完成)和Maybe (类似于Mono )。 太糟糕的Reactor并没有做到这一点。 没关系,适配器层是什么样的? 普通的Elastic API如下所示:

client.indexAsync(indexRequest, new ActionListener() {
    @Override
    public void onResponse(IndexResponse indexResponse) {
        //got response
    }
 
    @Override
    public void onFailure(Exception e) {
        //got error
    }
});

您可以看到前进的方向: callback hell 。 与其将自定义ActionListener公开为该逻辑的参数,不如将其包装在Mono

import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
 
import reactor.core.publisher.Mono;
import reactor.core.publisher.MonoSink;
 
private Mono<IndexResponse> indexDoc(Doc doc) {
    return Mono.create(sink -> {
        IndexRequest indexRequest = new IndexRequest("people", "person", doc.getUsername());
        indexRequest.source(doc.getJson(), XContentType.JSON);
        client.indexAsync(indexRequest, new ActionListener<IndexResponse>() {
            @Override
            public void onResponse(IndexResponse indexResponse) {
                sink.success(indexResponse);
            }
 
            @Override
            public void onFailure(Exception e) {
                sink.error(e);
            }
        });
    });
}

我们必须创建IndexRequest包装JSON文档,并通过RESTful API发送它。 但这不是重点。 我们正在使用Mono.create()方法,它有一些缺点,但稍后会介绍更多。 Mono是懒惰的,因此仅调用indexDoc()还不够,没有对ElasticSearch发出HTTP请求。 但是,每次有人订阅此单元素源时,都会执行create()内部的逻辑。 关键行是sink.success()sink.error() 。 它们将结果从ElasticSearch(来自后台异步线程)传播到流中。 在实践中如何使用这种方法? 非常简单!

Doc doc = //...
indexDoc(doc)
        .subscribe(
                indexResponse -> log.info("Got response")
        );

当然,反应流处理的真正能力来自于组合多个流。 但是我们迈出了第一步:将基于回调的异步API转换为通用流。 如果您不愿意使用MongoDB,它会在存储库中内置支持诸如MonoFlux类的反应类型。 CassandraRedis也是如此 。 在下一篇文章中,我们将学习如何生成一些虚假数据并对其进行索引。

翻译自: https://www.javacodegeeks.com/2018/01/spring-reactor-elasticsearch-callbacks-reactive-streams.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值