配色: 字号:
Ceph网络通信
2017-09-02 | 阅:  转:  |  分享 
  
Ceph网络通信Socket技术Socket起源于Unix,而Unix/linux基本哲学之一就是“一切皆文件”,都可以用“打开open-
>读写write/read->关闭close”模式来操作。Socket也是按照这个思想实现的,而我们工作中主要会用到它的以下
接口:intSocket(intdomain,inttype,intprotocol)这个函数创建一个Socket描述
符,唯一标识一个Socket,对应于普通文件的打开接口。intbind(intsockfd,conststructsoc
kaddraddr,socklen_taddrlen)该函数把一个具体的地址绑定到相应的Socket描述符上。通常服务器在
启动的时候都会绑定一个众所周知的地址(如ip地址+端口号),用于提供服务,客户可以通过它来接连服务器;而客户端就不用指定,有系统自
动分配一个端口号和自身的ip地址组合。intlisten(intsockfd,intbacklog)如果作为一个服务器,在
调用Socket()、bind()之后就会调用listen()来监听这个Socket。Socket()函数创建的Socket默认是
一个主动类型的,listen函数将Socket变为被动类型的,等待客户的连接请求。intconnect(intsockfd,
conststructsockaddraddr,socklen_taddrlen)客户端这时调用connect()发出
连接请求,服务器端就会接收到这个请求。intaccept(intsockfd,structsockaddraddr,
socklen_taddrlen)TCP服务器监听到这个请求之后,就会调用accept()函数取接收请求,这样连接就建立好了。
之后就可以开始网络I/O操作了,即类同于普通文件的读写I/O操作。以上这些接口,就可以用于实现客户端和服务器端的Socket连接
的建立了。之后就可以通过::send()和::recv()接口从Socket中读取数据了。Ceph网络通信是基于Socket技术实
现的,所以要想搞清楚网络部分,必须先了解Socket的工作机制,这里介绍一些基本的内容,要深入理解Socket请参阅《Linux网
络编程》一书和《tcp/ip详解》。代码结构在源代码src/msg里实现了ceph的网络通信模块。在msg目录下,定义了网络通
信的抽象接口。msg/Message.ccmsg/Message.h定义了messagemsg/Connection.h定义
了connectionmsg/Dispatcher.h定义了Dispatchermsg/Messenger.ccmsg/Me
ssenger.h定义了Messengermsg/msg_types.ccmsg/msg_types.h定义了消息的类型Ceph
网络通信架构总体上,Ceph的消息处理框架是发布者订阅者的设计结构。Messenger担当发布者的角色,Dispatcher担当订
阅者的角色。Messenger将接收到的消息通知给已注册的Dispatcher,由Dispatcher完成具体的消息处理。主要包括
Accepter、Pipe、Messenge和Connection这几个实体,这些还要和具体的dispatcher结合起来。各模
块作用在服务端,SimpleMessenger通过Accepter实例监听端口,接收来自客户端的连接。Accepter接受客户端的
连接后,为该连接创建一个Pipe实例。Pipe实例负责具体消息的接收和发送,一个Pipe实例包含一个读线程和一个写线程。读线程读取
到消息后,有三种分发消息的方法:快速分发,直接在Pipe的读线程中处理掉消息。可快速分发的消息在Dispatcher的ms_can
_fast_dispatch中注册。正常分发,将消息放入DispatchQueue,由单独的线程按照消息的优先级从高到低进行分发处
理。需要注意的是,属于同个SimpleMessenger实例的Pipe间使用同个DispatchQueue。延迟分发,为消息随机设
置延迟时间,定时时间到时由单独的线程走快速分发或正常分发的流程分发消息。4.1Accepter单说Socket部分,Ceph将其
分拆成了两个部分,Socket连接的维护(创建、绑定、监听、删除)主要在Accepter.cc文件中实现,以及I/O部分主要实现在
了Pipe.cc中。用一个公式表示就是:Accepter+Pipe=SocketAccepter类继承了Thread类,
本身是一个线程,主要用于server端的端口监听,接受连接。4.2Pipe类Pipe是PipeConnection的具体实现类。
其实现了两个端口之间类似管道的通信接口。对于每一个pipe,内部有一个Reader线程和一个Writer线程,分别用来处理有
关这个Pipe的消息接收和发送。线程DelayedDelivery用于故障注入测试使用。Pipe的写线程将消息放入out_q队列,
按照消息的优先级从高到低发送消息。另外,消息(Message)中携带了seq序列号,Pipe使用in_seq和out_seq记录它
接收到和发送出去的消息的序列号。发送消息时,Pipe用out_seq设置消息的序列号;接收消息时,通过比较消息的序列号和in_se
q来确定消息是否为旧消息,如果为旧消息则丢弃,否则使用消息的序列号更新in_seq。类Pipe的数据结构介绍如下:SimpleMe
ssengermsgr;//msgr的指针uint64_tconn_id;//分配给Pipe自己唯一的idcharr
ecv_buf;//接收缓存区intrecv_max_prefetch;//接收缓冲区一次预期的最大值intrecv_ofs;
//接收的偏移量intrecv_len;//接收的长度intsd;//pipe对应的sockedfdstructiov
ecmsgvec[IOV_MAX];//发送消息的iovec结构intport;//连接端口intpeer_typ
e;//链接对方的类型entity_addr_tpeer_addr;//对方地址Messenger::Policypo
licy;//策略Mutexpipe_lock;intstate;//当前链接的状态atomic_tstate_closed
;//non-zeroiffstate=STATE_CLOSEDPipeConnectionRefconnection
_state;//PipeConnection的引用utime_tbackoff;//backofftimebool
reader_running,reader_needs_join;boolreader_dispatching;//rea
derthreadisdispatchingwithoutpipe_lockboolnotify_on_dispatc
h_done;//somethingwantsasignalwhendispatchdoneboolwrit
er_running;map>out_q;//priorityqueuefo
routboundmsgs//准备发送的消息优先队列DispatchQueuein_q;//接收消息的DispatchQ
ueuelistsent;//要发送的消息Condcond;boolsend_keepalive;boo
lsend_keepalive_ack;utime_tkeepalive_ack_stamp;boolhalt_delive
ry;//ifapipe''squeueisdestroyed,stopaddingtoit__u32conne
ct_seq,peer_global_seq;uint64_tout_seq;//发送消息的序列号uint64_tin_s
eq,in_seq_acked;//接收到消息序号和应对的序号43MessengerMessenger它是Ceph网络
传输的抽象体,包括Accepter、Pipe、Dispatch_queue。Ceph通过OSDSession这个类来维护管理cli
ent端和server端之间建立的Session连接,并且维护了Pending在Session中的所有Op。OSDSession暴
露出来的接口就是一个ConnectionRef结构的send_message(),而在这个函数里,它实际上又返回过来找到messe
nger、pipe然后将请求发送出去。主要的使用流程是:1)通过Messenger::create_client_messenge
r()或者Messenger::create()创建一个messenger,目前支持SimpleMessenger、AsyncMe
ssenger、Xio这三种,可以在ceph的配置文件中配置。2)创建一个dispatcher,如果是客户端一般就是创建一个Obj
ecter和Monc,如果是服务端就创建对应的角色实例如OSD、Mon等,并且上一步创建的messenger会被设置为他们的mes
senger。3)设置Messenger的policy,用于配置Socket连接的异常处理策略。4)Dispatcher的初始化,
如objecter->init();5)如果是服务器端(比如OSD),则需要调用messenger的bind()接口,为messe
nger绑定一个地址和端口,如果是客户端(比如Objecter)则不需要。这个bind操作是通过Accepter的bind()接口
完成的,它里面实际上干了三件事情:创建Socket、bind地址和端口、listen地址和端口;6)通过Messenger的add
_dispatcher_head()/add_dispatcher_tail()将初始化后的dispatcher加入的messen
ger的dispatcherlist中。如果是添加的第一个dispatcher,则会调用Messenger的ready()接口,
这个接口会调用Accepter的start()接口。而在客户端和服务端,他们对于start()的处理是不一样的。服务器端会创建一个
线程使用poll机制去处理accept的连接,客户端则不会处理。服务器accept的Socket连接会创建相应的Pipe,丢到me
ssenger里面。7)当pipe建立好之后,客户端就会通过OSDSession中的conn找到messenger的接口,发送数据
了。参考资料:http://blog.csdn.net/scaleqiao/article/details/51205054htt
p://www.cnblogs.com/shanno/p/4014256.htmlhttp://www.cnblogs.com/s
hanno/p/4014256.htmlhttp://blog.csdn.net/zrs19800702/article/details/53005136http://blog.csdn.net/zrs19800702/article/details/53005136http://blog.csdn.net/changtao381/article/details/50915328http://blog.csdn.net/changtao381/article/details/50915328http://blog.csdn.net/ywy463726588/article/details/42742355
献花(0)
+1
(本文系hello_world...首藏)