Learn Influxdb the hard way (3) - Services in Influxdb I

简介:

前言

在上一篇文章中我们梳理了Influxdb的代码主流程结构,从本篇开始将会对Influxdb中的各种Service进行分析,由于篇幅的限制,在这篇文章中会先介绍下MonitorService。在上文中,我们知道Influxdb是通过如下的方式注册Service。

    //cmd/influxd/run/server.go 371
    s.appendMonitorService()
    s.appendPrecreatorService(s.config.Precreator)
    s.appendSnapshotterService()
    s.appendContinuousQueryService(s.config.ContinuousQuery)
    s.appendHTTPDService(s.config.HTTPD)
    s.appendStorageService(s.config.Storage)
    s.appendRetentionPolicyService(s.config.Retention)

并通过如下的代码进行依次的调用的

    //cmd/influxd/run/server.go 434
    for _, service := range s.Services {
        if err := service.Open(); err != nil {
            return fmt.Errorf("open service: %s", err)
        }
    }

那么我们就可以从此处入手,剥离每个Service。

MonitorService解析

我们先来看下MonitorService的核心代码,可以发现MonitorService的初始化更多是在确保MonitorService是运行态的单例,并且注册了一些诊断的实例,最后判断配置是否支持存储,如果支持存储,会开启goroutine定期异步将数据存储在TSM Engine中。

//influxdb/monitor/service.go 73行
// New returns a new instance of the monitor system.
func New(r Reporter, c Config) *Monitor {
    return &Monitor{
        globalTags:           make(map[string]string),
        diagRegistrations:    make(map[string]diagnostics.Client),
        reporter:             r,
        storeEnabled:         c.StoreEnabled,
        storeDatabase:        c.StoreDatabase,
        storeInterval:        time.Duration(c.StoreInterval),
        storeRetentionPolicy: MonitorRetentionPolicy,
        Logger:               zap.NewNop(),
    }
}

// open returns whether the monitor service is open.
func (m *Monitor) open() bool {
    m.mu.Lock()
    defer m.mu.Unlock()
    return m.done != nil
}

// Open opens the monitoring system, using the given clusterID, node ID, and hostname
// for identification purpose.
func (m *Monitor) Open() error {
    if m.open() {
        m.Logger.Info("Monitor is already open")
        return nil
    }

    m.Logger.Info("Starting monitor service")

    // Self-register various stats and diagnostics.
    m.RegisterDiagnosticsClient("build", &build{
        Version: m.Version,
        Commit:  m.Commit,
        Branch:  m.Branch,
        Time:    m.BuildTime,
    })
    m.RegisterDiagnosticsClient("runtime", &goRuntime{})
    m.RegisterDiagnosticsClient("network", &network{})
    m.RegisterDiagnosticsClient("system", &system{})

    m.mu.Lock()
    m.done = make(chan struct{})
    m.mu.Unlock()

    // If enabled, record stats in a InfluxDB system.
    if m.storeEnabled {
        hostname, _ := os.Hostname()
        m.SetGlobalTag("hostname", hostname)

        // Start periodic writes to system.
        m.wg.Add(1)
        go m.storeStatistics()
    }

    return nil
}

下面重点看下storeStatistics中做的事情,storeStatistics中主要是通过Statistics方法获取stats,然后再通过writePoints方法写入到相应的store中。

