作者:吴香伟 发表于 2017/01/08
版权声明:可以任意转载,转载时务必以超链接形式标明文章原始出处和作者信息以及版权声明


我上小学时家离学校很远,家在某某山脚,学校在镇里。每周回家一趟,周五放学后回家,周日带着一星期吃的梅干菜回学校。从家到学校有5公里山路,现在觉得5公里不算长,骑个自行车只要一溜烟的功夫,但那时还小觉得很远。从学校到家的路上零零散散有几个路亭,这些路亭有的建在岔路口,有的建在木桥边,还有的建在转弯处。不同位置的亭子有不同好处,岔路口的宜送别,小桥边的方便在亭里歇个脚后再到桥底洗把脸,转弯处的路亭告诉你前面还有人家。除此之外,路亭还有很多其它功能,下雨天可以供人避雨,艳阳天可以供人乘凉,到了过年过节时还可以供人拜祭各路神仙。对我来说印象最深刻的是期末时在路亭里和小伙伴们相互修改成绩单,开学时相互填家长评语,其乐无穷。

从用户发起请求到数据落盘,Ceph也有很长的IO路径。在这条路径上也同样散落着功能各异的亭子(队列),梅干菜从一个路亭到下一个路亭靠的是两只小脚丫,IO从一个队列到另一个队列靠的是线程(池)。没错,本文要讲的就是路亭和脚夫的故事。不对是队列和线程。

队列和线程的关系

队列和线程在一起,通常称为生产者和消费者模式。生产者向队列添加元素,消费者从队列提取元素。一个队列可以同时拥有多个生产者和多个消费者。

图中的Tgt是改造过的与原生版本略有不同。

Ceph客户端队列

Pending队列

一个Tgt进程有多个Pending队列,一个队列负责来自一个Pool的请求。一个Pending队列只有一个生产者,所有队列共享同一个生产者,这个生产者就是Tgt主线程。Tgt主线程负责接收网络报文,并依据iSCSI协议将报文转换为iSCSI请求。因为要负责接收来自所有Lun的请求,所以主线程并不空闲,如果继续负责请求的处理,必然导致后面的请求不能被及时接收,进而降低Initiator发送请求的速度。网络本来就慢,再延迟接收,将导致性能大大的低。

此时Pending队列起到了缓冲的作用,让主线程只负责请求的接收过程,接收到的请求不用立即处理先缓存到队列,缩短主线程的执行路径从而解决网络请求不能被及时接收的问题。从另一方面来说,Pending队列解耦请求的接收过程和处理过程,将这两个过程分别交给队列的生产者和消费者。

Ok,我们Get到了队列的第一个作用,缓冲,第一个附带作用,解耦。一般在设计队列时考虑缓冲,在分析代码时看解耦。因为有100种解耦方法,队列只是其中之一,解耦并不是设计队列的充分条件。

PointerWq队列

开发者调用Librbd API读写块设备时,请求入PointerWq队列后立即返回给调用者。话音未落,我似乎又Get到了队列的第3个作用:实现异步调用。调用者将请求以及请求结果处理函数一并提交给Rbd层,Rbd层完成请求后回调请求结果处理函数,以此实现异步调用。

PointerWq队列隶属于ImageCtx类,代表一个Image存储块,不论读请求还是写请求都入同个队列。Bs-worker线程池,Pending队列的消费者,PointerWq队列的生产者,主要工作是将一个Pool请求队列分裂成若干个Image请求队列。

Out_q队列

从请求的粒度来看,Image请求在Ceph客户端中有下面几种形态:Image请求、Object请求、Op以及MOsdOp消息。Image请求只关注块设备中的一段区域,一段块设备区域可能对应于多个Object,这些Object可能属于不同PG不同Osd节点。一个Out_q队列对应一个Osd节点,隶属于一个连接,一个OSDSession。

Out_q是个优先级队列(第4个功能),不过对优先级的处理比较粗暴,只有最高优先级的请求全部出队列后才允许次高优先级的请求出队列。Ceph社区中曾有人建议通过请求的优先级来实现QoS,这显然是行不通的,因为特别容易导致低优先级用户饿死。

作为Out_q队列的生产者,Rbd_op_threads线程池的压力不小,一方面默认情况下池中只有一个线程(不知道是怎么考虑的)但要处理所有Image请求;另一方面它处理的逻辑最多,包括在Rbd层把Image请求拆分成Object请求(还要考虑Rbd缓存),在Rados层计算每个Object的目标Osd,以及回调函数的层层封装。虽然事情很多,但总体而言还是计算密集型不存在等待问题。

对Out_q队列的消费者Worker,本文关注的是AsyncMessenger的实现,SimpleMessenger会不一样。Messenger隶属于RadosClient,一个RadosClient实例拥有一个Messenger实例。老版本的SimpleMessenger会为每个连接都创建一个读线程和一个写线程。原生Tgt中为每个存储块创建一个RadosClient对象,一个RadosClient将与一个Osd建立一个连接,一个连接将有两个线程。粗略想想线程数目,心里有点发毛。

AsyncMessenger将工作线程Worker和Messenger解耦,不论有多少个RadosClient实例总线程数目都固定不变。不过和给定Osd节点的连接数还是会随着RadosClient实例的增加递增。一个Worker会同时处理多个连接,主要职责是从队列提取消息发送给Osd进程。

Ceph Osd队列

