yfs是一个分布式文件系统,第三步在第一步的基础上改进,让锁服务支持缓存

前言

这次实验实现在锁客户端缓存锁,减轻锁服务器的负载.

例如应用使用YFS在同一目录下连续创建100个文件,按照LAB 3 的实现,需要acquire100次目录锁,同样也需要release100次. 这种方式增加了锁服务器的负载,其实只需要acquire一次锁,然后缓存锁,最后使用完后再释放就可以了,这样之前的100次acquire和100次release只需要1次acquire和1次release.

本次实验实现acquire锁后在锁客户端缓存锁, 当有其他客户端需要该锁时再释放该锁.

设计协议

  1. 每个客户端可能有多个线程获取同一个锁,但是每一个客户端只允许一个线程(并不要求是哪个特定的线程,而是不允许多个线程与锁服务器交互)与锁服务器进行交互, 一旦一个线程已经获取到锁,然后释放锁就可以唤醒同一客户端在等待的其他线程

  2. 当一个客户端使用acquireRPC请求一个锁,如果锁没有被其他的客户端获取(FREE),那么锁服务器返回OK, 如果锁不是FREE,并且有其他的客户端等待这个锁,那么返回一个ENTRY. 如果锁不是FREE,并且没有其他客户端等待这个锁,那么锁服务器revoke RPC到锁的拥有者,等待锁的拥有者释放这个锁,最终锁服务器发送一个retry RPC给等待该锁的下一个客户端,通知它再次尝试获取.

  3. 一旦一个客户端拥有了一个锁,那么客户端缓存这个锁(即当释放这个锁时并不发送一个release RPC给锁服务器). 客户端可以将锁给同一个客户端的其他线程而且不需要与服务器交互.

  4. 锁服务器通过发送一个revoke RPC给客户端来收回客户端缓存的锁,这个revoke请求告诉客户端当release锁时立即将锁返回给服务器,或者如果没有线程当前持有这个锁,那么就立即将锁返回给锁服务器.

  5. 锁服务器应该记录每个锁的持有者ID(hostname:port),这样锁服务器才知道revoke RPC发送给哪个客户端. 另外还需要记录哪些客户端在等待这个锁,这样锁服务器可以向其中一个发送retry RPC.

  6. 当发送一个RPC时不要持有任何mutex, RPC通常需要较长的时间,这会让其他的线程一直等待,另为在RPC时持有mutex容易导致分布式锁死.

锁客户端协议处理

锁客户端中缓存的锁有下面5个状态:

  • NONE: 客户端不知道该锁的任何信息,该锁可能还在服务器上,或者被别的客户端持有.
  • FREE:当前客户端拥有这个锁,并且在这个客户端上没有线程持有这个锁.
  • LOCKED: 当前客户端拥有这个锁,并且锁被某个线程持有.
  • ACQUIRGING: 当前客户端有线程正在向服务器尝试获取这个锁.
  • RELEASING 当前客户端正在尝试将锁返回给服务器.

锁服务器端协议处理

锁服务器端的锁有下面四个状态:

  • FREE: 该锁未被任何客户端持有
  • LOCKED: 锁被某个客户端持有,没有其他客户端等待该锁
  • LOCKED_AND_WAIT: 锁被某个客户端持有,并且有其他客户端等待该锁
  • RETRYING: 锁服务器正在向某个客户端发送retry RPC.

测试

其中一个终端:

$ export RPC_LOSSY=0
$ ./lock_server 3772

另一个终端:

$ ./lock_tester 3772
cache lock client
acquire a release a acquire a release a
...
...
./lock_tester: passed all tests successfully

开启RPC丢失后重复上面的测试:

export RPC_LOSSY = 5