开源的分布式存储系统比较多,比较有名的有: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 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消息后干了些啥?分布式
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建立的文件信息给列出来,这里就不讲究了,你们能够去看看源码,也是很是简单。高并发
待实现 …学习
到这里整个分布式文件系统就讲解完了,固然真正的分布式存储系统远比这个复杂的太多,否则怎么会到百万级别的代码。但愿这个简单的文件系统的讲解对你有点帮忙。