Flink读取Kafka数据批量写入ES(elasticsearch)

Flink实时消费kafka数据,数据经过处理,富化、清洗等操作,写入ES。在流式计算中,此场景十分常见。
本文采用ES的批量操作BulkProcessor方式,此方式使用的是TransportClient,基于Tcp协议;而rest方式采用的是restClient,基于http协议,并不能保证结果的准确性。
参考

一、依赖准备:

主要依赖:

<dependency>
    <groupId>org.elasticsearch</groupId>
    <artifactId>elasticsearch</artifactId>
    <version>${elasticsearch.version}</version>
</dependency>
<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>transport</artifactId>
    <version>${elasticsearch.version}</version>
</dependency>
<dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-streaming-java_2.11</artifactId>
    <version>${flink.version}</version>
</dependency>
<dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-connector-kafka_2.11</artifactId>
    <version>${flink.version}</version>
</dependency>

主函数:

```java
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.qianxin.ida.enrich.ElasticsearchSink;
import lombok.extern.slf4j.Slf4j;
import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.api.common.serialization.SimpleStringSchema;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.connectors.kafka.FlinkKafkaConsumer;

import java.util.Arrays;
import java.util.Properties;

@Slf4j
public class KafkaToEs {
    public static void main(String[] args) {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        try {
            env.enableCheckpointing(10000);
            //topic列表
            String[] topics = new String[]{"topic1", "topic2"};
            //多topic情况
            Arrays.stream(topics).forEach(topic -> {
                SingleOutputStreamOperator<JSONObject> dateStream = env.addSource(new FlinkKafkaConsumer<>(topic, new SimpleStringSchema(),
                        new Properties()).setStartFromLatest())
                        .map(new MapFunction<String, JSONObject>() {
                            //具体数据清洗操作
                            public JSONObject map(String value) throws Exception {
                                JSONObject jsonObject = new JSONObject();
                                jsonObject = JSON.parseObject(value);
                                return jsonObject;
                            }
                        });
                dateStream.print();
                //自定义sink
                dateStream.addSink(new ElasticsearchSink());

            });
            env.execute("kafka2es");
        } catch (Exception e) {
            log.error("kafka2es fail " + e.getMessage());
        }
    }
}

ElasticsearchSink函数:

package com.qianxin.ida.enrich;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.streaming.api.functions.sink.RichSinkFunction;
import org.apache.flink.streaming.api.functions.sink.SinkFunction;
import org.elasticsearch.action.bulk.BackoffPolicy;
import org.elasticsearch.action.bulk.BulkProcessor;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.common.unit.ByteSizeUnit;
import org.elasticsearch.common.unit.ByteSizeValue;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.transport.client.PreBuiltTransportClient;

import java.net.InetAddress;

import static com.qianxin.ida.utils.Constants.INDEX_SUFFIX;

@Slf4j
public class ElasticsearchSink extends RichSinkFunction<JSONObject> implements SinkFunction<JSONObject> {
 
    private static BulkProcessor bulkProcessor = null;

    @Override
    public void open(Configuration parameters) throws Exception {
		//BulkProcessor是一个线程安全的批量处理类,允许方便地设置 刷新 一个新的批量请求 
        Settings settings = Settings.builder()
                .put("cluster.name", "elasticsearch")
                .put("client.transport.sniff", false)
                .build();
        PreBuiltTransportClient preBuiltTransportClient = new PreBuiltTransportClient(settings);
        TransportClient client = preBuiltTransportClient.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"), 9300));
        BulkProcessor.Listener listener = buildListener();
        BulkProcessor.Builder bulk = BulkProcessor.builder(client, listener);
        //根据当前添加的操作数设置刷新新批量请求的时间(默认值为1000,-1禁用)
        bulk.setBulkActions(Property.getIntValue("bulk_actions"));
        //根据当前添加的操作大小设置刷新新批量请求的时间(默认为5Mb,-1禁用)
        bulk.setBulkSize(new ByteSizeValue(Property.getLongValue("bulk_size"), ByteSizeUnit.MB));
        //设置允许执行的并发请求数(默认为1,0仅允许执行单个请求)
        bulk.setConcurrentRequests(Property.getIntValue("concurrent_request"));
        //设置一个刷新间隔,如果间隔过去,刷新任何待处理的批量请求(默认为未设置)
        bulk.setFlushInterval(TimeValue.timeValueSeconds(Property.getLongValue("flush_interval")));
        //设置一个恒定的后退策略,最初等待1秒钟,最多重试3次
        bulk.setBackoffPolicy(BackoffPolicy
                .constantBackoff(TimeValue.timeValueSeconds(Property.getLongValue("time_wait")),
                        Property.getIntValue("retry_times")));
        bulkProcessor = bulk.build();
        super.open(parameters);
    }

    private static BulkProcessor.Listener buildListener() throws InterruptedException {
        BulkProcessor.Listener listener = new BulkProcessor.Listener() {
            @Override
            public void beforeBulk(long l, BulkRequest bulkRequest) {
            }
            @Override
            public void afterBulk(long l, BulkRequest bulkRequest, BulkResponse bulkResponse) {
            }
            @Override
            public void afterBulk(long l, BulkRequest bulkRequest, Throwable throwable) {
            }
        };
        return listener;
    }

    @Override
    public void invoke(JSONObject jsonObject, Context context) throws Exception {
        try {
            String topic = jsonObject.getString("topic");
            String index = "index_";
            bulkProcessor.add(new IndexRequest(index)
                    .type(topic)
                    .source(jsonObject));
        } catch (Exception e) {
            log.error("sink 出错 {{}},消息是{{}}", e.getMessage(), jsonObject);
        }
    }

}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答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中。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值