Flink写入数据到ElasticSearch (ElasticSearch详细使用指南及采坑记录)

一、ElasticSearchSink介绍

在使用Flink进行数据的处理的时候,一个必要步骤就是需要将计算的结果进行存储或导出,Flink中这个过程称为Sink,官方我们提供了常用的几种Sink Connector,例如:

  • Apache Kafka
  • Elasticsearch
  • Elasticsearch 2x
  • Hadoop FileSystem

这篇就选取其中一个常用的ElasticsearchSink来进行介绍,并讲解一下生产环境中使用时的一些注意点,以及其内部实现机制。

二、使用方式

a、添加pom依赖

        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-connector-elasticsearch2_2.10</artifactId>
            <version>1.3.1</version>
        </dependency>

根据自己所用的filnk版本以及es版本对上面的版本号进行调整

b、实现对应代码

DataStream<String> input = ...;

Map<String, String> config = new HashMap<>();
config.put("cluster.name", "my-cluster-name");
//该配置表示批量写入ES时的记录条数
config.put("bulk.flush.max.actions", "1");

List<InetSocketAddress> transportAddresses = new ArrayList<>();
transportAddresses.add(new InetSocketAddress(InetAddress.getByName("127.0.0.1"), 9300));
transportAddresses.add(new InetSocketAddress(InetAddress.getByName("10.2.3.1"), 9300));

input.addSink(new ElasticsearchSink<>(config, transportAddresses, new ElasticsearchSinkFunction<String>() {
    public IndexRequest createIndexRequest(String element) {
        Map<String, String> json = new HashMap<>();
        //将需要写入ES的字段依次添加到Map当中
        json.put("data", element);

        return Requests.indexRequest()
                .index("my-index")
                .type("my-type")
                .source(json);
    }

    @Override
    public void process(String element, RuntimeContext ctx, RequestIndexer indexer) {
        indexer.add(createIndexRequest(element));
    }
}));

c、扩展配置

经过上面的代码已经实现了一个基础版的EsSink,但是上述代码当ES集群出现波动的时候,由于不具备重试机制则有可能出现丢数据的情况。生产环境中为了实现数据完整性,我们需要添加一些失败重试配置,来实现写入失败情况下的容错处理,常用的失败重试配置有:

//1、用来表示是否开启重试机制
config.put("bulk.flush.backoff.enable", "true");
//2、重试策略,又可以分为以下两种类型
    //a、指数型,表示多次重试之间的时间间隔按照指数方式进行增长。eg:2 -> 4 -> 8 ...
    config.put("bulk.flush.backoff.type", "EXPONENTIAL");
    //b、常数型,表示多次重试之间的时间间隔为固定常数。eg:2 -> 2 -> 2 ...
    config.put("bulk.flush.backoff.type", "CONSTANT");
//3、进行重试的时间间隔。对于指数型则表示起始的基数
config.put("bulk.flush.backoff.delay", "2");
//4、失败重试的次数
config.put("bulk.flush.backoff.retries", "3");

其他的一些配置:

bulk.flush.max.actions: 批量写入时的最大写入条数

bulk.flush.max.size.mb: 批量写入时的最大数据量

bulk.flush.interval.ms: 批量写入的时间间隔,配置后则会按照该时间间隔严格执行,无视上面的两个批量写入配置

三、失败处理器

写入ES的时候很多时候由于ES集群队列满了,或者节点挂掉,经常会导致写入操作执行失败。考虑到这样的失败写入场景,EsSink为用户提供了失败处理器机制,创建Sink对象的时候,同时可以传入一个失败处理器,一旦出现写入失败的情况则会回调所传入的处理器用于错误恢复。具体的用法为:

DataStream<String> input = ...;

input.addSink(new ElasticsearchSink<>(
    config, transportAddresses,
    new ElasticsearchSinkFunction<String>() {...},
    new ActionRequestFailureHandler() {
        @Override
        void onFailure(ActionRequest action,
                Throwable failure,
                int restStatusCode,
                RequestIndexer indexer) throw Throwable {

            if (ExceptionUtils.containsThrowable(failure, EsRejectedExecutionException.class)) {
                // 将失败请求继续加入队列,后续进行重试写入
                indexer.add(action);
            } else if (ExceptionUtils.containsThrowable(failure, ElasticsearchParseException.class)) {
                // 添加自定义的处理逻辑
            } else {
                throw failure;
            }
        }
}));

如果仅仅只是想做失败重试,也可以直接使用官方提供的默认的 RetryRejectedExecutionFailureHandler ,该处理器会对 EsRejectedExecutionException 导致到失败写入做重试处理。