Osd进程从端口接收数据和Ceph客户端发送数据一样都由Worker线程完成。不同的是,Osd进程是服务端要监听端口。对监听Fd的Worker(处理连接打开和关闭)和属于该监听Fd连接(处理请求接收和响应发送)的Worker是同一个线程。

Osd接收到请求后根据不同的请求类型有两种处理方式:一种是Fast dispatch,要么直接处理掉要么入ShardedOp队列。另一种是入Dispatch队列。客户端读写请求走Fast dispatch路径,命令或者更新OsdMap版本的请求入Dispatch队列。

Mqueue分发队列

Mqueue是个优先级队列。

(待续)

参考资料

源码

拆开Ceph看队列和线程的更多相关文章

  1. 【Java并发】并发队列与线程池

    并发队列 阻塞队列与非阻塞队 ConcurrentLinkedQueue BlockingQueue ArrayBlockingQueue LinkedBlockingQueue PriorityBl ...

  2. 多线程多进程学习threading,queue线程安全队列,线程间数据状态读取。threading.local() threading.RLock()

    http://www.cnblogs.com/alex3714/articles/5230609.html python的多线程是通过上下文切换实现的,只能利用一核CPU,不适合CPU密集操作型任务, ...

  3. 自定义ThreadPoolExecutor带Queue缓冲队列的线程池 + JMeter模拟并发下单请求

    .原文:https://blog.csdn.net/u011677147/article/details/80271174 拓展: https://github.com/jwpttcg66/GameT ...

  4. 【Java】PS-查看Java进程-线程数

    PS-查看Java进程-线程数 ps 线程 个数_百度搜索 查看进程的线程数命令 - CSDN博客 java命令行运行jar里的main类 - coderland - 博客园

  5. python队列、线程、进程、协程

    目录: 一.queue 二.线程 基本使用 线程锁 自定义线程池 生产者消费者模型(队列) 三.进程 基本使用 进程锁 进程数据共享 默认数据不共享 queues array Manager.dict ...

  6. 6、TensorFlow基础(四)队列和线程

    队列和线程 和 TensorFlow 中的其他组件一样,队列(queue)本身也是图中的一个节点,是一种有状态的节点,其他节点,如入队节点(enqueue)和出队节点(dequeue),可以修改它的内 ...

  7. python队列、线程、进程、协程(转)

    原文地址: http://www.cnblogs.com/wangqiaomei/p/5682669.html 一.queue 二.线程 #基本使用 #线程锁 #自定义线程池 #生产者消费者模型(队列 ...

  8. exe崩溃用windbgattach后有宝贵现场,可看程序退出线程等,千万不要清屏

    exe崩溃用windbgattach后有宝贵现场,可看程序退出线程等,千万不要清屏

  9. 从JDK源码角度看java并发线程的中断

    线程的定义给我们提供了并发执行多个任务的方式,大多数情况下我们会让每个任务都自行执行结束,这样能保证事务的一致性,但是有时我们希望在任务执行中取消任务,使线程停止.在java中要让线程安全.快速.可靠 ...

随机推荐

  1. NSURLRequest POST方式请求服务器示例

    http://lizhuang.iteye.com/blog/1833297 1.  准备阶段 NSString *urlString = [NSString stringWithFormat:@&q ...

  2. 分布式数据库Google Spanner原理分析

    Spanner 是Google的全球级的分布式数据库 (Globally-Distributed Database) .Spanner的扩展性达到了令人咋舌的全球级,可以扩展到数百万的机器,数已百计的 ...

  3. Spring Junit 读取WEB-INF下的配置文件

    假设Spring配置文件为applicationContext.xml 一.Spring配置文件在类路径下面 在Spring的java应用程序中,一般我们的Spring的配置文件都是放在放在类路径下面 ...

  4. 使用getParameterMap()方法实现对请求参数的封装的工具类

    我们知道,HttpServletRequest这个类的getParameter(name),getParameterValues(name)可以分别实现对页面传来的单个参数和对多个同名参数的接受.特别 ...

  5. win7下wamp扩展memcache

    1.服务端安装 1.1 下载地址 http://download.csdn.net/detail/feiyuhit/5873533#comment 1.2 安装 将下载的压缩文件夹的memcached ...

  6. [转]centos 6.5安装caffe

    centos 6.5安装caffe 原文地址:http://blog.csdn.net/wqzghost/article/details/47447377   总结:在安装protobuf,hdf5等 ...

  7. adapter中报错:Can't create handler inside thread that has not called Looper.prepare()

    http://stackoverflow.com/questions/9357513/cant-create-handler-inside-thread-that-has-not-called-loo ...

  8. Java中实现Serializable接口为什么要声明serialVersionUID?

    什么情况下需要修改serialVersionUID 的值?      序列化运行时使用一个称为 serialVersionUID 的版本号与每个可序列化类相关联,该序列号在反序列化过程中用于验证序列化 ...

  9. endnote X7使用方法

    网页版的endnote不能添加新模版,只能用模版库里的那些,所以转而试试离线版客户端. 1.下载安装完以后(下载地址就不给了,网上有很多),在word里可以看到顶栏有插件,如果你同时也安装了在线版本, ...

  10. java 数据设置和显示

    1. 首先设置ModelAndView 的值 @Override public ModelAndView handleRequest(HttpServletRequest arg0, HttpServ ...