源码路径curve-release2.2\src\chunkserver
1. `braft_cli_service.cpp`
2. `clone_copyer.cpp`
3. `chunk_closure.cpp`
4. `chunkserver_metrics.cpp`
5. `conf_epoch_file.cpp`
6. `copyset_node_manager.cpp`
7. `heartbeat.cpp`
8. `scan_manager.cpp`
根据提供的文本内容,braft_cli_service.cpp
文件中包含了 BRaftCliServiceImpl
类的实现。这个类是 braft
客户端服务的实现,提供了一系列的接口来处理与 braft
节点操作相关的请求。下面是对 BRaftCliServiceImpl
类中可能存在的函数和方法的代码注释分析:
// braft_cli_service.cpp
// 命名空间声明
namespace curve {
namespace chunkserver {
// BRaftCliServiceImpl 类的命名空间声明
namespace braft {
// add_peer 操作的实现
void BRaftCliServiceImpl::add_peer(RpcController *controller, const AddPeerRequest *request, AddPeerResponse *response, Closure *done) {
// 验证请求参数的有效性
// 获取当前节点信息和要添加的节点信息
// 调用 braft 节点管理器添加节点
// 处理响应和错误
}
// remove_peer 操作的实现
void BRaftCliServiceImpl::remove_peer(RpcController *controller, const RemovePeerRequest *request, RemovePeerResponse *response, Closure *done) {
// 验证请求参数的有效性
// 获取当前节点信息和要移除的节点信息
// 调用 braft 节点管理器移除节点
// 处理响应和错误
}
// get_leader 操作的实现
void BRaftCliServiceImpl::get_leader(RpcController *controller, const GetLeaderRequest *request, GetLeaderResponse *response, Closure *done) {
// 验证请求参数的有效性
// 获取当前节点信息
// 调用 braft 节点管理器查询领导者
// 处理响应和错误
}
// transfer_leader 操作的实现
void BRaftCliServiceImpl::transfer_leader(RpcController *controller, const TransferLeaderRequest *request, TransferLeaderResponse *response, Closure *done) {
// 验证请求参数的有效性
// 获取当前节点信息和要转移的领导者节点信息
// 调用 braft 节点管理器转移领导者
// 处理响应和错误
}
// snapshot 操作的实现
void BRaftCliServiceImpl::snapshot(RpcController *controller, const SnapshotRequest *request, SnapshotResponse *response, Closure *done) {
// 验证请求参数的有效性
// 获取当前节点信息
// 调用 braft 节点管理器执行快照操作
// 处理响应和错误
}
} // namespace braft
} // namespace chunkserver
} // namespace curve
请注意,以上代码是基于提供的文本内容和一般的编程实践假设的 BRaftCliServiceImpl
类的方法实现。实际的 braft_cli_service.cpp
文件中的实现可能会有所不同,并且可能会包含更多的错误处理、日志记录和资源管理的逻辑。此外,BRaftCliServiceImpl
类可能还包含其他与 braft
客户端操作相关的特定方法。在实际的实现中,这些方法的具体实现将取决于系统的设计和需求。
根据提供的文本内容,clone_copyer.cpp
文件中包含了 CloneCopyer
类的实现。这个类负责处理与数据克隆相关的复制操作,例如从源端复制数据到目标端。下面是对 CloneCopyer
类中可能存在的函数和方法的代码注释分析:
// clone_copyer.cpp
// 命名空间声明
namespace curve {
namespace chunkserver {
// CloneCopyer 类的构造函数
CloneCopyer::CloneCopyer() {
// 初始化成员变量
// 初始化复制器状态
// ...
}
// CloneCopyer 类的析构函数
CloneCopyer::~CloneCopyer() {
// 清理资源,如关闭文件句柄、释放内存等
// ...
}
// 初始化 CloneCopyer 的配置
int CloneCopyer::Init(const CopyerOptions &options) {
// 根据提供的选项初始化 CloneCopyer
// 设置曲线文件的超时时间
// 初始化曲线客户端和S3客户端(如果需要)
// 返回初始化的结果
}
// 开始执行复制操作
int CloneCopyer::Start() {
// 启动复制线程或定时任务
// 开始监控和处理复制任务
// 返回启动的结果
}
// 停止复制操作
int CloneCopyer::Stop() {
// 停止所有复制任务
// 关闭所有打开的连接和文件句柄
// 清理资源
// 返回停止操作的结果
}
// 处理复制任务的下载部分
void CloneCopyer::DownloadAsync(DownloadClosure *done) {
// 异步下载数据
// 根据提供的下载上下文执行下载操作
// 处理下载结果,包括成功或失败的情况
}
// 从曲线文件系统下载数据
void CloneCopyer::DownloadFromCurve(const std::string &fileName, off_t offset, size_t size, char *buf, DownloadClosure *done) {
// 从曲线文件系统下载指定的数据块
// 打开曲线文件,读取指定的偏移和大小的数据
// 处理读取结果,包括成功或失败的情况
}
// 从S3下载数据
void CloneCopyer::DownloadFromS3(const std::string &objectName, off_t offset, size_t size, char *buf, DownloadClosure *done) {
// 从S3下载指定的数据块
// 使用S3客户端读取指定的偏移和大小的数据
// 处理读取结果,包括成功或失败的情况
}
// 清理过期的曲线文件缓存
void CloneCopyer::DeleteExpiredCurveCache(void *arg) {
// 定期检查并清理过期的曲线文件缓存
// 确保曲线文件的打开时间不超过预设的超时时间
// 维护曲线文件打开时间的列表
}
} // namespace chunkserver
} // namespace curve
请注意,以上代码是基于提供的文本内容和一般的编程实践假设的 CloneCopyer
类的方法实现。实际的 clone_copyer.cpp
文件中的实现可能会有所不同,并且可能会包含更多的错误处理、日志记录和资源管理的逻辑。此外,CloneCopyer
类可能还包含其他与数据复制相关的特定方法。在实际的实现中,这些方法的具体实现将取决于系统的设计和需求。
根据提供的文本内容,chunk_closure.cpp
文件中包含了 ChunkClosure
类的实现。这个类是 brpc
框架中的一个闭包(Closure),用于处理块服务器中与数据块操作相关的请求完成回调。下面是对 ChunkClosure
类中可能存在的函数和方法的代码注释分析:
// chunk_closure.cpp
// 命名空间声明
namespace curve {
namespace chunkserver {
// ChunkClosure 类的构造函数
ChunkClosure::ChunkClosure(ChunkRequest *request, ChunkResponse *response, google::protobuf::Closure *done)
: request_(request),
response_(response),
done_(done) {
// 初始化请求、响应和闭包对象
// ...
}
// ChunkClosure 类的析构函数
ChunkClosure::~ChunkClosure() {
// 清理资源,如果存在闭包对象,则释放它
// ...
}
// 运行闭包的函数
void ChunkClosure::Run() {
// 在闭包运行结束之后,自动析构自己,避免析构函数漏调
std::unique_ptr<ChunkClosure> selfGuard(this);
// 确保 brpc 闭包被调用
brpc::ClosureGuard doneGuard(done_);
// 根据请求的类型执行相应的操作
// 例如,处理响应、更新监控指标等
// ...
// 释放资源,如关闭文件句柄等
CleanUp();
}
// 清理资源的函数
void ChunkClosure::CleanUp() {
// 清理请求和响应对象
// 关闭任何打开的文件或网络连接
// 清理任何分配的临时缓冲区
// ...
}
// 处理请求完成的函数
void ChunkClosure::OnDone() {
// 处理请求完成后的逻辑
// 例如,更新应用索引、记录监控指标等
// ...
}
// 处理请求失败的函数
void ChunkClosure::OnFailed() {
// 处理请求失败的逻辑
// 例如,记录错误日志、更新监控指标等
// ...
}
} // namespace chunkserver
} // namespace curve
请注意,以上代码是基于提供的文本内容和一般的编程实践假设的 ChunkClosure
类的方法实现。实际的 chunk_closure.cpp
文件中的实现可能会有所不同,并且可能会包含更多的错误处理、日志记录和资源管理的逻辑。此外,ChunkClosure
类可能还包含其他与块服务器请求处理相关的特定方法。在实际的实现中,这些方法的具体实现将取决于系统的设计和需求。
在chunkserver_metrics.cpp
文件中,定义了多个与curve
命名空间下的chunkserver
子命名空间相关的类和函数,这些类和函数主要负责处理和监控块服务器的度量信息。以下是对这些函数的代码注释分析:
// chunkserver_metrics.cpp 文件包含了块服务器度量信息的实现
// IOMetric 类定义,用于跟踪 I/O 操作的度量信息
class IOMetric {
public:
// 构造函数,初始化各种度量指标
IOMetric() : rps_(&reqNum_, 1), iops_(&ioNum_, 1), eps_(&errorNum_, 1), bps_(&ioBytes_, 1) {
}
// 初始化度量指标,将它们暴露给外部监控系统
int Init(const std::string &prefix);
// 在请求处理开始时调用
void OnRequest();
// 在响应发送后调用,更新 I/O 操作数、字节数和错误数
void OnResponse(size_t size, int64_t latUs, bool hasError);
private:
// 各种度量指标的实现,包括请求数、I/O 操作数、错误数、I/O 字节数等
Counter reqNum_;
Counter ioNum_;
Counter errorNum_;
Counter ioBytes_;
Histogram latencyRecorder_;
Histogram sizeRecorder_;
Rate rps_;
Rate iops_;
Rate bps_;
Rate eps_;
};
// CSIOMetric 类定义,封装了对不同类型 I/O 操作的度量
class CSIOMetric {
public:
// 初始化所有 I/O 操作类型的度量指标
int Init(const std::string &prefix);
// 在请求处理开始时调用
void OnRequest(CSIOMetricType type);
// 在响应发送后调用
void OnResponse(CSIOMetricType type, size_t size, int64_t latUs, bool hasError);
// 获取特定类型的 I/O 度量指标
IOMetricPtr GetIOMetric(CSIOMetricType type);
private:
// 各种类型的 I/O 度量指标
std::shared_ptr<IOMetric> readMetric_;
std::shared_ptr<IOMetric> writeMetric_;
std::shared_ptr<IOMetric> recoverMetric_;
std::shared_ptr<IOMetric> pasteMetric_;
std::shared_ptr<IOMetric> downloadMetric_;
};
// CSCopysetMetric 类定义,用于跟踪特定副本集的度量信息
class CSCopysetMetric {
public:
// 初始化副本集的度量指标
int Init(const LogicPoolID &logicPoolId, const CopysetID ©setId);
// 监控数据存储的状态
void MonitorDataStore(CSDataStore *datastore);
// 监控日志存储的状态
void MonitorCurveSegmentLogStorage(CurveSegmentLogStorage *logStorage);
private:
// 各种度量指标的实现,包括块数、快照数、克隆块数等
std::shared_ptr<PassiveStatus<uint32_t>> chunkCount_;
std::shared_ptr<PassiveStatus<uint32_t>> snapshotCount_;
std::shared_ptr<PassiveStatus<uint32_t>> cloneChunkCount_;
std::shared_ptr<PassiveStatus<uint32_t>> walSegmentCount_;
};
// ChunkServerMetric 类定义,用于跟踪整个块服务器的度量信息
class ChunkServerMetric {
public:
// 获取块服务器度量信息的单例实例
static ChunkServerMetric* GetInstance();
// 初始化块服务器的度量指标
int Init(const ChunkServerMetricOptions &option);
// 清理并释放度量指标资源
int Fini();
// 创建特定副本集的度量指标
int CreateCopysetMetric(const LogicPoolID &logicPoolId, const CopysetID ©setId);
// 获取特定副本集的度量指标
CopysetMetricPtr GetCopysetMetric(const LogicPoolID &logicPoolId, const CopysetID ©setId);
// 移除特定副本集的度量指标
int RemoveCopysetMetric(const LogicPoolID &logicPoolId, const CopysetID ©setId);
// 在请求处理开始时调用
void OnRequest(const LogicPoolID &logicPoolId, const CopysetID ©setId, CSIOMetricType type);
// 在响应发送后调用
void OnResponse(const LogicPoolID &logicPoolId, const CopysetID ©setId, CSIOMetricType type, size_t size, int64_t latUs, bool hasError);
// 监控块文件池的状态
void MonitorChunkFilePool(FilePool *chunkFilePool);
// 监控 WAL 文件池的状态
void MonitorWalFilePool(FilePool *walFilePool);
// 监控垃圾回收站的状态
void MonitorTrash(Trash *trash);
// 增加领导者计数
void IncreaseLeaderCount();
// 减少领导者计数
void DecreaseLeaderCount();
// 将配置度量指标暴露给外部系统
void ExposeConfigMetric(common::Configuration *conf);
private:
// 各种度量指标的实现,包括领导者计数、块数、快照数、克隆块数等
std::shared_ptr<Adder<uint32_t>> leaderCount_;
std::shared_ptr<PassiveStatus<uint32_t>> chunkLeft_;
std::shared_ptr<PassiveStatus<uint32_t>> walSegmentLeft_;
std::shared_ptr<PassiveStatus<uint32_t>> chunkTrashed_;
std::shared_ptr<PassiveStatus<uint32_t>> chunkCount_;
std::shared_ptr<PassiveStatus<uint32_t>> snapshotCount_;
std::shared_ptr<PassiveStatus<uint32_t>> cloneChunkCount_;
std::shared_ptr<PassiveStatus<uint32_t>> walSegmentCount_;
// 用于存储特定副本集的度量指标映射
CopysetMetricMap copysetMetric Map_;
bool hasInited_;
// 块服务器度量信息的单例实例
static ChunkServerMetric* self_;
};
// 辅助函数,用于获取特定资源的当前状态
uint32_t GetChunkLeftFunc(void* arg);
uint32_t GetWalSegmentLeftFunc(void* arg);
uint32_t GetDatastoreChunkCountFunc(void* arg);
uint32_t GetLogStorageWalSegmentCountFunc(void* arg);
uint32_t GetDatastoreSnapshotCountFunc(void* arg);
uint32_t GetDatastoreCloneChunkCountFunc(void* arg);
uint32_t GetChunkTrashedFunc(void* arg);
uint32_t GetTotalChunkCountFunc(void* arg);
uint32_t GetTotalWalSegmentCountFunc(void* arg);
uint32_t GetTotalSnapshotCountFunc(void* arg);
uint32_t GetTotalCloneChunkCountFunc(void* arg);
这些类和函数共同构成了块服务器的度量和监控系统,它们可以提供关于块服务器性能和状态的重要信息,帮助管理员和开发者了解系统的运行情况,并进行相应的优化和调整。通过将度量信息暴露给外部监控系统,可以实现对块服务器的实时监控和报警。
在conf_epoch_file.cpp
文件中,定义了ConfEpochFile
类,该类封装了操作配置时代文件(conf.epoch)的逻辑。下面是对ConfEpochFile
类中函数的代码注释分析:
// ConfEpochFile 类定义,用于处理 conf.epoch 文件的加载和保存
// 定义 conf.epoch 文件的最大长度
const uint32_t kConfEpochFileMaxSize = 4096;
// 定义 conf.epoch 文件的魔数,用于验证文件的合法性
const uint64_t kConfEpochFileMagic = 0x6225929368674119;
// ConfEpochFile::Load 方法,用于从指定路径加载 conf.epoch 文件
int ConfEpochFile::Load(const std::string &path, LogicPoolID *logicPoolID, CopysetID *copysetID, uint64_t *epoch) {
// 打开指定路径的文件
int fd = fs_->Open(path.c_str(), O_RDWR);
if (0 > fd) {
// 如果打开文件失败,记录错误日志并返回错误代码
LOG(ERROR) << "LoadConfEpoch failed open file " << path
<< ", errno: " << errno << ", error message: " << strerror(errno);
return -1;
}
// 分配一个缓冲区用于存储文件内容
char json[kConfEpochFileMaxSize] = {0};
int size = 0;
// 读取文件内容到缓冲区
size = fs_->Read(fd, json, 0, kConfEpochFileMaxSize);
if (size <= 0) {
// 如果读取失败,记录错误日志并返回错误代码
LOG(ERROR) << "LoadConfEpoch read failed: " << path;
fs_->Close(fd);
return -1;
}
fs_->Close(fd); // 关闭文件描述符
// 反序列化 JSON 字符串到 ConfEpoch 消息
ConfEpoch confEpoch;
std::string jsonStr(json);
std::string err;
json2pb::Json2PbOptions opt;
opt.base64_to_bytes = true;
if (!json2pb::JsonToProtoMessage(jsonStr, &confEpoch, opt, &err)) {
// 如果反序列化失败,记录错误日志并返回错误代码
LOG(ERROR) << "Failed to decode conf epoch : " << jsonStr
<< ", error: " << err.c_str();
return -1;
}
// 验证 CRC32 校验和
uint32_t crc32c = ConfEpochCrc(confEpoch);
if (crc32c != confEpoch.checksum()) {
// 如果校验和不匹配,记录错误日志并返回错误代码
LOG(ERROR) << "conf epoch crc error: " << jsonStr;
return -1;
}
// 将解析出的数据赋值给输出参数
*logicPoolID = confEpoch.logicpoolid();
*copysetID = confEpoch.copysetid();
*epoch = confEpoch.epoch();
// 输出加载成功的日志信息
LOG(INFO) << "Load conf epoch " << path << " success."
<< "logicPoolID: " << *logicPoolID
<< ", copysetID: " << *copysetID
<< ", epoch: " << *epoch;
return 0; // 成功返回 0
}
// ConfEpochFile::Save 方法,用于保存 conf.epoch 文件到指定路径
int ConfEpochFile::Save(const std::string &path, const LogicPoolID logicPoolID, const CopysetID copysetID, const uint64_t epoch) {
// 创建 ConfEpoch 消息并填充数据
ConfEpoch confEpoch;
confEpoch.set_logicpoolid(logicPoolID);
confEpoch.set_copysetid(copysetID);
confEpoch.set_epoch(epoch);
// 计算 CRC32 校验和并添加到消息中
uint32_t crc32c = ConfEpochCrc(confEpoch);
confEpoch.set_checksum(crc32c);
// 将 ConfEpoch 消息序列化为 JSON 字符串
std::string out;
std::string err;
json2pb::Pb2JsonOptions opt;
opt.bytes_to_base64 = true;
opt.enum_option = json2pb::OUTPUT_ENUM_BY_NUMBER;
if (!json2pb::ProtoMessageToJson(confEpoch, &out, opt, &err)) {
// 如果序列化失败,记录错误日志并返回错误代码
LOG(ERROR) << "Failed to encode conf epoch, error: " << err;
return -1;
}
// 打开文件用于写入
int fd = fs_->Open(path.c_str(), O_RDWR | O_CREAT);
if (0 > fd) {
// 如果打开文件失败,记录错误日志并返回错误代码
LOG(ERROR) << "LoadConfEpoch failed open file " << path
<< ", errno: " << errno << ", error message: " << strerror(errno);
return -1;
}
// 将 JSON 字符串写入文件
if (out.size() != fs_->Write(fd, out.c_str(), 0, out.size())) {
// 如果写入失败,记录错误日志并返回错误代码
LOG(ERROR) << "SaveConfEpoch write failed, path: " << path
<< ", errno: " << errno << ", error message: " << strerror(errno);
fs_->Close(fd);
return -1;
}
// 确保写入的数据被同步到磁盘
if (0 != fs_->Fsync(fd)) {
// 如果同步失败,记录错误日志并返回错误代码
LOG(ERROR) << "SaveConfEpoch sync failed, path: " << path
<< ", errno: " << errno << ", error message: " << strerror(errno);
fs_->Close(fd);
return -1;
}
fs_->Close(fd); // 关闭文件描述符
return 0; // 成功返回 0
}
// ConfEpochFile::ConfEpochCrc 方法,用于计算 ConfEpoch 消息的 CRC32 校验和
uint32_t ConfEpochFile::ConfEpochCrc(const ConfEpoch &confEpoch) {
// 初始化 CRC32 校验和
uint32_t crc32c = 0;
// 计算并添加逻辑池 ID 的校验和
crc32c = curve::common::CRC32(crc32c,
reinterpret_cast<char *>(&confEpoch.logicpoolid()),
sizeof(confEpoch.logicpoolid()));
// 计算并添加副本集 ID 的校验和
crc32c = curve::common::CRC32(crc32c,
reinterpret_cast<char *>(&confEpoch.copysetid()),
sizeof(confEpoch.copysetid()));
// 计算并添加时代值的校验和
crc32c = curve::common::CRC32(crc32c,
reinterpret_cast<char *>(&confEpoch.epoch()),
sizeof(confEpoch.epoch()));
// 计算并添加魔数的校验和
crc32c = curve::common::CRC32(crc32c,
reinterpret_cast<char *>(&kConfEpochFileMagic),
sizeof(kConfEpochFileMagic));
// 返回计算出的 CRC32 校验和
return crc32c;
}
ConfEpochFile
类提供了加载和保存配置时代文件的功能。配置时代文件通常用于记录某个副本集的配置信息和版本号(时代值),以便在恢复或迁移数据时能够确保配置的一致性。通过计算和验证 CRC32 校验和,可以确保文件内容的完整性和正确性。这个类使用 JSON 格式来序列化和反序列化配置时代信息,使得配置信息易于阅读和处理。
在copyset_node_manager.cpp
文件中,定义了CopysetNodeManager
类,该类负责管理副本集节点的生命周期,包括创建、加载、删除等操作。以下是对CopysetNodeManager
类中函数的代码注释分析:
// copyset_node_manager.cpp 文件包含了副本集节点管理器的实现
// CopysetNodeManager 类定义,用于管理副本集节点的创建、加载、删除等操作
namespace curve {
namespace chunkserver {
// 初始化副本集节点管理器
int CopysetNodeManager::Init(const CopysetNodeOptions ©setNodeOptions) {
// 存储副本集节点选项
copysetNodeOptions_ = copysetNodeOptions;
// 如果设置了加载并发数,则创建并启动线程池
if (copysetNodeOptions_.loadConcurrency > 0) {
copysetLoader_ = std::make_shared<TaskThreadPool<>>(); // 创建线程池
}
return 0; // 成功返回 0
}
// 运行副本集节点管理器
int CopysetNodeManager::Run() {
// 确保管理器只运行一次
if (running_.exchange(true, std::memory_order_acq_rel)) {
return 0; // 如果已经在运行,则直接返回
}
// 启动线程池
int ret = 0;
if (copysetLoader_ != nullptr) {
ret = copysetLoader_->Start(copysetNodeOptions_.loadConcurrency); // 启动线程池
if (ret < 0) {
LOG(ERROR) << "CopysetLoadThrottle start error. ThreadNum: "
<< copysetNodeOptions_.loadConcurrency;
return -1; // 启动失败,返回错误代码
}
}
// 加载已有的副本集
ret = ReloadCopysets(); // 重新加载副本集
if (ret == 0) {
loadFinished_.exchange(true, std::memory_order_acq_rel); // 标记为加载完成
LOG(INFO) << "Reload copysets success."; // 输出加载成功的日志
}
return ret; // 返回操作结果
}
// 停止副本集节点管理器
int CopysetNodeManager::Fini() {
// 确保管理器不在运行中
if (!running_.exchange(false, std::memory_order_acq_rel)) {
return 0; // 如果没有在运行,则直接返回
}
loadFinished_.exchange(false, std::memory_order_acq_rel); // 标记为未加载完成
// 停止线程池
if (copysetLoader_ != nullptr) {
copysetLoader_->Stop(); // 停止线程池
copysetLoader_ = nullptr; // 释放线程池资源
}
// 遍历所有副本集节点并停止它们
{
ReadLockGuard readLockGuard(rwLock_); // 加读锁
for (auto ©setNode : copysetNodeMap_) {
copysetNode.second->Fini(); // 停止副本集节点
}
}
// 清除所有副本集节点
{
WriteLockGuard writeLockGuard(rwLock_); // 加写锁
copysetNodeMap_.clear(); // 清空副本集节点映射
}
return 0; // 成功返回 0
}
// 重新加载所有副本集
int CopysetNodeManager::ReloadCopysets() {
// 获取数据目录
std::string datadir = curve::common::UriParser::GetPathFromUri(copysetNodeOptions_.chunkDataUri);
if (!copysetNodeOptions_.localFileSystem->DirExists(datadir)) {
LOG(INFO) << datadir << " not exist. copysets was never created";
return 0; // 如果目录不存在,表示没有副本集需要加载
}
// 获取目录下的所有项目
vector<std::string> items;
if (copysetNodeOptions_.localFileSystem->List(datadir, &items) != 0) {
LOG(ERROR) << "Failed to get copyset list from data directory " << datadir;
return -1; // 列出目录失败,返回错误代码
}
// 遍历所有项目并加载副本集
for (const std::string &item : items) {
uint64_t groupId; // 用于存储解析出的 ID
if (!curve::common::StringToUll(item, &groupId)) {
LOG(ERROR) << "parse " << item << " to graoupId err";
return -1; // 解析 ID 失败,返回错误代码
}
// 解析逻辑池 ID 和副本集 ID
uint64_t poolId = GetPoolID(groupId);
uint64_t copysetId = GetCopysetID(groupId);
LOG(INFO) << "Parsed groupid " << groupId
<< " as " << ToGroupIdString(poolId, copysetId);
// 根据是否有线程池来决定如何加载副本集
if (copysetLoader_ == nullptr) {
LoadCopyset(poolId, copysetId, false); // 无线程池时直接加载
} else {
copysetLoader_->Enqueue(std::bind(&CopysetNodeManager::LoadCopyset,
this,
poolId,
copysetId,
true)); // 有线程池时加入队列
}
}
// 如果使用了线程池,等待所有副本集加载完成
if (copysetLoader_ != nullptr) {
while (copysetLoader_->QueueSize() != 0) {
::sleep(1); // 简单等待
}
copysetLoader_->Stop(); // 停止线程池
copysetLoader_ = nullptr; // 释放资源
}
return 0; // 成功返回 0
}
// 加载副本集节点
void CopysetNodeManager::LoadCopyset(const LogicPoolID &logicPoolId, const CopysetID ©setId, bool needCheckLoadFinished) {
// 获取当前时间
uint64_t beginTime = TimeUtility::GetTimeofDayMs();
LOG(INFO) << "Begin to load copyset " << ToGroupIdString(logicPoolId, copysetId)
<< ". check load finished? : " << (needCheckLoadFinished ? "Yes." : "No.");
// 创建副本集节点
Configuration conf;
std::shared_ptr<CopysetNode> copysetNode = CreateCopysetNodeUnlocked(logicPoolId, copysetId, conf);
if (copysetNode == nullptr) {
LOG(ERROR) << "Failed to create copyset " << ToGroupIdString(logicPoolId, copysetId);
return;
}
// 将副本集节点插入到映射中
if (!InsertCopysetNodeIfNotExist(logicPoolId, copysetId, copysetNode)) {
LOG(ERROR) << "Failed to insert copyset " << ToGroupIdString(logicPoolId, copysetId);
return;
}
// 如果需要检查加载完成,则等待副本集加载完成
if (needCheckLoadFinished) {
std::shared_ptr<CopysetNode> node = GetCopysetNode(logicPoolId, copysetId);
CheckCopysetUntilLoadFinished(node);
}
LOG(INFO) << "Load copyset " << ToGroupIdString(logicPoolId, copysetId)
<< " end, time used (ms): "
<< TimeUtility::GetTimeofDayMs() - beginTime;
}
// 检查副本集节点是否加载完成
bool CopysetNodeManager::CheckCopysetUntilLoadFinished(std::shared_ptr<CopysetNode> node) {
// 如果节点为空,则返回 false
if (node == nullptr) {
LOG(WARNING) << "CopysetNode ptr is null.";
return false;
}
// 重试次数
uint32_t retryTimes = 0;
LogicPoolID logicPoolId = node->GetLogicPoolId();
CopysetID copysetId = node->GetCopysetId();
// 循环检查直到副本集加载完成或重试次数用完
while (retryTimes < copysetNodeOptions_.checkRetryTimes) {
if (!running_.load(std::memory_order_acquire)) {
return false; // 如果管理器不在运行中,则返回 false
}
// 获取副本集节点的 leader 状态
NodeStatus leaderStatus;
bool getSuccess = node->GetLeaderStatus(&leaderStatus);
if (!getSuccess) {
++retryTimes; // 增加重试次数
::usleep(1000 * copysetNodeOptions_.electionTimeoutMs); // 等待一段时间后重试
continue; // 继续循环
}
// 获取副本集节点的状态
NodeStatus status;
node->GetStatus(&status);
// 如果当前副本集节点的最后一个日志落后于 leader 保存的第一个日志,则可能需要安装快照
bool mayInstallSnapshot = leaderStatus.first_index > status.last_index;
if (mayInstallSnapshot) {
LOG(WARNING) << "Copyset " << ToGroupIdString(logicPoolId, copysetId)
<< " may installing snapshot, stop checking.";
return false; // 如果需要安装快照,则停止检查
}
// 判断当前副本集是否已经追赶上 leader 的日志
int64_t margin = leaderStatus.committed_index
- status.known_applied_index;
bool catchupLeader = margin < (int64_t)copysetNodeOptions_.finishLoadMargin;
if (catchupLeader) {
LOG(INFO) << "Load copyset " << ToGroupIdString(logicPoolId, copysetId)
<< " finished, leader CommittedIndex: "
<< leaderStatus.committed_index
<< ", node appliedIndex: " << status.known_applied_index;
return true; // 已经追赶上,返回 true
}
retryTimes = 0; // 重置重试次数
::usleep(1000 * copysetNodeOptions_.checkLoadMarginIntervalMs); // 等待一段时间后重试
}
LOG(WARNING) << "check copyset " << ToGroupIdString(logicPoolId, copysetId) << " failed.";
return false; // 检查失败,返回 false
}
// 获取副本集节点
std::shared_ptr<CopysetNode> CopysetNodeManager::GetCopysetNode(const LogicPoolID &logicPoolId, const CopysetID ©setId) const {
// 加读锁
ReadLockGuard readLockGuard(rwLock_);
GroupId groupId = ToGroupId(logicPoolId, copysetId);
auto it = copysetNodeMap_.find(groupId); // 查找副本集节点
if (it != copysetNodeMap_.end()) {
return it->second; // 返回找到的副本集节点
}
return nullptr; // 未找到,返回 nullptr
}
// 获取所有副本集节点
void CopysetNodeManager::GetAllCopysetNodes(std::vector<CopysetNodePtr> *nodes) const {
// 加读锁
ReadLockGuard readLockGuard(rwLock_);
for (auto it = copysetNodeMap_.begin(); it != copysetNodeMap_.end(); ++it) {
nodes->push_back(it->second); // 将所有副本集节点添加到列表中
}
}
// 创建副本集节点
bool CopysetNodeManager::CreateCopysetNode(const LogicPoolID &logicPoolId, const CopysetID ©setId, const Configuration &conf) {
// 创建副本集节点
GroupId groupId = ToGroupId(logicPoolId, copysetId);
// 加写锁
WriteLockGuard writeLockGuard(rwLock_);
if (copysetNodeMap_.find(groupId) != copysetNodeMap_.end()) {
LOG(WARNING) << "Copyset node is already exists " << ToGroupIdString(logicPoolId, copysetId);
return false; // 如果已存在,则返回 false
}
std::shared_ptr<CopysetNode> copysetNode = std::make_shared<CopysetNode>(logicPoolId, copysetId, conf);
if (copysetNode->Init(copysetNodeOptions_) != 0) { // 初始化副本集节点
LOG(ERROR) << "Copyset " << ToGroupIdString(logicPoolId, copysetId) << " init failed";
return false; // 初始化失败,返回 false
}
if (copysetNode->Run() != 0) { // 运行副本集节点
LOG(ERROR) << "Copyset " << ToGroupIdString(logicPoolId, copysetId) << " run failed";
return false; // 运行失败,返回 false
}
copysetNodeMap_.insert({groupId, copysetNode}); // 将副本集节点添加到映射中
LOG(INFO) << "Create copyset success " << ToGroupIdString(logicPoolId, copysetId);
return true; // 创建成功,返回 true
}
// 删除副本集节点
bool CopysetNodeManager::DeleteCopysetNode(const LogicPoolID &logicPoolId, const CopysetID ©setId) {
// 删除副本集节点
GroupId groupId = ToGroupId(logicPoolId, copysetId);
// 加读锁
ReadLockGuard readLockGuard(rwLock_);
auto it = copysetNodeMap_.find(groupId);
if (it != copysetNodeMap_.end()) {
it->second->Fini(); // 停止副本集节点
// 加写锁
WriteLockGuard writeLockGuard(rwLock_);
copysetNodeMap_.erase(it); // 从映射中移除副本集节点
LOG(INFO) << "Delete copyset " << ToGroupIdString(logicPoolId, copysetId) << " success.";
return true; // 删除成功,返回 true
}
return false; // 未找到,返回 false
}
// 清除副本集节点数据
bool CopysetNodeManager::PurgeCopysetNodeData(const LogicPoolID &logicPoolId, const CopysetID ©setId) {
// 清除副本集节点数据
GroupId groupId = ToGroupId(logicPoolId, copysetId);
// 加读锁
ReadLockGuard readLockGuard(rwLock_);
auto it = copysetNodeMap_.find(groupId);
if (it != copysetNodeMap_.end()) {
it->second->Fini(); // 停止副本集节点
// 加写锁
WriteLockGuard writeLockGuard(rwLock_);
if (copysetNodeOptions_.trash->RecycleCopySet(it->second->GetCopysetDir()) != 0) { // 回收副本集
LOG(ERROR) << "Failed to remove copyset " << ToGroupIdString(logicPoolId, copysetId) << " persistently.";
return false; // 回收失败,返回 false
}
LOG(INFO) << "Move copyset " << ToGroupIdString(logicPoolId, copysetId) << " to trash success.";
copysetNodeMap_.erase(it); // 从映射中移除副本集节点
return true; // 回收成功,返回 true
}
return false; // 未找到,返回 false
}
// 检查副本集是否存在
bool CopysetNodeManager::IsExist(const LogicPoolID &logicPoolId, const CopysetID ©setId) {
// 加读锁
ReadLockGuard readLockGuard(rwLock_);
GroupId groupId = ToGroupId(logicPoolId, copysetId);
return copysetNodeMap_.end() != copysetNodeMap_.find(groupId); // 检查映射中是否存在副本集节点
}
// 插入副本集节点,如果不存在则插入
bool CopysetNodeManager::InsertCopysetNodeIfNotExist(const LogicPoolID &logicPoolId, const CopysetID ©setId, std::shared_ptr<CopysetNode> node) {
// 加写锁
WriteLockGuard writeLockGuard(rwLock_);
GroupId groupId = ToGroupId(logicPoolId, copysetId);
auto it = copysetNodeMap_.find(groupId);
if (copysetNodeMap_.end() == it) {
copysetNodeMap_.insert({groupId, node}); // 插入副本集节点
LOG(INFO) << "Insert copyset success " << ToGroupIdString(logicPoolId, copysetId);
return true; // 插入成功,返回 true
}
LOG(WARNING) << "Copyset node is already exists " << ToGroupIdString(logicPoolId, copysetId);
return false; // 已存在,返回 false
}
} // namespace chunkserver
} // namespace curve
CopysetNodeManager
类提供了块服务器中副本集节点的完整生命周期管理。它允许创建新的副本集节点、加载已存在的副本集节点、删除副本集节点以及清理副本集节点的数据。通过这些方法,CopysetNodeManager
确保了副本集节点能够被有效管理,从而维护了块服务器的数据一致性和可用性。此外,它还支持线程池来并发地加载副本集节点,提高了系统的整体性能。
在heartbeat.cpp
文件中,定义了Heartbeat
类,该类负责处理块服务器与元数据服务器(MDS)之间的心跳消息。以下是对Heartbeat
类中函数的代码注释分析:
// heartbeat.cpp 文件包含了块服务器心跳处理的实现
// Heartbeat 类定义,用于块服务器与元数据服务器之间的心跳通信
namespace curve {
namespace chunkserver {
// 清除副本集节点数据的函数,用于处理心跳响应中返回的配置变更命令
TaskStatus Heartbeat::PurgeCopyset(LogicPoolID poolId, CopysetID copysetId) {
// 尝试清理指定的副本集节点数据
if (!copysetMan_->PurgeCopysetNodeData(poolId, copysetId)) {
// 清理失败,记录错误日志
LOG(ERROR) << "Failed to clean copyset " << ToGroupIdStr(poolId, copysetId) << " and its data.";
return TaskStatus(-1, "Failed to clean copyset");
}
// 清理成功,记录信息日志
LOG(INFO) << "Successfully cleaned copyset " << ToGroupIdStr(poolId, copysetId) << " and its data.";
return TaskStatus::OK(); // 成功返回 OK 状态
}
// 初始化心跳管理器
int Heartbeat::Init(const HeartbeatOptions &options) {
// 存储心跳选项
options_ = options;
// 初始化相关变量,如本地文件系统、元数据服务器地址等
// 初始化定时器和其他管理器
// ...
return 0; // 成功返回 0
}
// 运行心跳管理器
int Heartbeat::Run() {
// 启动心跳工作线程
hbThread_ = Thread(&Heartbeat::HeartbeatWorker, this);
return 0; // 成功返回 0
}
// 停止心跳管理器
int Heartbeat::Stop() {
// 停止心跳等待定时器
waitInterval_.StopWait();
toStop_.store(true, std::memory_order_release);
hbThread_.join(); // 等待心跳工作线程结束
LOG(INFO) << "Stopped Heartbeat manager."; // 记录停止日志
return 0; // 成功返回 0
}
// 清理心跳管理器资源
int Heartbeat::Fini() {
// 停止心跳管理器
Stop();
// 清理扫描管理器等资源
// ...
LOG(INFO) << "Heartbeat manager cleaned up."; // 记录清理日志
return 0; // 成功返回 0
}
// 获取文件系统的空间信息
int Heartbeat::GetFileSystemSpaces(size_t *capacity, size_t *avail) {
// 调用文件系统接口获取空间信息
// ...
return 0; // 成功返回 0
}
// 构建心跳请求中的副本集信息
int Heartbeat::BuildCopysetInfo(curve::mds::heartbeat::CopySetInfo *info, CopysetNodePtr copyset) {
// 填充副本集信息,如逻辑池 ID、副本集 ID、时代值等
// ...
return 0; // 成功返回 0
}
// 构建心跳请求
int Heartbeat::BuildRequest(HeartbeatRequest *req) {
// 填充心跳请求,包括块服务器 ID、令牌、起始时间、IP、端口等
// ...
return 0; // 成功返回 0
}
// 发送心跳请求到元数据服务器并处理响应
int Heartbeat::SendHeartbeat(const HeartbeatRequest &request, HeartbeatResponse *response) {
// 创建或初始化 brpc 通道
// 发送心跳请求到元数据服务器
// 处理响应,包括执行配置变更命令等
// ...
return 0; // 成功返回 0
}
// 执行心跳响应中的任务
int Heartbeat::ExecTask(const HeartbeatResponse &response) {
// 根据心跳响应执行任务,如清理副本集、变更副本集配置等
// ...
return 0; // 成功返回 0
}
// 心跳工作线程的执行函数
void Heartbeat::HeartbeatWorker() {
// 循环发送心跳请求并处理响应
// ...
}
} // namespace chunkserver
} // namespace curve
Heartbeat
类提供了块服务器与元数据服务器之间心跳机制的实现。心跳机制是分布式系统中常用的一种手段,用于监控节点的健康状态、同步状态信息以及执行配置变更等。在Heartbeat
类中,定义了一系列方法来处理心跳的发送、接收和响应处理,确保块服务器能够及时与元数据服务器同步状态,并根据响应执行相应的操作。通过这种方式,系统能够保持高度的一致性和可靠性。
在scan_manager.cpp
文件中,定义了ScanManager
类,该类负责管理块服务器的数据扫描任务。以下是对ScanManager
类中函数的代码注释分析:
// scan_manager.cpp 文件包含了块服务器数据扫描管理器的实现
// ScanManager 类定义,用于管理块服务器的数据扫描任务
namespace curve {
namespace chunkserver {
// ScanManager::Init 方法,用于初始化扫描管理器
int ScanManager::Init(const ScanManagerOptions &options) {
// 设置扫描大小、超时时间、重试间隔等选项
scanSize_ = options.scanSize;
chunkMetaPageSize_ = options.chunkMetaPageSize;
timeoutMs_ = options.timeoutMs;
retry_ = options.retry;
retryIntervalUs_ = options.retryIntervalUs;
// 初始化等待间隔和扫描任务等待间隔
jobWaitInterval_.Init(options.intervalSec * 1000);
scanTaskWaitInterval_.Init(options.timeoutMs);
// 获取副本集节点管理器
copysetNodeManager_ = options.copysetNodeManager;
// 获取最大块大小
chunkSize_ = copysetNodeManager_->GetCopysetNodeOptions().maxChunkSize;
// 检查扫描大小是否有效
if (scanSize_ > chunkSize_ || scanSize_ <= 0 ||
chunkSize_ % scanSize_ != 0) {
LOG(ERROR) << "Init scan manager failed, the scan size: " << scanSize_;
return -1; // 初始化失败,返回错误代码
}
return 0; // 初始化成功,返回 0
}
// ScanManager::Fini 方法,用于清理扫描管理器资源
int ScanManager::Fini() {
// 停止等待间隔计时器
LOG(INFO) << "Stopping scan manager.";
jobWaitInterval_.StopWait();
toStop_.store(true, std::memory_order_release);
scanThread_.join(); // 等待扫描线程结束
LOG(INFO) << "Stopped scan manager.";
// 清空等待扫描和正在扫描的副本集队列
waitScanSet_.clear();
jobs_.clear();
return 0; // 清理成功,返回 0
}
// ScanManager::Enqueue 方法,用于将副本集加入到待扫描队列中
void ScanManager::Enqueue(LogicPoolID poolId, CopysetID id) {
// 创建扫描键并加入到待扫描集合中
ScanKey key(poolId, id);
WriteLockGuard writeGuard(waitSetLock_);
if (waitScanSet_.find(key) == waitScanSet_.end()) {
waitScanSet_.emplace(key); // 将副本集加入待扫描集合
} else {
LOG(WARNING) << "The scan key already exists"
<< "logical poolId: " << poolId
<< "copysetId: " << id; // 记录警告日志
}
}
// ScanManager::Dequeue 方法,用于从待扫描队列中移除一个副本集
ScanKey ScanManager::Dequeue() {
// 从待扫描集合中取出一个扫描键
ScanKey key;
WriteLockGuard writeGuard(waitSetLock_);
key = *(waitScanSet_.begin()); // 获取第一个待扫描的副本集
waitScanSet_.erase(waitScanSet_.begin()); // 从集合中移除
return key; // 返回扫描键
}
// ScanManager::Run 方法,用于启动扫描管理器线程
void ScanManager::Run() {
// 创建并启动扫描线程
scanThread_ = Thread(&ScanManager::Scan, this);
}
// ScanManager::Scan 方法,用于执行扫描任务
void ScanManager::Scan() {
// 循环处理待扫描的副本集
ScanKey key;
while (!toStop_.load(std::memory_order_acquire)) {
waitSetLock_.RDLock(); // 读锁保护共享数据
if (waitScanSet_.empty()) { // 如果待扫描集合为空
waitSetLock_.Unlock(); // 释放锁
jobWaitInterval_.WaitForNextExcution(); // 等待下一个时间间隔
continue; // 继续循环
}
waitSetLock_.Unlock(); // 释放锁
key = Dequeue(); // 取出待扫描的副本集
StartScanJob(key); // 启动扫描任务
jobWaitInterval_.WaitForNextExcution(); // 等待下一个时间间隔
}
LOG(INFO) << "Scan worker thread stopped."; // 记录停止日志
}
// ScanManager::StartScanJob 方法,用于启动一个新的扫描任务
void ScanManager::StartScanJob(ScanKey key) {
// 检查副本集是否存在
auto nodePtr = copysetNodeManager_->GetCopysetNode(key.first, key.second);
if (nullptr == nodePtr) {
LOG(ERROR) << "scan copyset failed, copyset node is not found:"
<< " logicalpoolId = " << key.first
<< " copysetId = " << key.second;
return; // 副本集节点不存在,返回
}
LOG(INFO) << "Start scan job(\" " << key.first
<< ", " << key.second << " \")."; // 记录开始扫描的日志
// 创建扫描任务并加入到任务映射中
std::shared_ptr<ScanJob> job = std::make_shared<ScanJob>();
job->poolId = key.first; // 设置逻辑池 ID
job->id = key.second; // 设置副本集 ID
job->type = ScanType::Init; // 设置扫描类型为初始化
job->isFinished = true; // 设置任务已完成
job->dataStore = nodePtr->GetDataStore(); // 获取数据存储
nodePtr->SetScan(true); // 标记副本集正在扫描
nodePtr->GetFailedScanMap().clear(); // 清除失败的扫描映射
jobMapLock_.WRLock(); // 写锁保护任务映射
jobs_.emplace(key, job); // 添加任务到映射
jobMapLock_.Unlock(); // 释放锁
GenScanJobs(key); // 生成扫描任务
}
// ScanManager::CancelScanJob 方法,用于取消一个扫描任务
int ScanManager::CancelScanJob(LogicPoolID poolId, CopysetID id) {
// 取消尚未开始的扫描任务
ScanKey key(poolId, id);
WriteLockGuard writeGuard(waitSetLock_);
auto iter = waitScanSet_.find(key); // 查找待扫描集合中的扫描键
if (iter != waitScanSet_.end()) {
waitScanSet_.erase(iter); // 取消并移除扫描键
return 0; // 取消成功,返回 0
}
// 取消已开始的扫描任务
auto job = GetJob(key); // 获取扫描任务
if (nullptr != job) {
auto nodePtr = copysetNodeManager_->GetCopysetNode(poolId, id);
nodePtr->SetScan(false); // 标记副本集不再扫描
nodePtr->GetFailedScanMap().clear(); // 清除失败的扫描映射
WriteLockGuard writeGuard(jobMapLock_); // 写锁保护任务映射
jobs_.erase(key); // 从映射中移除任务
}
return 0; // 取消成功,返回 0
}
// ScanManager::GenScanJob 方法,用于生成扫描任务
bool ScanManager::GenScanJob(std::shared_ptr<ScanJob> job) {
// 根据扫描任务的类型生成具体的扫描任务
// ...
return done; // 返回是否完成
}
// ScanManager::GenScanJobs 方法,用于为扫描任务生成所有需要的扫描作业
void ScanManager::GenScanJobs(ScanKey key) {
// 获取扫描任务并为其生成所有扫描作业
auto job = GetJob(key);
if (nullptr == job) {
LOG(ERROR) << "GenScanJob failed, can not find the job, "
<< "logical poolId = " << key.first
<< " copysetId = " << key.second;
return; // 任务不存在,返回
}
// 循环直到生成所有扫描作业或任务完成
while (!done) {
done = GenScanJob(job); // 生成扫描作业
}
}
// ScanManager::SetLocalScanMap 方法,用于设置本地扫描映射
void ScanManager::SetLocalScanMap(ScanKey key, ScanMap map) {
// 获取扫描任务并设置本地扫描映射
// ...
}
// ScanManager::DealFollowerScanMap 方法,用于处理副本集节点发送的跟随者扫描映射
void ScanManager::DealFollowerScanMap(const FollowScanMapRequest &request,
FollowScanMapResponse *response) {
// 获取请求中的扫描映射并处理
// ...
}
// ScanManager::CompareMap 方法,用于比较扫描映射并执行必要的操作
void ScanManager::CompareMap(std::shared_ptr<ScanJob> job) {
// 比较本地和跟随者的扫描映射
// ...
}
// ScanManager::ScanJobFinish 方法,用于完成扫描任务
void ScanManager::ScanJobFinish(std::shared_ptr<ScanJob> job) {
// 完成扫描任务并更新相关状态
// ...
}
// ScanManager::GetJob 方法,用于获取指定副本集的扫描任务
std::shared_ptr<ScanJob> ScanManager::GetJob(ScanKey key) {
// 获取扫描任务
// ...
}
// ScanManager::SetScanJobType 方法,用于设置扫描任务的类型
void ScanManager::SetScanJobType(ScanKey key, ScanType type) {
// 设置扫描任务的类型
// ...
}
} // namespace chunkserver
} // namespace curve
ScanManager
类负责管理块服务器的数据扫描任务,包括初始化、运行、停止扫描管理器,以及加入待扫描队列、开始扫描任务、取消扫描任务等操作。扫描任务用于检查数据的一致性和完整性,是维护分布式文件系统健康的重要机制。通过GenScanJob
、SetLocalScanMap
、DealFollowerScanMap
等方法,ScanManager
能够生成、执行和完成扫描任务,同时处理跟随者节点发送的扫描映射,确保数据的准确性和一致性。