【Codis源码】codis-proxy处理流程下(三)

前言:

接着上一章讲,上一章讲了loopReader。这一块会延续上一章节继续往下讲。

golang版本: go1.13.5 darwin/amd64
codis版本:codis 3.2
调试工具:dlv
codis源码:
https://github.com/CodisLabs/codis/

(一)proxy处理流程

在这里插入图片描述
上一章中讲proxy流程时只讲了session连接部分,这一章把完整的流程串一串。
如图中所示,codis-proxy会分为两大部分:
1)一部分是客户端交互部分,session的两个方法loopWriter和loopReader。
loopWriter负责返回客户端信息,loopReader负责分发slots部分。
2)另一部分是服务交互部分,backendConn的两个方法loopWriter和loopReader。
loopWriter负责写入服务端数据,loopReader部分负责处理返回客户端等待。

(二)源码解析

2.1 RequestChan结构与Request结构

type RequestChan struct {
   lock sync.Mutex  //互斥锁
   cond *sync.Cond  //条件锁

   data []*Request  
   buff []*Request

   waits  int      //等待数
   closed bool     //是否关闭
}

每一个session都有一个RequestChan结构。

type Request struct {
   Multi []*redis.Resp    //保存请求命令,按redis的resp协议类型将请求保存到Multi字段中
   Batch *sync.WaitGroup  /*返回响应时,会在Batch处等待,r.Batch.Wait(),
                           *所以可以做到当请求执行完成后才会执行返回函数*/
   Group *sync.WaitGroup

   Broken *atomic2.Bool

   OpStr string           //输入指令,
   OpFlag

   Database int32        //数据库编号
   UnixNano int64        //精确的unix时间 

   *redis.Resp           //保存响应数据,也是redis的resp协议类型
   Err error

   Coalesce func() error //聚合函数,适用于mget/mset等需要聚合响应的操作命令
}

上一章讲到session中的loopReader将请求放入RequestChan的data字段中,并且将请求放入bc.input 管道中。在Batch处加1。而bc.input管道中存放的则是一个Request结构。如下图:
在这里插入图片描述

下图中为Request交互时原型:
在这里插入图片描述

2.2 BackendConn的loopWriter处理

在这里插入图片描述
图中为dlv调试,BackendConn的loopWriter会开启一堆协程去轮询。

在这里插入图片描述
在bc.input 管道设置后,会在loopWriter的for r := range bc.input 轮询取出数据并且处理。

在这里插入图片描述
tasks也是一个管道,是BackendConn中 loopWriter和loopReader交流信息。

loopWriter源码

func (bc *BackendConn) loopWriter(round int) (err error) {
  // 。。。 省略
   c, tasks, err := bc.newBackendReader(round, bc.config) //调用newBackendReader函数。

   if err != nil {
      return err
   }
    
    //。。。省略

   for r := range bc.input {
      if r.IsReadOnly() && r.IsBroken() {
         bc.setResponse(r, nil, ErrRequestIsBroken)
         continue
      }
      if err := p.EncodeMultiBulk(r.Multi); err != nil { //将请求编码发送server
         return bc.setResponse(r, nil, fmt.Errorf("backend conn failure, %s", err))
      }
      if err := p.Flush(len(bc.input) == 0); err != nil {
         return bc.setResponse(r, nil, fmt.Errorf("backend conn failure, %s", err))
      } else {
         tasks <- r //将Request请求放入tasks管道中
      }
   }
   return nil
}

bc.newBackendReader的tasks也是一个存放*Request的channel,用来BackendConn中的loopWriter和loopReader交流信息。p.EncodeMultiBulk函数是将请求编码发送到server,这里的server是codis server。codis server是改版后的redis服务。

2.3 BackendConn的loopReader处理

func (bc *BackendConn) newBackendReader(round int, config *Config) (*redis.Conn, chan<- *Request, error) {
    //...省略

   if err := bc.verifyAuth(c, config.ProductAuth); err != nil { //验证权限
      c.Close()
      return nil, nil, err
   }
   if err := bc.selectDatabase(c, bc.database); err != nil { //选择数据库
      c.Close()
      return nil, nil, err
   }

   tasks := make(chan *Request, config.BackendMaxPipeline) ///创建task这个管道并且返回给loopWriter
   go bc.loopReader(tasks, c, round) //启动loopReader协程

   return c, tasks, nil
}

newBackendReader函数创建了tasks返回给loopWriter,并且启动loopReader协程。

loopReader函数:

func (bc *BackendConn) loopReader(tasks <-chan *Request, c *redis.Conn, round int) (err error) {
   //...省略
   for r := range tasks { //从tasks取出Request
      resp, err := c.Decode()
      if err != nil {
         return bc.setResponse(r, nil, fmt.Errorf("backend conn failure, %s", err))
      }
     //...省略
      bc.setResponse(r, resp, nil) //响应
   }
   return nil
}

从tasks中取出响应,设置到bc.setResponse函数中。
在这里插入图片描述
图中为设置到bc.setResponse函数的dlv调试结果。

setResponse函数:

func (bc *BackendConn) setResponse(r *Request, resp *redis.Resp, err error) error {
   r.Resp, r.Err = resp, err
   if r.Group != nil {
      r.Group.Done()
   }
   if r.Batch != nil {
      r.Batch.Done()  //r.Batch.Add(1)返回session中loopWriter的r.Batch.Wait等待
   }
   return err
}

在这里插入图片描述
图中为dlv调试结果,r.Batch设置时触发r.Batch.Done() 返回。

在这里插入图片描述
图中为dlv调试结果,r.Batch.Done() 触发session中loopWriter的r.Batch.Wait等待返回。

总结:

1.codis-proxy会分为两大部分:一部分是客户端交互部分,session的两个方法loopWriter和loopReader。
loopWriter负责返回客户端信息,loopReader负责分发slots部分。另一部分是服务交互部分,backendConn的两个方法loopWriter和loopReader。loopWriter负责写入服务端数据,loopReader部分负责处理返回客户端等待。
2.bc.input是连接session中loopReader与backendConn中loopWriter交流信息。
3.tasks是backendConn中loopWriter与loopReader的交流信息。
4.BackendConn中loopReader的r.Batch.Done() 触发返回session中loopWriter的r.Batch.Wait。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值