欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

一个极简的分布式文件系统

程序员文章站 2022-07-02 12:49:51
前言 开源的分布式存储系统比较多,比较有名的有:Ceph、GlusterFS、HDFS、TFS等。这些系统都比较复杂,代码动则几十上百万行,这些系统对初学者来说门槛比较高,特别是对于从事非分布式存储行业,但又想跨行学习分布式的同学来说,往往有这想法,但是不知道怎么入手。本文介绍之前实现的一个C++极 ......

前言

开源的分布式存储系统比较多,比较有名的有:ceph、glusterfs、hdfs、tfs等。这些系统都比较复杂,代码动则几十上百万行,这些系统对初学者来说门槛比较高,特别是对于从事非分布式存储行业,但又想跨行学习分布式的同学来说,往往有这想法,但是不知道怎么入手。本文介绍之前实现的一个c++极简版的分布式文件系统 , 代码只有一两百行,当然功能也很粗糙,只实现了简单的mkdir和ls这两条命令,但就像刚刚描述的,目的是学习,也便于大家对分布式有体感之后,方便阅读其他庞大的分布式存储系统,当然以后有空时间也会不断完善功能。

对于嵌入式,或者主要是从事单机开发的程序员来说,没接触分布式之前,都会感觉很神秘,往往会被高并发、海量数据分析处理等名词唬住。其实,职位没有智商之分,区别也就在于你有没有亲自动手摸过这些玩意儿。以往的经验告诉我,就算不会的东西,一个版本的时间,只要你稍微努点力基本就会达到行业的基本水平,当然越往上走就要看自己的兴趣和时间投入了。

好了,言归正传,下面开始介绍这个简单的分布式文件系统,选用的基础组件是leveldb + goyas-rpc,leveldb作为存储底座,goyas-rpc作为进程之间通信使用。有关leveldb的介绍网上非常多,这里就不再骜述,goyas-rpc可以参考之前的 一个基于protobuf的极简rpc 这篇文章。

思考

如果让你设计分布式文件系统,你会怎么设计?
1、如果自己设计一个简单的分布式存储系统,对于文件的读取存盘,你会怎么设计?
2、比如执行下面的命令经过怎么样的io路径local_file文件才会存储到磁盘? ./fs_client put local_file /user/ —把local_file文件存放到文件系统/user目录
3、怎么让local_file文件存储到分布式文件系统的3个不同结点,并且3副本保存?

架构设计

系统架构设计采用经典的gfs分布式存储模型,由3个不同的角色(client、master、chunkserver)负责管理不同的事务,client作为客户端,接受来自用户的请求。master作为元数据及namespace存储管理。chunkserver和磁盘打交道,作为最终的单机存储引擎。 执行./fs_client put local_file /user/ 命令,会大致经历下面图里面从左到右的流程,最终调用系统调用write把local_file存放到存储介质。

一个极简的分布式文件系统

fs_client

fs_client用于接受用户的请求,比如:./fs_client mkdir /file1执行这条命令,会最初调用下面的函数接口

int filesystemimpl::createdirectory(char* path) {
  printf("create directory %s\n", path);
  createfilerequest  request;
  createfileresponse response;
  request.set_sequence_id(0);
  request.set_file_name(path);
  request.set_type((1<<9)|0755);
  bool ret = rpc_wrapper_->sendrequest(masterserver_stub_, 
    &masterserver_stub::createfile, &request, &response, 5, 3);
  if (!ret || response.status() != 0) {
    printf("create directory fail\n");
    return -1;
  }
  
  return 0;
}

 

函数功能:把序列号、文件名及文件类型通过rpc发送到元数据管理进程masterserver进行处理。其实这里比我们经常单机环境写的fopen、fwrite也就仅仅多了个rpc,需要通过它把不用节点的信息发送到其他节点,呵呵,这就是分布式!再来看看masterserver进程收到这个request消息后干了些啥?

masterserver

void masterserverimpl::createfile(google::protobuf::rpccontroller* controller,
  const ::goya::fs::createfilerequest* request,
  goya::fs::createfileresponse* response,
  google::protobuf::closure* done) {
  printf("masterserver create file\n");
  response->set_sequence_id(request->sequence_id());
  const std::string& filename = request->file_name();
  if (filename.empty() || filename[0] != '/') {
    printf("path format error\n");
    response->set_status(3);
    done->run();
    return ; 
  }

  std::string file_value;
  leveldb::status s;
  s = db_->get(leveldb::readoptions(), filename, &file_value);
  if (s.isnotfound()) {
    fileinfoproto file_info;
    file_info.set_time(time(null));
    file_info.set_type(request->type());
    file_info.serializetostring(&file_value);
    s = db_->put(leveldb::writeoptions(), filename, file_value);
    if (s.ok()) {
      printf("createfile %s file\n", filename.c_str());
      response->set_status(0);
    } else {
      printf("createfile %s file\n", filename.c_str());
      response->set_status(2);
    }
  } else {
    printf("createfile %s fail: already exist\n", filename.c_str());
    response->set_status(1);
  }
  done->run();
}

函数功能:从收到的消息中提取出文件名作为key,文件类型和时间作为value,然后使用kv存储引擎leveldb存储,最后把完成消息通过response发送给调用方。这个过程到这里就完了,是不是很简单?!

一个极简的分布式文件系统

ls命令的功能和上面的介绍相反,就是把上面mkdir创建的文件信息给列出来,这里就不讲究了,大家可以去看看源码,也是非常简单。一个极简的分布式文件系统

chunkserver

待实现 

写在最后

到这里整个分布式文件系统就讲解完了,当然真正的分布式存储系统远比这个复杂的太多,不然怎么会到百万级别的代码。希望这个简单的文件系统的讲解对你有点帮忙。