01 引言
在前面的博客《JuiceFS-开源分布式文件系统入门(一篇就够了)》,我们大致了解了JuiceFS的一些基本概念,它的架构图大致如下:
本文主要针对Hadoop
的Java API
来分析下它的源码。
02 JuiceFS Hadoop Java API
官方使用教程:《在 Hadoop 生态使用 JuiceFS 存储》
2.1 如何使用?
使用Hadoop Java API
去调用JuiceFS是很简单的,一般分为如下几个步骤。
STEP1:引入依赖
<dependency> <groupId>org.apache.hadoop</groupId> <artifactId>hadoop-common</artifactId> <version>版本号</version> </dependency> <dependency> <groupId>io.juicefs</groupId> <artifactId>juicefs-hadoop</artifactId> <version>版本号</version> </dependency>
STEP2:代码使用,只需要修改下Configuration就可以了
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileStatus; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; public class JuiceFSDemo { public static void main(String[] args) throws Exception { Configuration conf = new Configuration(); conf.set("fs.jfs.impl", "io.juicefs.JuiceFileSystem"); conf.set("juicefs.meta", "redis://127.0.0.1:6379/0"); // JuiceFS 元数据引擎地址 Path p = new Path("jfs://{JFS_NAME}/"); // 请替换 {JFS_NAME} 为正确的值 FileSystem jfs = p.getFileSystem(conf); FileStatus[] fileStatuses = jfs.listStatus(p); // 遍历 JuiceFS 文件系统并打印文件路径 for (FileStatus status : fileStatuses) { System.out.println(status.getPath()); } } }
STEP3:接下来,直接使用hadoop-common的java api即可使用JuiceFS系统了。
可以看得出,操作是十分简单的,只要引入了hadoop-common以及juicefs-hadoop的依赖,修改一下配置,即可使用。那么其底层的代码又是怎么走的呢?接下来讲讲。
2.2 入口
从上面的代码,我们可以知道,仅仅是配置了两项,然后指定了地址,即可使用JuiceFS文件系统了,如下代码:
Configuration conf = new Configuration(); conf.set("fs.jfs.impl", "io.juicefs.JuiceFileSystem"); conf.set("juicefs.meta", "redis://127.0.0.1:6379/0"); // JuiceFS 元数据引擎地址 Path p = new Path("jfs://{JFS_NAME}/"); // 请替换 {JFS_NAME} 为正确的值 FileSystem jfs = p.getFileSystem(conf);
其中主要核心是 p.getFileSystem(conf)
这一段代码。
2.2.1 getFileSystem方法
跟着断点走,会进入到org.apache.hadoop.fs.FileSystem.Cache#getInternal方法:
其中较为核心的是createFileSystem方法,进去看看:
可以看到,createFileSystem方法主要做了获取类的元数据对象以及初始化操作,我们主要看卡getFileSystemClass方法:
该方法会加载所有的文件系统,对应的方法为loadFileSystems,继续断点:
当然到这里,并没有我们需要的JuiceFS系统,继续走下一步:
可以看到红框的那一行,原来是通过拼接scheme来去动态加载JuiceFileSystem的对象,也就是对应前面设置的:
conf.set("fs.jfs.impl", "io.juicefs.JuiceFileSystem");
2.2.2 小结
hadoop-common初始化JuiceFS文件系统的流程:
- 首先会解析地址获取scheme,如:(
jfs://{JFS_NAME}/
,它的scheme
为jfs
); - 接着
hadoop-common
里面的org.apache.hadoop.fs.FileSystem#getFileSystemClass方法通过拼接"fs." + scheme + ".impl"
作为key值去获取config的内容,即:
//conf.set("fs.jfs.impl", "io.juicefs.JuiceFileSystem"); conf.get("fs.jfs.impl", null);
- 最后即可实例化
JuiceFS
文件系统了。
2.3 JuiceFS源码
到这里我们知道了hadoop-common api对接JuiceFS的方式了,那么JuiceFS里的代码是如何的呢?
2.3.1 从hadoop-api到juicefs-api
我们可以从实际的业务入手,如下示例:
private void initHdfsPath() { Path path = new Path(RESOURCE_UPLOAD_PATH); try { if (!fs.exists(path)) { fs.mkdirs(path); } } catch (Exception e) { LOGGER.error(e.getMessage(), e); } }
断点看看fs.exists
方法:
从断点可以看到,当前的文件系统为JuiceFileSystem
了,继续断点进去:
会发下,这里会进入org.apache.hadoop.fs.FileSystem#exists方法,其中里面有一个getFileStatus
方法,点击查看:
原来它是一个抽象方法,由子类实现:
它有多个子类实现,当然也包括我们目前的JuiceFileSystemImpl
,进入看看:
终于进来了,接着继续看io.juicefs.JuiceFileSystemImpl#getFileStatusInternal方法:
可以看到,原来查询是否存在,JuiceFS是使用了lib
去查,这个lib
其实是调用到了底层的库了(JNI技术)。
2.3.2 JuiceFS底层库
我们通过搜索io.juicefs.JuiceFileSystemImpl#lib变量:
可以看到它是在io.juicefs.JuiceFileSystemImpl#loadLibrary方法初始化了,看看里面的代码:
可以看到它是通过识别当前操作系统来去加载JuiceFS的底层so
库的,那么底层库的接口支持哪些呢?看看:
其实就是支持文件系统的CURD这些,也没啥好看的了。
简单的说,它的核心并不在java代码,而是通过JNI来调用底层的库来实现的。
03 总结
通过阅读本文,我们可以知道,JucieFS对接hadoop-common api的流程如下:
- hadoop-common里面的
FileSystem
类首先会解析地址获取scheme
,如:(jfs://{JFS_NAME}/
,它的scheme
为jfs
);
- 接着
hadoop-common
里面的getFileSystemClass方法通过拼接"fs." + scheme + ".impl"
作为key
值去获取config
的内容并实例化JuiceFS
文件系统了; - JuiceFS文件系统对象是继承
FileSystem
对象实现的,所以使用FileSystem
的接口去调用,最终由JuiceFS
文件系统来实现; - 而JuiceFS文件系统的
java
代码最终是调用了底层的so
库去实现。
其实关键的核心还是在JuiceFS的底层库,不知道有无开源呢?感兴趣的小伙伴可以去参考它的源码:https://github.com/juicedata/juicefs
04 文末
本文主要讲解了hadoop-common java api
层面JuiceFS的实现流程,希望能帮助到大家,谢谢大家的阅读,本文完!