//influxdb/monitor/service.go 418行
func (m *Monitor) storeStatistics() {
    defer m.wg.Done()
    m.Logger.Info("Storing statistics", logger.Database(m.storeDatabase), logger.RetentionPolicy(m.storeRetentionPolicy), logger.DurationLiteral("interval", m.storeInterval))

    // Wait until an even interval to start recording monitor statistics.
    // If we are interrupted before the interval for some reason, exit early.
    if err := m.waitUntilInterval(m.storeInterval); err != nil {
        return
    }

    tick := time.NewTicker(m.storeInterval)
    defer tick.Stop()

    for {
        select {
        case now := <-tick.C:
            now = now.Truncate(m.storeInterval)
            func() {
                m.mu.Lock()
                defer m.mu.Unlock()
                m.createInternalStorage()
            }()

            stats, err := m.Statistics(m.globalTags)
            if err != nil {
                m.Logger.Info("Failed to retrieve registered statistics", zap.Error(err))
                return
            }

            // Write all stats in batches
            batch := make(models.Points, 0, 5000)
            for _, s := range stats {
                pt, err := models.NewPoint(s.Name, models.NewTags(s.Tags), s.Values, now)
                if err != nil {
                    m.Logger.Info("Dropping point", zap.String("name", s.Name), zap.Error(err))
                    return
                }
                batch = append(batch, pt)
                if len(batch) == cap(batch) {
                    m.writePoints(batch)
                    batch = batch[:0]

                }
            }

            // Write the last batch
            if len(batch) > 0 {
                m.writePoints(batch)
            }
        case <-m.done:
            m.Logger.Info("Terminating storage of statistics")
            return
        }
    }
}

比较有趣的是Statistics方法不仅返回了[]*Statistic类型的监控指标,还通过expvar将数据通过API进行暴露,Go 标准库有一个 expvar 包。 这个包可以通过 JSON 格式的 HTTP API 公开您的应用程序和 Go 运行时的指标,有点类似prometheus的API 抓取的数据格式。而MonitorService的数据来源是通过runtime包获取。重要包含内存、goroutines等相关系统指标。

    //influxdb/monitor/service.go 324行
    statistic.Values = map[string]interface{}{
        "Alloc":        int64(rt.Alloc),
        "TotalAlloc":   int64(rt.TotalAlloc),
        "Sys":          int64(rt.Sys),
        "Lookups":      int64(rt.Lookups),
        "Mallocs":      int64(rt.Mallocs),
        "Frees":        int64(rt.Frees),
        "HeapAlloc":    int64(rt.HeapAlloc),
        "HeapSys":      int64(rt.HeapSys),
        "HeapIdle":     int64(rt.HeapIdle),
        "HeapInUse":    int64(rt.HeapInuse),
        "HeapReleased": int64(rt.HeapReleased),
        "HeapObjects":  int64(rt.HeapObjects),
        "PauseTotalNs": int64(rt.PauseTotalNs),
        "NumGC":        int64(rt.NumGC),
        "NumGoroutine": int64(runtime.NumGoroutine()),
    }

我们可以看下在TSM Engine中的存储的内容
1.jpg

细心的同学可能已经发现,在_internal库中还包含了很多其他的监控数据,那么这些数据是哪里来的呢,其实在上文中,我们忽略了一个重要的对象Reportor,在实例化Monitor的时候传递一个实现了Reporter接口实例r,Reporter接口定义了一个Statistics的方法,我们可以发现QueryExecutor,TSDBStore,PointsWriter,Subscriber等核心组件都实现了Statistics方法。

// cmd/influxd/run/server.go 210行
// Statistics returns statistics for the services running in the Server.
func (s *Server) Statistics(tags map[string]string) []models.Statistic {
    var statistics []models.Statistic
    statistics = append(statistics, s.QueryExecutor.Statistics(tags)...)
    statistics = append(statistics, s.TSDBStore.Statistics(tags)...)
    statistics = append(statistics, s.PointsWriter.Statistics(tags)...)
    statistics = append(statistics, s.Subscriber.Statistics(tags)...)
    for _, srv := range s.Services {
        if m, ok := srv.(monitor.Reporter); ok {
            statistics = append(statistics, m.Statistics(tags)...)
        }
    }
    return statistics
}

在gatherStatistics方法中会调用reporter,并将相应的数据与runtime等diagnoseClient的数据进行合并,并写入到Influxdb中。

