├── test ├── gateway.list ├── http.pools ├── http.recv.trace ├── http.recv.filter ├── push └── debug ├── perfcounter.json ├── g ├── proc.go ├── model.go ├── g.go └── cfg.go ├── receiver ├── receiver.go ├── socket │ ├── socket.go │ └── socket_telnet.go └── rpc │ ├── rpc.go │ └── rpc_transfer.go ├── sender ├── sender_cron.go ├── sender.go ├── send_tasks.go └── conn_pool │ └── conn_pool_rpc.go ├── .gitignore ├── main.go ├── cfg.example.json ├── http ├── api_http.go ├── common.go ├── http.go └── proc_http.go ├── control └── README.md /test/gateway.list: -------------------------------------------------------------------------------- 1 | 127.0.0.1:6060 2 | -------------------------------------------------------------------------------- /perfcounter.json: -------------------------------------------------------------------------------- 1 | { 2 | "tags": "service=gateway", 3 | "bases": ["debug","runtime"], 4 | "push": { 5 | "enabled": true 6 | }, 7 | "http": { 8 | "enabled": true 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/http.pools: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | host_file=./gateway.list 3 | 4 | for i in `cat $host_file`; 5 | do 6 | printf "%s, %s\n" $i 7 | curl -s "$i/proc/transfer/pools" 8 | printf "\n" 9 | sleep 0.1 10 | done 11 | -------------------------------------------------------------------------------- /g/proc.go: -------------------------------------------------------------------------------- 1 | package g 2 | 3 | import ( 4 | nproc "github.com/toolkits/proc" 5 | ) 6 | 7 | var ( 8 | RecvDataTrace = nproc.NewDataTrace("RecvDataTrace", 5) 9 | RecvDataFilter = nproc.NewDataFilter("RecvDataFilter", 5) 10 | ) 11 | -------------------------------------------------------------------------------- /test/http.recv.trace: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | host_file=./gateway.list 3 | e=$1 4 | m=$2 5 | tags=$3 6 | 7 | for i in `cat $host_file`; 8 | do 9 | printf "%s\n" $i 10 | curl -s "$i/trace/$e/$m/$tags" 11 | printf "\n" 12 | sleep 0.1 13 | done 14 | -------------------------------------------------------------------------------- /receiver/receiver.go: -------------------------------------------------------------------------------- 1 | package receiver 2 | 3 | import ( 4 | "github.com/open-falcon/gateway/receiver/rpc" 5 | "github.com/open-falcon/gateway/receiver/socket" 6 | ) 7 | 8 | func Start() { 9 | go rpc.StartRpc() 10 | go socket.StartSocket() 11 | } 12 | -------------------------------------------------------------------------------- /test/http.recv.filter: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | host_file=./gateway.list 3 | e=$1 4 | m=$2 5 | opt=$3 6 | val=$4 7 | tags=$5 8 | 9 | for i in `cat $host_file`; 10 | do 11 | printf "%s\n" $i 12 | curl -s "$i/filter/$e/$m/$opt/$val/$tags" 13 | printf "\n" 14 | sleep 0.1 15 | done 16 | -------------------------------------------------------------------------------- /g/model.go: -------------------------------------------------------------------------------- 1 | package g 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type TransferResp struct { 8 | Msg string 9 | Total int 10 | ErrInvalid int 11 | Latency int64 12 | } 13 | 14 | func (t *TransferResp) String() string { 15 | s := fmt.Sprintf("TransferResp total=%d, err_invalid=%d, latency=%dms", 16 | t.Total, t.ErrInvalid, t.Latency) 17 | if t.Msg != "" { 18 | s = fmt.Sprintf("%s, msg=%s", s, t.Msg) 19 | } 20 | return s 21 | } 22 | -------------------------------------------------------------------------------- /sender/sender_cron.go: -------------------------------------------------------------------------------- 1 | package sender 2 | 3 | import ( 4 | "time" 5 | 6 | pfc "github.com/niean/goperfcounter" 7 | ) 8 | 9 | const ( 10 | DefaultProcCronPeriod = time.Duration(5) * time.Second //ProcCron的周期,默认1s 11 | ) 12 | 13 | // send_cron程序入口 14 | func startSenderCron() { 15 | go startProcCron() 16 | } 17 | 18 | func startProcCron() { 19 | for { 20 | time.Sleep(DefaultProcCronPeriod) 21 | refreshSendingCacheSize() 22 | } 23 | } 24 | 25 | func refreshSendingCacheSize() { 26 | pfc.Gauge("SendQueueSize", int64(SenderQueue.Len())) 27 | } 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | 26 | *.swp 27 | *.swo 28 | *.log 29 | .idea 30 | .DS_Store 31 | /var 32 | /falcon-gateway* 33 | /cfg.json 34 | /test/build 35 | git.go 36 | 37 | .gitversion 38 | perfcounter.json -------------------------------------------------------------------------------- /test/push: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | testdir=$(cd $(dirname $0)/; pwd) 3 | workdir=$(dirname $testdir) 4 | cd $workdir 5 | 6 | cfg=./cfg.json 7 | httpport=`cat cfg.json | grep -A3 "\"http\":" | grep "\"listen\"" | cut -d\" -f4 | cut -d: -f2` 8 | httpprex="127.0.0.1:$httpport" 9 | 10 | function http_push(){ 11 | e="e.niean" 12 | m="m.niean" 13 | t="t0=tag0,t1=tag1" 14 | ts=`date +%s` 15 | val=`expr $ts / 60 % 10` 16 | curl -s -X POST -d "[{\"metric\":\"$m\", \"endpoint\":\"$e\", \"timestamp\":$ts,\"step\":60, \"value\":$val, \"counterType\":\"GAUGE\",\"tags\":\"$t\"}]" "$httpprex/api/push" | python -m json.tool 17 | } 18 | 19 | http_push 20 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "os" 7 | 8 | "github.com/open-falcon/gateway/g" 9 | "github.com/open-falcon/gateway/http" 10 | "github.com/open-falcon/gateway/receiver" 11 | "github.com/open-falcon/gateway/sender" 12 | ) 13 | 14 | func main() { 15 | cfg := flag.String("c", "cfg.json", "configuration file") 16 | version := flag.Bool("v", false, "show version") 17 | flag.Parse() 18 | 19 | if *version { 20 | fmt.Println(g.VERSION) 21 | os.Exit(0) 22 | } 23 | 24 | // global config 25 | g.ParseConfig(*cfg) 26 | 27 | sender.Start() 28 | receiver.Start() 29 | 30 | // http 31 | http.Start() 32 | 33 | select {} 34 | } 35 | -------------------------------------------------------------------------------- /cfg.example.json: -------------------------------------------------------------------------------- 1 | { 2 | "debug": true, 3 | "http": { 4 | "enabled": true, 5 | "listen": "0.0.0.0:6060" 6 | }, 7 | "rpc": { 8 | "enabled": true, 9 | "listen": "0.0.0.0:8433" 10 | }, 11 | "socket": { 12 | "enabled": true, 13 | "listen": "0.0.0.0:4444", 14 | "timeout": 3600 15 | }, 16 | "transfer": { 17 | "enabled": true, 18 | "batch": 200, 19 | "retry": 1, 20 | "connTimeout": 1000, 21 | "callTimeout": 5000, 22 | "maxConns": 32, 23 | "maxIdle": 32, 24 | "cluster": { 25 | "t1": "127.0.0.1:8433" 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /receiver/socket/socket.go: -------------------------------------------------------------------------------- 1 | package socket 2 | 3 | import ( 4 | "log" 5 | "net" 6 | 7 | "github.com/open-falcon/gateway/g" 8 | ) 9 | 10 | func StartSocket() { 11 | if !g.Config().Socket.Enabled { 12 | return 13 | } 14 | 15 | addr := g.Config().Socket.Listen 16 | tcpAddr, err := net.ResolveTCPAddr("tcp", addr) 17 | if err != nil { 18 | log.Fatalf("net.ResolveTCPAddr fail: %s", err) 19 | } 20 | 21 | listener, err := net.ListenTCP("tcp", tcpAddr) 22 | if err != nil { 23 | log.Fatalf("listen %s fail: %s", addr, err) 24 | } else { 25 | log.Println("socket listening", addr) 26 | } 27 | 28 | defer listener.Close() 29 | 30 | for { 31 | conn, err := listener.Accept() 32 | if err != nil { 33 | log.Println("listener.Accept occur error:", err) 34 | continue 35 | } 36 | 37 | go socketTelnetHandle(conn) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /http/api_http.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "encoding/json" 5 | "net/http" 6 | 7 | cmodel "github.com/open-falcon/common/model" 8 | 9 | "github.com/open-falcon/gateway/g" 10 | trpc "github.com/open-falcon/gateway/receiver/rpc" 11 | ) 12 | 13 | func configApiHttpRoutes() { 14 | http.HandleFunc("/api/push", func(w http.ResponseWriter, req *http.Request) { 15 | if req.ContentLength == 0 { 16 | http.Error(w, "blank body", http.StatusBadRequest) 17 | return 18 | } 19 | 20 | decoder := json.NewDecoder(req.Body) 21 | var metrics []*cmodel.MetricValue 22 | err := decoder.Decode(&metrics) 23 | if err != nil { 24 | http.Error(w, "decode error", http.StatusBadRequest) 25 | return 26 | } 27 | 28 | reply := &g.TransferResp{} 29 | trpc.RecvMetricValues(metrics, reply, "http") 30 | 31 | RenderDataJson(w, reply) 32 | }) 33 | } 34 | -------------------------------------------------------------------------------- /g/g.go: -------------------------------------------------------------------------------- 1 | package g 2 | 3 | import ( 4 | "log" 5 | "runtime" 6 | ) 7 | 8 | // changelog: 9 | // 0.0.1: init project 10 | // 0.0.3: change conn pools, enlarge sending queue 11 | // 0.0.4: use relative paths in 'import', mv conn_pool to central libs 12 | // 0.0.5: use absolute paths in 'import' 13 | // 0.0.6: support load balance between transfer instances 14 | // 0.0.7: substitute common pkg for the old model pkg 15 | // 0.0.8: do not retry in send, change send concurrent 16 | // 0.0.9: add proc for send failure, rm git version 17 | // 0.0.10: control sending concurrent of slow transfers 18 | // 0.0.11: use pfc 19 | 20 | const ( 21 | VERSION = "0.0.11" 22 | GAUGE = "GAUGE" 23 | COUNTER = "COUNTER" 24 | DERIVE = "DERIVE" 25 | DEFAULT_STEP = 60 26 | MIN_STEP = 30 27 | ) 28 | 29 | func init() { 30 | runtime.GOMAXPROCS(runtime.NumCPU()) 31 | log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile) 32 | } 33 | -------------------------------------------------------------------------------- /receiver/rpc/rpc.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "log" 5 | "net" 6 | "net/rpc" 7 | "net/rpc/jsonrpc" 8 | 9 | "github.com/open-falcon/gateway/g" 10 | ) 11 | 12 | func StartRpc() { 13 | if !g.Config().Rpc.Enabled { 14 | return 15 | } 16 | 17 | addr := g.Config().Rpc.Listen 18 | tcpAddr, err := net.ResolveTCPAddr("tcp", addr) 19 | if err != nil { 20 | log.Fatalf("net.ResolveTCPAddr fail: %s", err) 21 | } 22 | 23 | listener, err := net.ListenTCP("tcp", tcpAddr) 24 | if err != nil { 25 | log.Fatalf("listen %s fail: %s", addr, err) 26 | } else { 27 | log.Println("rpc listening", addr) 28 | } 29 | 30 | server := rpc.NewServer() 31 | server.Register(new(Transfer)) 32 | 33 | for { 34 | conn, err := listener.Accept() 35 | if err != nil { 36 | log.Println("listener.Accept occur error:", err) 37 | continue 38 | } 39 | // go rpc.ServeConn(conn) 40 | go server.ServeCodec(jsonrpc.NewServerCodec(conn)) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /http/common.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "strings" 7 | 8 | "github.com/toolkits/file" 9 | 10 | "github.com/open-falcon/gateway/g" 11 | ) 12 | 13 | func configCommonRoutes() { 14 | http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { 15 | w.Write([]byte("ok\n")) 16 | }) 17 | 18 | http.HandleFunc("/version", func(w http.ResponseWriter, r *http.Request) { 19 | w.Write([]byte(fmt.Sprintf("%s\n", g.VERSION))) 20 | }) 21 | 22 | http.HandleFunc("/workdir", func(w http.ResponseWriter, r *http.Request) { 23 | w.Write([]byte(fmt.Sprintf("%s\n", file.SelfDir()))) 24 | }) 25 | 26 | http.HandleFunc("/config", func(w http.ResponseWriter, r *http.Request) { 27 | RenderDataJson(w, g.Config()) 28 | }) 29 | 30 | http.HandleFunc("/config/reload", func(w http.ResponseWriter, r *http.Request) { 31 | if strings.HasPrefix(r.RemoteAddr, "127.0.0.1") { 32 | g.ParseConfig(g.ConfigFile) 33 | RenderDataJson(w, "ok") 34 | } else { 35 | RenderDataJson(w, "no privilege") 36 | } 37 | }) 38 | } 39 | -------------------------------------------------------------------------------- /http/http.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "encoding/json" 5 | "log" 6 | "net/http" 7 | _ "net/http/pprof" 8 | 9 | "github.com/open-falcon/gateway/g" 10 | ) 11 | 12 | type Dto struct { 13 | Msg string `json:"msg"` 14 | Data interface{} `json:"data"` 15 | } 16 | 17 | func Start() { 18 | go startHttpServer() 19 | } 20 | 21 | func startHttpServer() { 22 | if !g.Config().Http.Enabled { 23 | return 24 | } 25 | 26 | addr := g.Config().Http.Listen 27 | if addr == "" { 28 | return 29 | } 30 | 31 | configCommonRoutes() 32 | configProcHttpRoutes() 33 | configApiHttpRoutes() 34 | 35 | s := &http.Server{ 36 | Addr: addr, 37 | MaxHeaderBytes: 1 << 30, 38 | } 39 | 40 | log.Println("http.startHttpServer ok, listening", addr) 41 | log.Fatalln(s.ListenAndServe()) 42 | } 43 | 44 | func RenderJson(w http.ResponseWriter, v interface{}) { 45 | bs, err := json.Marshal(v) 46 | if err != nil { 47 | http.Error(w, err.Error(), http.StatusInternalServerError) 48 | return 49 | } 50 | w.Header().Set("Content-Type", "application/json; charset=UTF-8") 51 | w.Write(bs) 52 | } 53 | 54 | func RenderDataJson(w http.ResponseWriter, data interface{}) { 55 | RenderJson(w, Dto{Msg: "success", Data: data}) 56 | } 57 | 58 | func RenderMsgJson(w http.ResponseWriter, msg string) { 59 | RenderJson(w, map[string]string{"msg": msg}) 60 | } 61 | 62 | func AutoRender(w http.ResponseWriter, data interface{}, err error) { 63 | if err != nil { 64 | RenderMsgJson(w, err.Error()) 65 | return 66 | } 67 | RenderDataJson(w, data) 68 | } 69 | -------------------------------------------------------------------------------- /test/debug: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ## test home 3 | testdir=$(cd $(dirname $0)/; pwd) 4 | ## word home 5 | workdir=$(dirname $testdir) 6 | cd $workdir 7 | 8 | control=./control 9 | 10 | cfg=./cfg.json 11 | httpport=80 12 | if [ -f $cfg ]; then 13 | httpport=`cat $cfg | grep -A3 "\"http\":" | grep "\"listen\"" | cut -d\" -f4 | cut -d: -f2` 14 | fi 15 | httpprex="127.0.0.1:$httpport" 16 | 17 | ## health 18 | function health(){ 19 | url="/health" 20 | curl -s "$httpprex$url" && printf "\n" 21 | } 22 | 23 | ## proc 24 | function proc(){ 25 | args=$@ 26 | for i in $@; do 27 | url="$url/$i" 28 | done 29 | curl -s "$httpprex$url" | python -m json.tool 30 | } 31 | 32 | ## config 33 | function config(){ 34 | args=$@ 35 | for i in $@; do 36 | url="$url/$i" 37 | done 38 | curl -s "$httpprex$url" | python -m json.tool 39 | } 40 | 41 | ## control 42 | function control(){ 43 | $control $@ 44 | } 45 | 46 | ## debug 47 | function debug(){ 48 | args=$@ 49 | for i in $@; do 50 | url="$url/$i" 51 | done 52 | curl -s "$httpprex$url" && printf "\n" 53 | } 54 | 55 | ## pfc 56 | function pfc(){ 57 | args=$@ 58 | for i in $@; do 59 | url="$url/$i" 60 | done 61 | curl -s "$httpprex$url" | python -m json.tool 62 | } 63 | 64 | action=$1 65 | case $action in 66 | "health") 67 | health 68 | ;; 69 | "debug") 70 | debug $@ 71 | ;; 72 | "config") 73 | config $@ 74 | ;; 75 | "proc") 76 | proc $@ 77 | ;; 78 | "pfc") 79 | pfc $@ 80 | ;; 81 | "") 82 | pfc "pfc/proc/metrics/gauge,meter/json" 83 | ;; 84 | *) 85 | control $@ 86 | ;; 87 | esac 88 | 89 | -------------------------------------------------------------------------------- /sender/sender.go: -------------------------------------------------------------------------------- 1 | package sender 2 | 3 | import ( 4 | "log" 5 | 6 | pfc "github.com/niean/goperfcounter" 7 | cmodel "github.com/open-falcon/common/model" 8 | nlist "github.com/toolkits/container/list" 9 | nproc "github.com/toolkits/proc" 10 | 11 | "github.com/open-falcon/gateway/g" 12 | cpool "github.com/open-falcon/gateway/sender/conn_pool" 13 | ) 14 | 15 | const ( 16 | DefaultSendQueueMaxSize = 1024000 //102.4w 17 | ) 18 | 19 | var ( 20 | SenderQueue = nlist.NewSafeListLimited(DefaultSendQueueMaxSize) 21 | SenderConnPools *cpool.SafeRpcConnPools 22 | 23 | TransferMap = make(map[string]string, 0) 24 | TransferHostnames = make([]string, 0) 25 | TransferSendCnt = make(map[string]*nproc.SCounterQps, 0) 26 | TransferSendFailCnt = make(map[string]*nproc.SCounterQps, 0) 27 | ) 28 | 29 | func Start() { 30 | initConnPools() 31 | startSendTasks() 32 | startSenderCron() 33 | log.Println("send.Start, ok") 34 | } 35 | 36 | func Push2SendQueue(items []*cmodel.MetaData) { 37 | for _, item := range items { 38 | 39 | // statistics 40 | pk := item.PK() 41 | g.RecvDataTrace.Trace(pk, item) 42 | g.RecvDataFilter.Filter(pk, item.Value, item) 43 | 44 | isOk := SenderQueue.PushFront(item) 45 | 46 | // statistics 47 | if !isOk { 48 | pfc.Meter("SendDrop", 1) 49 | } 50 | } 51 | } 52 | 53 | func initConnPools() { 54 | cfg := g.Config() 55 | 56 | // init transfer global configs 57 | addrs := make([]string, 0) 58 | for hn, addr := range cfg.Transfer.Cluster { 59 | TransferHostnames = append(TransferHostnames, hn) 60 | addrs = append(addrs, addr) 61 | TransferMap[hn] = addr 62 | } 63 | 64 | // init transfer send cnt 65 | for hn, addr := range cfg.Transfer.Cluster { 66 | TransferSendCnt[hn] = nproc.NewSCounterQps(hn + ":" + addr) 67 | TransferSendFailCnt[hn] = nproc.NewSCounterQps(hn + ":" + addr) 68 | } 69 | 70 | // init conn pools 71 | SenderConnPools = cpool.CreateSafeRpcConnPools(cfg.Transfer.MaxConns, cfg.Transfer.MaxIdle, 72 | cfg.Transfer.ConnTimeout, cfg.Transfer.CallTimeout, addrs) 73 | } 74 | -------------------------------------------------------------------------------- /g/cfg.go: -------------------------------------------------------------------------------- 1 | package g 2 | 3 | import ( 4 | "encoding/json" 5 | "log" 6 | "sync" 7 | 8 | "github.com/toolkits/file" 9 | ) 10 | 11 | type HttpConfig struct { 12 | Enabled bool `json:"enabled"` 13 | Listen string `json:"listen"` 14 | } 15 | 16 | type RpcConfig struct { 17 | Enabled bool `json:"enabled"` 18 | Listen string `json:"listen"` 19 | } 20 | 21 | type SocketConfig struct { 22 | Enabled bool `json:"enabled"` 23 | Listen string `json:"listen"` 24 | Timeout int32 `json:"timeout"` 25 | } 26 | 27 | type TransferConfig struct { 28 | Enabled bool `json:"enabled"` 29 | Batch int32 `json:"batch"` 30 | Retry int32 `json:"retry"` 31 | ConnTimeout int32 `json:"connTimeout"` 32 | CallTimeout int32 `json:"callTimeout"` 33 | MaxConns int32 `json:"maxConns"` 34 | MaxIdle int32 `json:"maxIdle"` 35 | Cluster map[string]string `json:"cluster"` 36 | } 37 | 38 | type GlobalConfig struct { 39 | Debug bool `json:"debug"` 40 | Http *HttpConfig `json:"http"` 41 | Rpc *RpcConfig `json:"rpc"` 42 | Socket *SocketConfig `json:"socket"` 43 | Transfer *TransferConfig `json:"transfer"` 44 | } 45 | 46 | var ( 47 | ConfigFile string 48 | config *GlobalConfig 49 | configLock = new(sync.RWMutex) 50 | ) 51 | 52 | func Config() *GlobalConfig { 53 | configLock.RLock() 54 | defer configLock.RUnlock() 55 | return config 56 | } 57 | 58 | func ParseConfig(cfg string) { 59 | if cfg == "" { 60 | log.Fatalln("use -c to specify configuration file") 61 | } 62 | 63 | if !file.IsExist(cfg) { 64 | log.Fatalln("config file:", cfg, "is not existent. maybe you need `mv cfg.example.json cfg.json`") 65 | } 66 | 67 | ConfigFile = cfg 68 | 69 | configContent, err := file.ToTrimString(cfg) 70 | if err != nil { 71 | log.Fatalln("read config file:", cfg, "fail:", err) 72 | } 73 | 74 | var c GlobalConfig 75 | err = json.Unmarshal([]byte(configContent), &c) 76 | if err != nil { 77 | log.Fatalln("parse config file:", cfg, "fail:", err) 78 | } 79 | 80 | // 配置文件正确性 校验, 不合法则直接 Exit(1) 81 | // TODO 82 | 83 | configLock.Lock() 84 | defer configLock.Unlock() 85 | config = &c 86 | 87 | log.Println("g.ParseConfig ok, file ", cfg) 88 | } 89 | -------------------------------------------------------------------------------- /sender/send_tasks.go: -------------------------------------------------------------------------------- 1 | package sender 2 | 3 | import ( 4 | "log" 5 | "math/rand" 6 | "time" 7 | 8 | pfc "github.com/niean/goperfcounter" 9 | cmodel "github.com/open-falcon/common/model" 10 | cutils "github.com/open-falcon/common/utils" 11 | nsema "github.com/toolkits/concurrent/semaphore" 12 | nlist "github.com/toolkits/container/list" 13 | 14 | "github.com/open-falcon/gateway/g" 15 | ) 16 | 17 | func startSendTasks() { 18 | cfg := g.Config() 19 | concurrent := cfg.Transfer.MaxConns * int32(len(cfg.Transfer.Cluster)) 20 | go forward2TransferTask(SenderQueue, concurrent) 21 | } 22 | 23 | func forward2TransferTask(Q *nlist.SafeListLimited, concurrent int32) { 24 | cfg := g.Config() 25 | batch := int(cfg.Transfer.Batch) 26 | maxConns := int64(cfg.Transfer.MaxConns) 27 | retry := int(cfg.Transfer.Retry) 28 | if retry < 1 { 29 | retry = 1 30 | } 31 | 32 | sema := nsema.NewSemaphore(int(concurrent)) 33 | transNum := len(TransferHostnames) 34 | 35 | for { 36 | items := Q.PopBackBy(batch) 37 | count := len(items) 38 | if count == 0 { 39 | time.Sleep(time.Millisecond * 50) 40 | continue 41 | } 42 | 43 | transItems := make([]*cmodel.MetricValue, count) 44 | for i := 0; i < count; i++ { 45 | transItems[i] = convert(items[i].(*cmodel.MetaData)) 46 | } 47 | 48 | sema.Acquire() 49 | go func(transItems []*cmodel.MetricValue, count int) { 50 | defer sema.Release() 51 | var err error 52 | 53 | // 随机遍历transfer列表,直到数据发送成功 或者 遍历完;随机遍历,可以缓解慢transfer 54 | resp := &g.TransferResp{} 55 | sendOk := false 56 | 57 | for j := 0; j < retry && !sendOk; j++ { 58 | rint := rand.Int() 59 | for i := 0; i < transNum && !sendOk; i++ { 60 | idx := (i + rint) % transNum 61 | host := TransferHostnames[idx] 62 | addr := TransferMap[host] 63 | 64 | // 过滤掉建连缓慢的host, 否则会严重影响发送速率 65 | cc := pfc.GetCounterCount(host) 66 | if cc >= maxConns { 67 | continue 68 | } 69 | 70 | pfc.Counter(host, 1) 71 | err = SenderConnPools.Call(addr, "Transfer.Update", transItems, resp) 72 | pfc.Counter(host, -1) 73 | 74 | if err == nil { 75 | sendOk = true 76 | // statistics 77 | TransferSendCnt[host].IncrBy(int64(count)) 78 | } else { 79 | // statistics 80 | TransferSendFailCnt[host].IncrBy(int64(count)) 81 | } 82 | } 83 | } 84 | 85 | // statistics 86 | if !sendOk { 87 | if cfg.Debug { 88 | log.Printf("send to transfer fail, connpool:%v", SenderConnPools.Proc()) 89 | } 90 | pfc.Meter("SendFail", int64(count)) 91 | } else { 92 | pfc.Meter("Send", int64(count)) 93 | } 94 | }(transItems, count) 95 | } 96 | } 97 | 98 | func convert(v *cmodel.MetaData) *cmodel.MetricValue { 99 | return &cmodel.MetricValue{ 100 | Metric: v.Metric, 101 | Endpoint: v.Endpoint, 102 | Timestamp: v.Timestamp, 103 | Step: v.Step, 104 | Type: v.CounterType, 105 | Tags: cutils.SortedTags(v.Tags), 106 | Value: v.Value, 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /receiver/socket/socket_telnet.go: -------------------------------------------------------------------------------- 1 | package socket 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "net" 7 | "strconv" 8 | "strings" 9 | "time" 10 | 11 | pfc "github.com/niean/goperfcounter" 12 | cmodel "github.com/open-falcon/common/model" 13 | 14 | "github.com/open-falcon/gateway/g" 15 | "github.com/open-falcon/gateway/sender" 16 | ) 17 | 18 | func socketTelnetHandle(conn net.Conn) { 19 | defer conn.Close() 20 | 21 | items := []*cmodel.MetaData{} 22 | buf := bufio.NewReader(conn) 23 | 24 | cfg := g.Config() 25 | timeout := time.Duration(cfg.Socket.Timeout) * time.Second 26 | 27 | for { 28 | conn.SetReadDeadline(time.Now().Add(timeout)) 29 | line, err := buf.ReadString('\n') 30 | if err != nil { 31 | break 32 | } 33 | 34 | line = strings.Trim(line, "\n") 35 | 36 | if line == "quit" { 37 | break 38 | } 39 | 40 | if line == "" { 41 | continue 42 | } 43 | 44 | t := strings.Fields(line) 45 | if len(t) < 2 { 46 | continue 47 | } 48 | 49 | cmd := t[0] 50 | 51 | if cmd != "update" { 52 | continue 53 | } 54 | 55 | item, err := convertLine2MetaData(t[1:]) 56 | if err != nil { 57 | continue 58 | } 59 | 60 | items = append(items, item) 61 | } 62 | 63 | // statistics 64 | count := int64(len(items)) 65 | pfc.Meter("SocketRecv", count) 66 | pfc.Meter("Recv", count) 67 | 68 | if cfg.Transfer.Enabled { 69 | sender.Push2SendQueue(items) 70 | } 71 | 72 | return 73 | } 74 | 75 | // example: endpoint counter timestamp value [type] [step] 76 | // default type is DERIVE, default step is 60s 77 | func convertLine2MetaData(fields []string) (item *cmodel.MetaData, err error) { 78 | if len(fields) != 4 && len(fields) != 5 && len(fields) != 6 { 79 | err = fmt.Errorf("not_enough_fileds") 80 | return 81 | } 82 | 83 | endpoint, metric := fields[0], fields[1] 84 | ts, err := strconv.ParseInt(fields[2], 10, 64) 85 | if err != nil { 86 | return 87 | } 88 | 89 | v, err := strconv.ParseFloat(fields[3], 64) 90 | if err != nil { 91 | return 92 | } 93 | 94 | type_ := g.COUNTER 95 | if len(fields) >= 5 { 96 | type_ = fields[4] 97 | } 98 | 99 | if type_ != g.DERIVE && type_ != g.GAUGE && type_ != g.COUNTER { 100 | err = fmt.Errorf("invalid_counter_type") 101 | return 102 | } 103 | 104 | var step int64 = g.DEFAULT_STEP 105 | if len(fields) == 6 { 106 | dst_args := strings.Split(fields[5], ":") 107 | if len(dst_args) == 1 { 108 | step, err = strconv.ParseInt(dst_args[0], 10, 64) 109 | if err != nil { 110 | return 111 | } 112 | } else if len(dst_args) == 4 { 113 | // for backend-compatible 114 | // heartbeat:min:max:step 115 | step, err = strconv.ParseInt(dst_args[3], 10, 64) 116 | if err != nil { 117 | return 118 | } 119 | } else { 120 | err = fmt.Errorf("invalid_counter_step") 121 | return 122 | } 123 | } 124 | 125 | item = &cmodel.MetaData{ 126 | Metric: metric, 127 | Endpoint: endpoint, 128 | Timestamp: ts, 129 | Step: step, 130 | Value: v, 131 | CounterType: type_, 132 | Tags: make(map[string]string), 133 | } 134 | 135 | return item, nil 136 | } 137 | -------------------------------------------------------------------------------- /http/proc_http.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "net/http" 5 | "strconv" 6 | "strings" 7 | 8 | cutils "github.com/open-falcon/common/utils" 9 | 10 | "github.com/open-falcon/gateway/g" 11 | "github.com/open-falcon/gateway/sender" 12 | ) 13 | 14 | func configProcHttpRoutes() { 15 | // TO BE DISCARDed 16 | http.HandleFunc("/counter/all", func(w http.ResponseWriter, r *http.Request) { 17 | RenderDataJson(w, make([]interface{}, 0)) 18 | }) 19 | http.HandleFunc("/statistics/all", func(w http.ResponseWriter, r *http.Request) { 20 | RenderDataJson(w, make([]interface{}, 0)) 21 | }) 22 | 23 | // proc 24 | http.HandleFunc("/proc/counters", func(w http.ResponseWriter, r *http.Request) { 25 | RenderDataJson(w, make([]interface{}, 0)) 26 | }) 27 | 28 | http.HandleFunc("/proc/transfer/pools", func(w http.ResponseWriter, r *http.Request) { 29 | RenderDataJson(w, sender.SenderConnPools.Proc()) 30 | }) 31 | 32 | http.HandleFunc("/proc/transfer/send", func(w http.ResponseWriter, r *http.Request) { 33 | ret := make([]interface{}, 0) 34 | for _, p := range sender.TransferSendCnt { 35 | ret = append(ret, p.Get()) 36 | } 37 | RenderDataJson(w, ret) 38 | }) 39 | 40 | http.HandleFunc("/proc/transfer/sendfail", func(w http.ResponseWriter, r *http.Request) { 41 | ret := make([]interface{}, 0) 42 | for _, p := range sender.TransferSendFailCnt { 43 | ret = append(ret, p.Get()) 44 | } 45 | RenderDataJson(w, ret) 46 | }) 47 | 48 | // trace 49 | http.HandleFunc("/trace/", func(w http.ResponseWriter, r *http.Request) { 50 | urlParam := r.URL.Path[len("/trace/"):] 51 | args := strings.Split(urlParam, "/") 52 | 53 | argsLen := len(args) 54 | endpoint := args[0] 55 | metric := args[1] 56 | tags := make(map[string]string) 57 | if argsLen > 2 { 58 | tagVals := strings.Split(args[2], ",") 59 | for _, tag := range tagVals { 60 | tagPairs := strings.Split(tag, "=") 61 | if len(tagPairs) == 2 { 62 | tags[tagPairs[0]] = tagPairs[1] 63 | } 64 | } 65 | } 66 | g.RecvDataTrace.SetPK(cutils.PK(endpoint, metric, tags)) 67 | RenderDataJson(w, g.RecvDataTrace.GetAllTraced()) 68 | }) 69 | 70 | // filter 71 | http.HandleFunc("/filter/", func(w http.ResponseWriter, r *http.Request) { 72 | urlParam := r.URL.Path[len("/filter/"):] 73 | args := strings.Split(urlParam, "/") 74 | 75 | argsLen := len(args) 76 | endpoint := args[0] 77 | metric := args[1] 78 | opt := args[2] 79 | 80 | threadholdStr := args[3] 81 | threadhold, err := strconv.ParseFloat(threadholdStr, 64) 82 | if err != nil { 83 | RenderDataJson(w, "bad threadhold") 84 | return 85 | } 86 | 87 | tags := make(map[string]string) 88 | if argsLen > 4 { 89 | tagVals := strings.Split(args[4], ",") 90 | for _, tag := range tagVals { 91 | tagPairs := strings.Split(tag, "=") 92 | if len(tagPairs) == 2 { 93 | tags[tagPairs[0]] = tagPairs[1] 94 | } 95 | } 96 | } 97 | 98 | err = g.RecvDataFilter.SetFilter(cutils.PK(endpoint, metric, tags), opt, threadhold) 99 | if err != nil { 100 | RenderDataJson(w, err.Error()) 101 | return 102 | } 103 | 104 | RenderDataJson(w, g.RecvDataFilter.GetAllFiltered()) 105 | }) 106 | } 107 | -------------------------------------------------------------------------------- /receiver/rpc/rpc_transfer.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "strconv" 5 | "time" 6 | 7 | pfc "github.com/niean/goperfcounter" 8 | cmodel "github.com/open-falcon/common/model" 9 | cutils "github.com/open-falcon/common/utils" 10 | 11 | "github.com/open-falcon/gateway/g" 12 | "github.com/open-falcon/gateway/sender" 13 | ) 14 | 15 | type Transfer int 16 | 17 | func (this *Transfer) Ping(req cmodel.NullRpcRequest, resp *cmodel.SimpleRpcResponse) error { 18 | return nil 19 | } 20 | 21 | func (t *Transfer) Update(args []*cmodel.MetricValue, reply *g.TransferResp) error { 22 | return RecvMetricValues(args, reply, "rpc") 23 | } 24 | 25 | // process new metric values 26 | func RecvMetricValues(args []*cmodel.MetricValue, reply *g.TransferResp, from string) error { 27 | start := time.Now() 28 | reply.ErrInvalid = 0 29 | 30 | items := []*cmodel.MetaData{} 31 | for _, v := range args { 32 | if v == nil { 33 | reply.ErrInvalid += 1 34 | continue 35 | } 36 | 37 | // 历史遗留问题. 38 | // 老版本agent上报的metric=kernel.hostname的数据,其取值为string类型,现在已经不支持了;所以,这里硬编码过滤掉 39 | if v.Metric == "kernel.hostname" { 40 | reply.ErrInvalid += 1 41 | continue 42 | } 43 | 44 | if v.Metric == "" || v.Endpoint == "" { 45 | reply.ErrInvalid += 1 46 | continue 47 | } 48 | 49 | if v.Type != g.COUNTER && v.Type != g.GAUGE && v.Type != g.DERIVE { 50 | reply.ErrInvalid += 1 51 | continue 52 | } 53 | 54 | if v.Value == "" { 55 | reply.ErrInvalid += 1 56 | continue 57 | } 58 | 59 | if v.Step <= 0 { 60 | reply.ErrInvalid += 1 61 | continue 62 | } 63 | 64 | if len(v.Metric)+len(v.Tags) > 510 { 65 | reply.ErrInvalid += 1 66 | continue 67 | } 68 | 69 | errtags, tags := cutils.SplitTagsString(v.Tags) 70 | if errtags != nil { 71 | reply.ErrInvalid += 1 72 | continue 73 | } 74 | 75 | // TODO 呵呵,这里需要再优雅一点 76 | now := start.Unix() 77 | if v.Timestamp <= 0 || v.Timestamp > now*2 { 78 | v.Timestamp = now 79 | } 80 | 81 | fv := &cmodel.MetaData{ 82 | Metric: v.Metric, 83 | Endpoint: v.Endpoint, 84 | Timestamp: v.Timestamp, 85 | Step: v.Step, 86 | CounterType: v.Type, 87 | Tags: tags, //TODO tags键值对的个数,要做一下限制 88 | } 89 | 90 | valid := true 91 | var vv float64 92 | var err error 93 | 94 | switch cv := v.Value.(type) { 95 | case string: 96 | vv, err = strconv.ParseFloat(cv, 64) 97 | if err != nil { 98 | valid = false 99 | } 100 | case float64: 101 | vv = cv 102 | case int64: 103 | vv = float64(cv) 104 | default: 105 | valid = false 106 | } 107 | 108 | if !valid { 109 | reply.ErrInvalid += 1 110 | continue 111 | } 112 | 113 | fv.Value = vv 114 | items = append(items, fv) 115 | } 116 | 117 | // statistics 118 | cnt := int64(len(items)) 119 | pfc.Meter("Recv", cnt) 120 | if from == "rpc" { 121 | pfc.Meter("RpcRecv", cnt) 122 | } else if from == "http" { 123 | pfc.Meter("HttpRecv", cnt) 124 | } 125 | 126 | cfg := g.Config() 127 | if cfg.Transfer.Enabled { 128 | sender.Push2SendQueue(items) 129 | } 130 | 131 | reply.Msg = "ok" 132 | reply.Total = len(args) 133 | reply.Latency = (time.Now().UnixNano() - start.UnixNano()) / 1000000 134 | 135 | return nil 136 | } 137 | -------------------------------------------------------------------------------- /control: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | workspace=$(cd $(dirname $0) && pwd) 3 | cd $workspace 4 | 5 | module=gateway 6 | app=falcon-$module 7 | conf=cfg.json 8 | pidfile=var/app.pid 9 | logfile=var/app.log 10 | gitversion=.gitversion 11 | pfc=perfcounter.json 12 | 13 | mkdir -p var &>/dev/null 14 | 15 | 16 | ## build & pack 17 | function build() { 18 | update_gitversion 19 | go build -o $app main.go 20 | sc=$? 21 | if [ $sc -ne 0 ];then 22 | echo "build error" 23 | exit $sc 24 | else 25 | echo -n "build ok, vsn=" 26 | version 27 | fi 28 | } 29 | 30 | function pack() { 31 | build 32 | version=`./$app -v` 33 | tar zcvf $app-$version.tar.gz control $app cfg.example.json $pfc $gitversion ./test/debug 34 | } 35 | 36 | function packbin() { 37 | build 38 | version=`./$app -v` 39 | tar zcvf $app-bin-$version.tar.gz $app $gitversion 40 | } 41 | 42 | ## opt 43 | function start() { 44 | check_pid 45 | running=$? 46 | if [ $running -gt 0 ];then 47 | echo -n "$app started, pid=" 48 | cat $pidfile 49 | return 1 50 | fi 51 | 52 | nohup ./$app -c $conf >>$logfile 2>&1 & 53 | echo $! > $pidfile 54 | echo "$app start ok, pid=$!" 55 | } 56 | 57 | function stop() { 58 | pid=`cat $pidfile` 59 | kill $pid 60 | echo "$app stoped" 61 | } 62 | 63 | function restart() { 64 | stop && sleep 1 && start 65 | } 66 | 67 | function reload() { 68 | build && stop && sleep 1 && start && sleep 1 && printf "\n" && tailf 69 | } 70 | 71 | ## other 72 | function status() { 73 | check_pid 74 | running=$? 75 | if [ $running -gt 0 ];then 76 | echo -n "$app running, pid=" 77 | cat $pidfile 78 | else 79 | echo "$app stoped" 80 | fi 81 | } 82 | 83 | function version() { 84 | v=`./$app -v` 85 | if [ -f $gitversion ];then 86 | g=`cat $gitversion` 87 | fi 88 | echo "$v $g" 89 | } 90 | 91 | function tailf() { 92 | tail -f $logfile 93 | } 94 | 95 | ## internal 96 | function check_pid() { 97 | if [ -f $pidfile ];then 98 | pid=`cat $pidfile` 99 | if [ -n $pid ]; then 100 | running=`ps -p $pid|grep -v "PID TTY" |wc -l` 101 | return $running 102 | fi 103 | fi 104 | return 0 105 | } 106 | 107 | function update_gitversion() { 108 | git log -1 --pretty=%h > $gitversion 109 | } 110 | 111 | ## usage 112 | function usage() { 113 | echo "$0 build|pack|packbin|start|stop|restart|reload|status|tail|version" 114 | } 115 | 116 | ## main 117 | action=$1 118 | case $action in 119 | ## build 120 | "build" ) 121 | build 122 | ;; 123 | "pack" ) 124 | pack 125 | ;; 126 | "packbin" ) 127 | packbin 128 | ;; 129 | ## opt 130 | "start" ) 131 | start 132 | ;; 133 | "stop" ) 134 | stop 135 | ;; 136 | "restart" ) 137 | restart 138 | ;; 139 | "reload" ) 140 | reload 141 | ;; 142 | ## other 143 | "status" ) 144 | status 145 | ;; 146 | "version" ) 147 | version 148 | ;; 149 | "tail" ) 150 | tailf 151 | ;; 152 | * ) 153 | usage 154 | ;; 155 | esac 156 | -------------------------------------------------------------------------------- /sender/conn_pool/conn_pool_rpc.go: -------------------------------------------------------------------------------- 1 | package conn_pool 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "net/rpc" 7 | "net/rpc/jsonrpc" 8 | "sync" 9 | "time" 10 | 11 | spool "github.com/toolkits/pool/simple_conn_pool" 12 | ) 13 | 14 | // RpcCient, 要实现io.Closer接口 15 | type RpcClient struct { 16 | cli *rpc.Client 17 | name string 18 | } 19 | 20 | func (this RpcClient) Name() string { 21 | return this.name 22 | } 23 | 24 | func (this RpcClient) Closed() bool { 25 | return this.cli == nil 26 | } 27 | 28 | func (this RpcClient) Close() error { 29 | if this.cli != nil { 30 | err := this.cli.Close() 31 | this.cli = nil 32 | return err 33 | } 34 | return nil 35 | } 36 | 37 | func (this RpcClient) Call(method string, args interface{}, reply interface{}) error { 38 | return this.cli.Call(method, args, reply) 39 | } 40 | 41 | // ConnPools Manager 42 | type SafeRpcConnPools struct { 43 | sync.RWMutex 44 | M map[string]*spool.ConnPool 45 | MaxConns int32 46 | MaxIdle int32 47 | ConnTimeout int32 48 | CallTimeout int32 49 | } 50 | 51 | func CreateSafeRpcConnPools(maxConns, maxIdle, connTimeout, callTimeout int32, cluster []string) *SafeRpcConnPools { 52 | cp := &SafeRpcConnPools{M: make(map[string]*spool.ConnPool), MaxConns: maxConns, MaxIdle: maxIdle, 53 | ConnTimeout: connTimeout, CallTimeout: callTimeout} 54 | 55 | ct := time.Duration(cp.ConnTimeout) * time.Millisecond 56 | for _, address := range cluster { 57 | if _, exist := cp.M[address]; exist { 58 | continue 59 | } 60 | cp.M[address] = createOnePool(address, address, ct, maxConns, maxIdle) 61 | } 62 | 63 | return cp 64 | } 65 | 66 | func (this *SafeRpcConnPools) Proc() []string { 67 | procs := []string{} 68 | for _, cp := range this.M { 69 | procs = append(procs, cp.Proc()) 70 | } 71 | return procs 72 | } 73 | 74 | // 同步发送, 完成发送或超时后 才能返回 75 | func (this *SafeRpcConnPools) Call(addr, method string, args interface{}, resp interface{}) error { 76 | connPool, exists := this.Get(addr) 77 | if !exists { 78 | return fmt.Errorf("%s has no connection pool", addr) 79 | } 80 | 81 | conn, err := connPool.Fetch() 82 | if err != nil { 83 | return fmt.Errorf("%s get connection fail: conn %v, err %v. proc: %s", addr, conn, err, connPool.Proc()) 84 | } 85 | 86 | rpcClient := conn.(RpcClient) 87 | callTimeout := time.Duration(this.CallTimeout) * time.Millisecond 88 | 89 | done := make(chan error, 1) 90 | go func() { 91 | done <- rpcClient.Call(method, args, resp) 92 | }() 93 | 94 | select { 95 | case <-time.After(callTimeout): 96 | connPool.ForceClose(conn) 97 | return fmt.Errorf("%s, call timeout", addr) 98 | case err = <-done: 99 | if err != nil { 100 | connPool.ForceClose(conn) 101 | err = fmt.Errorf("%s, call failed, err %v. proc: %s", addr, err, connPool.Proc()) 102 | } else { 103 | connPool.Release(conn) 104 | } 105 | return err 106 | } 107 | } 108 | 109 | func (this *SafeRpcConnPools) Get(address string) (*spool.ConnPool, bool) { 110 | this.RLock() 111 | defer this.RUnlock() 112 | p, exists := this.M[address] 113 | return p, exists 114 | } 115 | 116 | func (this *SafeRpcConnPools) Destroy() { 117 | this.Lock() 118 | defer this.Unlock() 119 | addresses := make([]string, 0, len(this.M)) 120 | for address := range this.M { 121 | addresses = append(addresses, address) 122 | } 123 | 124 | for _, address := range addresses { 125 | this.M[address].Destroy() 126 | delete(this.M, address) 127 | } 128 | } 129 | 130 | func createOnePool(name string, address string, connTimeout time.Duration, maxConns, maxIdle int32) *spool.ConnPool { 131 | p := spool.NewConnPool(name, address, maxConns, maxIdle) 132 | p.New = func(connName string) (spool.NConn, error) { 133 | _, err := net.ResolveTCPAddr("tcp", p.Address) 134 | if err != nil { 135 | //log.Println(p.Address, "format error", err) 136 | return nil, err 137 | } 138 | 139 | conn, err := net.DialTimeout("tcp", p.Address, connTimeout) 140 | if err != nil { 141 | //log.Printf("new conn fail, addr %s, err %v", p.Address, err) 142 | return nil, err 143 | } 144 | 145 | return RpcClient{cli: jsonrpc.NewClient(conn), name: connName}, nil 146 | } 147 | 148 | return p 149 | } 150 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Introduction 2 | 3 | 多IDC时,可能面对 "分区到中心的专线网络质量较差&公网ACL不通" 等问题。这时,可以在分区内部署一套数据路由服务,接收本分区内的所有流量(包括所有的agent流量),然后通过公网(开通ACL),将数据push给中心的Transfer。如下图, 4 | ![gateway.png](https://raw.githubusercontent.com/niean/niean.github.io/master/images/20150806/gateway.png) 5 | 6 | 站在client端的角度,gateway和transfer提供了完全一致的功能和接口。**只有遇到网络分区的情况时,才有必要使用gateway组件**。 7 | 8 | ## Installation 9 | 10 | 首先,通过github仓库的源码,编译出可执行的二进制文件。然后,将二进制文件部署到服务器上,并提供服务。 11 | 12 | ### Build 13 | 14 | ```bash 15 | cd $GOPATH/src/github.com/open-falcon 16 | git clone https://github.com/open-falcon/gateway.git 17 | 18 | cd gateway 19 | go get ./... 20 | ./control build 21 | ./control pack 22 | ``` 23 | 最后一步会pack出一个`falcon-gateway-$vsn.tar.gz`的安装包,拿着这个包去部署服务即可。我们也提供了编译好的安装包,在[这里](https://github.com/nieanan/gateway/releases/tag/v0.0.7)。 24 | 25 | ### Deploy 26 | 服务部署,包括配置修改、启动服务、检验服务、停止服务等。这之前,需要将安装包解压到服务的部署目录下。 27 | 28 | ```bash 29 | # download 'falcon-gateway-$vsn.tar.gz' 30 | # tar -zxf falcon-gateway-$vsn.tar.gz && rm -f falcon-gateway-$vsn.tar.gz 31 | 32 | # modify config 33 | mv cfg.example.json cfg.json 34 | vim cfg.json 35 | 36 | # start service 37 | ./control start 38 | 39 | # check, you should get 'ok' 40 | curl -s "127.0.0.1:6060/health" 41 | 42 | ... 43 | # stop service 44 | ./control stop 45 | 46 | ``` 47 | 服务启动后,可以通过日志查看服务的运行状态,日志文件地址为./var/app.log。可以通过调试脚本./test/debug查看服务器的内部状态数据,如 运行 bash ./test/debug 可以得到服务器内部状态的统计信息。 48 | 49 | gateway组件,部署于分区中。单个gateway实例的转发能力,为 {1核, 500MB内存, Qps不小于1W/s};但我们仍然建议,一个分区至少部署两个gateway实例,来实现高可用。 50 | 51 | 52 | ## Usage 53 | send items via transfer's http-api 54 | 55 | ```bash 56 | #!/bin/bash 57 | e="test.endpoint.1" 58 | m="test.metric.1" 59 | t="t0=tag0,t1=tag1,t2=tag2" 60 | ts=`date +%s` 61 | curl -s -X POST -d "[{\"metric\":\"$m\", \"endpoint\":\"$e\", \"timestamp\":$ts,\"step\":60, \"value\":9, \"counterType\":\"GAUGE\",\"tags\":\"$t\"}]" "127.0.0.1:6060/api/push" | python -m json.tool 62 | ``` 63 | 64 | ## Configuration 65 | **注意: 从v0.0.4版以后,配置文件格式发生了变更。**主要变更项,为 66 | 67 | 1. 开关控制符更名为enabled,原来为enable 68 | 2. transfer地址配置改为集群形式cluster,原来为单个地址addr 69 | 3. transfer添加重试次数retry,默认1、不重试 70 | 71 | 72 | ```python 73 | { 74 | "debug": true, 75 | "http": { 76 | "enabled": true, 77 | "listen": "0.0.0.0:6060" //http服务的监听端口 78 | }, 79 | "rpc": { 80 | "enabled": true, 81 | "listen": "0.0.0.0:8433" //go-rpc服务的监听端口 82 | }, 83 | "socket": { //即将被废弃,请避免使用 84 | "enabled": true, 85 | "listen": "0.0.0.0:4444", //telnet服务的监听端口 86 | "timeout": 3600 87 | }, 88 | "transfer": { 89 | "enabled": true, //true/false, 表示是否开启向tranfser转发数据 90 | "batch": 200, //数据转发的批量大小,可以加快发送速度,建议保持默认值 91 | "retry": 2, //重试次数,默认1、不重试 92 | "connTimeout": 1000, //毫秒,与后端建立连接的超时时间,可以根据网络质量微调,建议保持默认 93 | "callTimeout": 5000, //毫秒,发送数据给后端的超时时间,可以根据网络质量微调,建议保持默认 94 | "maxConns": 32, //连接池相关配置,最大连接数,建议保持默认 95 | "maxIdle": 32, //连接池相关配置,最大空闲连接数,建议保持默认 96 | "cluster": { //transfer服务器集群,支持多条记录 97 | "t1": "127.0.0.1:8433" //一个transfer实例,形如"node":"$hostname:$port" 98 | } 99 | } 100 | } 101 | ``` 102 | 103 | 从版本**v0.0.11**后,gateway组件引入了golang业务监控组件[GoPerfcounter](https://github.com/niean/goperfcounter)。GoPerfcounter会主动将gateway的内部状态数据,push给本地的falcon-agent,其配置文件`perfcounter.json`内容如下,含义见[这里](https://github.com/niean/goperfcounter/blob/master/README.md#配置) 104 | 105 | ```python 106 | { 107 | "tags": "service=gateway", // 业务监控数据的标签 108 | "bases": ["debug","runtime"], // 开启gvm基础信息采集 109 | "push": { // 开启主动推送,数据将被推送至本机的falcon-agent 110 | "enabled": true 111 | }, 112 | "http": { // 开启http调试,并复用gateway的http端口 113 | "enabled": true 114 | } 115 | } 116 | ``` 117 | 118 | ## Debug 119 | 可以通过调试脚本./test/debug查看服务器的内部状态数据,含义如下 120 | 121 | ```bash 122 | # bash ./test/debug 123 | { 124 | "data": { 125 | "gauge": { 126 | "SendQueueSize": { // size of cached items 127 | "value": 0 128 | } 129 | }, 130 | "meter": { 131 | "Recv": { // counter of items received 132 | "rate": 954.88407253945127, 133 | "rate.15min": 938.12973764674587, 134 | "rate.1min": 892.82060496256759, 135 | "rate.5min": 889.51059449035426, 136 | "sum": 2460636 137 | }, 138 | "Send": { // counter of items sent to transfer 139 | "rate": 950.21411950079619, 140 | "rate.15min": 918.55392627259835, 141 | "rate.1min": 886.32981239416608, 142 | "rate.5min": 888.16132862191205, 143 | "sum": 2458708 144 | }, 145 | "SendFail": { // counter of items sent to transfer failed 146 | "rate": 0, 147 | "rate.15min": 0, 148 | "rate.1min": 0, 149 | "rate.5min": 0, 150 | "sum": 0 151 | }, 152 | "SendDrop": { // counter of items sent to transfer drop 153 | "rate": 0, 154 | "rate.15min": 0, 155 | "rate.1min": 0, 156 | "rate.5min": 0, 157 | "sum": 0 158 | }, 159 | } 160 | }, 161 | "msg": "success" 162 | } 163 | ``` 164 | 165 | ## TODO 166 | + 加密gateway经过公网传输的数据 167 | --------------------------------------------------------------------------------