一個極簡的分佈式文件系統

前言

開源的分佈式存儲系統比較多,比較有名的有:Ceph、GlusterFS、HDFS、TFS等。這些系統都比較複雜,代碼動則幾十上百萬行,這些系統對初學者來講門檻比較高,特別是對於從事非分佈式存儲行業,但又想跨行學習分佈式的同窗來講,每每有這想法,可是不知道怎麼入手。本文介紹以前實現的一個C++極簡版的分佈式文件系統 https://github.com/goyas/goya-fs, 代碼只有一兩百行,固然功能也很粗糙,只實現了簡單的mkdir和ls這兩條命令,但就像剛剛描述的,目的是學習,也便於你們對分佈式有體感以後,方便閱讀其餘龐大的分佈式存儲系統,固然之後有空時間也會不斷完善功能。git

對於嵌入式,或者主要是從事單機開發的程序員來講,沒接觸分佈式以前,都會感受很神祕,每每會被高併發、海量數據分析處理等名詞唬住。其實,職位沒有智商之分,區別也就在於你有沒有親自動手摸過這些玩意兒。以往的經驗告訴我,就算不會的東西,一個版本的時間,只要你稍微努點力基本就會達到行業的基本水平,固然越往上走就要看本身的興趣和時間投入了。程序員

好了,言歸正傳,下面開始介紹這個簡單的分佈式文件系統,選用的基礎組件是leveldb + goyas-rpc,leveldb做爲存儲底座,goyas-rpc做爲進程之間通訊使用。有關leveldb的介紹網上很是多,這裏就再也不驁述,goyas-rpc能夠參考以前的 一個基於protobuf的極簡RPC 這篇文章。github

思考

若是讓你設計分佈式文件系統,你會怎麼設計?
一、若是本身設計一個簡單的分佈式存儲系統,對於文件的讀取存盤,你會怎麼設計?
二、好比執行下面的命令通過怎麼樣的IO路徑local_file文件纔會存儲到磁盤? ./fs_client put local_file /user/ —把local_file文件存放到文件系統/user目錄
三、怎麼讓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執行這條命令,會最初調用下面的函數接口app

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

待實現 學習

寫在最後

到這裏整個分佈式文件系統就講解完了,固然真正的分佈式存儲系統遠比這個複雜的太多,否則怎麼會到百萬級別的代碼。但願這個簡單的文件系統的講解對你有點幫忙。

相關文章
相關標籤/搜索