简介
spymemcached 是一个 memcached 的客户端, 使用 NIO 实现。采用Reactor模式实现,单线程,高性能Memcached客户端。
spymemcached源码分析:
Reactor模式介绍
所谓reactor模式,其实是event-driven pattern在网络服务设计中的应用,以平衡CPU与IO速率,最大化CPU资源与IO资源的利用率;
先来看看经典的服务器设计:
经典网络服务接受客户端请求,响应请求过程可以抽象为以下步骤:
- 读取请求数据 : read
- 解码数据: decode
- 计算:compute
- 编码数据:encode
- 发送数据:send
网络服务所有动作被抽象为这个五个步骤的handler;可能每个handler有单独线程执行,或者由一个线程顺序执行;
这种经典设计中存在以下问题:
- 每个连接分配一个线程,而每个连接发送请求数据较少,导致大量空闲线程;
- 大量线程上下文切换和锁竞争
为了解决上述问题,采用两个方法实现:
- 采用分治的思想,将连接划分为更小粒度的非阻塞任务:将用户连接划分为 多个用户请求,每个请求一个线程处理;减少 请求间空闲时间占用线程
- 采用事件处理模式,分派可执行任务;通过IO 事件触发 handler 处理
采用事件处理模式的单线程Reactor模式:
单线程Reactor 处理 连接请求,所有用户请求都在同一个线程中;通过IO事件触发相应的操作;IO事件触发机制可参考java nio机制;
单线程版本的Reactor模式,还有以下问题:
- 触发事件处理 与 处理事件 在同一个线程中;减缓了Reactor的事件触发及时性
- 所有请求必须等待之前请求中IO操作之外的处理过程
- 无法解决CPU与IO速率不一致问题
- 不能有效利用多核优势
为解决上述问题,采用多线程设计方案的Reactor模式:
多线程版本的Reactor模式,有以下优势:
- Reactor可以快速触发handler执行
- IO操作以外操作由线程池中线程独立处理
- Reactor 线程满负荷 IO操作
- 平衡CPU 与 IO 速率
reactor模式就介绍到此,具体看下spymemcached中reactor模式的应用吧
spymemcached中Reactor模式实现
spymemcached中reactor模式设计到以下概念:
- IO线程:MemcachedConnection 负责处理
- 工作线程:调用spymemcached操作的线程;通常是应用线程,例如tomcat线程等等;
工作线程职责:
- 通过MemcachedClient提交操作请求给MemcachedConnection
- MemachedClient提交操作过程:实例化异步操作回执OperationFuture(该回执都会实例化一个CountDownLatch(1));通过OperationFactory 生成定制Callback操作的Operation对象;将Operation操作加入对应ShardedNode 的inputQ;返回异步结果回执OperationFuture对象
- 调用OperationFuture对象的get操作阻塞在CountDownLatch,等待后台IO线程,调用countDown;
IO线程职责:
- 由MemcachedClient初始化启动后台MemcachedConnection 线程,接受工作线程提交操作:从inputQ拷贝到writableQ;发送操作请求到Memcached服务器;添加操作到readQ,并把操作从writableQ删除;修改监听IO事件
- 执行select操作,获取感兴趣IO事件
- 执行handleIO处理:根据SelectKey发生事件类型:执行读取操作 or 写入操作;
- 当IO操作完成后,调用Operation Callback对象的receivedStatus设置回执结果;调用countDown释放阻塞的工作线程;
由此可见,spymemcached Reactor模式实现中,工作线程相当于客户端请求; IO线程相当于单线程Reactor设计中的Reactor负责接收请求,处理请求;spymemcached对数据解码部分可以扩展实现线程池方式提供解码计算,无需占用Reactor线程资源,使得Reactor线程满负荷IO操作和事件触发;
在spymemcached的Reactor设计中:
MemcachedClient负责接收请求
MemcachedConnection负责处理IO请求
同时还可以扩展支持线程池TranscodeService对解码计算提供异步线程支持; 这也是OperationFuture.get()返回值仍未一个Futrue,内部再次调用future.get返回最终数据的原因。