//influxdb/monitor/service.go 347行
func (m *Monitor) gatherStatistics(statistics []*Statistic, tags map[string]string) []*Statistic {
   m.mu.RLock()
   defer m.mu.RUnlock()

   if m.reporter != nil {
      for _, s := range m.reporter.Statistics(tags) {
         statistics = append(statistics, &Statistic{Statistic: s})
      }
   }
   return statistics
}

总结

到此Influxdb的监控服务部分就比较清晰了,首先监控分为系统级别的监控和组件级别的监控,系统级别的监控会通过runtime的方式获取,组件级别的监控会通过各自组件实现Reportor接口进行实现,Influxdb会定期的存储监控数据到TSM Engine的_internal库中。同样Influxdb也会通过expvar将监控数据暴露到API上。

目录
相关文章
|
安全 Java 数据处理
Python网络编程基础(Socket编程)多线程/多进程服务器编程
【4月更文挑战第11天】在网络编程中,随着客户端数量的增加,服务器的处理能力成为了一个重要的考量因素。为了处理多个客户端的并发请求,我们通常需要采用多线程或多进程的方式。在本章中,我们将探讨多线程/多进程服务器编程的概念,并通过一个多线程服务器的示例来演示其实现。
|
前端开发 JavaScript Java
springboot实现用户统一认证、管理(单点登录)
springboot实现用户统一认证、管理(单点登录)
|
2月前
|
人工智能 自然语言处理 监控
2026 AI客服系统格局梳理:主流智能客服产品全解析
2026年,AI客服迈入“执行时代”,核心价值从“答得像人”转向“办成业务”。瓴羊Quick Service作为阿里云新一代智能客服平台,深度融合大模型与AI Agent技术,具备93%问答准确率、全渠道协同、工单闭环及后端系统直连能力,已服务长城汽车、上汽、申通等企业,推动客服从成本中心升级为增长引擎。(239字)
阿里云百炼Token Plan支持包季、包年订阅吗?年付比月付便宜多少?
阿里云百炼Token Plan个人版支持月付、季付、年付,时长越长越优惠:Lite版年付低至35元/月(原60元),Standard版127元/月(原180元),Pro版467元/月(原600元)。官网购还可领超7000万Tokens。阿里云百炼Token Plan官网:https://t.aliyun.com/U/EsRjVx
|
11月前
|
移动开发 小程序 JavaScript
技术科普|为什么 Perfect HandAR 在微信小程序中必须用 WebView?
🌟为何手部AR试戴必须用WebView?因HandAR需3D高精度渲染、多指追踪与PBR材质,依赖WebGL、WASM及完整浏览器环境,而小程序原生不支持DOM、getUserMedia且GPU性能受限,仅WebView可满足其高性能需求。
561 4
|
7月前
|
Web App开发 人工智能 持续交付
OpenClaw一键部署安装代码实现过程详解
OpenClaw一键部署脚本详解:通过.bat自动化实现Python环境检测、pip升级、Playwright安装及Chromium浏览器下载,支持国内镜像加速。用户双击即完成全环境配置,大幅降低AI Agent部署门槛,提升效率与稳定性。(238字)
|
10月前
|
弹性计算 人工智能 固态存储
2026年最新阿里云服务器价格表:轻量、ECS、GPU 服务器费用详解
在云服务器选购中,价格是用户关注的核心因素之一。2026 年阿里云针对不同用户需求,推出了覆盖轻量应用服务器、云服务器 ECS、GPU 服务器的多种套餐,新老用户可根据业务场景选择合适方案,同时享受续费同价等稳定成本福利。以下从核心品类价格、续费政策、附加资源费用三方面,详细梳理最新费用标准。
|
11月前
|
Linux C语言 C++
cpp-7.3.0-20190804.35.p06.ky10.x86_64.rpm 安装步骤
本文介绍在Linux系统中通过RPM包安装cpp(C++预处理器)的完整步骤:先检查是否已安装,再上传RPM包并验证依赖,随后提供纯RPM或YUM两种安装方式,最后通过版本命令确认安装成功,适合初学者快速上手。