背景
exclusive-lock为rbd image的一个feature,是一个分布式锁,主要用于防止多个客户端同时写入image导致数据不一致问题。基本概念介绍见Ceph官方文档即可。
本文主要结论
- exclusive-lock有两种policy
- StandardPolicy:需要对接librbd的应用程序手动进行加锁、抢锁动作。
- AutomaticPolicy:librbd客户端自动进行加锁、抢锁等动作。
- exclusive-lock的时间是基于ceph osd的watcher/notify机制,每个rbd client注册一个watcher,某个client进行锁操作时通过watcher通知其他client锁的变化情况,其通知包括
- RequestLockPayload:通知lock owner的client释放锁
- AcquiredLockPayload:通知其他client,本client已经在本次争抢中拿到锁
- ReleasedLockPayload:通知其他client,本client已经释放锁
- 抢锁流程概述
- 写IO在出队时抢锁
- 查看锁的是否有其他client拿着,如果有,通知其释放锁,接收通知的client进行释放锁动作
- 若其他client没有及时释放,进行重试
- 其他client释放后,本client加锁
- 加锁成功后,通知其他client加锁成功
- exclusive-lock的使用
- AutomaticPolicy
- rbd_open拿到image的ctx,包括锁信息
- rbd_aio_write写数据到image,并入队
- 出队时所有客户端自动抢锁,公平争抢
- 抢到后执行IO
- StandardPolicy
- rbd_open拿到image的ctx,包括锁信息
- rbd_lock_break将lock owner强制break掉,其IO加入blacklist中
- rbd_lock_acquire强制将AutomaticPolicy转换成StandardPolicy
- rbd_aio_write写数据到image
- AutomaticPolicy
加锁流程代码详解
下面以抢锁为例,结合代码实现进行介绍。
librbd::io::ImageRequestWQ: _void_dequeue // 在写IO出队时加锁,加锁前会判断是否时StandardPolicy,如果是则会重新入队,直到超时
librbd::ManagedLock: acquire_lock // 此时会执行ACTION_ACQUIRE_LOCK
librbd::ManagedLock: send_acquire_lock
librbd::ExclusiveLock: pre_acquire_lock_handler
librbd::exclusive_lock::PreAcquireRequest: send_prepare_lock // 准备拿锁
librbd::exclusive_lock::PreAcquireRequest: send_flush_notifies // 将通知写入watcher
librbd::ManagedLock: handle_pre_acquire_lock
librbd::managed_lock::AcquireRequest: send_get_locker
librbd::managed_lock::GetLockerRequest: send_get_lockers // 获取当前拿锁的client
librbd::managed_lock::AcquireRequest: handle_get_locker
librbd::managed_lock::AcquireRequest: send_lock // 第一次尝试拿锁,由于另外一个client拿着,所以失败
librbd::managed_lock::AcquireRequest: send_break_lock // break拿锁client
librbd::managed_lock::BreakRequest: send_get_watchers // 获取所有的watcher,并查看watcher是否还活着,如果还活着,那么break会失败
librbd::managed_lock::BreakRequest: handle_get_watchers // 并查看watcher是否还活着,如果还活着,那么break会失败,若其他watcher已经无法连接了,则break成功
librbd::managed_lock::AcquireRequest: handle_break_lock
librbd::ManagedLock: handle_acquire_lock // 由于有其他client拿锁,此时加锁失败
librbd::ExclusiveLock: post_acquire_lock_handler
librbd::ManagedLock: is_lock_owner // 发现没有拿到锁,说明有活着的client拿锁
librbd::ImageWatcher: notify request lock
librbd::image_watcher::NotifyLockOwner: send_notify // 通知拿到锁的watcher,通知的类型为RequestLockPayload,其他客户端在handle_payload(const RequestLockPayload &payload, C_NotifyAck *ack_ctx)函数中处理。如果为AutomaticPolicy,被通知client就将当前IO放到blacklist中,并释放锁。如果为StandardPolicy,被通知的client则不会释放锁。
librbd::ManagedLock: handle_post_acquire_lock
librbd::ImageWatcher: exclusive lock requested
librbd::ImageWatcher: handle_request_lock // 若没有拿到锁会进行重试
librbd::ImageWatcher: schedule_request_lock // 进行重试
librbd::ManagedLock: send_acquire_lock // 重新执行拿锁流程
librbd::ExclusiveLock: pre_acquire_lock_handler
librbd::exclusive_lock::PreAcquireRequest: send_prepare_lock // 准备拿锁
librbd::exclusive_lock::PreAcquireRequest: send_flush_notifies // 将通知写入watcher
librbd::ManagedLock: handle_pre_acquire_lock
librbd::managed_lock::AcquireRequest: send_get_locker
librbd::managed_lock::GetLockerRequest: send_get_lockers // 获取当前拿锁的client
librbd::managed_lock::AcquireRequest: handle_get_locker // 此时会发现已经没有人拿锁了,因为前面的owner watcher接受到notify之后,主动释放了锁
librbd::managed_lock::AcquireRequest: send_lock // 加锁,持久化header
librbd::ManagedLock: handle_acquire_lock // 加锁成功
librbd::ExclusiveLock: post_acquire_lock_handler // 进行加锁后处理
librbd::ExclusiveLock: handle_post_acquiring_lock
librbd::ExclusiveLock: handle_post_acquired_lock // 拿到锁之后进行处理
librbd::ImageWatcher: notify acquired lock // 通知所有其他watcher,当前client已经拿到锁了,通知的类型为AcquiredLockPayload,其他client接收到通知后执行handle_payload(const watch_notify::AcquiredLockPayload& payload, C_NotifyAck *ctx)函数处理,这里其他client会取消到其加锁的请求,因为此次的抢锁没有抢到
librbd::io::ImageRequestWQ: set_require_lock
librbd::io::ImageRequestWQ: unblock_writes // 此client拿到锁之后,将waitqueue中的IO不进行阻塞
librbd::ManagedLock: handle_post_acquire_lock
librbd::io::ImageRequestWQ: handle_acquire_lock // 加锁成功
如何使用exclusive-lock?
AutomaticPolicy
对于自动锁,参考前面的代码介绍可知,流程如下
- rbd_open拿到image的ctx,包括锁信息
- rbd_aio_write写数据到image,并入队
- 出队时所有客户端自动抢锁,公平争抢
- 抢到后执行IO
StandardPolicy
从前面代码介绍可知,通知lock owner释放锁时,如果是StandardPolicy则不会释放锁,那么StandardPolicy是如何产生的,又是怎么应用的呢?其流程如下
- rbd_open拿到image的ctx,包括锁信息
- rbd_lock_break将lock owner强制break掉,其IO加入blacklist中
- rbd_lock_acquire强制将AutomaticPolicy转换成StandardPolicy
- rbd_aio_write写数据到image