Flink实战之StreamingFileSink如何写数据到其它HA的Hadoop集群

Flink将数据写入到hdfs中 1. 场景 随机产生数据然后将产生的数据写入到hdfs 中。 2. 随机数据源 代码: package com.wudl.flink.hdfs.source; import org.apache.flink.api.java.tuple.Tuple; import org.apache.flink.api.java.tuple.Tuple4; import org.apache.flink.streaming.api.functions.source.SourceFunction; import jav 阅读详情


原创甄情 最后发布于2019-12-03 21:16:06 阅读数 426  收藏
分类专栏: 大数据
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.youkuaiyun.com/su20145104009/article/details/103311853
收起
前言
我们公司使用的集群都是 EMR 集群,于是就分别创建了一个 flink 集群专门用户实时计算,一个 hadoop 集群专门用于 spark、hive 的离线计算。两个集群是完全隔离的。但是有一些实时数据的采集需求,需要把数据写入到我们做离线计算的集群,有人说我只需要在 StreamingFileSink 需要传入的hdfs 路径前加上离线集群的 ip 就好了,比如:hdfs://otherIp:/usr/hive/warehouse/ 这样固然能写入数据,但是我们的hadoop 集群都是 HA 的。namenode 切换的时候会导致写不进去数据,所以此方法不可行。本文主要提供 flink 写入其它 HA 集群的方法和思路

原因查找
如果我们直接通过指定 StreamingFileSink 的写入路径为其它 HA 的 Hadoop 集群时,比如:hdfs://HDFS42143/usr/hive/warehouse/hour_hive ,会出现这样的异常


大家都知道我们在创建 HA 集群时,需要指定一个 nameservice,这个 nameservice 可以是你喜欢的符号,然后还需要一些额外的 HA 配置。比如

dfs.client.failover.proxy.provider.HDFS42142=org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider
dfs.nameservices=HDFS42142
dfs.namenode.rpc-address.HDFS42142.nn1=172.xx.xx.01:port
dfs.namenode.rpc-address.HDFS42142.nn2=172.xx.xx.02:port
dfs.ha.namenodes.HDFS42142=nn1,nn2
1
2
3
4
5
可是在 StreamingFileSink源码里面没有找到含有的 hadoop 配置的构造方法。怎么搞呢?我们可以先理解以下StreamingFileSink的写入原理

StreamingFileSink 源码剖析
一般我们创建 StreamingFileSink 都会使用以下方式

package com.tuya.sink;

import com.tuya.AppArgs;
import com.tuya.sink.filesystem.HdfsBucketAssigner;
import com.tuya.sink.filesystem.MyRollingPolicy;
import org.apache.flink.api.common.serialization.SimpleStringEncoder;
import org.apache.flink.core.fs.Path;
import org.apache.flink.formats.parquet.avro.ParquetAvroWriters;
import org.apache.flink.streaming.api.functions.sink.filesystem.StreamingFileSink;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * desc:
 *
 * @author scx
 * @create 2019/10/24
 */
public class FileSinkFactory {

    private static final Logger LOG = LoggerFactory.getLogger(FileSinkFactory.class);


    /**
     * 多久检测一次process状态文件
     */
    private static final long CHECK_INTERVAL = 30 * 1000L;

    /**
     * 默认多久未写入的文件为超时(超时后会生成一个新的文件)
     */
    private static final long DEFAULT_INACTIVITY_INTERVAL = 30 * 60L * 1000L;

    /**
     * 默认多久滚动生成一个hdfs文件
     */
    private static final long DEFAULT_ROLLOVER_INTERVAL = 40 * 60L * 1000L;

    /**
     * hdfs文件最大限制 128M
     */
    private static final long DEFAULT_MAX_PART_SIZE = 1024L * 1024L * 128L;


    public static <T> StreamingFileSink<T> bulkSink(AppArgs appArgs, Class<T> clazz) {
        return StreamingFileSink.forBulkFormat(new Path(appArgs.getHdfsPath()),
                ParquetAvroWriters.forReflectRecord(clazz))
                .withBucketAssigner(new HdfsBucketAssigner<>())
                .withBucketCheckInterval(CHECK_INTERVAL)
                .build();
    }