四、其他注意点

1、EsSink代码块不能使用try-catch-Exception来捕捉

之前在使用EsSink的时候,为了防止某次写入失败造成程序中断,对ElasticsearchSinkFunction的 process() 方法使用try-catch-exception语句块进行了捕捉,但实际运行的时候发现程序跑着跑着还是被一个 EsRejectedException 异常中断掉了。让人奇怪的是明明对异常进行了捕捉,为什么这个异常还是能够抛出来,下来通过查看源码发现,如果在初始化EsSink对象的时候没有传入 ActionRequestFailureHandler 则会使用默认的 ActionRequestFailureHandler ,这个处理器的源码如下:

public class NoOpFailureHandler implements ActionRequestFailureHandler {

    private static final long serialVersionUID = 737941343410827885L;

    @Override
    public void onFailure(ActionRequest action, Throwable failure, int restStatusCode, RequestIndexer indexer) throws Throwable {
        // 这里抛出的是一个throwable
        throw failure;
    }

可以看到,在发生异常的时候,默认的处理器会将异常包装成一个 Throw 对象抛出,这就是直接使用 try-Exception 无法捕捉到的原因。

解决方法:

  • 实现自己的失败处理器消化掉异常
  • 使用 throw 来捕捉异常

该问题一定要重点注意,负责会导致实时任务终止掉!

2、失败重试机制依赖于checkpoint

如果想要使用EsSink的失败重试机制,则需要通过env.enableCheckpoint()方法来开启Flink任务对checkpoint的支持,如果没有开启checkpoint机制的话,则失败重试策略是无法生效的。这个是通过跟踪 ElasticsearchSinkBase 类源码的时候发现的,核心的代码如下:

@Override
    public void initializeState(FunctionInitializationContext context) throws Exception {
        // no initialization needed
    }   

@Override
    public void snapshotState(FunctionSnapshotContext context) throws Exception {
        checkErrorAndRethrow();
        //如果没有开启checkPoint机制,则该变量为false,也就导致下面的flush重试代码不会执行到
        if (flushOnCheckpoint) {
            do {
                //失败重试的时机是发生在程序在打checkpoint的时候
                bulkProcessor.flush();
                checkErrorAndRethrow();
            } while (numPendingRequests.get() != 0);
        }
    }

3、 经验学习

可以通过第二点贴出的源码发现,虽然EsSink实现了 CheckpointedFunction 接口,并且重写了checkPoint的相关方法,但其并没有墨守成规的利用checkpoint定义的那样利用State机制用于故障恢复。而是利用了checkpoint的空壳,定时执行的框架来实现了自己的一套失败重试机制。这一点很值得我们借鉴,很多知识点要学会活学活用,他山之石可以攻玉!!!

