Flink 系列第10篇:Flink 分布式缓存详解

一、分布式缓存概述

Flink 提供的分布式缓存,核心作用是让用户在并行函数中便捷读取本地或远程文件,并将文件同步至所有 TaskManager 节点的本地文件系统,避免 Task 重复拉取文件,提升作业执行效率,降低网络开销。

二、分布式缓存工作机制

Flink 分布式缓存的工作流程可分为以下4个步骤,确保文件高效同步且仅执行一次:

  1. 用户注册文件/目录:可注册本地文件,或远程文件系统(如 HDFS、S3)中的文件/目录;

  2. 通过执行环境注册:借助 ExecutionEnvironment 注册缓存文件,并为其指定一个唯一名称(后续用于查找文件);

  3. 自动同步至 TaskManager:程序执行时,Flink 会自动将注册的文件/目录复制到所有 TaskManager 节点的本地文件系统,该同步操作仅执行一次;

  4. 本地访问文件:用户在并行函数中,通过注册时指定的名称查找文件/目录,从 TaskManager 本地文件系统直接访问,无需重复拉取。

三、代码示例

3.1 注册缓存文件

通过 ExecutionEnvironment 注册缓存文件,支持本地文件或远程文件(如 HDFS),示例如下:

java 复制代码
// 1. 获取Flink批处理运行环境(分布式缓存主要用于批处理,流处理需结合特定场景)
ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment();

// 2. 注册缓存文件:第一个参数为文件路径(本地路径/远程路径),第二个参数为缓存名称(唯一标识)
// 示例:注册本地文件,缓存名称为"a.txt"
env.registerCachedFile("/Users/wangzhiwu/WorkSpace/quickstart/text", "a.txt");

3.2 在并行函数中访问缓存文件

需通过继承 RichFunction(如 RichMapFunction),借助 RuntimeContext 读取缓存文件。原因是 RichFunction 提供了 RuntimeContext 实例,可用于获取分布式缓存资源。

java 复制代码
// 3. 在RichMapFunction中访问缓存文件
DataSet<String> result = data.map(new RichMapFunction<String, String>() {
    // 用于存储缓存文件中的数据,供后续业务逻辑使用
    private ArrayList<String> dataList = new ArrayList<String>();

    // open方法:在Task启动时执行一次,适合初始化操作(如读取缓存)
    @Override
    public void open(Configuration parameters) throws Exception {
        super.open(parameters);
        // 4. 通过缓存名称"a.txt"获取本地文件
        File myFile = getRuntimeContext().getDistributedCache().getFile("a.txt");
        // 5. 读取文件内容(需导入org.apache.commons.io.FileUtils)
        List<String> lines = FileUtils.readLines(myFile);
        // 6. 将文件内容存入dataList,供map方法使用
        for (String line : lines) {
            this.dataList.add(line);
            System.err.println("分布式缓存内容:" + line);
        }
    }

    // map方法:并行处理每条数据,可直接使用缓存中的dataList
    @Override
    public String map(String value) throws Exception {
        // 打印缓存数据和当前处理的value,便于调试
        System.err.println("使用缓存数据:" + dataList + "------------" + value);
        // 业务逻辑:将缓存数据与当前value拼接返回
        return dataList + ":" + value;
    }
});

// 打印结果(printToErr()用于区分标准输出和错误输出,便于查看缓存相关日志)
result.printToErr();

3.3 完整代码(含注释)

以下为完整可运行代码,包含环境初始化、缓存注册、缓存访问及结果输出,注释详细可直接复用:

java 复制代码
import org.apache.flink.api.common.functions.RichMapFunction;
import org.apache.flink.api.java.ExecutionEnvironment;
import org.apache.flink.api.java.DataSet;
import org.apache.flink.api.java.DataSource;
import org.apache.flink.configuration.Configuration;
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class DisCacheTest {

    public static void main(String[] args) throws Exception{
        // 1. 获取Flink批处理运行环境
        ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment();

        // 2. 注册缓存文件:本地文件路径(可替换为HDFS路径,如hdfs://xxx/text)
        // 备注:缓存文件text中包含4个单词:hello flink hello FLINK
        env.registerCachedFile("/Users/wangzhiwu/WorkSpace/quickstart/text", "a.txt");

        // 3. 构造测试数据源(4条数据:a、b、c、d)
        DataSource<String> data = env.fromElements("a", "b", "c", "d");

        // 4. 利用RichMapFunction访问缓存并处理数据
        DataSet<String> result = data.map(new RichMapFunction<String, String>() {
            // 存储缓存文件内容的集合
            private ArrayList<String> dataList = new ArrayList<String>();

            // Task启动时执行,仅执行一次,用于读取缓存文件
            @Override
            public void open(Configuration parameters) throws Exception {
                super.open(parameters);
                // 通过缓存名称"a.txt"获取TaskManager本地的缓存文件
                File myFile = getRuntimeContext().getDistributedCache().getFile("a.txt");
                // 读取文件所有行(依赖commons-io包,需引入相关依赖)
                List<String> lines = FileUtils.readLines(myFile);
                // 将文件内容存入dataList
                for (String line : lines) {
                    this.dataList.add(line);
                    System.err.println("分布式缓存内容:" + line);
                }
            }

            // 并行处理每条输入数据,可直接使用缓存中的dataList
            @Override
            public String map(String value) throws Exception {
                // 打印缓存数据和当前处理的value,用于调试
                System.err.println("使用缓存数据:" + dataList + "------------" + value);
                // 业务逻辑:将缓存数据与当前value拼接,作为结果返回
                return dataList + ":" + value;
            }
        });

        // 5. 输出结果(使用printToErr(),避免与缓存日志混淆)
        result.printToErr();
    }
}

四、输出结果

运行上述代码后,输出结果如下(包含缓存读取日志和最终处理结果):

plain 复制代码
分布式缓存内容:hello
分布式缓存内容:flink
分布式缓存内容:hello
分布式缓存内容:FLINK
使用缓存数据:[hello, flink, hello, FLINK]------------a
[hello, flink, hello, FLINK]:a
使用缓存数据:[hello, flink, hello, FLINK]------------b
[hello, flink, hello, FLINK]:b
使用缓存数据:[hello, flink, hello, FLINK]------------c
[hello, flink, hello, FLINK]:c
使用缓存数据:[hello, flink, hello, FLINK]------------d
[hello, flink, hello, FLINK]:d
相关推荐
三十..2 小时前
Ceph分布式存储核心技术精要与运维实践指南
运维·分布式·ceph
憧憬成为java架构高手的小白6 小时前
黑马八股redis
数据库·redis·缓存
cfm_29146 小时前
Redis高并发分布式锁了解
redis·分布式
Geoking.7 小时前
【大模型 Token 计费】输入、输出、缓存命中
缓存
小小编程路7 小时前
分布式核心知识
分布式
bukeyiwanshui7 小时前
20260528 Ceph 分布式存储 集群配置
分布式·ceph
我叫张小白。8 小时前
基于Redis与FastAPI的分布式共享会话体系
数据库·redis·分布式·缓存·中间件·fastapi·依赖注入
大大大大晴天8 小时前
告别 Lambda 架构!Flink 批流一体底层原理解析
flink
天河归来8 小时前
国产数据库安全可靠测评产品观察:从集中式、分布式到 HTAP 的发展趋势
数据库·分布式
小碗羊肉8 小时前
【Redis | 第五篇】分布式锁
数据库·redis·分布式