1、源碼編譯transfer模塊
1.1搭建環境
安裝C語言、golang、mysql、redis、open-falcon環境等過程上一次已經在對alarm模塊報警信息處理的文章裏記錄過了,就不再重複記錄了。1.2編譯transfer源碼
進入transfer源碼目錄,命令行輸入如下內容回車:liang@ubuntu:~/goproj/src/github.com/open-falcon/transfer$ ./control build
打印如下信息即編譯成功:
0.0.14 190a7eb
使用源代碼編譯方便以後可以按照自身的需求,自定義transfer的功能,如果沒有這樣的需求可以直接命令行安裝open-falcon,安裝完後修改配置文件,啓動各個模塊即可。
2、監控數據的傳遞過程
2.1數據的來源
監控數據來自於agent模塊,agent模塊收集到主機的各項數據後發送到transfer模塊,包括:內存使用情況、CPU使用情況、磁盤使用情況、磁盤IO情況、網絡使用情況、內核狀態、自定義數據等等。2.2數據的傳遞方法
transfer模塊與agent模塊使用rpc的通信方式傳遞監控數據,agent發送rpc請求,包含:方法、參數。方法是transfer響應rpc請求的函數,參數是上傳的監控數據,以metric爲單位上傳,每一個metric對應一個監控指標。transfer接受rpc請求後,將會給agent返回一個rpc請求響應結果,agent即可得知監控數據是否上傳成功。2.3數據的接收過程
transfer模塊註冊監聽rpc請求的函數,定義該函數的參數爲結構體指針數組的形式,那麼,agent模塊傳入的請求方法使用的參數也是要對應爲數組的形式。agent模塊發送rpc請求後,transfer執行該函數,開始分析傳入的參數,最後返回rpc請求結果。3、監控數據的分析過程
3.1數據的分析過程
open-flacon的官方文檔裏面可以得知,agent模塊上傳的監控數據項是以json的格式上傳,而且監控數據被保存在一個結構體指針數組裏面,每一個監控數據項是一個json格式結構體,一個數組包含多個json結構(即多個監控數據項),transfer拿到rpc請求參數後,遍歷該數組,拿到每一數組之後,按照json結構體格式進行分析,並且判斷關鍵項的值是否合法。最後,把每一項合法的json格式監控數據重新組織成數據結構體,再添加到另一個結構體指針裏面等待處理。3.2數據分析原函數
open-falcon transfer模塊監控數據上傳rpc請求函數原型:
// process new metric values
func RecvMetricValues(args []*cmodel.MetricValue, reply *cmodel.TransferResponse, from string) error {
start := time.Now()
reply.Invalid = 0
items := []*cmodel.MetaData{}
for _, v := range args {
if v == nil {
reply.Invalid += 1
continue
}
// 歷史遺留問題.
// 老版本agent上報的metric=kernel.hostname的數據,其取值爲string類型,現在已經不支持了;所以,這裏硬編碼過濾掉
if v.Metric == "kernel.hostname" {
reply.Invalid += 1
continue
}
if v.Metric == "" || v.Endpoint == "" {
reply.Invalid += 1
continue
}
if v.Type != g.COUNTER && v.Type != g.GAUGE && v.Type != g.DERIVE {
reply.Invalid += 1
continue
}
if v.Value == "" {
reply.Invalid += 1
continue
}
if v.Step <= 0 {
reply.Invalid += 1
continue
}
if len(v.Metric)+len(v.Tags) > 510 {
reply.Invalid += 1
continue
}
// TODO 呵呵,這裏需要再優雅一點
now := start.Unix()
if v.Timestamp <= 0 || v.Timestamp > now*2 {
v.Timestamp = now
}
fv := &cmodel.MetaData{
Metric: v.Metric,
Endpoint: v.Endpoint,
Timestamp: v.Timestamp,
Step: v.Step,
CounterType: v.Type,
Tags: cutils.DictedTagstring(v.Tags), //TODO tags鍵值對的個數,要做一下限制
}
valid := true
var vv float64
var err error
switch cv := v.Value.(type) {
case string:
vv, err = strconv.ParseFloat(cv, 64)
if err != nil {
valid = false
}
case float64:
vv = cv
case int64:
vv = float64(cv)
default:
valid = false
}
if !valid {
reply.Invalid += 1
continue
}
fv.Value = vv
items = append(items, fv)
}
// statistics
cnt := int64(len(items))
proc.RecvCnt.IncrBy(cnt)
if from == "rpc" {
proc.RpcRecvCnt.IncrBy(cnt)
} else if from == "http" {
proc.HttpRecvCnt.IncrBy(cnt)
}
cfg := g.Config()
if cfg.Graph.Enabled {
sender.Push2GraphSendQueue(items, cfg.Graph.Migrating)
}
if cfg.Judge.Enabled {
sender.Push2JudgeSendQueue(items)
}
reply.Message = "ok"
reply.Total = len(args)
reply.Latency = (time.Now().UnixNano() - start.UnixNano()) / 1000000
return nil
}
4、使用netcat上傳數據
4.1數據的上傳格式
rpc請求函數中的參數,該參數使用json格式。4.2數據的上傳命令
使用nc(netcate)網絡控制命令完成rpc請求echo "{\"method\":\"Transfer.Update\",\"params\":[[{\"endpoint\":\"bogon\",\"metric\":\"agent.test\",\"value\":2,\"step\":60,\"counterType\":\"GAUGE\",\"tags\":\"open-falocn\",\"timestamp\":123456789}]]}" | nc 192.168.12.88 8433
這裏因爲rpc請求的傳入參數是結構體指針數組的形式,糾結了一段很長的時間,以後考慮問題一定要全面,既然使用到rpc請求,就要考慮到rpc請求函數的傳參問題,參數不對rpc請求肯定失敗。
5、使用socket上傳數據
5.1數據的上傳格式
rpc請求函數中的參數,該參數使用json格式。"{\"method\":\"Transfer.Update\",\"params\":[[{\"endpoint\":\"bogon\",\"metric\":\"agent.test\",\"value\":2,\"step\":60,\"counterType\":\"GAUGE\",\"tags\":\"open-falocn\",\"timestamp\":123456789}]]}"
5.2數據的上傳過程
一個socket客戶端,請求連接transfer監聽的rpc端口,發送rpc請求數據如上格式,接受rpc請求響應結果,關閉socket。這裏曾經嘗試了好多方法:libjrpc、libjson、golang、python、curl、openrpc、等都沒有實現。只能說生活狀態亂,工作效率也是很低下的。折騰了好長時間,一個簡單的socket就把數據發送過去了,真是淚崩,也是沒有完全理解當中的原理,一直埋頭看jsonrpc上的內容。
這一次又是一個教訓!