curve源码分析 chunkserver -----3

源码路径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 &copysetDir) {
    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 &copysetPath, 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 &copysetPath) {
    // 递归遍历副本集中的所有文件和目录,计算块数量
    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则作为对外的接口。这样的设计使得扫描服务的管理逻辑与外部调用分离,便于维护和扩展。

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值