  • 7
    点赞
  • 69
    收藏
    觉得还不错? 一键收藏
  • 15
    评论
### 回答1: 要使用Flink数据索引到Elasticsearch,你需要使用FlinkElasticsearch connector,该connector可以在Flink的官方文档中找到。 以下是实现索引数据Elasticsearch的步骤: 1. 首先,你需要创建一个Flink程序,这个程序可以连接到数据源,例如Kafka或者其他的数据源。你需要使用Flink的DataStream API来处理数据。 2. 在程序中,使用Elasticsearch connector将数据写入Elasticsearch。要使用Elasticsearch connector,你需要在pom.xml文件中添加以下依赖项: ``` <dependency> <groupId>org.apache.flink</groupId> <artifactId>flink-connector-elasticsearch7_2.12</artifactId> <version>1.13.0</version> </dependency> ``` 3. 在程序中,使用Elasticsearch connector将数据写入Elasticsearch。以下是使用Elasticsearch connector将数据写入Elasticsearch的示例代码: ``` DataStream<Tuple2<String, Integer>> dataStream = ... //从数据源获取数据 //将数据转换为Elasticsearch需要的格式 DataStream<JSONObject> esDataStream = dataStream.map(new MapFunction<Tuple2<String, Integer>, JSONObject>() { @Override public JSONObject map(Tuple2<String, Integer> value) throws Exception { JSONObject jsonObject = new JSONObject(); jsonObject.put("word", value.f0); jsonObject.put("count", value.f1); return jsonObject; } }); //将数据写入Elasticsearch List<HttpHost> httpHosts = new ArrayList<>(); httpHosts.add(new HttpHost("localhost", 9200, "http")); esDataStream.addSink( new ElasticsearchSink.Builder<JSONObject>(httpHosts, new ElasticsearchSinkFunction<JSONObject>() { public IndexRequest createIndexRequest(JSONObject element) { return Requests.indexRequest() .index("my-index") .type("_doc") .source(element.toJSONString(), XContentType.JSON); } @Override public void process(JSONObject element, RuntimeContext ctx, RequestIndexer indexer) { indexer.add(createIndexRequest(element)); } }).build() ); ``` 在这个例子中,我们将每个单词的计数写入Elasticsearch。要将数据写入Elasticsearch,我们需要将数据转换为JSON格式,并使用ElasticsearchSinkFunction将数据写入Elasticsearch。在ElasticsearchSinkFunction中,我们需要实现createIndexRequest方法,它将数据转换为IndexRequest对象,然后使用RequestIndexer将IndexRequest发送到Elasticsearch。 4. 启动Flink程序,并等待数据被索引到Elasticsearch。 这就是使用Flink数据索引到Elasticsearch的步骤。注意,在实际生产环境中,你可能需要处理更复杂的数据并在Elasticsearch中建立更复杂的索引。 ### 回答2: Flink是一个开源的流处理框架,具有效、可扩展和容错等特性。使用Flink可以将索引数据实时发送到Elasticsearch。 为了实现索引数据Elasticsearch,我们需要进行以下步骤: 1. 连接到数据源:首先,我们需要从数据源获取索引数据。可以是日志文件、消息队列或其他流式数据源。借助Flink的连接器,我们可以轻松地从这些数据源中读取数据。 2. 数据转换和处理:接下来,我们需要对获取的数据进行转换和处理。可以使用Flink的转换操作对数据进行清洗、过滤、格式化等操作,以使其适合索引到Elasticsearch。 3. 将数据发送到Elasticsearch:一旦数据转换和处理完成,我们就可以使用Flink提供的Elasticsearch连接器将数据发送到Elasticsearch。连接器会自动将数据批量发送到Elasticsearch集群中的相应索引。 4. 容错和恢复:在数据处理过程中,可能会出现故障或网络中断等情况。Flink提供了容错机制,可以保证数据处理的可用性和可靠性。如果出现故障,Flink会自动恢复并重新处理丢失的数据。 使用Flink实现索引数据Elasticsearch具有以下优势: 1. 实时性:Flink作为一个流处理框架,可以使索引数据几乎实时地传输到Elasticsearch,确保数据的最新性。 2. 可扩展性:Flink具有良好的扩展性,可以处理大规模的数据,并且可以根据需要动态地扩展集群规模。 3. 容错性:Flink的容错机制可以保证在发生故障时数据的安全性和可恢复性,避免数据丢失或损坏。 总结而言,使用Flink可以轻松地将索引数据实时发送到Elasticsearch,并享受其效、可扩展和容错的优势。 ### 回答3: 使用Flink实现索引数据Elasticsearch是一个相对简单且效的过程。Flink是一个实时流处理框架,可以通过连接到数据源,并以流式方式处理和转换数据。 首先,我们需要连接到数据源。可以通过Flink提供的API或者适配器来连接到不同类型的数据源,如Kafka、RabbitMQ等。一旦连接到数据源,我们可以使用Flink的DataStream API将数据流转换为可供索引的格式。 接下来,我们需要将转换后的数据流发送到Elasticsearch进行索引。可以使用FlinkElasticsearch连接器来实现此功能。该连接器提供了一种将数据流中的记录自动索引到Elasticsearch的方式。 为了使用Elasticsearch连接器,我们需要在Flink作业中添加相应的依赖。然后,在代码中配置Elasticsearch连接和索引的相关信息,如主机地址、索引名称等。一旦配置完成,我们可以使用DataStream的addSink()方法将数据流发送到Elasticsearch。 在将数据流发送到Elasticsearch之前,可以进行一些额外的转换和处理。例如,可以对数据流进行过滤、映射或聚合操作,以便索引的数据满足特定的需求。 最后,运行Flink作业并监控其运行状态。一旦作业开始运行,Flink将自动将数据流中的记录发送到Elasticsearch进行索引。 使用Flink实现索引数据Elasticsearch的好处是它提供了流式处理的能力,能够实时处理和索引数据。另外,Flink还提供了容错和恢复机制,以确保数据的准确性和可靠性。 总之,通过Flink实现索引数据Elasticsearch是一种快速、简单且效的方法,可以帮助我们充分利用实时流数据并实时索引到Elasticsearch中。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值