豌豆夹Redis解决方案Codis源码剖析:Proxy代理

本文深入探讨了豌豆夹Redis解决方案Codis的Proxy代理源码,涵盖了预备知识,如Codis、Golang、Redis通信协议和Zookeeper,以及Proxy的源码剖析,包括程序入口、Server核心类、请求接收与分发、请求发送和响应处理。文章详细解释了Router、Session、TaskRunner的工作流程,展示了Codis如何实现高效的数据分发和处理。
摘要由CSDN通过智能技术生成

豌豆夹Redis解决方案Codis源码剖析:Proxy代理

1.预备知识

1.1 Codis

Codis就不详细说了,摘抄一下GitHub上的一些项目描述

Codis is a proxy based high performance Redis cluster solution written in Go/C, an alternative to Twemproxy. It supports multiple stateless proxy with multiple redis instances and is engineered to elastically scale, Easily add or remove redis or proxy instances on-demand/dynamicly.

  • Auto rebalance
  • Support both redis or rocksdb transparently
  • GUI dashboard & admin tools
  • Supports most of Redis commands, Fully compatible with twemproxy
  • Native Redis clients are supported
  • Safe and transparent data migration, Easily add or remove nodes on-demand
  • Command-line interface is also provided
  • RESTful APIs

安装步骤官方网站上也写的很清楚了:

// Golang环境安装配置
[root@vm root]$ tar -C /usr/local -zxf go1.4.2.linux-amd64.tar.gz
[root@vm root]$ vim /etc/profile
export GOROOT=/usr/local/go
export PATH=$GOROOT/bin:$PATH
export GOPATH=/home/user/go
[root@vm root]$ source /etc/profile
[root@vm root]$ go version

// 下载Codis依赖,编译Codis
[root@vm root]$ cd codis-1.92
[root@vm root]$ ./bootstrap.sh

1.2 Golang

Codis的核心代码都是用Golang开发的,所以在一头扎进源代码里之前,先了解Golang的语法特性是必不可少的!好在Golang除了少数一些“古怪之处”外,还算容易上手。具体请参考笔者的另一篇文章《 Java程序员的Golang入门指南(上)》

1.3 Redis通信协议

Redis通信协议简称为RESP,在分析网络通信时需要这一部分的知识。RESP本身的设计非常简单,所以还是快速过一下吧。具体请参考笔者的另一篇文章《用Netty解析Redis网络协议》以及官网上的协议具体规范

1.4 Zookeeper

Codis以及现今很多后端中间件都使用Zookeeper来协调分布式通信,所以在阅读源码前我们至少要知道Zookeeper是干什么的,有哪些基本操作和监听器。具体请参考笔者的另一篇文章《Apache Curator入门实战》

2.Proxy源码剖析

Codis可以分为客户端Jodis、代理中间件Codis Proxy、Zookeeper协调、监控界面、Redis定制版Codis Server等组件。这里第一部分主要关注最核心的Proxy部分的源码。

2.1 程序入口main.go

codis-1.92/cmd/proxy/main.go是Proxy组件的main函数入口,完成的主要工作就是设置日志级别、解析命令行参数(CPU核数、绑定地址等)、加载配置文件、Golang环境(runtime.GOMAXPROCS并发数)、启动Socket监听等常规任务。顺藤摸瓜,我们要分析的关键应该就在router中。

func main() {
    // 1.打印banner,设置日志级别
    fmt.Print(banner)
    log.SetLevelByString("info")

    // 2.解析命令行参数
    args, err := docopt.Parse(usage, nil, true, "codis proxy v0.1", true)
    if err != nil {
        log.Error(err)
    }
    if args["-c"] != nil {
        configFile = args["-c"].(string)
    }
    ...

    dumppath := utils.GetExecutorPath()

    log.Info("dump file path:", dumppath)
    log.CrashLog(path.Join(dumppath, "codis-proxy.dump"))

    // 3.设置Golang并发数等
    router.CheckUlimit(1024)
    runtime.GOMAXPROCS(cpus)

    // 4.启动Http监听
    http.HandleFunc("/setloglevel", handleSetLogLevel)
    go http.ListenAndServe(httpAddr, nil)
    log.Info("running on ", addr)
    conf, err := router.LoadConf(configFile)
    if err != nil {
        log.Fatal(err)
    }

    // 5.创建Server,启动Socket监听
    s := router.NewServer(addr, httpAddr, conf)
    s.Run()
    log.Warning("exit")
}

2.2 核心类Server

打开codis-1.92/pkg/proxy/router/router.go,在分析请求接收和分发前,先来看一个最核心的类Server,它就是在main.go中调用router.NewServer()时创建的。说一下比较重要的几个字段:

  • reqCh:Pipeline请求的Channel。
  • pools:Slot与cachepool的map。
  • evtbus/top:处理Zookeeper消息,更新拓扑结构。
  • bufferedReq:Slot处于migrate期间被缓冲的请求。
  • pipeConns:Slot对应的taskrunner。

注意interface{},它表示空interface,按照Golang的Duck Type继承方式,任何类都是空接口的子类。所以interface{}有点像C语言中的void*/char*。

因为Codis是先启动监听再开始接收Socket请求,所以对go s.handleTopoEvent()的分析放到后面。在下一节我们先看一下Codis是如何启动对Socket端口监听并将接收到的请求放入到Server的reqCh管道中的。

type Server struct {
    slots  [models.DEFAULT_SLOT_NUM]*Slot
    top    *topo.Topology
    evtbus chan interface{}
    reqCh  chan *PipelineRequest

    lastActionSeq int
    pi            models.ProxyInfo
    startAt       time.Time
    addr          string

    moper       *MultiOperator
    pools       *cachepool.CachePool
    counter     *stats.Counters
    OnSuicide   OnSuicideFun
    bufferedReq *list.List
    conf        *Conf

    pipeConns map[string]*taskRunner //redis->taskrunner
}

func NewServer(addr string, debugVarAddr string, conf *Conf) *Server {
    log.Infof("start with configuration: %+v", conf)

    // 1.创建Server类
    s := &Server{
        conf:          conf,
        evtbus:        make(chan interface{}, 1000),
        top:           topo.NewTopo(conf.productName, conf.zkAddr, conf.f, conf.provider),
        counter:       stats.NewCounters("router"),
        lastActionSeq: -1,
        startAt:       time.Now(),
        addr:          addr,
        moper:         NewMultiOperator(addr),
        reqCh:         make(chan *PipelineRequest, 1000),
        pools:         cachepool.NewCachePool(),
        pipeConns:     make(map[string]*taskRunner),
        bufferedReq:   list.New(),
    }
    ...

    // 2.启动Zookeeper监听器
    s.RegisterAndWait()
    _, err = s.top.WatchChildren(models.GetWatchActionPath(conf.productName),
评论 27
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值