源码路径curve-release2.2\src\chunkserver
1. `trash.cpp`
2. `op_request.cpp`
3. `register.cpp`
4. `passive_getfn.cpp`
5. `scan_service.cpp`
在trash.cpp
文件中,定义了Trash
类,该类负责管理块服务器中的回收站功能,包括删除文件和目录、移动文件到回收站等。以下是对Trash
类中函数的代码注释分析:
// trash.cpp 文件包含了块服务器回收站功能的实现
// Trash 类定义,用于管理块服务器的回收站
namespace curve {
namespace chunkserver {
// Trash::Init 方法,用于初始化回收站
int Trash::Init(TrashOptions options) {
// 设置回收站路径
isStop_ = true; // 初始化时停止状态
if (curve::common::UriParser::ParseUri(options.trashPath, &trashPath_).empty()) {
// 解析回收站路径失败
LOG(ERROR) << "not support trash uri's protocol" << options.trashPath;
return -1; // 返回错误代码
}
if (trashPath_.empty()) {
LOG(ERROR) << "trash path is empty, please check!"; // 回收站路径为空
return -1; // 返回错误代码
}
expiredAfterSec_ = options.expiredAfterSec; // 设置文件过期时间
scanPeriodSec_ = options.scanPeriodSec; // 设置扫描周期
localFileSystem_ = options.localFileSystem; // 设置本地文件系统接口
chunkFilePool_ = options.chunkFilePool; // 设置块文件池接口
walPool_ = options.walPool; // 设置 WAL 文件池接口
chunkNum_.store(0); // 初始化块数量计数器
// 读取回收站目录下的所有目录
std::vector<std::string> files;
localFileSystem_->List(trashPath_, &files);
for (auto &file : files) { // 遍历目录
if (!IsCopysetInTrash(file)) { // 如果不是副本集目录,跳过
continue;
}
std::string copysetDir = trashPath_ + "/" + file; // 构建副本集目录路径
uint32_t chunkNum = CountChunkNumInCopyset(copysetDir); // 计算副本集中的块数量
chunkNum_.fetch_add(chunkNum); // 更新块数量计数器
}
LOG(INFO) << "Init trash success. Current num of chunks in trash: " << chunkNum_.load(); // 记录初始化成功的日志
return 0; // 初始化成功,返回 0
}
// Trash::Run 方法,用于启动回收站线程
int Trash::Run() {
if (isStop_.exchange(false)) { // 如果回收站已经停止,则返回
return -1; // 返回错误代码
}
// 创建并启动回收站线程
recycleThread_ = Thread(&Trash::DeleteEligibleFileInTrashInterval, this);
LOG(INFO) << "Start trash thread ok."; // 记录启动成功的日志
return 0; // 启动成功,返回 0
}
// Trash::Fini 方法,用于停止回收站并清理资源
int Trash::Fini() {
if (!isStop_.exchange(true)) { // 如果回收站正在运行,则停止
LOG(INFO) << "stop Trash..."; // 记录停止日志
sleeper_.interrupt(); // 打断等待
recycleThread_.join(); // 等待线程结束
}
LOG(INFO) << "stop trash ok."; // 记录停止成功的日志
return 0; // 清理成功,返回 0
}
// Trash::RecycleCopySet 方法,用于将副本集移动到回收站
int Trash::RecycleCopySet(const std::string &dirPath) {
// 检查回收站目录是否存在,如果不存在则创建
if (!localFileSystem_->DirExists(trashPath_)) {
LOG(INFO) << "Copyset recyler directory " << trashPath_
<< " does not exist, creating it"; // 记录创建目录的日志
if (0 != localFileSystem_->Mkdir(trashPath_)) { // 创建目录
LOG(ERROR) << "Failed to create copyset recyler directory: " << trashPath_;
return -1; // 创建失败,返回错误代码
}
}
// 如果回收站已存在该目录,本次删除失败
std::string dst = trashPath_ + "/" + dirPath.substr(dirPath.find_last_of('/') + 1) + "."
+ std::to_string(std::time(nullptr)); // 构建目标路径
if (localFileSystem_->DirExists(dst)) { // 检查目标路径是否存在
LOG(WARNING) << "recycle error: " << dst << " already exist in " << trashPath_;
return -1; // 已存在,返回错误代码
}
{
LockGuard lg(mtx_); // 加锁
if (0 != localFileSystem_->Rename(dirPath, dst)) { // 重命名目录到回收站
LOG(ERROR) << "rename " << dirPath << " to " << dst << " error"; // 记录错误日志
return -1; // 重命名失败,返回错误代码
}
uint32_t chunkNum = CountChunkNumInCopyset(dst); // 计算副本集中的块数量
chunkNum_.fetch_add(chunkNum); // 更新块数量计数器
}
LOG(INFO) << "Recycle copyset success. Copyset path: " << dst << ", current num of chunks in trash: "
<< chunkNum_.load(); // 记录成功日志
return 0; // 移动成功,返回 0
}
// Trash::DeleteEligibleFileInTrashInterval 方法,用于定期扫描并删除过期文件
void Trash::DeleteEligibleFileInTrashInterval() {
while (sleeper_.wait_for(std::chrono::seconds(scanPeriodSec_))) { // 等待扫描周期
DeleteEligibleFileInTrash(); // 删除过期文件
}
}
// Trash::DeleteEligibleFileInTrash 方法,用于扫描并删除过期文件
void Trash::DeleteEligibleFileInTrash() {
if (!localFileSystem_->DirExists(trashPath_)) { // 如果回收站目录不存在,则返回
return;
}
// 读取回收站目录下的所有目录
std::vector<std::string> files;
if (0 != localFileSystem_->List(trashPath_, &files)) { // 列出目录
LOG(ERROR) << "Trash failed list files in " << trashPath_; // 记录错误日志
return;
}
for (auto &file : files) { // 遍历目录
if (!IsCopysetInTrash(file)) { // 如果不是副本集目录,跳过
continue;
}
std::string copysetDir = trashPath_ + "/" + file; // 构建副本集目录路径
if (!NeedDelete(copysetDir)) { // 如果不需要删除,跳过
continue;
}
if (!RecycleChunksAndWALInDir(copysetDir, file)) { // 回收块文件和 WAL 文件
continue; // 回收失败,跳过
}
// 删除副本集目录
if (0 != localFileSystem_->Delete(copysetDir)) { // 删除目录
LOG(ERROR) << "Trash fail to delete " << copysetDir; // 记录错误日志
return; // 删除失败,返回
}
}
}
// Trash::IsCopysetInTrash 方法,用于判断目录是否是副本集目录
bool Trash::IsCopysetInTrash(const std::string &dirName) {
// 合法的副本集目录格式为:池 ID.副本集 ID,例如:2860448220024
uint64_t groupId;
int n = dirName.find(".");
if (n == std::string::npos) {
return false; // 格式不正确
}
if (!::curve::common::StringToUll(dirName.substr(0, n), &groupId)) { // 转换字符串到 uint64_t
return false; // 转换失败
}
return GetPoolID(groupId) >= 1 && GetCopysetID(groupId) >= 1; // 检查池 ID 和副本集 ID 是否合法
}
// Trash::NeedDelete 方法,用于判断副本集是否需要删除
bool Trash::NeedDelete(const std::string ©setDir) {
int fd = localFileSystem_->Open(copysetDir, O_RDONLY); // 打开目录
if (0 > fd) { // 打开失败
LOG(ERROR) << "Trash fail open " << copysetDir; // 记录错误日志
return false; // 打开失败,返回 false
}
struct stat info;
if (0 != localFileSystem_->Fstat(fd, &info)) { // 获取文件状态
localFileSystem_->Close(fd); // 关闭文件描述符
return false; // 获取文件状态失败,返回 false
}
time_t now;
time(&now); // 获取当前时间
if (difftime(now, info.st_ctime) < expiredAfterSec_) { // 检查文件是否过期
localFileSystem_->Close(fd);// 关闭文件描述符
return false; // 未过期,返回 false
}
localFileSystem_->Close(fd); // 关闭文件描述符
return true; // 已过期,返回 true
}
// Trash::IsChunkOrSnapShotFile 方法,用于判断文件是否是块文件或快照文件
bool Trash::IsChunkOrSnapShotFile(const std::string &chunkName) {
return FileNameOperator::FileType::UNKNOWN != FileNameOperator::ParseFileName(chunkName).type;
}
// Trash::RecycleChunksAndWALInDir 方法,用于回收目录中的块文件和 WAL 文件
bool Trash::RecycleChunksAndWALInDir(const std::string ©setPath, const std::string &filename) {
// 判断是否是文件,如果是则回收
if (!localFileSystem_->DirExists(copysetPath)) { // 如果是文件,则检查文件类型
if (IsChunkOrSnapShotFile(filename)) { // 如果是块文件或快照文件
return RecycleChunkfile(copysetPath, filename); // 回收块文件
} else if (IsWALFile(filename)) { // 如果是 WAL 文件
return RecycleWAL(copysetPath, filename); // 回收 WAL 文件
} else { // 文件类型不匹配
return true; // 跳过
}
}
// 如果是目录,则继续列出文件并回收
std::vector<std::string> files;
if (0 != localFileSystem_->List(copysetPath, &files)) { // 列出目录中的文件
LOG(ERROR) << "Trash failed to list files in " << copysetPath; // 记录错误日志
return false; // 列出文件失败,返回 false
}
bool ret = true; // 初始化返回值为 true
for (auto &file : files) { // 遍历文件
std::string filePath = copysetPath + "/" + file; // 构建文件路径
// 递归调用自身来回收文件
if (!RecycleChunksAndWALInDir(filePath, file)) { // 回收文件
ret = false; // 如果回收失败,则更新返回值为 false
}
}
return ret; // 返回回收结果
}
// Trash::RecycleChunkfile 方法,用于回收块文件
bool Trash::RecycleChunkfile(const std::string &filepath, const std::string &filename) {
LockGuard lg(mtx_); // 加锁
if (0 != chunkFilePool_->RecycleFile(filepath)) { // 回收块文件
LOG(ERROR) << "Trash failed recycle chunk " << filepath << " to FilePool"; // 记录错误日志
return false; // 回收失败,返回 false
}
chunkNum_.fetch_sub(1); // 减少块数量计数器
return true; // 回收成功,返回 true
}
// Trash::RecycleWAL 方法,用于回收 WAL 文件
bool Trash::RecycleWAL(const std::string &filepath, const std::string &filename) {
LockGuard lg(mtx_); // 加锁
if (walPool_ != nullptr && 0 != walPool_->RecycleFile(filepath)) { // 回收 WAL 文件
LOG(ERROR) << "Trash failed recycle WAL " << filepath << " to WALPool"; // 记录错误日志
return false; // 回收失败,返回 false
}
chunkNum_.fetch_sub(1); // 减少块数量计数器
return true; // 回收成功,返回 true
}
// Trash::IsWALFile 方法,用于判断文件是否是 WAL 文件
bool Trash::IsWALFile(const std::string &fileName) {
// 检查文件名是否符合 WAL 文件的命名模式
int match = 0;
int64_t first_index = 0;
int64_t last_index = 0;
match = sscanf(fileName.c_str(), CURVE_SEGMENT_CLOSED_PATTERN, &first_index, &last_index); // 检查是否闭合段 WAL 文件
if (match == 2) {
LOG(INFO) << "recycle closed segment wal file, path: " << fileName << " first_index: " << first_index << " last_index: " << last_index;
return true; // 是闭合段 WAL 文件,返回 true
}
match = sscanf(fileName.c_str(), CURVE_SEGMENT_OPEN_PATTERN, &first_index); // 检查是否开放段 WAL 文件
if (match == 1) {
LOG(INFO) << "recycle open segment wal file, path: " << fileName << " first_index: " << first_index;
return true; // 是开放段 WAL 文件,返回 true
}
return false; // 不是 WAL 文件,返回 false
}
// Trash::CountChunkNumInCopyset 方法,用于计算副本集中的块数量
uint32_t Trash::CountChunkNumInCopyset(const std::string ©setPath) {
// 递归遍历副本集中的所有文件和目录,计算块数量
std::vector<std::string> files;
if (0 != localFileSystem_->List(copysetPath, &files)) { // 列出目录中的文件
LOG(ERROR) << "Trash failed to list files in " << copysetPath; // 记录错误日志
return 0; // 列出文件失败,返回 0
}
uint32_t chunkNum = 0; // 初始化块数量计数器
for (auto &file : files) { // 遍历文件
std::string filePath = copysetPath + "/" + file; // 构建文件路径
bool isDir = localFileSystem_->DirExists(filePath); // 检查是否是目录
if (!isDir) { // 如果是文件,则判断文件类型
// 有效文件类型包括:块文件、快照文件、WAL 文件
if (!(IsChunkOrSnapShotFile(file) || IsWALFile(file))) {
LOG(WARNING) << "Trash find a illegal file:" << file << " in " << copysetPath; // 记录警告日志
continue; // 跳过非法文件
}
++chunkNum; // 增加块数量计数器
} else { // 如果是目录,则递归调用自身
chunkNum += CountChunkNumInCopyset(filePath); // 递归计算块数量
}
}
return chunkNum; // 返回块数量
}
} // namespace chunkserver
} // namespace curve
Trash
类实现了块服务器中的回收站功能,它允许将不再需要的副本集移动到回收站,并定期检查和删除过期的文件。通过这些方法,Trash
类确保了块服务器能够有效地管理其存储空间,通过回收和删除不再需要的文件来释放空间。此外,它还提供了对回收站状态的查询和管理,如计算回收站中的块数量、判断文件是否属于回收站等。这些功能对于维护块服务器的健康和性能至关重要。
在op_request.cpp
文件中,定义了ChunkOpRequest
类,该类负责处理块服务器中的操作请求。以下是对ChunkOpRequest
类中函数的代码注释分析:
// op_request.cpp 文件包含了块操作请求处理的实现
// ChunkOpRequest 类定义,用于封装块操作请求的处理
namespace curve {
namespace chunkserver {
// ChunkOpRequest 构造函数
ChunkOpRequest::ChunkOpRequest() : datastore_(nullptr), node_(nullptr), cntl_(nullptr), request_(nullptr), response_(nullptr), done_(nullptr) {
// 初始化成员变量
}
// ChunkOpRequest 构造函数,用于创建块操作请求实例
ChunkOpRequest::ChunkOpRequest(std::shared_ptr<CopysetNode> nodePtr, RpcController *cntl, const ChunkRequest *request, ChunkResponse *response, ::google::protobuf::Closure *done) :
datastore_(nodePtr->GetDataStore()), node_(nodePtr), cntl_(dynamic_cast<brpc::Controller*>(cntl)), request_(request), response_(response), done_(done) {
// 初始化成员变量
}
// ChunkOpRequest::Process 方法,用于处理块操作请求
void ChunkOpRequest::Process() {
brpc::ClosureGuard doneGuard(done_); // 确保 done_ 在析构时被调用
if (0 == Propose(request_, cntl ? &cntl->request_attachment() : nullptr)) { // 提出请求
doneGuard.release(); // 如果请求成功提出,释放闭包守卫
}
}
// ChunkOpRequest::Propose 方法,用于提出块操作请求
int ChunkOpRequest::Propose(const ChunkRequest *request, const butil::IOBuf *data) {
// 检查当前节点是否是 leader
if (!node_->IsLeaderTerm()) { // 如果不是 leader,则重定向请求
RedirectChunkRequest(); // 重定向请求到其他节点
return -1; // 返回错误代码
}
// 打包操作请求为任务
braft::Task task; // 创建 braft 任务
butil::IOBuf log; // 创建日志缓冲区
if (0 != Encode(request, data, &log)) { // 编码请求为日志条目
LOG(ERROR) << "chunk op request encode failure"; // 记录错误日志
response_->set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_FAILURE_UNKNOWN); // 设置响应状态为未知失败
return -1; // 编码失败,返回错误代码
}
task.data = &log; // 设置任务数据
task.done = new ChunkClosure(shared_from_this()); // 设置任务完成回调
// 设置期望的任期,用于处理日志复现
task.expected_term = node_->LeaderTerm();
node_->Propose(task); // 提出任务到 raft
return 0; // 请求成功提出,返回 0
}
// ChunkOpRequest::RedirectChunkRequest 方法,用于重定向块操作请求
void ChunkOpRequest::RedirectChunkRequest() {
// 如果当前节点不是 leader,尝试获取 leader 的信息并重定向请求
PeerId leader = node_->GetLeaderId();
if (!leader.is_empty()) {
response_->set_redirect(leader.to_string()); // 设置重定向信息
}
response_->set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_REDIRECTED); // 设置响应状态为已重定向
}
// ChunkOpRequest::Encode 方法,用于编码块操作请求
int ChunkOpRequest::Encode(const ChunkRequest *request, const butil::IOBuf *data, butil::IOBuf *log) {
// 将请求长度、操作请求和数据追加到日志缓冲区
// ...
return 0; // 编码成功,返回 0
}
// ChunkOpRequest::Decode 方法,用于解码块操作请求
std::shared_ptr<ChunkOpRequest> ChunkOpRequest::Decode(butil::IOBuf log, ChunkRequest *request, butil::IOBuf *data, uint64_t index, PeerId leaderId) {
// 从日志中解码块操作请求
// ...
return nullptr; // 返回解码后的请求实例
}
// DeleteChunkRequest 类定义,用于处理删除块操作请求
class DeleteChunkRequest : public ChunkOpRequest {
public:
// OnApply 方法,用于在日志应用时处理删除块操作
void OnApply(uint64_t index, ::google::protobuf::Closure *done) {
// 确保 done_ 在析构时被调用
brpc::ClosureGuard doneGuard(done);
// 执行删除块操作
// ...
}
// OnApplyFromLog 方法,用于从日志中恢复删除块操作
void OnApplyFromLog(std::shared_ptr<CSDataStore> datastore, const ChunkRequest &request, const butil::IOBuf &data) {
// 从日志中恢复删除块操作
// ...
}
};
// ReadChunkRequest 类定义,用于处理读取块操作请求
class ReadChunkRequest : public ChunkOpRequest {
public:
// 构造函数
ReadChunkRequest(std::shared_ptr<CopysetNode> nodePtr, CloneManager* cloneMgr, RpcController *cntl, const ChunkRequest *request, ChunkResponse *response, ::google::protobuf::Closure *done) :
ChunkOpRequest(nodePtr, cntl, request, response, done), cloneMgr_(cloneMgr), concurrentApplyModule_(nodePtr->GetConcurrentApplyModule()), applyIndex(0) {
// 初始化成员变量
}
// Process 方法,用于处理读取块操作请求
void Process() {
// 确保 done_ 在析构时被调用
brpc::ClosureGuard doneGuard(done_);
// 根据当前节点是否是 leader 和请求是否携带已应用索引来决定如何处理请求
// ...
}
// OnApply 方法,用于在日志应用时处理读取块操作
void OnApply(uint64_t index, ::google::protobuf::Closure *done) {
// 确保 done_ 在析构时被调用
brpc::ClosureGuard doneGuard(done);
// 执行读取块操作
// ...
}
// OnApplyFromLog 方法,用于从日志中恢复读取块操作
void OnApplyFromLog(std::shared_ptr<CSDataStore> datastore, const ChunkRequest &request, const butil::IOBuf &data) {
// 从日志中恢复读取块操作
// ...
}
// NeedClone 方法,用于判断是否需要从克隆源读取数据
bool NeedClone(const CSChunkInfo &chunkInfo) {
// 判断是否需要克隆
// ...
}
// ReadChunk 方法,用于实际读取块数据
void ReadChunk() {
// 读取块数据
// ...
}
};
// WriteChunkRequest 类定义,用于处理写入块操作请求
class WriteChunkRequest : public ChunkOpRequest {
public:
// OnApply 方法,用于在日志应用时处理写入块操作
void OnApply(uint64_t index, ::google::protobuf::Closure *done) {
// 确保 done_ 在析构时被调用
brpc::ClosureGuard doneGuard(done);
// 执行写入块操作
// ...
}
// OnApplyFromLog 方法,用于从日志中恢复写入块操作
void OnApplyFromLog(std::shared_ptr<CSDataStore> datastore, const ChunkRequest &request, const butil::IOBuf &data) {
// 从日志中恢复写入块操作
// ...
}
};
// 其他请求处理类(如 ReadSnapshotRequest, DeleteSnapshotRequest 等)的实现省略...
} // namespace chunkserver
} // namespace curve
ChunkOpRequest
类及其子类(如DeleteChunkRequest
, ReadChunkRequest
, WriteChunkRequest
等)封装了块操作请求的处理逻辑。这些类负责将请求编码为日志条目,提出到raft进行一致性保证,并在日志应用时执行相应的操作。此外,它们还处理了请求的重定向和错误处理,确保了块服务器操作的一致性和可靠性。每个操作请求都有其特定的处理逻辑,例如读取、写入、删除块或快照等。这些类共同工作,为块服务器提供了一套完整的块操作请求处理机制。
在register.cpp
文件中,定义了Register
类,该类负责将块服务器注册到元数据服务器(MDS)。以下是对Register
类中函数的代码注释分析:
// register.cpp 文件包含了块服务器注册到元数据服务器(MDS)的实现
// Register 类定义,用于管理块服务器的注册流程
namespace curve {
namespace chunkserver {
// Register 构造函数,初始化注册选项
Register::Register(const RegisterOptions &ops) {
this->ops_ = ops; // 存储注册选项
// 解析元数据服务器的多个地址
curve::common::SplitString(ops.mdsListenAddr, ",", &mdsEps_);
// 验证每个地址的合法性
for (auto addr : mdsEps_) {
butil::EndPoint endpt;
if (butil::str2endpoint(addr.c_str(), &endpt) < 0) {
LOG(FATAL) << "Invalid sub mds ip:port provided: " << addr; // 记录致命错误日志
}
}
inServiceIndex_ = 0; // 设置当前服务索引
}
// Register::RegisterToMDS 方法,用于将块服务器注册到元数据服务器
int Register::RegisterToMDS(ChunkServerMetadata *metadata) {
// 创建元数据服务器的注册请求
curve::mds::topology::ChunkServerRegistRequest req;
curve::mds::topology::ChunkServerRegistResponse resp;
// 设置注册请求的参数
req.set_disktype(ops_.chunkserverDiskType); // 设置磁盘类型
req.set_diskpath(ops_.chunserverStoreUri); // 设置磁盘路径
req.set_hostip(ops_.chunkserverInternalIp); // 设置内部 IP 地址
if (ops_.enableExternalServer) { // 如果启用外部服务器
req.set_externalip(ops_.chunkserverExternalIp); // 设置外部 IP 地址
}
req.set_port(ops_.chunkserverPort); // 设置端口
LOG(INFO) << " Registering to MDS " << mdsEps_[inServiceIndex_] // 记录注册日志
<< "(internal ip: " << ops_.chunkserverInternalIp
<< ", port: " << ops_.chunkserverPort
<< ", enable external server: " << ops_.enableExternalServer
<< ", external ip: " << ops_.chunkserverExternalIp;
// 尝试注册到元数据服务器
int retries = ops_.registerRetries; // 获取重试次数
while (retries >= 0) {
brpc::Channel channel; // 创建 brpc 通道
brpc::Controller cntl; // 创建 brpc 控制器
cntl.set_timeout_ms(ops_.registerTimeout); // 设置超时时间
// 初始化 brpc 通道
if (channel.Init(mdsEps_[inServiceIndex_].c_str(), NULL) != 0) {
LOG(ERROR) << ops_.chunkserverInternalIp << ":" << ops_.chunkserverPort
<< " Fail to init channel to MDS " << mdsEps_[inServiceIndex_];
return -1; // 初始化失败,返回错误代码
}
// 创建元数据服务器的拓扑服务 stub
curve::mds::topology::TopologyService_Stub stub(&channel);
// 调用注册方法
stub.RegistChunkServer(&cntl, &req, &resp, NULL);
// 检查是否注册成功
if (!cntl.Failed() && resp.statuscode() == 0) {
break; // 注册成功,退出循环
} else { // 注册失败
LOG(ERROR) << ops_.chunkserverInternalIp << ":" << ops_.chunkserverPort
<< " Fail to register to MDS " << mdsEps_[inServiceIndex_]
<< ", cntl errorCode: " << cntl.ErrorCode() << ", "
<< " cntl error: " << cntl.ErrorText() << ", "
<< " going to sleep and try again."; // 记录错误日志
// 如果元数据服务器关闭或日志已关闭,则尝试切换到下一个服务器
if (cntl.ErrorCode() == EHOSTDOWN || cntl.ErrorCode() == brpc::ELOGOFF) {
inServiceIndex_ = (inServiceIndex_ + 1) % mdsEps_.size(); // 切换到下一个服务索引
}
sleep(1); // 等待一段时间后重试
--retries; // 减少重试次数
}
}
// 检查是否重试次数耗尽
if (retries <= 0) {
LOG(ERROR) << ops_.chunkserverInternalIp << ":" << ops_.chunkserverPort
<< " Fail to register to MDS for " << ops_.registerRetries << " times."; // 记录错误日志
return -1; // 重试次数耗尽,返回错误代码
}
// 设置元数据到 ChunkServerMetadata
metadata->set_version(CURRENT_METADATA_VERSION); // 设置版本号
metadata->set_id(resp.chunkserverid()); // 设置块服务器 ID
metadata->set_token(resp.token()); // 设置令牌
metadata->set_checksum(ChunkServerMetaHelper::MetadataCrc(*metadata)); // 设置校验和
LOG(INFO) << ops_.chunkserverInternalIp << ":" << ops_.chunkserverPort
<< " Successfully registered to MDS: " << mdsEps_[inServiceIndex_]
<< ", chunkserver id: " << metadata->id() << ", "
<< " token: " << metadata->token() << ", "
<< " persisting them to local storage."; // 记录成功注册日志
// 将元数据持久化到本地存储
if (PersistChunkServerMeta(*metadata) < 0) { // 持久化元数据
LOG(ERROR) << "Failed to persist chunkserver meta data"; // 记录错误日志
return -1; // 持久化失败,返回错误代码
}
return 0; // 注册成功,返回 0
}
// Register::PersistChunkServerMeta 方法,用于将块服务器元数据持久化到本地存储
int Register::PersistChunkServerMeta(const ChunkServerMetadata &metadata) {
// 将元数据编码为字符串
std::string metaStr;
if (!ChunkServerMetaHelper::EncodeChunkServerMeta(metadata, &metaStr)) { // 编码元数据
LOG(ERROR) << "Failed to encode chunkserver meta data."; // 记录错误日志
return -1; // 编码失败,返回错误代码
}
// 打开或创建元数据文件
int fd = ops_.fs->Open(metaFile.c_str(), O_RDWR | O_CREAT); // 打开文件
if (fd < 0) { // 打开文件失败
LOG(ERROR) << "Fail to open chunkserver metadata file for write"; // 记录错误日志
return -1; // 打开文件失败,返回错误代码
}
// 将元数据写入文件
if (ops_.fs->Write(fd, metaStr.c_str(), 0, metaStr.size()) < metaStr.size()) { // 写入文件
LOG(ERROR) << "Failed to write chunkserver metadata file"; // 记录错误日志
return -1; // 写入文件失败,返回错误代码
}
// 关闭文件描述符
if (ops_.fs->Close(fd)) { // 关闭文件
LOG(ERROR) << "Failed to close chunkserver metadata file"; // 记录错误日志
return -1; // 关闭文件失败,返回错误代码
}
return 0; // 持久化成功,返回 0
}
} // namespace chunkserver
} // namespace curve
Register
类负责将块服务器的信息注册到元数据服务器,以便元数据服务器能够管理和调度块服务器。RegisterToMDS
方法尝试将块服务器的元数据发送到元数据服务器,并在成功注册后将元数据持久化到本地存储。PersistChunkServerMeta
方法将元数据编码为字符串并写入到本地文件中,以确保块服务器的配置信息不会因为服务重启而丢失。这些机制确保了块服务器能够正确地集成到分布式文件系统中,并且能够在系统启动时恢复其状态。
在passive_getfn.cpp
文件中,定义了一系列函数,这些函数用于获取块服务器中各种状态信息,通常是用于监控和度量。以下是对passive_getfn.cpp
中函数的代码注释分析:
// passive_getfn.cpp 文件包含了被动获取函数的实现,用于监控和度量块服务器的状态
// GetChunkLeftFunc 函数,用于获取文件池中剩余的预分配块数量
uint32_t GetChunkLeftFunc(void* arg) {
FilePool* chunkFilePool = reinterpret_cast<FilePool*>(arg); // 将参数转换为 FilePool 指针
uint32_t chunkLeft = 0; // 初始化剩余块数量
if (chunkFilePool != nullptr) { // 如果文件池有效
FilePoolState poolState = chunkFilePool->GetState(); // 获取文件池状态
chunkLeft = poolState.preallocatedChunksLeft; // 获取剩余的预分配块数量
}
return chunkLeft; // 返回剩余块数量
}
// GetWalSegmentLeftFunc 函数,用于获取 WAL 文件池中剩余的预分配段数量
uint32_t GetWalSegmentLeftFunc(void* arg) {
FilePool* walFilePool = reinterpret_cast<FilePool*>(arg); // 将参数转换为 FilePool 指针
uint32_t segmentLeft = 0; // 初始化剩余段数量
if (walFilePool != nullptr) { // 如果文件池有效
FilePoolState poolState = walFilePool->GetState(); // 获取文件池状态
segmentLeft = poolState.preallocatedChunksLeft; // 获取剩余的预分配段数量
}
return segmentLeft; // 返回剩余段数量
}
// GetDatastoreChunkCountFunc 函数,用于获取数据存储中的块文件数量
uint32_t GetDatastoreChunkCountFunc(void* arg) {
CSDataStore* dataStore = reinterpret_cast<CSDataStore*>(arg); // 将参数转换为 CSDataStore 指针
uint32_t chunkCount = 0; // 初始化块文件数量
if (dataStore != nullptr) { // 如果数据存储有效
DataStoreStatus status = dataStore->GetStatus(); // 获取数据存储状态
chunkCount = status.chunkFileCount; // 获取块文件数量
}
return chunkCount; // 返回块文件数量
}
// GetLogStorageWalSegmentCountFunc 函数,用于获取日志存储中的 WAL 段文件数量
uint32_t GetLogStorageWalSegmentCountFunc(void* arg) {
CurveSegmentLogStorage* logStorage = reinterpret_cast<CurveSegmentLogStorage*>(arg); // 将参数转换为 CurveSegmentLogStorage 指针
uint32_t walSegmentCount = 0; // 初始化 WAL 段文件数量
if (nullptr != logStorage) { // 如果日志存储有效
walSegmentCount = logStorage->GetStatus().walSegmentFileCount; // 获取 WAL 段文件数量
}
return walSegmentCount; // 返回 WAL 段文件数量
}
// GetDatastoreSnapshotCountFunc 函数,用于获取数据存储中的快照数量
uint32_t GetDatastoreSnapshotCountFunc(void* arg) {
CSDataStore* dataStore = reinterpret_cast<CSDataStore*>(arg); // 将参数转换为 CSDataStore 指针
uint32_t snapshotCount = 0; // 初始化快照数量
if (dataStore != nullptr) { // 如果数据存储有效
DataStoreStatus status = dataStore->GetStatus(); // 获取数据存储状态
snapshotCount = status.snapshotCount; // 获取快照数量
}
return snapshotCount; // 返回快照数量
}
// GetDatastoreCloneChunkCountFunc 函数,用于获取数据存储中的克隆块数量
uint32_t GetDatastoreCloneChunkCountFunc(void* arg) {
CSDataStore* dataStore = reinterpret_cast<CSDataStore*>(arg); // 将参数转换为 CSDataStore 指针
uint32_t cloneChunkCount = 0; // 初始化克隆块数量
if (dataStore != nullptr) { // 如果数据存储有效
DataStoreStatus status = dataStore->GetStatus(); // 获取数据存储状态
cloneChunkCount = status.cloneChunkCount; // 获取克隆块数量
}
return cloneChunkCount; // 返回克隆块数量
}
// GetChunkTrashedFunc 函数,用于获取被移动到垃圾箱的块数量
uint32_t GetChunkTrashedFunc(void* arg) {
Trash* trash = reinterpret_cast<Trash*>(arg); // 将参数转换为 Trash 指针
uint32_t chunkTrashed = 0; // 初始化被移动到垃圾箱的块数量
if (trash != nullptr) { // 如果垃圾箱有效
chunkTrashed = trash->GetChunkNum(); // 获取被移动到垃圾箱的块数量
}
return chunkTrashed; // 返回被移动到垃圾箱的块数量
}
// GetTotalChunkCountFunc 函数,用于获取块服务器中所有副本集的块总数
uint32_t GetTotalChunkCountFunc(void* arg) {
ChunkServerMetric* csMetric = reinterpret_cast<ChunkServerMetric*>(arg); // 将参数转换为 ChunkServerMetric 指针
uint32_t chunkCount = 0; // 初始化块总数
auto copysetMetricMap = csMetric->GetCopysetMetricMap()->GetMap(); // 获取副本集度量映射
for (auto metricPair : copysetMetricMap) { // 遍历所有副本集度量
chunkCount += metricPair.second->GetChunkCount(); // 累加每个副本集的块数量
}
return chunkCount; // 返回块总数
}
// GetTotalWalSegmentCountFunc 函数,用于获取块服务器中所有副本集的 WAL 段总数
uint32_t GetTotalWalSegmentCountFunc(void* arg) {
ChunkServerMetric* csMetric = reinterpret_cast<ChunkServerMetric*>(arg); // 将参数转换为 ChunkServerMetric 指针
uint32_t walSegmentCount = 0; // 初始化 WAL 段总数
auto copysetMetricMap = csMetric->GetCopysetMetricMap()->GetMap(); // 获取副本集度量映射
for (auto metricPair : copysetMetricMap) { // 遍历所有副本集度量
walSegmentCount += metricPair.second->GetWalSegmentCount(); // 累加每个副本集的 WAL 段数量
}
return walSegmentCount; // 返回 WAL 段总数
}
// GetTotalSnapshotCountFunc 函数,用于获取块服务器中所有副本集的快照总数
uint32_t GetTotalSnapshotCountFunc(void* arg) {
ChunkServerMetric* csMetric = reinterpret_cast<ChunkServerMetric*>(arg); // 将参数转换为 ChunkServerMetric 指针
uint32_t snapshotCount = 0; // 初始化快照总数
auto copysetMetricMap = csMetric->GetCopysetMetricMap()->GetMap(); // 获取副本集度量映射
for (auto metricPair : copysetMetricMap) { // 遍历所有副本集度量
snapshotCount += metricPair.second->GetSnapshotCount(); // 累加每个副本集的快照数量
}
return snapshotCount; // 返回快照总数
}
// GetTotalCloneChunkCountFunc 函数,用于获取块服务器中所有副本集的克隆块总数
uint32_t GetTotalCloneChunkCountFunc(void* arg) {
ChunkServerMetric* csMetric = reinterpret_cast<ChunkServerMetric*>(arg); // 将参数转换为 ChunkServerMetric 指针
uint32_t cloneChunkCount = 0; // 初始化克隆块总数
auto copysetMetricMap = csMetric->GetCopysetMetricMap()->GetMap(); // 获取副本集度量映射
for (auto metricPair : copysetMetricMap) { // 遍历所有副本集度量
cloneChunkCount += metricPair.second->GetCloneChunkCount(); // 累加每个副本集的克隆块数量
}
return cloneChunkCount; // 返回克隆块总数
}
这些函数通常被设计为无状态的,并且不修改任何内部状态,使它们适合作为被动的监控和度量函数。这些函数通过访问块服务器内部的状态和度量信息,为外部监控系统提供了获取块服务器状态的能力。例如,它们可以用来报告文件池中剩余的预分配块数量、数据存储中的块文件数量、快照数量、克隆块数量等。这些信息对于理解块服务器的健康状况和性能至关重要。
在scan_service.cpp
文件中,定义了ScanServiceImpl
类,该类提供了扫描服务的实现。以下是对ScanServiceImpl
类中函数的代码注释分析:
// scan_service.cpp 文件包含了扫描服务实现的定义
// ScanServiceImpl 类定义,用于处理扫描请求
namespace curve {
namespace chunkserver {
// FollowScanMap 方法,用于处理跟随者节点的扫描映射请求
void ScanServiceImpl::FollowScanMap(RpcController *controller,
const FollowScanMapRequest *request,
FollowScanMapResponse *response,
Closure *done) {
// 调用 scanManager_ 的 DealFollowerScanMap 方法来处理请求
scanManager_->DealFollowerScanMap(*request, response); // 处理跟随者扫描映射
}
} // namespace chunkserver
} // namespace curve
ScanServiceImpl
类提供了一个方法FollowScanMap
,它接收一个FollowScanMapRequest
请求,并生成一个FollowScanMapResponse
响应。这个方法主要通过调用scanManager_
的DealFollowerScanMap
方法来处理请求。DealFollowerScanMap
方法的实现细节在scan_manager.cpp
中,它负责处理来自跟随者节点的扫描映射,并根据映射更新本地的扫描状态。
ScanServiceImpl
类的实现相对简单,它主要作为ScanManager
的一个接口,允许外部通过RPC调用来与扫描管理器交互。这种方法的实现遵循了良好的模块化设计原则,将扫描处理的逻辑封装在ScanManager
中,而ScanServiceImpl
则作为对外的接口。这样的设计使得扫描服务的管理逻辑与外部调用分离,便于维护和扩展。