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
相关推荐
Flynt9 小时前
Redis Cluster主节点挂了,为什么"高可用"还全员掉线?我把三次kill的记录翻出来了
数据库·redis·分布式
JosieBook11 小时前
【数据库】MySQL 实战精通系列 · 第10篇:分库分表与分布式事务实战
数据库·分布式·mysql
写后端的胖头鱼12 小时前
一文详解Cache Aside(旁路缓存模式)
redis·缓存·延迟双删·缓存一致
code_slave(码畜)13 小时前
微服务架构落地:基础服务 —— 报表服务(中篇:元数据、查询引擎、缓存与权限落地)
spring boot·spring cloud·缓存·微服务·架构
梦帮科技14 小时前
vLLM / TensorRT-LLM 极限推理:PagedAttention 细粒度物理页表管理与连续批处理(Continuous Batching)实战
数据结构·人工智能·分布式·python·深度学习·算法·vllm
hweiyu0017 小时前
Redis命令:HPEXPIREAT
redis·缓存
code2cat18 小时前
【随笔】MCP缓存期限与共享范围:让Agent复用资料时记住边界
java·后端·缓存·ai agent·mcp
Frank_refuel20 小时前
分布式RPC框架实战(二):服务端模块划分与底层设计
分布式·网络协议·rpc
吴声子夜歌20 小时前
Nginx应用与运维——Nginx缓存服务应用实战(一)
运维·nginx·缓存
ShineWinsu20 小时前
对于Redis:ZSet类型的解析
数据库·c++·redis·缓存·面试·有序集合·zset