Nginx and InfluxDB through unix socket

InfluxDB API

向influxDB中写数据有多种方式,最熟悉的就是HTTP API了,InfluxDB 的 HTTP Service 提供了一套完整的管理接口,另外你也可以用命令行工具 influx 来操作,但是它底层也是调用的HTTP API。

另外,在一些场景中我们可能对数据的写入性能要求比较高,于是influxDB提供了 UDP 的API,UDP Service 是以服务出现的,可以在配置中开启:

[[udp]]
  enabled = false
  # bind-address = ""
  # database = "udp"
  # retention-policy = ""

  # These next lines control how batching works. You should have this enabled
  # otherwise you could get dropped metrics or poor performance. Batching
  # will buffer points in memory if you have many coming in.

  # batch-size = 1000 # will flush if this many points get buffered
  # batch-pending = 5 # number of batches that may be pending in memory
  # batch-timeout = "1s" # will flush at least this often even if we haven't hit buffer limit
  # read-buffer = 0 # UDP Read buffer size, 0 means OS default. UDP listener will fail if set above OS max.

  # set the expected UDP payload size; lower values tend to yield better performance, default is max UDP size 65536
  # udp-payload-size = 65536

从配置中可以看到,需要配置一个 databaseretention-policy. 于是 udp 你可以配置多个,每个绑定的地址只能往对应的数据库里面写数据。GAP!

InfluxDB Unix Domain Socket

https://github.com/influxdata/influxdb/pull/7130 中,我向官方提交了一个unix socket service 的PR,在整个项目中,它的地位和UDP Service是一样的(逻辑地位,不是技术细节,这里也导致了influxDB官方人员的误解),作为一种服务的存在,很可惜,influxDB的官方并不想只是用unix socket来write points,由于unix socket是双向的,与TCP极为相像,于是他们想把整个HTTP Service expose出来,承载在整个unix domain socket之上。

我觉的想法非常好,于是,我又新建了一个branch work on it

配置:

[http]
  enabled = true
  bind-address = ":8086"
  auth-enabled = false
  log-enabled = true
  write-tracing = false
  pprof-enabled = false
  https-enabled = false
  https-certificate = "/etc/ssl/influxdb.pem"
  ### Use a separate private key location.
  # https-private-key = ""
  max-row-limit = 10000
  realm = "InfluxDB"

  unix-socket-enabled = true # enable http service over unix domain socket
  bind-socket = "/var/run/influxdb.sock"

最下面我增加了两个配置:一个用于启用 unix socket, 一个用于绑定。

开启之后,HTTP API 可以同时跑在TCP和unix domain socket上

simple client 测试代码:

package main

import (
        "fmt"
        "io/ioutil"
        "net"
        "net/http"
        "net/url"
)

var sock = "/tmp/test.sock"

// all your client.Get or client.Post calls has to be a valid url (http://xxxx.xxx/path not unix://...),
// the domain name doesn't matter since it won't be used in connecting.
const URL = "http://test/"

func unixsocketDial(proto, addr string) (conn net.Conn, err error) {
        return net.Dial("unix", sock)
}

func main() {
        tr := &http.Transport{
                Dial: unixsocketDial,
        }

        client := &http.Client{Transport: tr}

        q := url.Values{}
        q.Set("db", "unixsocket")
        q.Set("q", "SELECT value FROM cpu WHERE region='us_west' LIMIT 10")

        req, err := http.NewRequest("GET", URL+"query?"+q.Encode(), nil)
        req.Header.Add("Content-Type", "application/json;charset=UTF-8")
        resp, err := client.Do(req)
        if err != nil {
                panic(err)
        }

        defer resp.Body.Close()

        body, err := ioutil.ReadAll(resp.Body)
        fmt.Println(string(body))
}

更多信息参考:https://github.com/influxdata/influxdb/pull/7135

With Nginx

有意思的是nginx可以直接把http服务通过unix socket传递给后端:

upstream influxdb {
    server unix:/tmp/influxdb.sock;
}

# the nginx server instance
server {
    listen 80;
    server_name test.com;
    error_log /var/log/nginx/go_error.log;

    location / {
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_set_header X-NginX-Proxy true;
      proxy_http_version 1.1; # for keep-alive
      proxy_pass http://influxdb/;
      proxy_redirect off;
    }
 }

由于不占用本地的TCP连接,在大访问量的情况下 性能会非常好。