etcd网络层(三)——stream实现过程

stream消息通道主要是通过长连接的方式和对端进行数据交互,peer结构体中的stream相关数据如下(忽略V2版本):

type peer struct {

       writer         *streamWriter   //负责向Stream消息通道中写消息

       msgAppReader   *streamReader  //负责从Stream消息通道中读消息

       .......其他字段省略

}

从中我们看到stream是读写分离的,主要有streamWriter和streamReader结构体实现相关的功能。

stream实现的相关源码在  etcdserver/api/rafthttp/stream.go文件中。

 

 

一、streamWriter

streamWriter结构体如下:

 type streamWriter struct {

       lg *zap.Logger



       localID types.ID   //本端的ID

       peerID  types.ID  //对端节点的ID



       status *peerStatus

       fs     *stats.FollowerStats

       r      Raft   //底层的Raft实例



       mu      sync.Mutex // guard field working and closer

       closer  io.Closer    //负责关闭底层的长连接

       working bool   //负责标识当前的streamWriter是否可用



       msgc  chan raftpb.Message   //Peer会将待发送的消息写入到该通道,streamWriter则从该通道中读取消息并发送出去

       connc chan *outgoingConn   //通过该通道获取当前streamWriter实例关联的底层网络连接,  outgoingConn其实是对网络连接的一层封装,其中记录了当前连接使用的协议版本,以及用于关闭连接的Flusher和Closer等信息。

       stopc chan struct{}

       done  chan struct{}

}

 

从中我们看到:

localID和peerID分别记录本端ID和对端ID

status记录当前peer的可用性状态

r 是底层的Raft实例,用于和底层的raft模块交互

closer负责关闭底层的长连接

msgc peer会将待发送到对端的消息(非快照数据)写入到该通道,streamWriter则从该通道中读取并发送到对端节点。

connc  outgoingConn其实是对网络连接的一层封装,connc用于等待对端的主动连接,当对端主动和当前节点建立连接之后,本节点才会向对端发送数据。

 

1. streamWriter的初始化

streamWriter的初始化过程在startStreamWriter函数中

func startStreamWriter(lg *zap.Logger, local, id types.ID, status *peerStatus, fs *stats.FollowerStats, r Raft) *streamWriter {

       w := &streamWriter{

              lg: lg,



              localID: local,

              peerID:  id,



              status: status,

              fs:     fs,

              r:      r,

              msgc:   make(chan raftpb.Message, streamBufSize),

              connc:  make(chan *outgoingConn),

              stopc:  make(chan struct{}),

              done:   make(chan struct{}),

       }

       go w.run()

       return w

}

 

该方法主要是用于初始化streamWriter中的相关字段,并开启一个协程执行run方法。

 

 

2.run方法

run方法是streamWriter的核心,源码如下:

func (cw *streamWriter) run() {

       var (

              msgc       chan raftpb.Message   //指向当前streamWriter.msgc字段

              heartbeatc <-chan time.Time  //定时器会定时向该通道发送信号,触发心跳消息的发送,该心跳消息与后台介绍的Raft的心跳消息有所不同,该心跳的主要目的是为了防止连接长时间不用断开的

              t          streamType    //用来记录消息的版本信息

              enc        encoder  //编码器,负责将消息序列化并写入连接的缓冲区

              flusher    http.Flusher  //负责刷新底层连接,将数据真正发送出去

              batched    int   //当前未Flush的消息个数

       )

       tickc := time.NewTicker(ConnReadTimeout / 3)  //发送心跳的定时器

       defer tickc.Stop()

       unflushed := 0   //为Flush的字节数



       if cw.lg != nil {

              cw.lg.Info(

                     "started stream writer with remote peer",

                     zap.String("local-member-id", cw.localID.String()),

                     zap.String("remote-peer-id", cw.peerID.String()),

              )

       } else {

              plog.Infof("started streaming with peer %s (writer)", cw.peerID)

       }



       for {

              select {

              case <-heartbeatc:   //定时器到期,触发心跳消息

                     err := enc.encode(&linkHeartbeatMessage)  //通过encoder将心跳编码并写入到writer

                     unflushed += linkHeartbeatMessage.Size()  //增加未Flush出去的字节数

                     if err == nil { //若没有异常,则使用flusher将缓存的消息全部发送出去,并重置batched和unflushed两个统计变量

                            flusher.Flush()

                            batched = 0

                            sentBytes.WithLabelValues(cw.peerID.String()).Add(float64(unflushed))

                            unflushed = 0

                            continue

                     }

                     //如果有异常则设置peer的status 为  不连通状态

                     cw.status.deactivate(failureType{source: t.String(), action: "heartbeat"}, err.Error())



                     sentFailures.WithLabelValues(cw.peerID.String()).Inc()

                     cw.close()  //若发生异常,则关闭streamWriter,会导致底层连接的关闭

                     if cw.lg != nil {

                            cw.lg.Warn(

                                   "lost TCP streaming connection with remote peer",

                                   zap.String("stream-writer-type", t.String()),

                                   zap.String("local-member-id", cw.localID.String()),

                                   zap.String("remote-peer-id", cw.peerID.String()),

                            )

                     } else {

        &nbs
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值