    public static <T> StreamingFileSink<T> rowSink(AppArgs appArgs, Class<T> clazz) {
        return StreamingFileSink.forRowFormat(new Path(appArgs.getHdfsPath()),
                new SimpleStringEncoder<T>())
                .withBucketAssigner(new HdfsBucketAssigner<>())
                .withRollingPolicy(new MyRollingPolicy<>(appArgs, DEFAULT_MAX_PART_SIZE, DEFAULT_ROLLOVER_INTERVAL, DEFAULT_INACTIVITY_INTERVAL))
                .withBucketCheckInterval(CHECK_INTERVAL)
                .build();
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
为了处理无界的流数据,StreamingFileSink 会将数据写入到桶中。如何分桶是可以配置的,比如我配置的就是自定义的HdfsBucketAssigner,是根据数据的事件时间写入到不同的桶中。

默认策略是基于时间的分桶,这种策略每个小时创建并写入一个新的桶,从而得到流数据在特定时间间隔内接收记录所对应的文件。
桶目录中包含多个实际输出数据的部分文件(part file),对于每一个接收桶数据的 Sink Subtask ,至少存在一个部分文件(part file)。额外的部分文件(part file)将根据滚动策略创建,滚动策略是可以配置的,比如我配置的就是MyRollingPolicy 。默认的策略是根据文件大小和超时时间来滚动文件。超时时间指打开文件的最长持续时间,以及文件关闭前的最长非活动时间。

StreamingFileSink.forBulkFormat 和 StreamingFileSink.forRowFormat 分别表示行编码格式和块编码格式的写入。由于我使用的是行编码格式并且两者内部获取 hdfs 的 filesyStem 逻辑基本一致,就从 StreamingFileSink.forRowFormat 为入口分析。

在 StreamingFileSink 源码中重写了 initializeState 方法,该方法会在程序启动的时候调用一次

    @Override
    public void initializeState(FunctionInitializationContext context) throws Exception {
        final int subtaskIndex = getRuntimeContext().getIndexOfThisSubtask();
        this.buckets = bucketsBuilder.createBuckets(subtaskIndex);

        final OperatorStateStore stateStore = context.getOperatorStateStore();
        bucketStates = stateStore.getListState(BUCKET_STATE_DESC);
        maxPartCountersState = stateStore.getUnionListState(MAX_PART_COUNTER_STATE_DESC);

        if (context.isRestored()) {
            buckets.initializeState(bucketStates, maxPartCountersState);
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
主要查看 这一行 this.buckets = bucketsBuilder.createBuckets(subtaskIndex) 使用bucketsBuilder根据task下表创建所有 bucket 的管理者 buckets

BucketsBuilder 有两个实现类,分别是BulkFormatBuilder、RowFormatBuilder.
查看RowFormatBuilder实现类

    @PublicEvolving
    public static class RowFormatBuilder<IN, BucketID> extends StreamingFileSink.BucketsBuilder<IN, BucketID> {
        //省略部分代码

        @Override
        Buckets<IN, BucketID> createBuckets(int subtaskIndex) throws IOException {
            return new Buckets<>(
                    basePath,
                    bucketAssigner,
                    bucketFactory,
                    new RowWisePartWriter.Factory<>(encoder),
                    rollingPolicy,
                    subtaskIndex);
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
createBuckets 方法会返回一个 Buckets 对象,继续进入 Buckets 构造方法内

    Buckets(
            final Path basePath,
            final BucketAssigner<IN, BucketID> bucketAssigner,
            final BucketFactory<IN, BucketID> bucketFactory,
            final PartFileWriter.PartFileFactory<IN, BucketID> partFileWriterFactory,
            final RollingPolicy<IN, BucketID> rollingPolicy,
            final int subtaskIndex) throws IOException {

        this.basePath = Preconditions.checkNotNull(basePath);
        //省略部分代码
        try {
            this.fsWriter = FileSystem.get(basePath.toUri()).createRecoverableWriter();
        } catch (IOException e) {
            LOG.error("Unable to create filesystem for path: {}", basePath);
            throw e;
        }
        //省略部分代码
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
在这里我们终于看到熟悉的 FileSystem 了,FileSystem.get(basePath.toUri()) 通过我们传入的 basePath 路径来获得一个 FileSystem,点进去

/**
     * Returns a reference to the {@link FileSystem} instance for accessing the
     * file system identified by the given {@link URI}.
     *
     * @param uri
     *        the {@link URI} identifying the file system
     * @return a reference to the {@link FileSystem} instance for accessing the file system identified by the given
     *         {@link URI}.
     * @throws IOException
     *         thrown if a reference to the file system instance could not be obtained
     */
    public static FileSystem get(URI uri) throws IOException {
        return FileSystemSafetyNet.wrapWithSafetyNetWhenActivated(getUnguardedFileSystem(uri));
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
FileSystemSafetyNet.wrapWithSafetyNetWhenActivated 方法封装了FileSystem 来防止未关闭流而导致的资源泄漏问题,不是我们观察的重点,进入getUnguardedFileSystem 方法。

    public static FileSystem getUnguardedFileSystem(final URI fsUri) throws IOException {
        checkNotNull(fsUri, "file system URI");

        LOCK.lock();
        try {
            final URI uri;
            //判断我们的写入的路径有没有传入scheme,即:hdfs://,file://等前缀
            if (fsUri.getScheme() != null) {
                //如果传入了scheme直接赋值给uri
                uri = fsUri;
            }
            //省略部分代码

            final FSKey key = new FSKey(uri.getScheme(), uri.getAuthority());

            // 先检查缓存
            {
                FileSystem cached = CACHE.get(key);
                if (cached != null) {
                    return cached;
                }
            }

            //如果FS_FACTORIES为空进行一下初始化加载
            if (FS_FACTORIES.isEmpty()) {
                initialize(new Configuration());
            }

            final FileSystem fs;
            final FileSystemFactory factory = FS_FACTORIES.get(uri.getScheme());

            //如果fileSystem工厂存在,创建fileSystem
            if (factory != null) {
                fs = factory.create(uri);
            }
            else {
                try {
                    //不存在使用失败重试的factory进行创建fileSystem
                    fs = FALLBACK_FACTORY.create(uri);
                }
                catch (UnsupportedFileSystemSchemeException e) {
                    throw new UnsupportedFileSystemSchemeException(
                            "Could not find a file system implementation for scheme '" + uri.getScheme() +
                                    "'. The scheme is not directly supported by Flink and no Hadoop file " +
                                    "system to support this scheme could be loaded.", e);
                }
            }

            CACHE.put(key, fs);
            return fs;
        }
        finally {
            LOCK.unlock();
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
上面代码简单进行了注释,首先判断fsUri 的 schema 是否存在,如果不存在或进行一些默认操作。我们配置的是hdfs://HDFS42143/usr/hive/warehouse/hour_hive ,scheme 为 hdfs,然后先检查缓存是否已经存在,存在的话直接返回。不存在的话判断 FS_FACTORIES 中是否存在,如果继续不存在就使用默认的FALLBACK_FACTORY 工厂创建 filesystem 。这里主要看 initialize 方法

    public static void initialize(Configuration config) throws IOException, IllegalConfigurationException {
        LOCK.lock();
        try {
            //省略部分代码
            for (FileSystemFactory factory : RAW_FACTORIES) {
                factory.configure(config);
                String scheme = factory.getScheme();

                FileSystemFactory fsf = ConnectionLimitingFactory.decorateIfLimited(factory, scheme, config);
                FS_FACTORIES.put(scheme, fsf);
            }
            // configure the default (fallback) factory
            FALLBACK_FACTORY.configure(config);
            //省略部分代码
        }
        finally {
            LOCK.unlock();
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
在 initialize 方法里我们可以看到遍历 RAW_FACTORIES 集合,首先调用configure 方法加载配置,然后把该集合内的 FileSystemFactory 实例以其所支持的 schema 为 key,本身对象为 value 放到FS_FACTORIES 的 map 中,下面还对 FALLBACK_FACTORY 进行了 configure 配置加载。需要注意的是initialize方法会在很多地方被调用,比如jobManager、taskManager启动的时候。
看到这里有两个疑问,RAW_FACTORIES 和 FALLBACK_FACTORY 分别是在哪里创建的
首先看 RAW_FACTORIES

    /** All available file system factories. */
        private static final List<FileSystemFactory> RAW_FACTORIES = loadFileSystems();
        private static List<FileSystemFactory> loadFileSystems() {
        final ArrayList<FileSystemFactory> list = new ArrayList<>();
        list.add(new LocalFileSystemFactory());
        LOG.debug("Loading extension file systems via services");
        try {
            ServiceLoader<FileSystemFactory> serviceLoader = ServiceLoader.load(FileSystemFactory.class);
            Iterator<FileSystemFactory> iter = serviceLoader.iterator();

            while (iter.hasNext()) {
                try {
                    FileSystemFactory factory = iter.next();
                    list.add(factory);
                    LOG.debug("Added file system {}:{}", factory.getScheme(), factory.getClass().getName());
                }
                catch (Throwable t) {
                    ExceptionUtils.rethrowIfFatalErrorOrOOM(t);
                    LOG.error("Failed to load a file system via services", t);
                }
            }
        }
        catch (Throwable t) {
            ExceptionUtils.rethrowIfFatalErrorOrOOM(t);
            LOG.error("Failed to load additional file systems via services", t);
        }
        return Collections.unmodifiableList(list);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
RAW_FACTORIES 是创建的静态变量,然后从静态方法 loadFileSystems 加载。需要注意的是,loadFileSystems 方法中首先会加一个默认的 factory 即LocalFileSystemFactory。然后其它的 factory使用ServiceLoader.load(FileSystemFactory.class) 通过 SPI 获取,但是我并没有发现Flink源码在 src/main/resources/META-INF/services/org.apache.flink.core.fs.FileSystemFactory 目录下配置FileSystemFactory的实现类,也就是说这些需要我们自己配置,如果不配置,那么默认情况下 SPI 获得的 FileSystemFactory 是为空的。也就是说RAW_FACTORIES 只有一个 LocalFileSystemFactory 实例。而LocalFileSystemFactory 的 scheme 为 file。

然后看 FALLBACK_FACTORY

        private static final FileSystemFactory FALLBACK_FACTORY = loadHadoopFsFactory();
        private static FileSystemFactory loadHadoopFsFactory() {
        final ClassLoader cl = FileSystem.class.getClassLoader();

        // first, see if the Flink runtime classes are available
        final Class<? extends FileSystemFactory> factoryClass;
        try {
            factoryClass = Class
                    .forName("org.apache.flink.runtime.fs.hdfs.HadoopFsFactory", false, cl)
                    .asSubclass(FileSystemFactory.class);
        }
        catch (ClassNotFoundException e) {
            LOG.info("No Flink runtime dependency present. " +
                    "The extended set of supported File Systems via Hadoop is not available.");
            return new UnsupportedSchemeFactory("Flink runtime classes missing in classpath/dependencies.");
        }
        catch (Exception | LinkageError e) {
            LOG.warn("Flink's Hadoop file system factory could not be loaded", e);
            return new UnsupportedSchemeFactory("Flink's Hadoop file system factory could not be loaded", e);
        }

        // check (for eager and better exception messages) if the Hadoop classes are available here
        try {
            Class.forName("org.apache.hadoop.conf.Configuration", false, cl);
            Class.forName("org.apache.hadoop.fs.FileSystem", false, cl);
        }
        catch (ClassNotFoundException e) {
            LOG.info("Hadoop is not in the classpath/dependencies. " +
                    "The extended set of supported File Systems via Hadoop is not available.");
            return new UnsupportedSchemeFactory("Hadoop is not in the classpath/dependencies.");
        }

        // Create the factory.
        try {
            return factoryClass.newInstance();
        }
        catch (Exception | LinkageError e) {
            LOG.warn("Flink's Hadoop file system factory could not be created", e);
            return new UnsupportedSchemeFactory("Flink's Hadoop file system factory could not be created", e);
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
这里就很简单了,直接通过反射的方式新建 org.apache.flink.runtime.fs.hdfs.HadoopFsFactory 实例,也就是说 FALLBACK_FACTORY 的值为 HadoopFsFactory,scheme 为 *

到这里我们先总结一下

我们的 HA 集群的路径为 hdfs://HDFS42143/usr/hive/warehouse/hour_hive,scheme 为 hdfs
我们对 HA 集群的 CRUD 操作的 FileSystem 是在FileSystem.getUnguardedFileSystem方法中获得的
RAW_FACTORIES 集合内的factory都是通过JavaSPI的方式加载的,并且只有一个实例 LocalFileSystemFactory,支持的 scheme为 file
FALLBACK_FACTORY 的值为 HadoopFsFactory,支持的 scheme 为 *
我们的 HA 集群就是使用 FALLBACK_FACTORY创建的 fileSystem。在上面的 initialize(Configuration config) 方法中会执行 FALLBACK_FACTORY.configure(config);

进入 HadoopFsFactory

public class HadoopFsFactory implements FileSystemFactory {

    private static final Logger LOG = LoggerFactory.getLogger(HadoopFsFactory.class);

    /** Flink's configuration object. */
    private Configuration flinkConfig;

    /** Hadoop's configuration for the file systems. */
    private org.apache.hadoop.conf.Configuration hadoopConfig;

    @Override
    public String getScheme() {
        // the hadoop factory creates various schemes
        return "*";
    }

    @Override
    public void configure(Configuration config) {
        flinkConfig = config;
        hadoopConfig = null; // reset the Hadoop Config
    }

    @Override
    public FileSystem create(URI fsUri) throws IOException {
        checkNotNull(fsUri, "fsUri");

        final String scheme = fsUri.getScheme();
        checkArgument(scheme != null, "file system has null scheme");

        // from here on, we need to handle errors due to missing optional
        // dependency classes
        try {
            // -- (1) get the loaded Hadoop config (or fall back to one loaded from the classpath)

            final org.apache.hadoop.conf.Configuration hadoopConfig;
            if (this.hadoopConfig != null) {
                hadoopConfig = this.hadoopConfig;
            }
            else if (flinkConfig != null) {
                hadoopConfig = HadoopUtils.getHadoopConfiguration(flinkConfig);
                this.hadoopConfig = hadoopConfig;
            }
            else {
                LOG.warn("Hadoop configuration has not been explicitly initialized prior to loading a Hadoop file system."
                        + " Using configuration from the classpath.");

                hadoopConfig = new org.apache.hadoop.conf.Configuration();
            }

            // -- (2) get the Hadoop file system class for that scheme

            final Class<? extends org.apache.hadoop.fs.FileSystem> fsClass;
            try {
                fsClass = org.apache.hadoop.fs.FileSystem.getFileSystemClass(scheme, hadoopConfig);
            }
            catch (IOException e) {
                throw new UnsupportedFileSystemSchemeException(
                        "Hadoop File System abstraction does not support scheme '" + scheme + "'. " +
                                "Either no file system implementation exists for that scheme, " +
                                "or the relevant classes are missing from the classpath.", e);
            }

            // -- (3) instantiate the Hadoop file system

            LOG.debug("Instantiating for file system scheme {} Hadoop File System {}", scheme, fsClass.getName());

            final org.apache.hadoop.fs.FileSystem hadoopFs = fsClass.newInstance();

            // -- (4) create the proper URI to initialize the file system

            final URI initUri;
            if (fsUri.getAuthority() != null) {
                initUri = fsUri;
            }
            else {
                LOG.debug("URI {} does not specify file system authority, trying to load default authority (fs.defaultFS)");

                String configEntry = hadoopConfig.get("fs.defaultFS", null);
                if (configEntry == null) {
                    // fs.default.name deprecated as of hadoop 2.2.0 - see
                    // http://hadoop.apache.org/docs/current/hadoop-project-dist/hadoop-common/DeprecatedProperties.html
                    configEntry = hadoopConfig.get("fs.default.name", null);
                }

                if (LOG.isDebugEnabled()) {
                    LOG.debug("Hadoop's 'fs.defaultFS' is set to {}", configEntry);
                }

                if (configEntry == null) {
                    throw new IOException(getMissingAuthorityErrorPrefix(fsUri) +
                            "Hadoop configuration did not contain an entry for the default file system ('fs.defaultFS').");
                }
                else {
                    try {
                        initUri = URI.create(configEntry);
                    }
                    catch (IllegalArgumentException e) {
                        throw new IOException(getMissingAuthorityErrorPrefix(fsUri) +
                                "The configuration contains an invalid file system default name " +
                                "('fs.default.name' or 'fs.defaultFS'): " + configEntry);
                    }

                    if (initUri.getAuthority() == null) {
                        throw new IOException(getMissingAuthorityErrorPrefix(fsUri) +
                                "Hadoop configuration for default file system ('fs.default.name' or 'fs.defaultFS') " +
                                "contains no valid authority component (like hdfs namenode, S3 host, etc)");
                    }
                }
            }

            // -- (5) configure the Hadoop file system

            try {
                hadoopFs.initialize(initUri, hadoopConfig);
            }
            catch (UnknownHostException e) {
                String message = "The Hadoop file system's authority (" + initUri.getAuthority() +
                        "), specified by either the file URI or the configuration, cannot be resolved.";

                throw new IOException(message, e);
            }

            HadoopFileSystem fs = new HadoopFileSystem(hadoopFs);

            // create the Flink file system, optionally limiting the open connections
            if (flinkConfig != null) {
                return limitIfConfigured(fs, scheme, flinkConfig);
            }
            else {
                return fs;
            }
        }
        catch (ReflectiveOperationException | LinkageError e) {
            throw new UnsupportedFileSystemSchemeException("Cannot support file system for '" + fsUri.getScheme() +
                    "' via Hadoop, because Hadoop is not in the classpath, or some classes " +
                    "are missing from the classpath.", e);
        }
        catch (IOException e) {
            throw e;
        }
        catch (Exception e) {
            throw new IOException("Cannot instantiate file system for URI: " + fsUri, e);
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
代码都比较简单,在

    @Override
    public void configure(Configuration config) {
        flinkConfig = config;
        hadoopConfig = null; // reset the Hadoop Config
    }
1
2
3
4
5
我们可以看到 hadoopConfig 会被设置为 null,然后把配置赋值给 flinkConfig 然后在

            final org.apache.hadoop.conf.Configuration hadoopConfig;
            if (this.hadoopConfig != null) {
                hadoopConfig = this.hadoopConfig;
            }
            else if (flinkConfig != null) {
                hadoopConfig = HadoopUtils.getHadoopConfiguration(flinkConfig);
                this.hadoopConfig = hadoopConfig;
            }
            else {
                LOG.warn("Hadoop configuration has not been explicitly initialized prior to loading a Hadoop file system."
                        + " Using configuration from the classpath.");

                hadoopConfig = new org.apache.hadoop.conf.Configuration();
            }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
hadoopConfig 通过 HadoopUtils.getHadoopConfiguration 方法从 flinkConfig 解析。
在最下面通过反射的方式创建 HadoopFileSystem
我们进入 HadoopUtils.getHadoopConfiguration 方法

public static Configuration getHadoopConfiguration(org.apache.flink.configuration.Configuration flinkConfiguration) {

        Configuration result = new HdfsConfiguration();
        boolean foundHadoopConfiguration = false;

        //从flink配置中读取fs.hdfs.hdfsdefault配置
        final String hdfsDefaultPath =
            flinkConfiguration.getString(ConfigConstants.HDFS_DEFAULT_CONFIG, null);

        //加载 fs.hdfs.hdfsdefault 路径的资源
        if (hdfsDefaultPath != null) {
            result.addResource(new org.apache.hadoop.fs.Path(hdfsDefaultPath));
            LOG.debug("Using hdfs-default configuration-file path form Flink config: {}", hdfsDefaultPath);
            foundHadoopConfiguration = true;
        } else {
            LOG.debug("Cannot find hdfs-default configuration-file path in Flink config.");
        }
        //从flink配置中获取fs.hdfs.hdfssite的值
        final String hdfsSitePath = flinkConfiguration.getString(ConfigConstants.HDFS_SITE_CONFIG, null);
        //加载 fs.hdfs.hdfssite 路径的资源
        if (hdfsSitePath != null) {
            result.addResource(new org.apache.hadoop.fs.Path(hdfsSitePath));
            LOG.debug("Using hdfs-site configuration-file path form Flink config: {}", hdfsSitePath);
            foundHadoopConfiguration = true;
        } else {
            LOG.debug("Cannot find hdfs-site configuration-file path in Flink config.");
        }

        String[] possibleHadoopConfPaths = new String[4];
        //从flink配置中获取fs.hdfs.hadoopconf的配置路径
        possibleHadoopConfPaths[0] = flinkConfiguration.getString(ConfigConstants.PATH_HADOOP_CONFIG, null);
        //从环境变量中获取HADOOP_CONF_DIR的路径
        possibleHadoopConfPaths[1] = System.getenv("HADOOP_CONF_DIR");
        //从环境变量中获取HADOOP_HOME的路径
        final String hadoopHome = System.getenv("HADOOP_HOME");
        if (hadoopHome != null) {
            possibleHadoopConfPaths[2] = hadoopHome + "/conf";
            possibleHadoopConfPaths[3] = hadoopHome + "/etc/hadoop"; // hadoop 2.2
        }
        //从这些可能的路径中加载hadoop配置资源
        for (String possibleHadoopConfPath : possibleHadoopConfPaths) {
            if (possibleHadoopConfPath != null) {
                if (new File(possibleHadoopConfPath).exists()) {
                    if (new File(possibleHadoopConfPath + "/core-site.xml").exists()) {
                        result.addResource(new org.apache.hadoop.fs.Path(possibleHadoopConfPath + "/core-site.xml"));
                        LOG.debug("Adding " + possibleHadoopConfPath + "/core-site.xml to hadoop configuration");
                        foundHadoopConfiguration = true;
                    }
                    if (new File(possibleHadoopConfPath + "/hdfs-site.xml").exists()) {
                        result.addResource(new org.apache.hadoop.fs.Path(possibleHadoopConfPath + "/hdfs-site.xml"));
                        LOG.debug("Adding " + possibleHadoopConfPath + "/hdfs-site.xml to hadoop configuration");
                        foundHadoopConfiguration = true;
                    }
                }
            }
        }

        if (!foundHadoopConfiguration) {
            LOG.debug("Could not find Hadoop configuration via any of the supported methods " +
                "(Flink configuration, environment variables).");
        }

        return result;
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
代码已经加了简单的注释,通过这段代码我们可以发现 hadoop 配置都是通过 flink 配置以及环境变量中获得。而我们的flink集群的环境变量中是不可能存在我们另外一个 hadoop 集群的配置的。所以此时我们有两种解决办法。

解决办法
第一种:
通过上面的分析,我们可以知道我们的hadoop配置是通过 flink 配置加载以及flink运行时机器的环境变量解析的,所以我们可以通过,在flink的配置文件中增加我们另外一个 HA 集群的配置路径。注意,此种方法有风险,因为我们的 checkpoint 集群通常是在本地的,如果你直接把另外一个集群的配置 copy 过来又可能会导致 flink 本地的 HA 集群报异常,所以你需要增加一个配置为:hdfs.dfs.nameservices=HDFS42142,HDFS42198。需要注意的是你要把这些配置同步到其它 flink机器上。

第二种:
这种方法为新建一个 CustomerHadoopFsFactory 类,该类在从 flink 配置中获取 hadoop 配置之后添加自己的 HA 集群的配置,在上面的分析我们知道所有的factory实例都是通过 SPI 获得,所以需要自己在src/main/resources/META-INF/services/ 目录新建org.apache.flink.core.fs.FileSystemFactory 文件,文件内容为自定义CustomerHadoopFsFactory 类的权限定名,该类比较简单。并且适用于所有 flink 集群的所有机器。

总结
以上两种方法都能够解决 StreamingFileSink 如何写数据到其它 HA 的Hadoop 集群的问题。第一种不太灵活,需要把另外一个集群的配置文件移到flink集群,并且每台机器都要配置。第二种就比较灵活了,配置可以通过flink启动传入,或者放到 properties 文件中,自己读取加载。我使用的第二种方式,为了避免有些人不懂如何创建,怎么放置,附上我的使用方式截图

————————————————
版权声明:本文为优快云博主「甄情」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.youkuaiyun.com/su20145104009/article/details/103311853

FLINK 1.12.2 流式写入HDFS(hive)的几种方式 Flink 1.12.2 写入hdfs有3种方式,依照api出现的先后依次介绍,重点介绍Flink SQL on Hive的方式。 目录 1 streaming file sink 2 FileSink 3 Flink SQL on Hive 3.1添加依赖 3.2 配置Hive Catalog及使用Flink终端访问Hive 3.3 代码调用Flink SQL写入Hive 4 总结 1 streaming file sink 1.7版Flink开始支持通过StreamingFileSi 阅读详情

相关推荐

Flink1.9系列-StreamingFileSink vs BucketingSink篇

Flink1.9系列-StreamingFileSink vs BucketingSink篇 详细全面的从demo代码和源码层面剖析和解释了为什么Flink新版本的StreamingFileSink写hdfs会时常出问题

枫叶的落寞的博客 6995

[1133]flink问题集锦

文章目录 问题1: Could not get job jar and dependencies from JAR file: JAR file does not exist: -yn 原因:flink1.8版本之后已弃用该参数,ResourceManager将自动启动所需的尽可能多的容器,以满足作业请求的并行性。解决方法:去掉即可 问题2: java.lang.IllegalStateException: No Executor found. Please make sure to export t

周小董 6652

flink无法连接yarn- Connecting to ResourceManager at /0.0.0.0:8032

org.apache.hadoop.yarn.client.RMProxy - Connecting to ResourceManager at /0.0.0.0:8032 问题 flink-session启动无法连接到hadoop的resourceManager 尝试重新连接Retrying connect to server: 0.0.0.0/0.0.0.0:8032. Already tried 0 time(s)失败. 2021-03-19 07:43:15,103 WARN org.apache

饶一熊的博客 5407

Flink Cluster On YARN部署

Flink Cluster On YARN部署 Flink版本:1.8.2 Hadoop: 3.0.3 参考文章: 文中关于Per-Job、Session模式的描述摘自《Deploy Apache Flink® Natively on YARN/Kubernetes》 https://ververica.cn/developers/deploy-apache-flink-natively-on-yarn-kubernetes/ Flink在YARN上部署有两种模式,一种是Session模式,另一种是Pe

shirukai 2486

Flink sink HDFS的三个方法

package batch; import java.util.Properties; import java.util.concurrent.TimeUnit; import org.apache.flink.api.common.functions.MapFunction; import org.apache.flink.api.common.serialization.SimpleStringEncoder; import org.apache.flink.core.fs.Path; import

JustinMars的博客 2057

StreamingFileSink概念和keypoint

使用StreamingFileSink需要注意的几点: 在使用StreamingFileSink时需要开启checkpoint支持,因为part files只有在做checkpoint时才会finalize,否则文件状态一直是in-progress or pending状态,下游非常容易误读。== flink hdfs sink基本概念 flink有按行和批量两种写入文件的方法,比如要想用 Apache Parquet格式存储文件,就有两种builder创建方法: 按行编码的sink:Streami

u011624157的专栏 1158

Flink实战之Flink必须依赖Hadoop2.7+?

====

weixin_41608066的博客 5044

Oracle PLM Agile936 单点登录配置方式

Oracle PLM Agile936 单点登录配置方式

Jay的个人博客 824

StreamingFileSink fails due to truncating HDFS file failure

[FLINK-18592][Connectors/FileSystem] StreamingFileSink fails due to truncating 我这里产生这个问题过程如下: 环境描述: flink 12.2.2 hdfs 3.1.1, 集群, 2 个 namenode, 8 个 datanode, 设置为 2 副本 yarn 3.1.1 fink on yarn 方式部署 问题发生过程 某个 DATANODE 挂掉/重启/负载过高(datanode 上部署了 HBASE, scan 时导

忘掉地平线 2545

Frida三层次钩子快速逆向VMP+OLLVM加固APK

VMP(虚拟机保护)和OLLVM(控制流平坦化)是Android应用常见的双重混淆技术,其核心原理是将Java逻辑虚拟化、Native代码控制流打散,从而阻断传统静态分析与简单Hook。但任何混淆都必须在CPU上执行,因此必然暴露JNI注册行为、ABI参数传递规律及系统调用痕迹。基于此,Frida可通过跨层联动——从JNI_OnLoad捕获Native库加载、在VMP解释器入口截获字节码上下文、于关键系统调用(如read/gettimeofday)反向追踪数据流——构建可观测、可复现的动态分析链。该方法无需

weixin_30888413的博客 1001

io_uring vs epoll ,谁在网络编程领域更胜一筹?

简介:从定量分析的角度,通过量化 io_uring 和 epoll 两种编程框架下的相关操作的耗时,来分析二者的性能差异。 本文作者:王小光,「高性能存储技术SIG」核心成员。 背景 io_uring 在传统存储 io 场景已经证明其价值,但 io_uring 不仅支持传统存储 io,也支持网络 io。io_uring 社区有众多的开发者尝试将 io_uring 用于网络应用。我们之前也在《你认为 io_uring 只适用于存储 IO?大错特错!》中也探索过 io_uring 在网络场景的应用及其

阿里云云栖号 3034

Flink 修改BucketingSink解决小文件的问题

Flink 修改BucketingSink解决小文件的问题0、背景1、BucketingSink 解析2、修改 0、背景      用flink往hdfs写文件的时候,我们一般会用到HDFS Connector 。其实flink里提供了两种HDFS Connector,一个是Bucketing File Sink,另一个是Streaming File Sink。因为我们使用的Hadoop是2.6.5的,对StreamFileSink的支持不太好,所以在我们构建我们的基

CarbonDioxide12138的博客 2892

基于STM32 HAL库的L298N四驱电机PWM调速与方向控制实战

本文详细介绍了基于STM32 HAL库驱动L298N电机控制板实现四驱小车PWM调速与方向控制的实战方法。涵盖硬件连接、GPIO与定时器配置、PWM生成、方向控制逻辑及电源管理等核心内容,并提供代码优化与常见问题解决方案,帮助开发者快速实现高效稳定的电机控制。

jump7的博客 222

flink批量写入到hdfs上

import org.apache.flink.streaming.api.functions.source.SourceFunction /** * @Author: wpp * @Date: 2020/3/13 18:28 * */ class MyNoParallelScala extends SourceFunction[String]{ var count=...

wppwpp1的专栏 2383

数据分享:中国历年省、市、县行政区划shp(1949~2023年共计75年)

中华人民共和国县级以上行政区沿革》(三册,1949-1983年)“The Atlas of Changes of Administrative Regions of China”;《中华人民共和国行政区划沿革地图集》The yearly publications of “The Administrative Regions”, 1985-2014;每年的出版行政区划简册(1985-2014年)行政区划网:www.xzqh.org。行政区划网:www.xzqh.org。

J_Giser的博客 3685

实现画布拖拽算子排布,通过flink实现算子编排计算,如何实现?

🏆本文收录于 《全栈 Bug 调优(实战版)》 专栏。专栏聚焦真实项目中的各类疑难 Bug,从成因剖析 → 排查路径 → 解决方案 → 预防优化全链路拆解,形成一套可复用、可沉淀的实战知识体系。无论你是初入职场的开发者,还是负责复杂项目的资深工程师,都可以在这里构建一套属于自己的「问题诊断与性能调优」方法论,助你稳步进阶、放大技术价值 。

**My Coding Family** 1069

Flink+hadoop部署及Demo

上传并解压到3台服务器配置3台主机的hosts和免密登录。

Persistence___的博客 2321

泰凌微8258入门教程 环境篇④——开发板+烧录器 8258mesh新手套件购买

工欲善其事,必先利其器。 淘宝链接:https://item.taobao.com/item.htm?

4128

flink自定义Sink到HDFS

public class HdfsSourceAndSink { public static void main(String[] args) throws Exception { StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); env.setParallelism(1); // TODO 读取hdfs Da.

m0_58820420的博客 1527

[AHK]获取通达信软件上的股票代码

PC上的股票交易软件的自动止损、自动下单等功能比较弱,不如期货交易软件。 自力更生,程序员都是自己打造工具。 根据IPO模型(Input 输入,Process处理,Output输出),为了方便自动化首先要获取当前交易软件上浏览的股票代码。 截图再OCR显然太麻烦,通过Winspy工具查看下交易软件自带功能的消息号(33780),大家记住这个号码! 下面在用AutoHotkey作为变成语...

liuyukuan的专栏 1万+

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

Flink数据,写入HDFS - 使用 StreamingFileSink

一、应用场景: Flink 消费 Kafka 数据进行实时处理,并将结果写入 HDFS。 二、Streaming File Sink 由于流数据本身是无界的,所以,流数据将数据写入到分桶(bucket)中。默认使用基于系统时间(yyyy-MM-dd–HH)的分桶策略。在分桶中,又根据滚动策略,将输出拆分为 part 文件。 1、Flink 提供了两个分桶策略,分桶策略实现了 org.apache.flink.streaming.api.functions.sink.filesystem.BucketAss

weixin_43947279的博客 1万+

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

flink-streaming消费kafka动态分区写入HDFS(SequenceFile)文件

主要记录下streaming模式下动态分区怎么写文件,sql模式直接写就是了,streaming模式需要自己写下分区方法。大致的数据流程是消费kafka,拆解json,数据写到hdfs(sequenceFile)路径。 1、分区需要自定义,这里是读取流数据,获取分区字段 package partitionassigner; import org.apache.flink.api.java.tuple.Tuple2; import org.apache.flink.core.io.S...

huobumingbai1234的博客 1278

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

Flink StreamingFileSink 文件到hdfs 文件一直处于inprogress状态无法生成正式文件

问题描述: 任务逻辑是通过实时读取Kafka数据,一分钟计算一次数据,并利用Flink StreamingFileSink将数据落地到HDFS文件中。为了应对大促剧增的数据量,对当前运行稳定的集群进行了扩容处理,任务重启后发现写入的hdfs文件一直处于inprogress状态无法滚动生成正式文件。 解决过程: 开始是猜想可能是并行度过多,导致产生大量临时文件,文件句柄太多,关闭耗时导致文件一直无法完成合并?将并行度调整到1,发现问题并没有解决。 又猜想是因为调整了checkpoint参数,禁用掉che

cullinans的博客 8403

flink sink 到hdfs文件系统

flink 写入 hdfs

lck_csdn的博客 1368

实现flink集成hudi,将数据写入到hdfs中,并解决一些问题

本机环境 在集成之前首先你的服务器必须具有jdk,hadoop,scala,flink,maven环境。 其中jdk1.8以上,hadoop最好用3.0以上,至于scala和flink的版本受限于hudi的版本,我这里使用的是0.9版本,对应flink-1.12.2,scala-2.11.12。 首先从hudi官网下载hudi源码,上传到服务器上,进行编译 mvn clean package -DskipTests 注意:默认是用scala-2.11编译的 如果我们用的是flink1.12.2-2.12

weixin_43857576的博客 2943
上一篇: Maven之阿里云镜像仓库配置
下一篇: 内部类和外部类之间的相互调用
王树民
博客等级 码龄10年 912粉丝 560原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值