File的存储分为3步,open,write和close1、open会检查chunk和id的相关属性,也就是和nameserver进行通信,如果chunkid和fileid为0,则nameserver会分配chunkid,dataserver会分配fileid(在write的时候分配fileid)。
nameserver会根据chunkid和fileid,查找该chunk的版本,租约id以及合适的ds列表。
int MetaManager::write_block_info(uint32_t& block_id, int32_t mode, uint32_t& lease_id, int32_t& version,
VUINT64& ds_list)
{
...........
block_id为0,也就是分配一个新的chunk,用来存储
if ((block_id == 0)
&& (mode & BLOCK_CREATE))
{
VINT64 fail_ds;
//这里选择block的时候,并没有通过file的具体大小选择合适的block,应该在策略上有一定的问题,但是TFS有虚拟块的概念,可以通过虚拟块来规避部分的问题
block_id = elect_write_block(fail_ds);
........................................
//采用了存储分组的方式,二次索引(不确定)
BlockChunkPtr ptr = meta_mgr_.get_block_chunk(block_id);
ptr->mutex_.rdlock();
BlockCollect* block_collect = ptr->find(block_id);
//如果该chunkid没有dataserver服务器,直接返回失败
if (block_collect->get_ds()->size() == 0)
{
ptr->mutex_.unlock();
TBSYS_LOG(ERROR, "add new block(%u) failed, dataserver not found", block_id);
return EXIT_NO_DATASERVER;
}
//获取chunk的version已经对应的dataserver列表
version = block_collect->get_block_info()->version_;
ds_list.assign(block_collect->get_ds()->begin(), block_collect->get_ds()->end());
//获取一个租约
if (!(mode & BLOCK_NOLEASE))
{
lease_id = lease_mgr_.register_lease(block_id);
if (lease_id == WriteLease::INVALID_LEASE)
{
TBSYS_LOG(ERROR, "lease not found by block id(%u)", block_id);
return EXIT_CANNOT_GET_LEASE;
}
}
return TFS_SUCCESS;
}
从这些里面我们可以看出,系统的负载均衡策略并没有体现出来,没有根据file大小以及dataserver的负载信息进行动态的分配,而是随机的选择dataserver
同时如果是新增文件的时候,nameserver为其分配一个chunkid,而fileid是由dataserver进行分配的
2、open后,client通过与nameserver交互,拿到需要的信息后,与dataserver进行交互,写入数据到dataserver
int DataManagement::write_data(const WriteDataInfo& write_info, const int32_t lease_id, int32_t& version,
const char* data_buffer, UpdateBlockType& repair)
{
//如果是刚开始写,则需要检查chunkid的版本,TFS不支持随机写入,所以第一次写入可以使用offset进行判断
if (0 == write_info.offset_)
{
LogicBlock* logic_block = BlockFileManager::get_instance()->get_logic_block(write_info.block_id_);
.............................................
int ret = logic_block->check_block_version(version, repair);
......................................
}
//开始写数据,如果文件比较大,数据流是分段传输的,首先需要找到第一次写入时的缓存数据,数据的写入是先全部写入缓存,然后全部刷入磁盘
DataFileMapIter bit = data_file_map_.find(write_info.file_number_);
DataFile* datafile = NULL;
if (bit != data_file_map_.end())
{
datafile = bit->second;
}
else // not found
{
// control datafile size
if (data_file_map_.size() >= static_cast (SYSPARAM_DATASERVER.max_datafile_nums_))
{
TBSYS_LOG(ERROR, "blockid: %u, datafile nums: %u is large than default.", write_info.block_id_,
data_file_map_.size());
data_file_mutex_.unlock();
return EXIT_DATAFILE_OVERLOAD;
}
........................................................................
// 开始写入数据,这里有个需要注意的地方,当file比较大的时候,系统会把数据存储在磁盘临时文件,而不是内存缓存,这个文件大小临界值暂设置为2M
int32_t write_len = datafile->set_data(data_buffer, write_info.length_, write_info.offset_);
if (write_len != write_info.length_)
{
erase_data_file(write_info.file_number_);
return EXIT_DATA_FILE_ERROR;
}
return TFS_SUCCESS;
}
完成主dataserver数据写入后(分批写入),会将数据传送到slave 的dataserver
TFS与GFS不同之处在于,TFS是主dataserver采用异步的方式,将数据发送到全部slave,也就是1-n的方式,而GFS是采用流水的方式,也就是1-1-1-1的方式。
message->set_server(Slave_Server_Role);
message->set_lease_id(lease_id);
message->set_block_version(version);
ret = post_message_to_server(message, message->get_ds_list());
3、数据在写入完成后,client会发送close的命令,dataserver将数据刷入磁盘
int DataManagement::close_write_file(const CloseFileInfo& closefileInfo, int32_t& write_file_size)
{
1、找到接收到的数据
.......................................
DataFileMapIter bit = data_file_map_.find(file_number);
if (bit != data_file_map_.end())
{
datafile = bit->second;
}
..............................................
2、设置属性
datafile->set_last_update();
datafile->add_ref();
data_file_mutex_.unlock();
3、计算crc值
//compare crc
uint32_t datafile_crc = datafile->get_crc();
......................................
write_file_size = datafile->get_length();
//找到chunk的具体信息,准备写数据了
LogicBlock* logic_block = BlockFileManager::get_instance()->get_logic_block(block_id);
................................................
TIMER_START();
//这个写数据的时候,有几个特殊的地方,上面我们提到过,nameserver选择chunk的时候,不会根据文件的大小进行判断,那么如果一个文件的大小超过了chunk的剩余存储空//间,TFS的处理流程:
//TFS会使用虚拟块,系统会申请虚拟块存储空间,系统默认最多申请30个虚拟块,如果30个虚拟块都不满足需要的空间,则返回失败。如果申请到需要的存储空间,则file在///物理存储上会分布多个文件内,但在逻辑存储上,依然存储在同一个chunk上,同时chunk的大小会增加
//也就是说,TFS支持存储大小超过64M的文件,但由于文件分片无法跨服务器存储,无法支持真正的大文件存储
int ret = logic_block->close_write_file(file_id, datafile, datafile_crc);
..................................................
datafile->sub_ref();
erase_data_file(file_number);
return TFS_SUCCESS;
}