一、分布式缓存概述
Flink 提供的分布式缓存,核心作用是让用户在并行函数中便捷读取本地或远程文件,并将文件同步至所有 TaskManager 节点的本地文件系统,避免 Task 重复拉取文件,提升作业执行效率,降低网络开销。
二、分布式缓存工作机制
Flink 分布式缓存的工作流程可分为以下4个步骤,确保文件高效同步且仅执行一次:
-
用户注册文件/目录:可注册本地文件,或远程文件系统(如 HDFS、S3)中的文件/目录;
-
通过执行环境注册:借助 ExecutionEnvironment 注册缓存文件,并为其指定一个唯一名称(后续用于查找文件);
-
自动同步至 TaskManager:程序执行时,Flink 会自动将注册的文件/目录复制到所有 TaskManager 节点的本地文件系统,该同步操作仅执行一次;
-
本地访问文件:用户在并行函数中,通过注册时指定的名称查找文件/目录,从 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