<repo_name>gebiWangshushu/Hei.PrometheusFileBaseServiceDiscover<file_sep>/prometheus-filebase-servicediscover.lua
ngx.header.content_type = 'application/json'
local targetFilePath = '/home/website/prometheus/targets.yml' --prometheus targe配置，777权限
local json = require('cjson')

--common metheds
local function fileRead(path)
    local file = io.open(path, 'r')
    local json = file:read('*a')
    file:close()
    return json
end
local function fileWrite(path, content)
    local file = io.open(path, 'w+')
    file:write(content)
    file:close()
end

local function success(msg)
    ngx.say(string.format([[{"code":%d,"message":"%s"}]], 200, msg))
end
local function fail(msg)
    ngx.say(string.format([[{"code":%d,"message":"%s"}]], 300, msg))
end

local function tableKeyFind(tbl, key)
    if tbl == nil then
        return false
    end
    for k, v in pairs(tbl) do
        if k == key then
            return true
        end
    end
    return false
end
local function tableValueFind(tbl, value)
    if tbl == nil then
        return false
    end
    for k, v in pairs(tbl) do
        if v == value then
            return true
        end
    end
    return false
end

local function appFind(configObj, postConfigObj)
    if configObj == nil or postConfigObj == nil then
        print('configObj or postConfigObj can not be nil')
        return false
    end
    for i, v in ipairs(configObj) do
        if v.labels.app == postConfigObj.labels.app then
            return i
        end
    end
    return 0
end

local function registeConfig(configObj, postConfigObj, targetFile)
    local index = appFind(configObj, postConfigObj)

    if index > 0 then
        for i, target in ipairs(postConfigObj.targets) do
            if tableValueFind(configObj[index].targets, target) == false then
                table.insert(configObj[index].targets, target)
            end
        end
        for key, value in pairs(postConfigObj.labels) do
            if tableValueFind(configObj[index].labels, key) == false then
                configObj[index].labels[key] = value
            end
        end
    else
        local config = {targets = postConfigObj.targets, labels = postConfigObj.labels}
        table.insert(configObj, config)
    end

    local newConfig = json.encode(configObj)
    fileWrite(targetFile, newConfig)
    print('更新新配置成功', newConfig)
    return true
end

local function deregisteConfig(configObj, postConfigObj, targetFile)
    local index = appFind(configObj, postConfigObj)
    if index > 0 then
        for i, target in ipairs(configObj[index].targets) do
            if tableValueFind(postConfigObj.targets, target) then
                table.remove(configObj[index].targets, i)
            end
        end

        local len = table.getn(configObj[index].targets)
        if len == 0 then
            table.remove(configObj, index)
        end

        local newConfig = json.encode(configObj)
        fileWrite(targetFile, table.getn(configObj) > 0 and newConfig or '')
        print('注销配置成功', newConfig)
        return true
    else
        print('注销配置失败，配置不存在：', postConfigObj.labels.app)
        return false
    end
end
--common metheds end

--main chunk

--read and valid post json config
ngx.req.read_body()
local params = ngx.req.get_body_data()

local postConfigObj = json.decode(params)
print('postConfigObj.targets:', type(postConfigObj.targets) == 'userdata')
if
    (postConfigObj.labels == nil) or (type(postConfigObj.labels) == 'userdata') or
        (type(postConfigObj.targets) == 'userdata') or
        (postConfigObj.labels.app) == nil
 then
    return fail('labels.app、targets 均不能为空')
end

--init target config
local configFile = fileRead(targetFilePath)
if configFile == nil or configFile == '' then
    configFile = '[]'
end
print('原配置', configFile)

--update config
local configObj = json.decode(configFile)
local result = false
if postConfigObj.type == 'deregiste' then
    result = deregisteConfig(configObj, postConfigObj, targetFilePath)
    if result then
        return success('注销配置成功')
    end
    return fail('注销配置失败,请检查app是否存在')
else
    result = registeConfig(configObj, postConfigObj, targetFilePath)
    return success('注册配置成功')
end
<file_sep>/README.md
# Hei.PrometheusFileBaseServiceDiscover

监控解决方案 prometheus，基于文件的服务发现配置脚本，使用 OpenResty+Lua 实现

# 背景

prometheus 可以使用 Consul、k8s 等方式做服务发现，也可以基于文件配置的服务发现。

基于文件配置服务发现需要手动去修改文件，然后 prometheus 再去刷新文件配置这样，而如果我们服务很多，我想用 Rest 接口 Post 请求配置，直接修改文件。这样就可以程序在启动时调用注册接口，程序停止时调用撤销注册接口，就可以做到一个粗糙版本的服务发现了。

我选用的是 OpenResty+Lua 实现。

# 部署

## prometheus 配置

```
scrape_configs:
- job_name: 'file_ds'
  file_sd_configs:
  - refresh_interval: 10s #10秒刷新一次
    files:
     /etc/prometheus/*.yml
```

## OpenResty

```
 location /prometheus {
	   content_by_lua_file /home/website/prometheus/prometheus-filebase-servicediscover.lua;
 }

error_log   /var/log/nginx/prometheus.error.log   debug; #刚开始跑的时候建议把debug日志打开
```

# 使用

## 注册服务

```
curl --location --request POST 'http://your_id:port/prometheus' \
--header 'Content-Type: application/json' \
--data-raw '  {
    "type":"registe",
    "targets": ["172.16.3.119:91012", "172.16.3.117:91221"],
    "labels": {
      "env": "dev",
      "app": "container3"
    }
  }'
```

响应：

```
{
    "code": 200,
    "message": "注册配置成功"
}
```

## 注销服务

```
curl --location --request POST 'http://your_id:port/prometheus' \
--header 'Content-Type: application/json' \
--data-raw '  {
    "type":"deregiste",
    "targets": ["172.16.3.119:91012", "172.16.3.117:91221"],
    "labels": {
      "env": "dev",
      "app": "container3"
    }
  }'
```

响应：

```
{
    "code": 200,
    "message": "注销配置成功"
}
```
