Ceph librados 开发指南:用 RADOS API 构建自定义存储接口
【免费下载链接】cephCeph is a distributed object, block, and file storage platform项目地址: https://gitcode.com/gh_mirrors/ce/ceph
Ceph 存储集群(Ceph Storage Cluster)在底层由 RADOS(Reliable Autonomic Distributed Object Store)驱动,同时向上提供对象、块和文件三类存储服务;但你不必局限于 RESTful、块设备或 POSIX 接口——基于 RADOS 的libradosAPI 允许你为 Ceph 存储集群编写属于自己的客户端接口。本指南以 librados-intro.rst 为主线,从环境安装、集群句柄配置、I/O 上下文创建到会话关闭,完整演示 C/C++、Python、Java 与 PHP 四种绑定的接入流程,并结合仓库源码(librados.h、librados.hpp、rados.pyx 及 examples/librados 下的示例)说明底层机制,使读者能够独立完成“连接集群 → 读写对象 → 清理资源”的完整开发闭环。
1. 理解 librados:对象、块、文件三种接口的统一底座
Ceph 存储集群提供了最基本的存储服务,使 Ceph 能够在同一套统一系统中提供对象、块和文件三种存储形态。关键在于:这些上层接口都构建在同一个底层协议之上——RADOS。librados就是这个底层协议的原生编程接口,它让你能够绕开 RBD、RGW 等现成服务,直接与存储集群对话,自由定义自己的访问方式。
使用libradosAPI 时,客户端应用需要与存储集群中的两类守护进程交互:
- Ceph Monitor(监视器):维护集群地图(Cluster Map)的主副本,负责集群状态、认证与协调;
- Ceph OSD Daemon(对象存储守护进程):以对象为单位把数据存放到存储节点上,并负责数据的复制与恢复。
librados是 Ceph 的“母语”——RBD(块设备)、RGW(对象网关)、CephFS(文件系统)在仓库中无一例外地构建在它之上。例如 src/librbd 与 src/rgw 的代码都以librados作为唯一的存储后端,这正体现了“一套底座、三种接口”的设计哲学。
2. 环境准备:安装各语言绑定
使用 API 前需要一个正在运行的 Ceph 存储集群,部署可参考 安装指南(Quick)。librados本身用 C++ 编写,并提供了 C、Python、Java 与 PHP 的语言绑定,针对不同发行版需要安装不同的开发包。
2.1 C/C++ 开发包
Debian/Ubuntu 系列:
sudo apt-get install librados-devRHEL/CentOS 系列:
sudo yum install librados2-devel libradospp-devel安装完成后,C/C++ 所需的头文件位于/usr/include/rados,可用以下命令确认:
ls /usr/include/rados其中librados.h是 C 接口头文件,librados.hpp是 C++ 接口头文件——两者都定义了完整的 API 声明与详细的文档注释(如 librados.h 中rados_create2、rados_connect、rados_conf_read_file等函数的签名与前置条件说明)。
2.2 Python 绑定
rados模块为 Python 应用提供librados支持,安装python3-rados包即可:
# Debian/Ubuntu sudo apt-get install python3-rados # RHEL/CentOS sudo yum install python3-rados # SLE/openSUSE sudo zypper install python3-rados模块位置因发行版而异:Debian 系统通常位于/usr/share/pyshared,CentOS/RHEL 通常位于/usr/lib/python*/site-packages。该绑定由 Cython 编写,主实现在 src/pybind/rados/rados.pyx,其中定义了Rados集群类(第 390 行起)与IoCtx上下文类,并把 C++ 错误转换为 Python 异常。
2.3 Java 绑定
Java 绑定基于 JNA 调用 C 库,需要按以下步骤安装:
安装
jna.jar:- Debian/Ubuntu:
sudo apt-get install libjna-java - CentOS/RHEL:
sudo yum install jna - JAR 文件位于
/usr/share/java。
- Debian/Ubuntu:
克隆
rados-java仓库并构建:
git clone --recursive https://github.com/ceph/rados-java.git cd rados-java ant- 将构建出的 JAR 复制到公共位置,并确保其与 JNA JAR 都在 JVM 的 classpath 中:
sudo cp target/rados-0.1.3.jar /usr/share/java/rados-0.1.3.jar sudo ln -s /usr/share/java/jna-3.2.7.jar /usr/lib/jvm/default-java/jre/lib/ext/jna-3.2.7.jar sudo ln -s /usr/share/java/rados-0.1.3.jar /usr/lib/jvm/default-java/jre/lib/ext/rados-0.1.3.jar构建文档可执行ant docs。
2.4 PHP 扩展
安装 php-dev:
- Debian/Ubuntu:
sudo apt-get install php5-dev build-essential - CentOS/RHEL:
sudo yum install php-devel
- Debian/Ubuntu:
克隆并编译
phprados:
git clone https://github.com/ceph/phprados.git cd phprados phpize ./configure make sudo make install- 在
php.ini中添加一行启用扩展:
extension=rados.so3. 核心概念:客户端如何找到数据
在使用 API 之前,先理解客户端的数据定位流程:Ceph 客户端通过librados与 OSD 直接交互来存取数据。为了与 OSD 通信,客户端应用必须调用librados并先连接到一个 Ceph Monitor;连接成功后,librados从 Monitor 获取集群地图(Cluster Map)。当客户端要读写数据时,它创建一个I/O 上下文(I/O Context)并绑定到某个存储池(Pool)。池关联了一条CRUSH 规则,该规则定义了数据在集群中的放置方式。
经由 I/O 上下文,客户端把对象名交给librados,librados结合对象名与集群地图(即集群拓扑)计算出对象所属的放置组(Placement Group)以及负责该 PG 的 OSD,随后客户端即可直接读写数据——客户端应用完全不需要了解集群的物理拓扑。关于 PG 的计算细节见 计算 PG ID,PG 到 OSD 的映射见 将 PG 映射到 OSD,OSD 负责复制与“智能守护进程”机制见 智能守护进程实现超大规模。
整个流程可概括为:客户端从 Monitor 拉取集群地图 → 创建 I/O 上下文绑定到 Pool → Pool 通过 CRUSH 规则决定数据放置 → 客户端直接与 OSD 通信。
4. 第二步:创建并配置集群句柄
集群句柄(Cluster Handle)封装了客户端连接所需的全部配置:
rados_create()使用的用户 ID(如admin),或rados_create2()使用的用户名(如client.admin,推荐使用后者,因为它允许完整指定名字与集群名);- cephx 认证密钥;
- Monitor ID 与 IP 地址;
- 日志级别与调试级别。
因此使用集群的第一步是:① 创建集群句柄;② 用该句柄连接集群。连接时应用必须提供 Monitor 地址、用户名和认证密钥(CephX 默认启用)。
提示:连接不同的 Ceph 存储集群,或以不同用户连接同一个集群,都需要不同的集群句柄。
RADOS 提供了多种设置所需值的方式。对 Monitor 与加密密钥设置而言,最便捷的办法是确保 Ceph 配置文件中包含指向 keyring 文件的keyring路径,以及至少一个 Monitor 地址(如mon_host)。例如:
[global] mon_host = 192.168.1.1 keyring = /etc/ceph/ceph.client.admin.keyring创建句柄之后,你可以通过三种途径为其注入配置:
- 读取 Ceph 配置文件:
rados_conf_read_file();若传入NULL路径,则按$CEPH_CONF→/etc/ceph/ceph.conf→~/.ceph/config→ 当前目录下ceph.conf的顺序查找默认位置(见 librados.h 中rados_conf_read_file的注释); - 解析命令行参数:
rados_conf_parse_argv(),支持任何 Ceph 命令行选项,例如--mon-host 10.0.0.1:6789、--mon_host 10.0.0.1:6789与-m 10.0.0.1:6789三者等价; - 解析环境变量:
rados_conf_parse_env(),默认读取CEPH_ARGS环境变量,其内容按命令行选项语法解析。
部分语言包装可能未实现这些便捷方法,需要自行实现。值得注意的是,rados_create()在调用时就会读取 Ceph 环境变量——如果$CEPH_ARGS已经指定了连接所需的一切,就无需进一步配置。
连接成功后,仅凭集群句柄即可调用影响整个集群的函数,例如:获取集群统计信息、执行池操作(存在性检查、创建、列举、删除)、读取与设置配置。而 Ceph 的强力特性之一是能够绑定不同的池:每个池可有不同的放置组数量、对象副本数与复制策略——例如 SSD 承载热数据的“热池”,或使用纠删码的“冷池”。
C 绑定与 C++/Java/Python 这类面向对象绑定最大的差异在于:后者使用对象来抽象集群句柄、I/O 上下文、迭代器与异常等概念。
4.1 C 示例:创建句柄并连接
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <rados/librados.h> int main (int argc, const char **argv) { /* 声明集群句柄与所需参数。 */ rados_t cluster; char cluster_name[] = "ceph"; char user_name[] = "client.admin"; uint64_t flags = 0; /* 以 "ceph" 集群名和 "client.admin" 用户初始化集群句柄。 */ int err; err = rados_create2(&cluster, cluster_name, user_name, flags); if (err < 0) { fprintf(stderr, "%s: Couldn't create the cluster handle! %s\n", argv[0], strerror(-err)); exit(EXIT_FAILURE); } else { printf("\nCreated a cluster handle.\n"); } /* 读取 Ceph 配置文件来配置集群句柄。 */ err = rados_conf_read_file(cluster, "/etc/ceph/ceph.conf"); if (err < 0) { fprintf(stderr, "%s: cannot read config file: %s\n", argv[0], strerror(-err)); exit(EXIT_FAILURE); } else { printf("\nRead the config file.\n"); } /* 解析命令行参数。 */ err = rados_conf_parse_argv(cluster, argc, argv); if (err < 0) { fprintf(stderr, "%s: cannot parse command line arguments: %s\n", argv[0], strerror(-err)); exit(EXIT_FAILURE); } else { printf("\nRead the command line arguments.\n"); } /* 连接集群。 */ err = rados_connect(cluster); if (err < 0) { fprintf(stderr, "%s: cannot connect to cluster: %s\n", argv[0], strerror(-err)); exit(EXIT_FAILURE); } else { printf("\nConnected to the cluster.\n"); } }编译并链接librados(-lrados):
gcc ceph-client.c -lrados -o ceph-client仓库中的 hello_world_c.c 提供了一个更完整的 C 参考实现:它使用rados_create()+rados_conf_parse_argv()+rados_conf_read_file()完成初始化,随后创建自有池、写对象、异步读回、设置 xattr,最后删除池并rados_shutdown()优雅退出——该文件头部注释明确提示“install the librados-dev package to get this”,与上文安装步骤呼应。
4.2 C++ 示例
Ceph 项目在ceph/examples/librados目录提供了 C++ 示例。C++ 使用librados::Rados集群句柄对象:
#include <iostream> #include <string> #include <rados/librados.hpp> int main(int argc, const char **argv) { int ret = 0; /* 声明集群句柄与所需变量。 */ librados::Rados cluster; char cluster_name[] = "ceph"; char user_name[] = "client.admin"; uint64_t flags = 0; /* 以 "ceph" 集群名和 "client.admin" 用户初始化集群句柄。 */ { ret = cluster.init2(user_name, cluster_name, flags); if (ret < 0) { std::cerr << "Couldn't initialize the cluster handle! error " << ret << std::endl; return EXIT_FAILURE; } else { std::cout << "Created a cluster handle." << std::endl; } } /* 读取 Ceph 配置文件。 */ { ret = cluster.conf_read_file("/etc/ceph/ceph.conf"); if (ret < 0) { std::cerr << "Couldn't read the Ceph configuration file! error " << ret << std::endl; return EXIT_FAILURE; } else { std::cout << "Read the Ceph configuration file." << std::endl; } } /* 解析命令行参数。 */ { ret = cluster.conf_parse_argv(argc, argv); if (ret < 0) { std::cerr << "Couldn't parse command line options! error " << ret << std::endl; return EXIT_FAILURE; } else { std::cout << "Parsed command line options." << std::endl; } } /* 连接集群。 */ { ret = cluster.connect(); if (ret < 0) { std::cerr << "Couldn't connect to cluster! error " << ret << std::endl; return EXIT_FAILURE; } else { std::cout << "Connected to the cluster." << std::endl; } } return 0; }编译源文件并链接librados:
g++ -g -c ceph-client.cc -o ceph-client.o g++ -g ceph-client.o -lrados -o ceph-client对应地,examples/librados/hello_world.cc 展示了完整的 C++ 流程,其中特别指出bufferlist是 Ceph 原生的传输类型,设计上尽量减少拷贝;只要 bufferlist 仍在使用,就不要释放其内存。C++ 示例还用到了ObjectWriteOperation把“写数据 + 设置 xattr + xattr 条件比较”合并为一次原子操作(cmpxattr条件不满足时写入失败),展示了 librados 对象操作的强大表达能力。仓库中的 Makefile 同时给出了针对构建树内 librados 与系统 librados(make all-system)两种编译方式,并链接了-lrados -lradosstriper。
4.3 Python 示例
Python 绑定默认使用admin用户和ceph集群名;当conffile参数设为空字符串时,会读取标准的ceph.conf文件。Python 绑定把 C++ 错误转换为异常:
import rados try: cluster = rados.Rados(conffile='') except TypeError as e: print('Argument validation error: {}'.format(e)) raise e print("Created cluster handle.") try: cluster.connect() except Exception as e: print("connection error: {}".format(e)) raise e finally: print("Connected to the cluster.")执行示例验证连接:
python ceph-client.py从源码看,rados.pyx 中的Rados.__init__会做参数校验,connect()(第 674 行)与pool_exists()(第 731 行)、open_ioctx()(第 975 行)等方法都直接封装了对应的 C 函数,并把返回值转换为 Python 异常类型。
4.4 Java 示例
Java 需要指定用户 ID(admin)或用户名(client.admin),默认使用ceph集群名。Java 绑定同样把 C++ 错误转换为异常:
import com.ceph.rados.Rados; import com.ceph.rados.RadosException; import java.io.File; public class CephClient { public static void main (String args[]){ try { Rados cluster = new Rados("admin"); System.out.println("Created cluster handle."); File f = new File("/etc/ceph/ceph.conf"); cluster.confReadFile(f); System.out.println("Read the configuration file."); cluster.connect(); System.out.println("Connected to the cluster."); } catch (RadosException e) { System.out.println(e.getMessage() + ": " + e.getReturnValue()); } } }编译并运行(若已将 JAR 复制到/usr/share/java并从ext目录做了符号链接,则无需指定 classpath):
javac CephClient.java java CephClient4.5 PHP 示例
启用 RADOS 扩展后,PHP 中创建集群句柄非常直接:
<?php $r = rados_create(); rados_conf_read_file($r, '/etc/ceph/ceph.conf'); if (!rados_connect($r)) { echo "Failed to connect to Ceph cluster"; } else { echo "Successfully connected to Ceph cluster"; }保存为rados.php并运行:
php rados.php5. 第三步:创建 I/O 上下文并进行对象读写
获得集群句柄并成功连接后,就可以创建I/O 上下文(I/O Context)开始读写数据。I/O 上下文把连接绑定到特定池;用户必须具备访问该池的相应CAPS 权限——例如只有读权限的用户只能读数据。关于用户管理与授权能力(CAPS)的配置详见 用户管理。I/O 上下文提供的功能包括:
- 读写数据与扩展属性(xattr);
- 列举并迭代对象与扩展属性;
- 对池做快照、列举快照等。
RADOS 同时支持同步与异步交互。一旦拥有 I/O 上下文,读写操作只需知道对象/xattr 的名字即可;封装在librados中的 CRUSH 算法利用集群地图确定合适的 OSD,OSD 守护进程负责复制(见 智能守护进程实现超大规模),librados同时负责把对象映射到放置组(见 计算 PG ID)。
以下示例使用默认的data池。当然也可以用 API 列举池、确认池存在,或创建、删除池——写入操作展示同步模式,读取操作展示异步模式。
重要:使用 API 删除池务必谨慎!删除池将导致池及其全部数据永久丢失。
5.1 C 示例:读写对象与 xattr
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <rados/librados.h> int main (int argc, const char **argv) { /* 承接前一个 C 示例:集群句柄与连接已建立。先声明 I/O 上下文。 */ rados_ioctx_t io; char *poolname = "data"; err = rados_ioctx_create(cluster, poolname, &io); if (err < 0) { fprintf(stderr, "%s: cannot open rados pool %s: %s\n", argv[0], poolname, strerror(-err)); rados_shutdown(cluster); exit(EXIT_FAILURE); } else { printf("\nCreated I/O context.\n"); } /* 同步写入数据。 */ err = rados_write(io, "hw", "Hello World!", 12, 0); if (err < 0) { fprintf(stderr, "%s: Cannot write object \"hw\" to pool %s: %s\n", argv[0], poolname, strerror(-err)); rados_ioctx_destroy(io); rados_shutdown(cluster); exit(1); } else { printf("\nWrote \"Hello World\" to object \"hw\".\n"); } char xattr[] = "en_US"; err = rados_setxattr(io, "hw", "lang", xattr, 5); if (err < 0) { fprintf(stderr, "%s: Cannot write xattr to pool %s: %s\n", argv[0], poolname, strerror(-err)); rados_ioctx_destroy(io); rados_shutdown(cluster); exit(1); } else { printf("\nWrote \"en_US\" to xattr \"lang\" for object \"hw\".\n"); } /* 异步读数据。首先建立异步 I/O 完成回调。 */ rados_completion_t comp; err = rados_aio_create_completion(NULL, NULL, NULL, &comp); if (err < 0) { fprintf(stderr, "%s: Could not create aio completion: %s\n", argv[0], strerror(-err)); rados_ioctx_destroy(io); rados_shutdown(cluster); exit(1); } else { printf("\nCreated AIO completion.\n"); } /* 使用 rados_aio_read 读取数据。 */ char read_res[100]; err = rados_aio_read(io, "hw", comp, read_res, 12, 0); if (err < 0) { fprintf(stderr, "%s: Cannot read object. %s %s\n", argv[0], poolname, strerror(-err)); rados_ioctx_destroy(io); rados_shutdown(cluster); exit(1); } else { printf("\nRead object \"hw\". The contents are:\n %s \n", read_res); } /* 等待操作完成。 */ rados_aio_wait_for_complete(comp); /* 释放异步 I/O 完成句柄,避免内存泄漏。 */ rados_aio_release(comp); char xattr_res[100]; err = rados_getxattr(io, "hw", "lang", xattr_res, 5); if (err < 0) { fprintf(stderr, "%s: Cannot read xattr. %s %s\n", argv[0], poolname, strerror(-err)); rados_ioctx_destroy(io); rados_shutdown(cluster); exit(1); } else { printf("\nRead xattr \"lang\" for object \"hw\". The contents are:\n %s \n", xattr_res); } err = rados_rmxattr(io, "hw", "lang"); if (err < 0) { fprintf(stderr, "%s: Cannot remove xattr. %s %s\n", argv[0], poolname, strerror(-err)); rados_ioctx_destroy(io); rados_shutdown(cluster); exit(1); } else { printf("\nRemoved xattr \"lang\" for object \"hw\".\n"); } err = rados_remove(io, "hw"); if (err < 0) { fprintf(stderr, "%s: Cannot remove object. %s %s\n", argv[0], poolname, strerror(-err)); rados_ioctx_destroy(io); rados_shutdown(cluster); exit(1); } else { printf("\nRemoved object \"hw\".\n"); } }注:
rados_write(io, oid, buf, len, offset)的最后一个参数是写入偏移量,本例为 0 表示从对象头部写入。异步接口rados_aio_read的返回只表示请求是否成功提交,真正的读取结果要通过 completion 等待与检查(见 librados.h 中 aio 系列接口注释)。
5.2 C++ 示例
#include <iostream> #include <string> #include <rados/librados.hpp> int main(int argc, const char **argv) { /* 承接前一个 C++ 示例。先声明 I/O 上下文。 */ librados::IoCtx io_ctx; const char *pool_name = "data"; { ret = cluster.ioctx_create(pool_name, io_ctx); if (ret < 0) { std::cerr << "Couldn't set up ioctx! error " << ret << std::endl; exit(EXIT_FAILURE); } else { std::cout << "Created an ioctx for the pool." << std::endl; } } /* 同步写入一个对象。 */ { librados::bufferlist bl; bl.append("Hello World!"); ret = io_ctx.write_full("hw", bl); if (ret < 0) { std::cerr << "Couldn't write object! error " << ret << std::endl; exit(EXIT_FAILURE); } else { std::cout << "Wrote new object 'hw' " << std::endl; } } /* 给对象添加一个 xattr。 */ { librados::bufferlist lang_bl; lang_bl.append("en_US"); ret = io_ctx.setxattr("hw", "lang", lang_bl); if (ret < 0) { std::cerr << "failed to set xattr version entry! error " << ret << std::endl; exit(EXIT_FAILURE); } else { std::cout << "Set the xattr 'lang' on our object!" << std::endl; } } /* 异步读回对象。 */ { librados::bufferlist read_buf; int read_len = 4194304; // 创建 I/O Completion。 librados::AioCompletion *read_completion = librados::Rados::aio_create_completion(); // 发送读请求。 ret = io_ctx.aio_read("hw", read_completion, &read_buf, read_len, 0); if (ret < 0) { std::cerr << "Couldn't start read object! error " << ret << std::endl; exit(EXIT_FAILURE); } // 等待请求完成,并检查是否成功。 read_completion->wait_for_complete(); ret = read_completion->get_return_value(); if (ret < 0) { std::cerr << "Couldn't read object! error " << ret << std::endl; exit(EXIT_FAILURE); } else { std::cout << "Read object hw asynchronously with contents.\n" << read_buf.c_str() << std::endl; } } /* 读取 xattr。 */ { librados::bufferlist lang_res; ret = io_ctx.getxattr("hw", "lang", lang_res); if (ret < 0) { std::cerr << "failed to get xattr version entry! error " << ret << std::endl; exit(EXIT_FAILURE); } else { std::cout << "Got the xattr 'lang' from object hw!" << lang_res.c_str() << std::endl; } } /* 移除 xattr。 */ { ret = io_ctx.rmxattr("hw", "lang"); if (ret < 0) { std::cerr << "Failed to remove xattr! error " << ret << std::endl; exit(EXIT_FAILURE); } else { std::cout << "Removed the xattr 'lang' from our object!" << std::endl; } } /* 移除对象。 */ { ret = io_ctx.remove("hw"); if (ret < 0) { std::cerr << "Couldn't remove object! error " << ret << std::endl; exit(EXIT_FAILURE); } else { std::cout << "Removed object 'hw'." << std::endl; } } }C++ 的IoCtx类在 librados.hpp 中声明了完整的操作集:write_full(第 923 行)、aio_read(第 1144 行起)、aio_write/aio_append/aio_remove(第 1205-1229 行)等。仓库 examples/librados/hello_world.cc 还展示了cmpxattr条件原子更新:先构造一个 xattr 条件不满足的写操作验证其失败,再用正确的条件值完成“比较-写-更新 xattr”的三合一原子操作。
5.3 Python 示例
print("\n\nI/O Context and Object Operations") print("=================================") print("\nCreating a context for the 'data' pool") if not cluster.pool_exists('data'): raise RuntimeError('No data pool exists') ioctx = cluster.open_ioctx('data') print("\nWriting object 'hw' with contents 'Hello World!' to pool 'data'.") ioctx.write("hw", b"Hello World!") print("Writing XATTR 'lang' with value 'en_US' to object 'hw'") ioctx.set_xattr("hw", "lang", b"en_US") print("\nWriting object 'bm' with contents 'Bonjour tout le monde!' to pool 'data'.") ioctx.write("bm", b"Bonjour tout le monde!") print("Writing XATTR 'lang' with value 'fr_FR' to object 'bm'") ioctx.set_xattr("bm", "lang", b"fr_FR") print("\nContents of object 'hw'\n------------------------") print(ioctx.read("hw")) print("\n\nGetting XATTR 'lang' from object 'hw'") print(ioctx.get_xattr("hw", "lang")) print("\nContents of object 'bm'\n------------------------") print(ioctx.read("bm")) print("\n\nGetting XATTR 'lang' from object 'bm'") print(ioctx.get_xattr("bm", "lang")) print("\nRemoving object 'hw'") ioctx.remove_object("hw") print("Removing object 'bm'") ioctx.remove_object("bm")Python 的write/read/set_xattr/get_xattr/remove_object等方法的底层实现同样位于 rados.pyx 的IoCtx类中,对 C API 做了面向对象封装并统一抛出异常。
5.4 Java 示例
import com.ceph.rados.Rados; import com.ceph.rados.RadosException; import java.io.File; import com.ceph.rados.IoCTX; public class CephClient { public static void main (String args[]){ try { Rados cluster = new Rados("admin"); System.out.println("Created cluster handle."); File f = new File("/etc/ceph/ceph.conf"); cluster.confReadFile(f); System.out.println("Read the configuration file."); cluster.connect(); System.out.println("Connected to the cluster."); IoCTX io = cluster.ioCtxCreate("data"); String oidone = "hw"; String contentone = "Hello World!"; io.write(oidone, contentone); String oidtwo = "bm"; String contenttwo = "Bonjour tout le monde!"; io.write(oidtwo, contenttwo); String[] objects = io.listObjects(); for (String object: objects) System.out.println(object); io.remove(oidone); io.remove(oidtwo); cluster.ioCtxDestroy(io); } catch (RadosException e) { System.out.println(e.getMessage() + ": " + e.getReturnValue()); } } }5.5 PHP 示例
<?php $io = rados_ioctx_create($r, "mypool"); rados_write_full($io, "oidOne", "mycontents"); rados_remove("oidOne"); rados_ioctx_destroy($io);注意:PHP 示例中创建的池名为
mypool,与默认的data池不同——使用前需确认该池已存在且当前用户具备相应 CAPS 权限。
6. 第四步:关闭会话与资源清理
应用结束对 I/O 上下文与集群句柄的使用后,应当关闭连接并关闭句柄。对于异步 I/O,还应确保所有挂起的异步操作已完成——C API 文档特别提醒:rados_shutdown()并不保证异步写入已经完成,必须先对全部打开的 I/O 上下文调用rados_aio_flush()(见 librados.h 中rados_shutdown的注释);同时它隐式调用rados_watch_flush(),如果使用了 watch 机制,应在销毁相关 IoCtx 前显式调用。
各语言关闭方式对照:
/* C */ rados_ioctx_destroy(io); rados_shutdown(cluster);// C++ io_ctx.close(); cluster.shutdown();// Java cluster.ioCtxDestroy(io); cluster.shutDown();# Python print("\nClosing the connection.") ioctx.close() print("Shutting down the handle.") cluster.shutdown()<?php rados_shutdown($r);仓库示例 examples/librados/hello_world_c.c 与 examples/librados/hello_world.cc 都在out:标签处统一执行rados_ioctx_destroy/rados.pool_delete/rados_shutdown,是资源清理的标准范式:即使中途出错,也要先销毁 ioctx,再删除自建测试池,最后关闭集群句柄。
7. 从入门到实践:参考路径清单
- 入门与 API 总览:librados-intro.rst(本文对应原文档)、C API 参考、C++ API 参考、Python API 参考、API 索引
- C 接口声明:src/include/rados/librados.h(含
rados_create/rados_create2、rados_conf_read_file、rados_conf_parse_argv、rados_connect、rados_shutdown及全部 aio 接口的完整签名与注释) - C++ 接口声明:src/include/rados/librados.hpp(
Rados集群类、IoCtx上下文类、AioCompletion、ObjectWriteOperation) - Python 绑定实现:src/pybind/rados/rados.pyx(
Rados类与IoCtx类的 Cython 实现) - 可运行示例:examples/librados(C 版 hello_world_c.c、C++ 版 hello_world.cc、构建脚本 Makefile)
- 测试用例:src/test/librados(
aio.cc/aio_cxx.cc覆盖异步 I/O,io.cc/io_cxx.cc覆盖对象读写,pool.cc覆盖池操作,watch_notify.cc覆盖 watch/notify 机制,可作为 API 行为验证的权威参考) - 配套概念文档:集群架构、用户管理与 CAPS、池操作、计算 PG ID、智能守护进程与高可用
掌握上述流程后,你便可以从零构建基于librados的自定义存储客户端:先安装对应语言的开发包,创建并配置集群句柄(配置文件/命令行/环境变量三种注入方式),连接 Monitor 获取集群地图,再创建绑定到池的 I/O 上下文,以同步或异步方式读写对象与扩展属性,最后规范地关闭会话释放资源。
【免费下载链接】cephCeph is a distributed object, block, and file storage platform项目地址: https://gitcode.com/gh_mirrors/ce/ceph
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考