Flink写出数据到Elasticsearch(带用户名密码)

前言

记录一下Flink带用户名密码写出到Elasticsearch的踩坑过程及解决方案。

版本:Flink1.13,Elasticsearch7.17.4

正文

maven依赖如下:

<?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">
    <parent>
        <artifactId>poas</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>core</artifactId>

    <properties>
        <java.version>1.8</java.version>
        <flink.version>1.13.0</flink.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-java</artifactId>
            <version>${flink.version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.flink/flink-clients -->
        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-clients_2.12</artifactId>
            <version>${flink.version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.flink/flink-streaming-java -->
        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-streaming-java_2.12</artifactId>
            <version>${flink.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-json</artifactId>
            <version>${flink.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-table-planner-blink_2.12</artifactId>
            <version>${flink.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-table-api-java-bridge_2.12</artifactId>
            <version>${flink.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-connector-jdbc_2.12</artifactId>
            <version>${flink.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-connector-kafka_2.12</artifactId>
            <version>${flink.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-connector-elasticsearch7_2.12</artifactId>
            <version>${flink.version}</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.83</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
        </dependency>
        <dependency>
            <groupId>commons-beanutils</groupId>
            <artifactId>commons-beanutils</artifactId>
            <version>1.9.4</version>
        </dependency>

    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Flink写出数据到Elasticsearch的代码如下:

package com.poas.task;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.poas.bean.Event;
import com.poas.bean.OriginalEvent;
import com.poas.constants.ESConstant;
import com.poas.utils.FlinkUtil;
import com.poas.utils.KafkaUtil;
import com.poas.utils.NLPUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.flink.api.common.functions.FlatMapFunction;
import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.api.common.functions.RuntimeContext;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.connectors.elasticsearch.ElasticsearchSinkBase;
import org.apache.flink.streaming.connectors.elasticsearch.ElasticsearchSinkFunction;
import org.apache.flink.streaming.connectors.elasticsearch.RequestIndexer;
import org.apache.flink.streaming.connectors.elasticsearch.util.RetryRejectedExecutionFailureHandler;
import org.apache.flink.streaming.connectors.elasticsearch7.ElasticsearchSink;
import org.apache.flink.streaming.connectors.elasticsearch7.RestClientFactory;
import org.apache.flink.util.Collector;
import org.apache.http.HttpHost;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.client.Requests;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.poas.constants.KafkaConstant.EVENT;
@Slf4j
public class EventFlink {
    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = FlinkUtil.getEnvironment();

        DataStreamSource<String> kafkaDS = env.addSource(KafkaUtil.getFlinkKafkaConsumer(EVENT, "default"));

        SingleOutputStreamOperator<OriginalEvent> originalEventStream = kafkaDS.flatMap(new FlatMapFunction<String, OriginalEvent>() {
            @Override
            public void flatMap(String value, Collector<OriginalEvent> out) throws Exception {
                try {
                    JSONObject object = JSON.parseObject(value);
                    out.collect(object.toJavaObject(OriginalEvent.class));
                } catch (Exception e) {
                    log.info("过滤脏数据");
                }
            }
        });

        SingleOutputStreamOperator<Event> eventStream = originalEventStream.map(new MapFunction<OriginalEvent, Event>() {
            @Override
            public Event map(OriginalEvent originalEvent) throws Exception {
                Event event = new Event();
                BeanUtils.copyProperties(event, originalEvent);
                List<String> segmentList = NLPUtil.getEventSegmentList(event.getContent());
                event.setKeywords(segmentList);
                System.out.println(event);
                return event;
            }
        });

        RestClientFactory restClientFactory = ESConstant.restClientFactory;
        ArrayList<HttpHost> httpHosts = new ArrayList<>();
        httpHosts.add(new HttpHost(ESConstant.HOST, 9200));
        ElasticsearchSink.Builder<Event> esBuilder = new ElasticsearchSink.Builder<>(
                httpHosts,
                new ElasticsearchSinkFunction<Event>() {
                    @Override
                    public void process(Event event, RuntimeContext runtimeContext, RequestIndexer indexer) {
                        indexer.add(createIndexRequest(event));
                    }

                    public IndexRequest createIndexRequest(Event element) {
                        Map<String, Object> map = new HashMap<>();
                        map.put("content", element.getContent());
                        map.put("trending", element.getTrending());
                        map.put("url", element.getUrl());
                        map.put("origin", element.getOrigin());
                        map.put("keywords", element.getKeywords().toArray());
                        map.put("crawlTime", element.getCrawlTime());
                        return Requests.indexRequest()
                                .index("poas_event")
                                .source(map);
                    }
                });
        //设置用户名密码
        esBuilder.setRestClientFactory(restClientFactory);
        // 每100条数据执行一次或5秒执行一次
        esBuilder.setBulkFlushMaxActions(100);
        esBuilder.setBulkFlushInterval(5000);
        // 设置失败重试的次数
        esBuilder.setBulkFlushBackoffRetries(3);
        // 设置重试的时间间隔
        esBuilder.setBulkFlushBackoffDelay(2);
        // 设置重试策略
        esBuilder.setBulkFlushBackoffType(ElasticsearchSinkBase.FlushBackoffType.EXPONENTIAL);
        // 设置失败处理
        esBuilder.setFailureHandler(new RetryRejectedExecutionFailureHandler());

        eventStream.addSink(esBuilder.build());

        env.execute();
    }
}

因为我的ES设置了安全认证,所以连接时要带上用户名和密码,这部分内容写在RestClientFactory中:

package com.poas.constants;

import org.apache.flink.streaming.connectors.elasticsearch7.RestClientFactory;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.elasticsearch.client.RestClientBuilder;

public class ESConstant {
    public static final String HOST = "xxxx";

    public static final String ES_USERNAME = "elastic";

    public static final String ES_PASSWORD = "xxxx";

    public static RestClientFactory restClientFactory = new RestClientFactory() {
        @Override
        public void configureRestClientBuilder(RestClientBuilder restClientBuilder) {
            CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
            credentialsProvider.setCredentials(AuthScope.ANY,
                    new UsernamePasswordCredentials(ES_USERNAME, ES_PASSWORD));
            restClientBuilder.setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {
                @Override
                public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpAsyncClientBuilder) {
                    httpAsyncClientBuilder.disableAuthCaching();
                    return httpAsyncClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
                }
            });
        }
    };
}

然而启动Flink后,报错:NoSuchMethodError,截图如下:
在这里插入图片描述

看报错的意思是缺少了某个方法,第一反应是缺少依赖,于是搜了一下org.apache.commons.codec.binary.Base64相关的依赖:

<dependency>
  <groupId>commons-codec</groupId>
  <artifactId>commons-codec</artifactId>
  <version>1.10</version>
</dependency>

重新启动Flink,依然报错!上网搜了一下说可能是jar包冲突,然而我并不知道是哪个包冲突了

经过了各种尝试之后,我又细看了一下报错信息,推断问题是出在安全认证的时候,于是百度了一下org.apache.commons.codec.binary.Base64 的具体功能,发现它是用来加密字符串的。后来又在某篇博客中得到启发,决定自己实现用户名和密码的加密,修改RestClientFactory中的代码如下:

package com.poas.constants;

import org.apache.flink.calcite.shaded.org.apache.commons.codec.binary.Base64;
import org.apache.flink.streaming.connectors.elasticsearch7.RestClientFactory;
import org.apache.http.message.BasicHeader;
import org.elasticsearch.client.RestClientBuilder;

public class ESConstant {
    public static final String HOST = "xxxx";

    public static final String ES_USERNAME = "elastic";

    public static final String ES_PASSWORD = "xxxx";

    public static RestClientFactory restClientFactory = new RestClientFactory() {
        @Override
        public void configureRestClientBuilder(RestClientBuilder restClientBuilder) {
            /*CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
            credentialsProvider.setCredentials(AuthScope.ANY,
                    new UsernamePasswordCredentials(ES_USERNAME, ES_PASSWORD));
            restClientBuilder.setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {
                @Override
                public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpAsyncClientBuilder) {
                    httpAsyncClientBuilder.disableAuthCaching();
                    return httpAsyncClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
                }
            });*/
            String user = ES_USERNAME;
            String pwd = ES_PASSWORD;
            String auth = Base64.encodeBase64String((user+":"+pwd).getBytes());
            restClientBuilder.setDefaultHeaders(new BasicHeader[]{new BasicHeader("Authorization", "Basic " + auth)});
        }
    };
}

再次启动Flink程序,终于成功!!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 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、付费专栏及课程。

余额充值