指南:在 Ceph 樹外構(gòu)建獨立對象類(objclass.h / cls_sdk 示例全解))
存儲分布式文件系統(tǒng)對象存儲后端高可用【免費下載鏈接】cephCeph is a distributed object, block, and file storage platform項目地址https://gitcode.com/gh_mirrors/ce/ceph點擊查看免費下載Ceph 允許通過共享對象類Object Class即cls_開頭的動態(tài)庫在 OSD 端擴展對象語義傳統(tǒng)上對象類必須依賴 Ceph 內(nèi)部實現(xiàn)、只能在源碼樹內(nèi)構(gòu)建。本文以倉庫中的官方文檔 doc/rados/api/objclass-sdk.rst 為骨架結(jié)合 SDK 頭文件 src/include/rados/objclass.h、示例對象類cls_sdksrc/cls/sdk/及其單元測試 src/test/cls_sdk/test_cls_sdk.cc系統(tǒng)講解 Ceph Object Class SDK 的設(shè)計動機、安裝方式、核心 API、示例實現(xiàn)與測試驗證方法。讀完本文你將掌握如何基于公開 SDK 接口在 Ceph 源碼樹之外獨立開發(fā)、打包和分發(fā)自己的 RADOS 對象類。一、為什么需要 Object Class SDK從樹內(nèi)開發(fā)到樹外開發(fā)Ceph 的存儲能力可以通過**對象類Object Class**擴展把一段邏輯編譯成共享庫如libcls_sdk.so由 OSD 在服務(wù)端動態(tài)加載客戶端再通過exec調(diào)用它從而在數(shù)據(jù)所在的 OSD 進程內(nèi)完成原子、高效的數(shù)據(jù)操作——這正是 RADOS 網(wǎng)關(guān)RGW的索引、RBD 的元數(shù)據(jù)、以及各類鎖、日志、隊列等能力的基礎(chǔ)。樹內(nèi)對象類的典型代表在 src/cls/CMakeLists.txt 中可以看到cls_rbd、cls_rgw、cls_lock、cls_log、cls_queue、cls_fifo、cls_lua等一系列以add_library(... SHARED ...)構(gòu)建并安裝到${CMAKE_INSTALL_LIBDIR}/rados-classes的共享庫。傳統(tǒng)框架的痛點過去構(gòu)建對象類依賴 Ceph 的大量內(nèi)部實現(xiàn)開發(fā)者只能在 Ceph 源碼樹內(nèi)編寫并隨主版本一起編譯無法獨立迭代和分發(fā)。SDK 的目標Object Class SDK 提供了一個獨立的對象類接口允許在 Ceph 樹外構(gòu)建對象類。由此對象類被劃分為兩類樹內(nèi)對象類依賴 Ceph 內(nèi)部實現(xiàn)必須留在源碼樹中如src/cls/rbd/、src/cls/rgw/等SDK 對象類只依賴公開的 SDK 接口不依賴任何 Ceph 內(nèi)部實現(xiàn)可以在樹外構(gòu)建、以軟件包形式獨立分發(fā)。SDK 將對象類開發(fā)與 Ceph 主項目解耦鼓勵把對象類作為獨立軟件包創(chuàng)建和分發(fā)。二、安裝 objclass.hSDK 接口的獲取對象類對外開發(fā)接口位于src/include/rados/目錄下隨 Ceph 安裝一起被安裝到系統(tǒng)。核心頭文件 src/include/rados/objclass.h 會在 src/include/CMakeLists.txt 中登記第 16 行rados/objclass.h隨make install安裝。安裝完成后可驗證接口頭文件是否就位ls /usr/local/include/rados預(yù)期輸出中應(yīng)包含對象類 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 直接相關(guān)的三個文件是src/include/rados/objclass.h對象類核心接口包含宏、注冊 API 與對象操作方法src/include/rados/cls_traits.hppC 模板元編程輔助提供ClsMethod、ClassRegistrar與讀寫標志Tagsrc/include/rados/cls_flags.hpp方法標志位定義。安裝路徑prefix/include/rados會隨構(gòu)建時的CMAKE_INSTALL_PREFIX變化默認即為/usr/local/include/rados。樹外開發(fā)時只需在編譯命令中加入-I/usr/local/include或?qū)?yīng) prefix 的 include 目錄無需鏈接任何 Ceph 內(nèi)部庫——這正是 SDK 解耦設(shè)計的關(guān)鍵。三、讀懂核心接口 objclass.h3.1 三個必備宏版本、類名與初始化每個對象類必須以固定模式聲明版本、名稱與初始化函數(shù)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 加載共享庫時的入口點負責注冊類和類內(nèi)方法相當于對象類的“main 函數(shù)”。3.2 三個句柄類型typedef void *cls_handle_t; // 對象類句柄cls_register 輸出 typedef void *cls_method_handle_t; // 方法句柄注冊方法時輸出 typedef void* cls_method_context_t; // 方法執(zhí)行上下文由 OSD 傳入每個方法調(diào)用cls_method_context_t是方法體內(nèi)與具體對象交互的“句柄”所有cls_cxx_*對象操作 API 都以它為首參。3.3 類與方法的注冊 APIcls_log(int level, const char *format, ...)在對象類內(nèi)輸出日志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不可代理到基礎(chǔ) 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有關(guān)標記該標志的方法在緩存池上執(zhí)行時不能被代理到基礎(chǔ)池必須先提升對象保證語義正確。此外還有兩個便捷日志宏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__)它們自動附帶文件:行號前綴調(diào)試時非常有用。3.4 對象操作 API方法體內(nèi)可用API功能cls_cxx_create(hctx, exclusive)創(chuàng)建對象exclusivetrue時對象已存在則失敗cls_cxx_remove(hctx)刪除對象cls_cxx_stat(hctx, size*, mtime*)獲取對象大小與修改時間cls_cxx_read(hctx, ofs, len, bl*)從偏移ofs讀取len字節(jié)到 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 對應(yīng)的值cls_cxx_map_set_val(hctx, key, inbl*)寫入對象 omap 中 key 對應(yīng)的值這些 API 全部返回負數(shù) errno 表示失敗、0表示成功是對象類方法體內(nèi)訪問對象數(shù)據(jù)、元數(shù)據(jù)和 omap 的完整入口集合。3.5 C 側(cè)的現(xiàn)代封裝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則把“方法定義”與“方法實現(xiàn)函數(shù)”綁定起來并在內(nèi)部將Tag翻譯成注冊標志后調(diào)用cls_register_cxx_method_implobjclass.h。這套模板讓方法聲明cls_sdk_ops.h與實現(xiàn).cc分離且標志類型化、編譯期可檢查還提供了is_safe_for_ro_v常量用于表達方法是否只讀。四、示例對象類 cls_sdk 源碼逐段解析官方示例cls_sdk位于 src/cls/sdk/只有兩個文件非常精簡但完整地覆蓋了“聲明 → 實現(xiàn) → 注冊 → 構(gòu)建 → 測試”全流程。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讀寫??蛻舳藗?cè)也可以 include 這個頭文件直接通過method::test_coverage_write發(fā)起調(diào)用見第六節(jié)測試代碼實現(xiàn)“一份聲明、兩端共用”。4.2 實現(xiàn)與注冊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演示一個方法內(nèi)連續(xù)進行多種寫操作覆蓋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) 創(chuàng)建對象非排他 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) 向?qū)ο髮懭霐?shù)據(jù) 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) 設(shè)置 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); // 讀回對象數(shù)據(jù) 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逐個注冊方法 → 方法實現(xiàn)為靜態(tài)函數(shù)。注意CLS_INIT宏展開后帶CEPH_CLS_API[[gnu::visibility(default)]]可見性屬性保證__cls_init符號可被動態(tài)加載器找到。4.3 構(gòu)建集成CMake 如何編譯與安裝在 src/cls/CMakeLists.txt 中cls_sdk與其他樹內(nèi)對象類一樣構(gòu)建為共享庫并安裝到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})關(guān)鍵點cls_sdk只鏈接了include/rados/objclass.h及其依賴buffer.h、cls_traits.hpp、cls_flags.hpp沒有鏈接任何src/下的內(nèi)部庫。這正是 SDK 對象類可以在樹外復(fù)現(xiàn)構(gòu)建的前提——你只需把cls_sdk.cccls_sdk_ops.h 安裝好的頭文件帶走用任意構(gòu)建系統(tǒng)Makefile/CMake/其他編譯成libcls_xxx.so再復(fù)制到 OSD 的rados-classes目錄即可被加載。構(gòu)建后生成的庫文件為libcls_sdk.soSONAME 為 1版本 1.0.0安裝到lib/rados-classes/下。OSD 啟動時會掃描該目錄并動態(tài)加載其中的對象類。五、用 ceph_test_cls_sdk 單測驗證對象類單元測試位于 src/test/cls_sdk/由 src/test/cls_sdk/CMakeLists.txt 構(gòu)建出ceph_test_cls_sdk可執(zhí)行文件鏈接librados、global、radostest-cxx與 gtest。src/test/cls_sdk/test_cls_sdk.cc 展示了客戶端如何通過 librados 的ObjectWriteOperation::exec調(diào)用對象類方法TEST_P(TestClsSDK, TestSDKCoverageWrite) { bufferlist in; librados::ObjectWriteOperation op; op.exec(method::test_coverage_write, in); // exec 調(diào)用 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); // 先寫后讀驗證數(shù)據(jù)一致性 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); } );幾點值得注意的工程實踐執(zhí)行方式對象類方法通過ObjectWriteOperation::exec(method, in)加入操作序列再由ioctx.operate(oid, op)一次性提交。op.exec的第一個參數(shù)直接復(fù)用cls_sdk_ops.h中聲明的method::test_coverage_write常量——方法名在客戶端與服務(wù)端由同一頭文件保證一致杜絕手寫字符串拼寫錯誤。對象命名測試固定使用對象myobjectTestSDKCoverageReplay先寫后讀完整驗證了“寫路徑數(shù)據(jù)xattromap→ 讀路徑數(shù)據(jù)xattromap→ 刪除”的往返一致性。池類型參數(shù)化測試通過INSTANTIATE_TEST_SUITE_P同時跑在REPLICATED副本池與FAST_EC快速糾刪碼池上說明對象類方法對這兩種主流池類型都兼容——這對驗證 SDK 對象類在 EC 池上的可用性很有價值。運行該測試的方式在已構(gòu)建的測試環(huán)境中./ceph_test_cls_sdk或通過 CTest 執(zhí)行ctest -R cls_sdk測試通過即證明cls_sdk被 OSD 成功加載、方法被正確注冊、對象類在副本池與 EC 池上均能完成數(shù)據(jù)讀寫。六、開發(fā)自己的樹外對象類推薦流程結(jié)合上述全部內(nèi)容可以總結(jié)出一份“樹外 SDK 對象類”開發(fā)清單獲取接口安裝 Ceph 后確認include/rados/objclass.h、cls_traits.hpp、cls_flags.hpp、buffer.h等頭文件可用準備三件套方法聲明頭文件仿照 cls_sdk_ops.h用ClsMethodTag, ClassDef定義類名與方法及讀寫標志實現(xiàn)文件仿照 cls_sdk.cc包含CLS_VER、CLS_NAME、方法靜態(tài)函數(shù)與CLS_INIT注冊邏輯構(gòu)建腳本把實現(xiàn)編譯為libcls_名字.so僅需 include 安裝好的頭文件目錄無需鏈接 Ceph 內(nèi)部庫部署將.so放入 OSD 的rados-classes目錄默認lib/rados-classes/與樹內(nèi)對象類安裝位置一致客戶端調(diào)用客戶端代碼 include 你的方法聲明頭文件用ObjectWriteOperation::execioctx.operate調(diào)用方法與ceph_test_cls_sdk的測試寫法完全一致測試驗證參考 test_cls_sdk.cc至少在 REPLICATED 池上做寫/讀往返測試有條件時再覆蓋 EC 池。七、總結(jié)Ceph Object Class SDK 通過公開、獨立的objclass.h接口把“對象類開發(fā)”從 Ceph 源碼樹中解放出來樹內(nèi)對象類繼續(xù)依賴內(nèi)部實現(xiàn)隨 Ceph 發(fā)布而 SDK 對象類只依賴安裝后的公開頭文件可在樹外構(gòu)建并以獨立軟件包分發(fā)。本文從設(shè)計動機、頭文件安裝、objclass.h全量 API宏、句柄、注冊、對象操作、C 封裝、cls_sdk示例逐行解析、構(gòu)建集成到單測驗證完整覆蓋了 SDK 對象類的開發(fā)閉環(huán)。動手實踐時直接以 src/cls/sdk/ 為模板、以 src/test/cls_sdk/test_cls_sdk.cc 為驗收標準即可快速產(chǎn)出自己的樹外對象類。贊分享存儲分布式文件系統(tǒng)對象存儲后端高可用【免費下載鏈接】cephCeph is a distributed object, block, and file storage platform項目地址https://gitcode.com/gh_mirrors/ce/ceph點擊查看免費下載相關(guān)推薦如何用 start-cluster.sh 為 LibreChat 本地搭建三節(jié)點 Redis Cluster 并通過 .env 連接驗證如何用 start cluster.sh 為 LibreChat 本地搭建三節(jié)點 Redis Cluster 并通過 .env 連接驗證 LibreChat 本存儲分布式文件系統(tǒng)對象存儲后端高可用Ceph 對象網(wǎng)關(guān)Ceph Object Gateway / radosgw架構(gòu)與實戰(zhàn)指南S3/Swift 兼容對象存儲從部署到管理Ceph 對象網(wǎng)關(guān)Ceph Object Gateway / radosgw架構(gòu)與實戰(zhàn)指南S3/Swift 兼容對象存儲從部署到管理 Ceph Objec存儲分布式文件系統(tǒng)對象存儲后端高可用擴展 Ceph用 Ceph Class 為對象存儲編寫自定義對象方法的完整指南擴展 Ceph用 Ceph Class 為對象存儲編寫自定義對象方法的完整指南 導(dǎo)讀 Ceph 是一個分布式對象、塊與文件存儲平臺其對象存儲層RADOS存儲分布式文件系統(tǒng)對象存儲后端高可用創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考