ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Ceph Object Class SDK 实战指南:在 Ceph 树外构建独立对象类(objclass.h / cls_sdk 示例全解)

2026/9/23 22:49:09 拓冰建站 浏览量
Ceph Object Class SDK 实战指南:在 Ceph 树外构建独立对象类(objclass.h / cls_sdk 示例全解) 存储分布式文件系统对象存储后端高可用【免费下载链接】cephCeph is a distributed object, block, and file storage platform项目地址https://gitcode.com/gh_mirrors/ce/ceph点击查看免费下载Ceph 允许通过共享对象类Object Class即cls_开头的动态库在 OSD 端扩展对象语义传统上对象类必须依赖 Ceph 内部实现、只能在源码树内构建。本文以仓库中的官方文档 doc/rados/api/objclass-sdk.rst 为骨架结合 SDK 头文件 src/include/rados/objclass.h、示例对象类cls_sdksrc/cls/sdk/及其单元测试 src/test/cls_sdk/test_cls_sdk.cc系统讲解 Ceph Object Class SDK 的设计动机、安装方式、核心 API、示例实现与测试验证方法。读完本文你将掌握如何基于公开 SDK 接口在 Ceph 源码树之外独立开发、打包和分发自己的 RADOS 对象类。一、为什么需要 Object Class SDK从树内开发到树外开发Ceph 的存储能力可以通过**对象类Object Class**扩展把一段逻辑编译成共享库如libcls_sdk.so由 OSD 在服务端动态加载客户端再通过exec调用它从而在数据所在的 OSD 进程内完成原子、高效的数据操作——这正是 RADOS 网关RGW的索引、RBD 的元数据、以及各类锁、日志、队列等能力的基础。树内对象类的典型代表在 src/cls/CMakeLists.txt 中可以看到cls_rbd、cls_rgw、cls_lock、cls_log、cls_queue、cls_fifo、cls_lua等一系列以add_library(... SHARED ...)构建并安装到${CMAKE_INSTALL_LIBDIR}/rados-classes的共享库。传统框架的痛点过去构建对象类依赖 Ceph 的大量内部实现开发者只能在 Ceph 源码树内编写并随主版本一起编译无法独立迭代和分发。SDK 的目标Object Class SDK 提供了一个独立的对象类接口允许在 Ceph 树外构建对象类。由此对象类被划分为两类树内对象类依赖 Ceph 内部实现必须留在源码树中如src/cls/rbd/、src/cls/rgw/等SDK 对象类只依赖公开的 SDK 接口不依赖任何 Ceph 内部实现可以在树外构建、以软件包形式独立分发。SDK 将对象类开发与 Ceph 主项目解耦鼓励把对象类作为独立软件包创建和分发。二、安装 objclass.hSDK 接口的获取对象类对外开发接口位于src/include/rados/目录下随 Ceph 安装一起被安装到系统。核心头文件 src/include/rados/objclass.h 会在 src/include/CMakeLists.txt 中登记第 16 行rados/objclass.h随make install安装。安装完成后可验证接口头文件是否就位ls /usr/local/include/rados预期输出中应包含对象类 SDK 所需的全部头文件buffer.h cls_flags.hpp crc32c.h librados.h librados_fwd.hpp objclass.h buffer_fwd.h cls_traits.hpp inline_memory.h librados.hpp librgw.h page.h rados_types.h rados_types.hpp rgw_file.h其中与对象类 SDK 直接相关的三个文件是src/include/rados/objclass.h对象类核心接口包含宏、注册 API 与对象操作方法src/include/rados/cls_traits.hppC 模板元编程辅助提供ClsMethod、ClassRegistrar与读写标志Tagsrc/include/rados/cls_flags.hpp方法标志位定义。安装路径prefix/include/rados会随构建时的CMAKE_INSTALL_PREFIX变化默认即为/usr/local/include/rados。树外开发时只需在编译命令中加入-I/usr/local/include或对应 prefix 的 include 目录无需链接任何 Ceph 内部库——这正是 SDK 解耦设计的关键。三、读懂核心接口 objclass.h3.1 三个必备宏版本、类名与初始化每个对象类必须以固定模式声明版本、名称与初始化函数CLS_VER(1,0) // 声明版本号__cls_ver_maj1, __cls_ver_min0 CLS_NAME(sdk) // 声明类名__cls_name sdk CLS_INIT(sdk) { ... } // 定义类加载时的入口 __cls_init()宏定义位于 objclass.h#define CLS_VER(maj,min) \ int __cls_ver__## maj ## _ ##min 0; \ int __cls_ver_maj maj; \ int __cls_ver_min min; #define CLS_NAME(name) \ int __cls_name__## name 0; \ const char *__cls_name #name; #define CLS_INIT(name) \ CEPH_CLS_API void __cls_init()__cls_init()是 OSD 加载共享库时的入口点负责注册类和类内方法相当于对象类的“main 函数”。3.2 三个句柄类型typedef void *cls_handle_t; // 对象类句柄cls_register 输出 typedef void *cls_method_handle_t; // 方法句柄注册方法时输出 typedef void* cls_method_context_t; // 方法执行上下文由 OSD 传入每个方法调用cls_method_context_t是方法体内与具体对象交互的“句柄”所有cls_cxx_*对象操作 API 都以它为首参。3.3 类与方法的注册 APIcls_log(int level, const char *format, ...)在对象类内输出日志cls_register(const char *name, cls_handle_t *handle)注册对象类cls_register_cxx_method_impl(hclass, method, flags, class_call, handle)注册一个 C 方法其中flags取CLS_METHOD_RD读/CLS_METHOD_WR写/CLS_METHOD_PROMOTE不可代理到基础 tier值定义见 src/include/rados/cls_flags.hppconstexpr int CLS_METHOD_RD 0x1; /// method executes read operations constexpr int CLS_METHOD_WR 0x2; /// method executes write operations constexpr int CLS_METHOD_PROMOTE 0x8; /// method cannot be proxied to base tierCLS_METHOD_PROMOTE与缓存分层cache tier有关标记该标志的方法在缓存池上执行时不能被代理到基础池必须先提升对象保证语义正确。此外还有两个便捷日志宏objclass.h#define CLS_LOG(level, fmt, ...) \ cls_log(level, cls %s:%d: fmt, __FILE__, __LINE__, ##__VA_ARGS__) #define CLS_ERR(fmt, ...) CLS_LOG(0, fmt, ##__VA_ARGS__)它们自动附带文件:行号前缀调试时非常有用。3.4 对象操作 API方法体内可用API功能cls_cxx_create(hctx, exclusive)创建对象exclusivetrue时对象已存在则失败cls_cxx_remove(hctx)删除对象cls_cxx_stat(hctx, size*, mtime*)获取对象大小与修改时间cls_cxx_read(hctx, ofs, len, bl*)从偏移ofs读取len字节到 bufferlistcls_cxx_write(hctx, ofs, len, bl*)从偏移ofs写入 bufferlistcls_cxx_getxattr(hctx, name, outbl*)读取对象扩展属性xattrcls_cxx_setxattr(hctx, name, inbl*)写入对象扩展属性cls_cxx_map_get_val(hctx, key, outbl*)读取对象 omap 中 key 对应的值cls_cxx_map_set_val(hctx, key, inbl*)写入对象 omap 中 key 对应的值这些 API 全部返回负数 errno 表示失败、0表示成功是对象类方法体内访问对象数据、元数据和 omap 的完整入口集合。3.5 C 侧的现代封装ClsMethod 与 ClassRegistrar为了在 C 中安全地表达“方法名 读/写标志”SDK 提供了 cls_traits.hpptemplate int Flags struct MethodTag {}; using RdTag MethodTagCLS_METHOD_RD; using WrTag MethodTagCLS_METHOD_WR; using RdWrTag MethodTagCLS_METHOD_RD | CLS_METHOD_WR; // ... 以及 RdPromoteTag / WrPromoteTag / RdWrPromoteTag 等组合 template typename Tag, typename ClassDef struct ClsMethod { const char* cls; // 所属类名取自 ClassDef::name const char* name; // 方法名 constexpr ClsMethod(const char* n) : cls(ClassDef::name), name(n) {} };ClassRegistrarClassDef则把“方法定义”与“方法实现函数”绑定起来并在内部将Tag翻译成注册标志后调用cls_register_cxx_method_implobjclass.h。这套模板让方法声明cls_sdk_ops.h与实现.cc分离且标志类型化、编译期可检查还提供了is_safe_for_ro_v常量用于表达方法是否只读。四、示例对象类 cls_sdk 源码逐段解析官方示例cls_sdk位于 src/cls/sdk/只有两个文件非常精简但完整地覆盖了“声明 → 实现 → 注册 → 构建 → 测试”全流程。4.1 方法声明cls_sdk_ops.hsrc/cls/sdk/cls_sdk_ops.h 声明了类与方法名以及方法的读写标志namespace cls::sdk { struct ClassId { static constexpr auto name sdk; }; namespace method { constexpr auto test_coverage_write ClsMethodRdWrTag, ClassId(test_coverage_write); constexpr auto test_coverage_replay ClsMethodRdWrTag, ClassId(test_coverage_replay); } }ClassId::name就是对象类的注册名sdk两个方法都标记为RdWrTag读写。客户端侧也可以 include 这个头文件直接通过method::test_coverage_write发起调用见第六节测试代码实现“一份声明、两端共用”。4.2 实现与注册cls_sdk.ccsrc/cls/sdk/cls_sdk.cc 开头声明版本与类名#include include/rados/objclass.h #include cls_sdk_ops.h CLS_VER(1,0) CLS_NAME(sdk) cls_handle_t h_class; cls_method_handle_t h_test_coverage_write; cls_method_handle_t h_test_coverage_replay;写方法test_coverage_write演示一个方法内连续进行多种写操作覆盖create / stat / write / setxattr / map_set_valstatic int test_coverage_write(cls_method_context_t hctx, ceph::buffer::list *in, ceph::buffer::list *out) { // 1) 创建对象非排他 int ret cls_cxx_create(hctx, false); if (ret 0) { CLS_LOG(0, ERROR: %s(): cls_cxx_create returned %d, __func__, ret); return ret; } uint64_t size; ret cls_cxx_stat(hctx, size, NULL); // 2) 获取对象大小 if (ret 0) return ret; std::string c test; ceph::buffer::list bl; bl.append(c); ret cls_cxx_write(hctx, 0, bl.length(), bl); // 3) 向对象写入数据 if (ret 0) return ret; uint64_t new_size; ret cls_cxx_stat(hctx, new_size, NULL); // 4) 写后再次 stat if (ret 0) return ret; ret cls_cxx_setxattr(hctx, foo, bl); // 5) 设置 xattr foo if (ret 0) return ret; ret cls_cxx_map_set_val(hctx, foo, bl); // 6) 写入 omap 键 foo if (ret 0) return ret; return 0; }读方法test_coverage_replay对称地覆盖stat / read / getxattr / map_get_val / remove并做一致性校验static int test_coverage_replay(cls_method_context_t hctx, ceph::buffer::list *in, ceph::buffer::list *out) { CLS_LOG(0, reading already written object); uint64_t size; int ret cls_cxx_stat(hctx, size, NULL); if (ret 0) return ret; ceph::buffer::list bl; ret cls_cxx_read(hctx, 0, size, bl); // 读回对象数据 if (ret 0) return ret; if (bl.length() ! size) // 长度校验 return -EIO; bl.clear(); ret cls_cxx_getxattr(hctx, foo, bl); // 读回 xattr if (ret 0) return ret; if (bl.length() ! size) return -EIO; bl.clear(); ret cls_cxx_map_get_val(hctx, foo, bl); // 读回 omap if (ret 0) return ret; if (bl.length() ! size) return -EIO; ret cls_cxx_remove(hctx); // 最后删除对象 if (ret 0) return ret; return 0; }注册入口CLS_INIT(sdk)CLS_INIT(sdk) { CLS_LOG(0, loading cls_sdk); using namespace cls::sdk; cls_register(ClassId::name, h_class); // 注册类 sdk ClassRegistrarClassId cls(h_class); cls.register_cxx_method(method::test_coverage_write, test_coverage_write, h_test_coverage_write); cls.register_cxx_method(method::test_coverage_replay, test_coverage_replay, h_test_coverage_replay); }这段代码是任何 SDK 对象类的标准模板cls_register注册类 →ClassRegistrar逐个注册方法 → 方法实现为静态函数。注意CLS_INIT宏展开后带CEPH_CLS_API[[gnu::visibility(default)]]可见性属性保证__cls_init符号可被动态加载器找到。4.3 构建集成CMake 如何编译与安装在 src/cls/CMakeLists.txt 中cls_sdk与其他树内对象类一样构建为共享库并安装到rados-classes目录set(cls_dir ${CMAKE_INSTALL_LIBDIR}/rados-classes) # cls_sdk add_library(cls_sdk SHARED sdk/cls_sdk.cc) set_target_properties(cls_sdk PROPERTIES VERSION 1.0.0 SOVERSION 1 INSTALL_RPATH CXX_VISIBILITY_PRESET hidden) install(TARGETS cls_sdk DESTINATION ${cls_dir})关键点cls_sdk只链接了include/rados/objclass.h及其依赖buffer.h、cls_traits.hpp、cls_flags.hpp没有链接任何src/下的内部库。这正是 SDK 对象类可以在树外复现构建的前提——你只需把cls_sdk.cccls_sdk_ops.h 安装好的头文件带走用任意构建系统Makefile/CMake/其他编译成libcls_xxx.so再复制到 OSD 的rados-classes目录即可被加载。构建后生成的库文件为libcls_sdk.soSONAME 为 1版本 1.0.0安装到lib/rados-classes/下。OSD 启动时会扫描该目录并动态加载其中的对象类。五、用 ceph_test_cls_sdk 单测验证对象类单元测试位于 src/test/cls_sdk/由 src/test/cls_sdk/CMakeLists.txt 构建出ceph_test_cls_sdk可执行文件链接librados、global、radostest-cxx与 gtest。src/test/cls_sdk/test_cls_sdk.cc 展示了客户端如何通过 librados 的ObjectWriteOperation::exec调用对象类方法TEST_P(TestClsSDK, TestSDKCoverageWrite) { bufferlist in; librados::ObjectWriteOperation op; op.exec(method::test_coverage_write, in); // exec 调用 sdk 类的 test_coverage_write ASSERT_EQ(0, ioctx.operate(myobject, op)); } TEST_P(TestClsSDK, TestSDKCoverageReplay) { bufferlist in; librados::ObjectWriteOperation op; op.exec(method::test_coverage_write, in); ASSERT_EQ(0, ioctx.operate(myobject, op)); librados::ObjectWriteOperation op2; op2.exec(method::test_coverage_replay, in); // 先写后读验证数据一致性 ASSERT_EQ(0, ioctx.operate(myobject, op2)); } INSTANTIATE_TEST_SUITE_P(, TestClsSDK, ::testing::Values(PoolType::REPLICATED, PoolType::FAST_EC), [](const ::testing::TestParamInfoPoolType info) { return pool_type_name(info.param); } );几点值得注意的工程实践执行方式对象类方法通过ObjectWriteOperation::exec(method, in)加入操作序列再由ioctx.operate(oid, op)一次性提交。op.exec的第一个参数直接复用cls_sdk_ops.h中声明的method::test_coverage_write常量——方法名在客户端与服务端由同一头文件保证一致杜绝手写字符串拼写错误。对象命名测试固定使用对象myobjectTestSDKCoverageReplay先写后读完整验证了“写路径数据xattromap→ 读路径数据xattromap→ 删除”的往返一致性。池类型参数化测试通过INSTANTIATE_TEST_SUITE_P同时跑在REPLICATED副本池与FAST_EC快速纠删码池上说明对象类方法对这两种主流池类型都兼容——这对验证 SDK 对象类在 EC 池上的可用性很有价值。运行该测试的方式在已构建的测试环境中./ceph_test_cls_sdk或通过 CTest 执行ctest -R cls_sdk测试通过即证明cls_sdk被 OSD 成功加载、方法被正确注册、对象类在副本池与 EC 池上均能完成数据读写。六、开发自己的树外对象类推荐流程结合上述全部内容可以总结出一份“树外 SDK 对象类”开发清单获取接口安装 Ceph 后确认include/rados/objclass.h、cls_traits.hpp、cls_flags.hpp、buffer.h等头文件可用准备三件套方法声明头文件仿照 cls_sdk_ops.h用ClsMethodTag, ClassDef定义类名与方法及读写标志实现文件仿照 cls_sdk.cc包含CLS_VER、CLS_NAME、方法静态函数与CLS_INIT注册逻辑构建脚本把实现编译为libcls_名字.so仅需 include 安装好的头文件目录无需链接 Ceph 内部库部署将.so放入 OSD 的rados-classes目录默认lib/rados-classes/与树内对象类安装位置一致客户端调用客户端代码 include 你的方法声明头文件用ObjectWriteOperation::execioctx.operate调用方法与ceph_test_cls_sdk的测试写法完全一致测试验证参考 test_cls_sdk.cc至少在 REPLICATED 池上做写/读往返测试有条件时再覆盖 EC 池。七、总结Ceph Object Class SDK 通过公开、独立的objclass.h接口把“对象类开发”从 Ceph 源码树中解放出来树内对象类继续依赖内部实现随 Ceph 发布而 SDK 对象类只依赖安装后的公开头文件可在树外构建并以独立软件包分发。本文从设计动机、头文件安装、objclass.h全量 API宏、句柄、注册、对象操作、C 封装、cls_sdk示例逐行解析、构建集成到单测验证完整覆盖了 SDK 对象类的开发闭环。动手实践时直接以 src/cls/sdk/ 为模板、以 src/test/cls_sdk/test_cls_sdk.cc 为验收标准即可快速产出自己的树外对象类。赞分享存储分布式文件系统对象存储后端高可用【免费下载链接】cephCeph is a distributed object, block, and file storage platform项目地址https://gitcode.com/gh_mirrors/ce/ceph点击查看免费下载相关推荐如何用 start-cluster.sh 为 LibreChat 本地搭建三节点 Redis Cluster 并通过 .env 连接验证如何用 start cluster.sh 为 LibreChat 本地搭建三节点 Redis Cluster 并通过 .env 连接验证 LibreChat 本存储分布式文件系统对象存储后端高可用Ceph 对象网关Ceph Object Gateway / radosgw架构与实战指南S3/Swift 兼容对象存储从部署到管理Ceph 对象网关Ceph Object Gateway / radosgw架构与实战指南S3/Swift 兼容对象存储从部署到管理 Ceph Objec存储分布式文件系统对象存储后端高可用扩展 Ceph用 Ceph Class 为对象存储编写自定义对象方法的完整指南扩展 Ceph用 Ceph Class 为对象存储编写自定义对象方法的完整指南 导读 Ceph 是一个分布式对象、块与文件存储平台其对象存储层RADOS存储分布式文件系统对象存储后端高可用创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考