2. 网络通信
在上一节,我们介绍了etcd集群的启动过程。在其中也简单介绍了会初始化与其他成员节点通信的连接,以及启动为其他成员节点提供服务的GRPC服务和Http服务以及启动服务客户端的服务。本章我们将详细介绍通信相关的实现。
2.1 成员节点间通信(peer)
前一章节介绍了peer服务端启动过程。下面,我们来讲解下调用端是如何初始化以及如何调用的。
与成员节点间通信传输的初始化之前介绍过,是调用Transport.AddPeer方法。
前面我们介绍了Transport的作用:向成员节点发送raft消息,并从成员节点获取raft消息。下面将详细描述其作用。
首先来看其初始化和启动过程:
1 | tr := &rafthttp.Transport{ |
接下来,继续了解AddPeer实现:
1 | Transport/AddPeer |
从上面的流程可以看出 peer使用stream模式通信方式。读写分别用不同的协程去监听处理。其中streamWriter负责消息发送,streamReader负责消息接收。见下面详情:
1 | w := &streamWriter{ |
streamWriter里比较关键的一点是,这里的conn从哪来?往cw.connc传递conn的只有peer.attachOutgoingConn方法。
而在上面初始化并启动peer的时候是没有发现调用这个方法的 ???哪是哪里进行执行的呢?通过 追踪peer.attachOutgoingConn的调用方,最终发现,其在rafthhtp/http.go/ServeHTTP() L483处调用。而这个方法会在客户端建立连接进行http服务调用的时候执行。所以,在这里调用 peer.attachOutgoingConn 的作用是复用连接。当我们分析完读处理过程后,再来整体看 etcd节点是怎么建立通信链路的。
StreamReader的处理比StreamWriter的处理要简单一些。初始化完其属性后,调用start方法启动监听读请求过程。
1 | StreamReader/start |
这样每个peer之间的链路就建立了。回过头来,可以看出每对peer之间至少会有两条connection。且他们之间交互交错使用。
单peer往对方发送的消息,是通过 对方跟自己建立的连接 来发送的。
最后,我们通过单次请求响应过程,来介绍节点间的通信过程。
当要想集群其他成员节点发送消息时,最终会调用peer.send方法:
1 | peer.send |
目标节点的 StreamReader(p.msgAppReader)接收到消息后,通过解析后传递到 EtcdServer(Process),即完成单次通信;对于请求的响应,则通过对方的peer来发送给本节点,本节点的StreamReader来接受响应消息。
2.2 客户端通信
对于客户端通信,因为是GRPC或者HTTP简单的请求响应方式,因此这里就不再介绍了。