Ceph的src/os 中保存了 ObjectStore代码实现。
基本概念
对象
对象存储的概念,把对象作为存储的实体。 在Ceph 文件系统里,对象的命名空间分了两层, 第一层是Collection的概念,一个Collection就是许多Object的集合,相当于其他对象存储的group的概念,用coll_t来标识。
ObjectStore 类 就代表了一个Object, 其继承关系为 FileStore 继承了 JournalingObjectStore,JournalingObjectStore继承了ObjectStore
事务
Transaction类代表了一个事务操作。所有的操作都必须通过一个transaction来执行。 一个transaction几乎实现了所有对对象和集合的操作。
ObjectMap的概念
ObjectMap, 就是一些key,value的map属性,其保存在Object里面。 在Ceph文件系统了,ObjectMap由levelDB这个google开源的KV数据库来实现。
还有一个attributes的 概念,attributes也是一些 key/value的属性值。
首先看一下对外的接口,我们可以通过src/test/filestore/store_test.cc文件来看一下其实如何运行的:
- virtual void SetUp() {
- ::mkdir("store_test_temp_dir", 0777);
- ObjectStore *store_ = new FileStore(string("store_test_temp_dir"), string("store_test_temp_journal"));
- store.reset(store_);
- store->mkfs();
- store->mount();
- }
首先mkdir一个目录,store_test_temp_dir, 新建一个FileStore对象,其后面的参数分别是:文件的目录和日志的目录,分别也可以是一个block device. 调用mkfs 函数和 mount 函数,后面会详细介绍。
- ObjectStore::Transaction t;
- t.create_collection(cid);
- cerr << "create collection" << std::endl;
- r = store->apply_transaction(t);
- ASSERT_EQ(r, 0);
这是一个创建collection的代码 先通过一个transaction 来创建,之后通过 store 的 apply_transaction 函数来应用日志,来完成创建一个collection。
- hobject_t hoid(sobject_t("Object 1", CEPH_NOSNAP));
- {
- ObjectStore::Transaction t;
- t.touch(cid, hoid);
- cerr << "Creating object " << hoid << std::endl;
- r = store->apply_transaction(t);
- ASSERT_EQ(r, 0);
- }
同样的,可以通过transaction的接口touch来在一个collection 中创建一个对象。
在store_test.cc测试文件中,还有其它的测试操作其包括:
- 创建/删除一个collection
- 创建/删除一个collection 的 object
- omap的set/get , 这里 omap也许不容易理解,就是key/value 的map队保存在object 中,所以叫omap
- 属性的set/get
- 其它collection的操作,这里就不详述。
总之,通过store_test.cc的测试文件,可以清晰的了解Object的对外的接口。
- 在了解Object, Collection, OMap 对象存储 的基本概念,之后大家需要了解文件系统的基本的知识,如transaction, journal等,这里就不详细介绍了。下面具体分析OS模块的代码。
主要的类就是ObjectStore类,其内部有一个重要的transaction类。 JournalingObjectStore继承了ObjectStore的,添加了Journal的功能。FileStore继承了JournalingObjectStore类,实现了在文件中存储。
另一个模块就是Index,在这里实现了两层index,第一层是用来查找collection,第二层是在collection中查找一个object。 其基类为CollectionIndex, LFNIndex 类继承了它,添加了long filename index (LFNIndex)。在第二层index,也就是查找object的index 实现了两类index ,一个是HashIndex, 另一个是FlatIndex,它们都继承了LFNIndex类。其管理功能的就是IndexManager类。
在这里,HashIndex, 指的是在一个Collection的 Object 是层次的结构,说白了,就是当存储对象的目录可以有子目录。而在FlatIndex里,Object 是没有层次结构的,说白了,就是在一个目录中。
我们主要看一个典型的对象操作流程,来看一下内部的操作。
其操作一般是 操作序列号(encode)--> 写入日志(write-journal) --> 写入对象
- t.create_collection(cid);
- cerr << "create collection" << std::endl;
- r = store->apply_transaction(t);
如上图所示是一个典型的对象的操作, 我们在这里大概给出一个内部操作的流程:
1) t.create_collection(cid),
- void create_collection(coll_t cid) {
- __u32 op = OP_MKCOLL;
- ::encode(op, tbl);
- ::encode(cid, tbl);
- ops++;
- }
其调用了类transaction中的函数,把操作类型和参数通过 encode 函数 序列化到 tbl 中, 我们看到其他的操作都是类型的功能。
2)
store->apply_transaction(t);
这个函数调用了ObjectStore的函数
首先调用
unsigned apply_transaction(Transaction& t, Context *ondisk=0)
其调用 unsigned ObjectStore::apply_transactions(Sequencer *osr,
list<Transaction*> &tls,
Context *ondisk)
最终调用函数
- int ObjectStore::queue_transactions(
- Sequencer *osr,
- list<Transaction*>& tls,
- Context *onreadable,
- Context *oncommit,
- Context *onreadable_sync,
- Context *oncomplete,
- TrackedOpRef op = TrackedOpRef())
- int queue_transactions(Sequencer *osr, list<Transaction*>& tls,
- Context *onreadable, Context *ondisk=0,
- Context *onreadable_sync=0,
- TrackedOpRef op = TrackedOpRef()) {
- assert(!tls.empty());
- tls.back()->register_on_applied(onreadable);
- tls.back()->register_on_commit(ondisk);
- tls.back()->register_on_applied_sync(onreadable_sync);
- return queue_transactions(osr, tls, op);
- }
最后调用了函数
- virtual int queue_transactions(
- Sequencer *osr, list<Transaction*>& tls,
- TrackedOpRef op = TrackedOpRef()) = 0;
这个函数式virturl 函数,其实现在其继承类FileStore类函数中
- int FileStore::queue_transactions(Sequencer *posr, list<Transaction*> &tls,
- TrackedOpRef osd_op)
核心的处理模块就这这个函数里.
在这个函数里,主要分了两种情况:
1) 有日志处理的逻辑:
- if (journal && journal->is_writeable() && !m_filestore_journal_trailing) {
- {
- Op *o = build_op(tls, onreadable, onreadable_sync, osd_op);
- op_queue_reserve_throttle(o);
- journal->throttle();
- uint64_t op_num = submit_manager.op_submit_start();
- o->op = op_num;
-
-
- if (m_filestore_do_dump)
- dump_transactions(o->tls, o->op, osr);
-
-
- if (m_filestore_journal_parallel) {
- dout(5) << "queue_transactions (parallel) " << o->op << " " << o->tls << dendl;
-
- _op_journal_transactions(o->tls, o->op, ondisk, osd_op);
-
- // queue inside submit_manager op submission lock
- queue_op(osr, o);
- } else if (m_filestore_journal_writeahead) {
- dout(5) << "queue_transactions (writeahead) " << o->op << " " << o->tls << dendl;
-
- osr->queue_journal(o->op);
-
-
- _op_journal_transactions(o->tls, o->op,
- <span style="white-space:pre"> </span> new C_JournaledAhead(this, osr, o, ondisk),
- <span style="white-space:pre"> </span> osd_op);
- } else {
- assert(0);
- }
- submit_manager.op_submit_finish(op_num);
- return 0;
- }
2) 无日志处理逻辑
在int FileStore::_do_transactions 函数里,我们可以看到各种对象的操作,以及如何实现的。
DBMap函数里: omap 保存在一个key/value的数据库里。
LevelDBStore *omap_store = new LevelDBStore(omap_dir);
stringstream err;
if (omap_store->create_and_open(err)) {
delete omap_store;
derr << "Error initializing leveldb: " << err.str() << dendl;
ret = -1;
goto close_current_fd;
}
DBObjectMap *dbomap = new DBObjectMap(omap_store);
ret = dbomap->init(do_update);
if (ret < 0) {
delete dbomap;
derr << "Error initializing DBObjectMap: " << ret << dendl;
goto close_current_fd;
}
attrs 设置在 文件的扩展属性里