OpenFalcon源码分析(Gateway组件)

Gateway版本

VERSION = "0.0.11"

Gateway组件功能

多IDC时,可能面对 "分区到中心的专线网络质量较差&公网ACL不通" 等问题。这时,可以在分区内部署一套数据路由服务,接收本分区内的所有流量(包括所有的agent流量),然后通过公网(开通ACL),将数据push给中心的Transfer。
站在client端的角度,gateway和transfer提供了完全一致的功能和接口。只有遇到网络分区的情况时,才有必要使用gateway组件。【官方说明】

Gateway组件逻辑图

OpenFalcon源码分析(Gateway组件)_第1张图片
官方介绍图

main入口分析

func main() {
    cfg := flag.String("c", "cfg.json", "configuration file")
    version := flag.Bool("v", false, "show version")
    flag.Parse()

    if *version {
        fmt.Println(g.VERSION)
        os.Exit(0)
    }

    // 全局配置解析
    g.ParseConfig(*cfg)               //【参考详细分析】

    if g.Config().Debug {
        g.InitLog("debug")
    } else {
        g.InitLog("info")
    }

    sender.Start()   // 发送数据服务运行  【参考详细分析】
    receiver.Start() // 接收数据服务运行  【参考详细分析】
    
    //http API服务
    http.Start()

    select {}
}

g.ParseConfig(*cfg) 全局配置解析

# 默认配置为当前目录下cfg.json 
func ParseConfig(cfg string) {
    if cfg == "" {
        log.Fatalln("use -c to specify configuration file")
    }
    
    //判断文件是否存在
    if !file.IsExist(cfg) {
        log.Fatalln("config file:", cfg, "is not existent. maybe you need `mv cfg.example.json cfg.json`")
    }

    ConfigFile = cfg

    //配置字符串化
    configContent, err := file.ToTrimString(cfg)
    if err != nil {
        log.Fatalln("read config file:", cfg, "fail:", err)
    }

    var c GlobalConfig
    // Json反序列化为结构
    err = json.Unmarshal([]byte(configContent), &c)
    if err != nil {
        log.Fatalln("parse config file:", cfg, "fail:", err)
    }

    configLock.Lock()
    defer configLock.Unlock()
    config = &c

    log.Println("g.ParseConfig ok, file ", cfg)
}

type GlobalConfig struct {
    Debug    bool            `json:"debug"`
    Http     *HttpConfig     `json:"http"`
    Rpc      *RpcConfig      `json:"rpc"`
    Socket   *SocketConfig   `json:"socket"`
    Transfer *TransferConfig `json:"transfer"`
}

sender.Start() 转发数据至后端transfer

func Start() {
    initConnPools()  //初始化连接池
    startSendTasks() //周期任务线程,转发数据至后端Transfer
    startSenderCron() //goperfcounter统计功能周期发送
    log.Println("send.Start, ok")
}

#初始化连接池
func initConnPools() {
    cfg := g.Config()

    //初始化Transfer集群配置
    addrs := make([]string, 0)
    for hn, addr := range cfg.Transfer.Cluster {
        TransferHostnames = append(TransferHostnames, hn)
        addrs = append(addrs, addr)
        TransferMap[hn] = addr
    }

    //初始化transfer发送计算器
    for hn, addr := range cfg.Transfer.Cluster {
        TransferSendCnt[hn] = nproc.NewSCounterQps(hn + ":" + addr)
        TransferSendFailCnt[hn] = nproc.NewSCounterQps(hn + ":" + addr)
    }

    //初始化发送Transfer连接池
    SenderConnPools = backend.CreateSafeJsonrpcConnPools(int(cfg.Transfer.MaxConns), int(cfg.Transfer.MaxIdle),
        int(cfg.Transfer.ConnTimeout), int(cfg.Transfer.CallTimeout), addrs)
}

#启动后台发送线程
func startSendTasks() {
    cfg := g.Config()
    concurrent := cfg.Transfer.MaxConns * int32(len(cfg.Transfer.Cluster))
    go forward2TransferTask(SenderQueue, concurrent) //发送线程
}

