2023-07-30  阅读(2)
原文作者:Ressmix 原文地址:https://www.tpvlog.com/article/320

dfs-client,也就是分布式文件系统的客户端,需要提供各种文件/目录操作的命令,比如目录创建,文件上传/下载等等,然后实现跟分布式文件系统的通信。

本章,我以"目录创建"命令为例,来实现客户端文件操作,客户端的操作接口我定义在FileSystem接口中:

202307302136346881.png

一、客户端接口

首先,我们需要定义好gRPC接口。

1.1 RPC接口存根

我直接修改dfs-rpc工程src/main/proto目录下的NameNodeServiceProto.proto文件,新增一个“目录创建”接口,然后执行mvn clean compile命令生成新的接口存根:

    syntax = "proto3";
    
    option java_multiple_files = true;
    option java_outer_classname = "NameNodeServiceProto";
    
    service NameNodeService {
        rpc register(RegisterRequest) returns (RegisterResponse){}
        rpc heartbeat(HeartbeatRequest) returns (HeartbeatResponse){}
        rpc mkdir(MkDirRequest) returns (MkDirResponse){}
    }
    
    message RegisterRequest{
        string ip  = 1;
        string hostname  = 2;
    }
    message RegisterResponse{
        int32 status  = 1;
    }
    message HeartbeatRequest{
        string ip  = 1;
        string hostname  = 2;
    }
    message HeartbeatResponse{
        int32 status  = 1;
    }
    message MkDirRequest{
        string path  = 1;
    }
    message MkDirResponse{
        int32 status  = 1;
    }

然后将存根复制到dfs-rpc工程com.tpvlog.dfs.rpc.service包中。

1.2 客户端实现

客户端的实现比较简单,定义一个文件系统接口FileSystem。各个需要使用文件命令的应用引入dfs-client工程后,直接使用该接口就可以进行文件操作:

    /**
     * 作为文件系统的接口
     */
    public interface FileSystem {
    
        /**
         * 创建目录
         *
         * @param path 目录对应的路径
         * @throws Exception
         */
        void mkdir(String path) throws Exception;
    }

我们来看下FileSystem的实现,就是一些gRPC调用的标准模板代码:

    public class FileSystemImpl implements FileSystem {
    
        // 这里指定NameNode的地址
        private static final String NAMENODE_HOSTNAME = "localhost";
        private static final Integer NAMENODE_PORT = 50070;
    
        private NameNodeServiceGrpc.NameNodeServiceBlockingStub namenode;
    
        public FileSystemImpl() {
            ManagedChannel channel = NettyChannelBuilder
                    .forAddress(NAMENODE_HOSTNAME, NAMENODE_PORT)
                    .negotiationType(NegotiationType.PLAINTEXT)
                    .build();
            this.namenode = NameNodeServiceGrpc.newBlockingStub(channel);
        }
    
        /**
         * 创建目录
         */
        public void mkdir(String path) throws Exception {
            // 调用RPC服务
            MkDirRequest request = MkDirRequest.newBuilder().setPath(path).build();
            MkDirResponse response = namenode.mkdir(request);
    
            System.out.println("创建目录的响应:" + response.getStatus());
        }
    }

二、服务端实现

我们再来看NameNode服务端的RPC服务实现。

2.1 RPC服务实现

首先,NameNode需要在NameNodeServiceImpl中实现gRPC自动生成的接口存根方法,也就是上一节中的mkdir方法:

    /**
     * NameNode的RPC服务接口
     */
    public class NameNodeServiceImpl extends NameNodeServiceGrpc.NameNodeServiceImplBase {
    
        public static final Integer STATUS_SUCCESS = 1;
        public static final Integer STATUS_FAILURE = 2;
    
        //...
    
        // 负责管理元数据的核心组件
        private FSNameSystem namesystem;
    
        public NameNodeServiceImpl(FSNameSystem namesystem, DataNodeManager datanodeManager,
                                   EditLogReplicator replicator) {
            this.namesystem = namesystem;
            this.datanodeManager = datanodeManager;
            this.replicator = replicator;
        }
    
        /**
         * 创建目录
         */
        @Override
        public void mkdir(MkdirRequest request, StreamObserver<MkdirResponse> responseObserver) {
            try {
                MkdirResponse response = null;
    
                if(!isRunning) {
                    response = MkdirResponse.newBuilder().setStatus(STATUS_SHUTDOWN).build();
                } else {
                    this.namesystem.mkdir(request.getPath());
                    response = MkdirResponse.newBuilder().setStatus(STATUS_SUCCESS).build();
                }
    
                responseObserver.onNext(response);
                responseObserver.onCompleted();
            } catch (Exception e) {
                e.printStackTrace(); 
            }
        }
    }

上面的接口实现比较简单,本质就是用gRPC的标准代码实现了NameNodeServiceGrpc.NameNodeServiceImplBase中的mkdir接口,然后调用FSNameSystem.mkdir()方法完成文件操作。

2.2 FSNameSystem

FSNameSystem是NameNode进行元数据管理的核心组件,客户端发送过来的所有文件操作命令,都会交由FSNameSystem处理。FSNameSystem会在内存中维护 文件目录树 ,比如下面这样:

    /root
      /usr
      /local
      /app
    /home
      /kafka
        /data
          /access.log

同时,FSNameSystem还负责将操作命令写入 edits log日志 。关于edits log日志,我在后面章节会详细讲解,它有点类似于MySQL中binlog,可以用于NameNode宕机后的数据恢复。

三、总结

本章,我对dfs-client工程、客户端的文件操作逻辑以及服务端的操作命令处理逻辑进行了讲解,其中最核心是FSNameSysytem这个组件,它负责维护NameNode的内存文件目录树,以及写入edits log日志。我在后续章节会对这两块内容进行详细讲解。

本章的内容,我用下面这张图总结,帮助读者理解:

202307302136355382.png


Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。

它的内容包括:

  • 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
  • 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
  • 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
  • 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
  • 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
  • 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
  • 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
  • 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw

目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:

想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询

同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。

阅读全文