在demo实现中,第一版的异步化处理中,采用的是类似redis中对list的BLOCK类型操作,但是与list BLOCK操作不同的是,list的BLOCK操作是全部在主线程完成,不会涉及多线程的资源竞争,实现起来也比较容易,访问发生磁盘IO时,需要BIO线程处理,根据BIO线程的操作结果来修改内存的db。
上一版的实现没有充分考虑到多线程对临界资源保护,key和client的block状态维护比较复杂,导致频繁core,内存写飞,所以对实现进行了重新梳理和重构。
重构后的思路:
所有的内存数据操作严格交给主线程,BIO线程只是单独的进行磁盘读写和内存对象的序列化和反序列化,绝不涉及修改主线程中的db或其他的状态。
主线程和BIO线程通过队列进行通信,主线程创建异步IO任务,通知IO线程,IO线程会将任务执行结果按顺序加入结果队列,主线程会处理结果返回队列,真正完成IO任务。
所以主线程和异步IO线程只需要对通信队列进行加锁就可以防止产生竞争。
如果压力过大,或者磁盘IO抖动,会导致任务队列堆积,占用大量内存,加剧server对数据的强制淘汰,进而引发雪崩,所以需要对任务队列进行控制。
当任务队列大于特定大小后,主线程会等待IO线程处理任务,这时主线程会进行等待,阻塞客户端,在保护自身的同时,进行了客户端限流。
db中需要两个dict用来保存load和save任务中的key所阻塞的客户端列表,客户端需要保存被哪些key阻塞,主线程在收到异步线程的执行结果后,将key从客户端中阻塞key中移除,检查客户端中是否还有被阻塞的key,如果没有主线程会再次对客户端进行处理。
对于RocksDB,删除操作就是写入操作,所以将删除也当作写入来对待,异步任务交给BIO SAVE线程处理。
这样对于阻塞状态管理和多线程竞争的处理,降低逻辑复杂度。