#转发数据至Transfer服务器实现函数
func forward2TransferTask(Q *nlist.SafeListLimited, concurrent int32) {
     //全局配置读取
    cfg := g.Config()
    batch := int(cfg.Transfer.Batch)
    maxConns := int64(cfg.Transfer.MaxConns)
    retry := int(cfg.Transfer.Retry)
    if retry < 1 {
        retry = 1
    }

    sema := nsema.NewSemaphore(int(concurrent)) //并发量
    transNum := len(TransferHostnames)  //transfer集群数

    for {
        items := Q.PopBackBy(batch) //队列取出批量数据
        count := len(items)
        if count == 0 {
            time.Sleep(time.Millisecond * 50)
            continue
        }

        transItems := make([]*cmodel.MetricValue, count)
        for i := 0; i < count; i++ {
            transItems[i] = convert(items[i].(*cmodel.MetaData)) //数据格式化transItems结构
        }

        sema.Acquire() 
        go func(transItems []*cmodel.MetricValue, count int) {
            defer sema.Release()
            var err error

            //随机遍历transfer列表,直到数据发送成功 或者 遍历完;随机遍历,可以缓解慢transfer
            resp := &cmodel.TransferResponse{}
            sendOk := false

            for j := 0; j < retry && !sendOk; j++ {
                rint := rand.Int() //随机
                for i := 0; i < transNum && !sendOk; i++ {
                    idx := (i + rint) % transNum  //随机选择
                    host := TransferHostnames[idx]
                    addr := TransferMap[host]

                    //过滤掉建连缓慢的host, 否则会严重影响发送速率
                    cc := pfc.GetCounterCount(host)
                    if cc >= maxConns {
                        continue
                    }

                    pfc.Counter(host, 1) //统计
                    err = SenderConnPools.Call(addr, "Transfer.Update", transItems, resp)   //RPC调用上传数据
                    pfc.Counter(host, -1)

                    if err == nil {
                        sendOk = true
                        
                        //统计正常
                       TransferSendCnt[host].IncrBy(int64(count))
                    } else {
                        //统计错误 
                        log.Errorf("transfer update fail, items size:%d, error:%v, resp:%v", len(transItems), err, resp)
                        TransferSendFailCnt[host].IncrBy(int64(count))
                    }
                }
            }

            //goperfcounter统计
            if !sendOk {
                pfc.Meter("SendFail", int64(count))
            } else {
                pfc.Meter("Send", int64(count))
            }
        }(transItems, count)
    }
}

//goperfcounter统计数据发送周期线程
func startSenderCron() {
    go startProcCron()
}
func startProcCron() {
    for {
        time.Sleep(DefaultProcCronPeriod)
        refreshSendingCacheSize()
    }
}
func refreshSendingCacheSize() {
    pfc.Gauge("SendQueueSize", int64(SenderQueue.Len()))
}


cmodel.MetricValue{
        Metric:    v.Metric,
        Endpoint:  v.Endpoint,
        Timestamp: v.Timestamp,
        Step:      v.Step,
        Type:      v.CounterType,
        Tags:      cutils.SortedTags(v.Tags),
        Value:     v.Value,
}

receiver.Start() 接收服务【与Transfer相同,详细可参考Transfer分析】

func Start() {
    go rpc.StartRpc()  //RPC服务
    go socket.StartSocket() //Socket TCP
}

http.Start() HTTP API服务器运行与监听处理

func Start() {
    go startHttpServer()  //后台运行HTTP API 服务
}

func startHttpServer() {
    if !g.Config().Http.Enabled {
        return
    }
    
    addr := g.Config().Http.Listen
    if addr == "" {
        return
    }

    configCommonRoutes()   //标准公共接口路由【可以参考Agent模块】
    configProcHttpRoutes() //统计接口路由  
    configApiHttpRoutes()  //API PUSH接口路由

    s := &http.Server{
        Addr:           addr,
        MaxHeaderBytes: 1 << 30,
    }

    log.Println("http.startHttpServer ok, listening", addr)
    log.Fatalln(s.ListenAndServe())
}

// API方式提交Metric数值
func configApiHttpRoutes() {
    http.HandleFunc("/api/push", func(w http.ResponseWriter, req *http.Request) {
        if req.ContentLength == 0 {
            http.Error(w, "blank body", http.StatusBadRequest)
            return
        }

        decoder := json.NewDecoder(req.Body)
        var metrics []*cmodel.MetricValue
        err := decoder.Decode(&metrics)
        if err != nil {
            http.Error(w, "decode error", http.StatusBadRequest)
            return
        }

        reply := &cmodel.TransferResponse{}
        trpc.RecvMetricValues(metrics, reply, "http") //接收Metric Data
        RenderDataJson(w, reply)
    })
}

你可能感兴趣的:(OpenFalcon源码分析(Gateway组件))