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
相关推荐
我是唐青枫1 天前
别只会用 MemoryCache!C#.NET CacheManager 详解:多级缓存、Region 与 Redis 实战
缓存·c#·.net
gQ85v10Db1 天前
Redis分布式锁进阶第十七篇:微服务分布式锁全局治理 + 跨团队统一规范落地 + 全链路稳定性提升方案
redis·分布式·微服务
Lyyaoo.2 天前
Redisson
数据库·缓存
倒霉蛋小马2 天前
【Redis】什么是缓存击穿?
数据库·redis·缓存
Justice Young2 天前
Flink第三章:Flink运行及部署
大数据·flink
gQ85v10Db2 天前
Redis分布式锁进阶第十八篇:本地缓存+分布式锁双锁架构 + 高并发削峰兜底 + 极致性能无损优化实战
redis·分布式·缓存
小江的记录本2 天前
【Kafka核心】Kafka高性能的四大核心支柱:零拷贝、批量发送、页缓存、压缩
java·数据库·分布式·后端·缓存·kafka·rabbitmq
gQ85v10Db2 天前
Redis分布式锁进阶第十四篇:全系列终局架构复盘 + 锁体系统一规范 + 线上全年零事故收官方案
redis·分布式·架构
KmSH8umpK2 天前
Redis分布式锁进阶第十二篇
数据库·redis·分布式
gQ85v10Db2 天前
Redis分布式锁进阶第十六篇:番外高阶避坑篇 + 隐性埋点锁故障深挖 + 疑难杂症终极兜底方案
数据库·redis·分布式