func getHosts(w http.ResponseWriter, req *http.Request, hostKeyword string) { if len(hostKeyword) == 1 { hostKeyword = ".+" } rand.Seed(time.Now().UTC().UnixNano()) random64 := rand.Float64() _r := strconv.FormatFloat(random64, 'f', -1, 32) maxQuery := strconv.Itoa(g.Config().Api.Max) url := "/api/endpoints" + "?q=" + hostKeyword + "&tags&limit=" + maxQuery + "&_r=" + _r + "®ex_query=1" if strings.Index(g.Config().Api.Query, req.Host) >= 0 { url = "http://localhost:9966" + url } else { url = g.Config().Api.Query + url } reqGet, err := http.NewRequest("GET", url, nil) if err != nil { StdRender(w, "", err) } client := &http.Client{} resp, err := client.Do(reqGet) if err != nil { StdRender(w, "", err) } defer resp.Body.Close() result := []interface{}{} if resp.Status == "200 OK" { body, _ := ioutil.ReadAll(resp.Body) var nodes = make(map[string]interface{}) if err := json.Unmarshal(body, &nodes); err != nil { StdRender(w, "", err) } for _, host := range nodes["data"].([]interface{}) { item := map[string]interface{}{ "text": host, "expandable": true, } result = append(result, item) } RenderJson(w, result) } else { RenderJson(w, result) } }
// internal functions func initConnPools() { cfg := g.Config() // TODO 为了得到Slice,这里做的太复杂了 graphInstances := nset.NewSafeSet() for _, address := range cfg.Graph.Cluster { graphInstances.Add(address) } GraphConnPools = spool.CreateSafeRpcConnPools(cfg.Graph.MaxConns, cfg.Graph.MaxIdle, cfg.Graph.ConnTimeout, cfg.Graph.CallTimeout, graphInstances.ToSlice()) }
func Info(para cmodel.GraphInfoParam) (resp *cmodel.GraphFullyInfo, err error) { endpoint, counter := para.Endpoint, para.Counter pool, addr, err := selectPool(endpoint, counter) if err != nil { return nil, err } conn, err := pool.Fetch() if err != nil { return nil, err } rpcConn := conn.(spool.RpcClient) if rpcConn.Closed() { pool.ForceClose(conn) return nil, errors.New("conn closed") } type ChResult struct { Err error Resp *cmodel.GraphInfoResp } ch := make(chan *ChResult, 1) go func() { resp := &cmodel.GraphInfoResp{} err := rpcConn.Call("Graph.Info", para, resp) ch <- &ChResult{Err: err, Resp: resp} }() select { case <-time.After(time.Duration(g.Config().Graph.CallTimeout) * time.Millisecond): pool.ForceClose(conn) return nil, fmt.Errorf("%s, call timeout. proc: %s", addr, pool.Proc()) case r := <-ch: if r.Err != nil { pool.ForceClose(conn) return nil, fmt.Errorf("%s, call failed, err %v. proc: %s", addr, r.Err, pool.Proc()) } else { pool.Release(conn) fullyInfo := cmodel.GraphFullyInfo{ Endpoint: endpoint, Counter: counter, ConsolFun: r.Resp.ConsolFun, Step: r.Resp.Step, Filename: r.Resp.Filename, Addr: addr, } return &fullyInfo, nil } } }
func Start() { if !g.Config().Http.Enabled { log.Println("http.Start warning, not enabled") return } // config http routes configCommonRoutes() configProcHttpRoutes() configGraphRoutes() configApiRoutes() configGrafanaRoutes() // start http server addr := g.Config().Http.Listen s := &http.Server{ Addr: addr, MaxHeaderBytes: 1 << 30, } log.Println("http.Start ok, listening on", addr) log.Fatalln(s.ListenAndServe()) }
func configCommonRoutes() { http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("ok\n")) }) http.HandleFunc("/version", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte(fmt.Sprintf("%s\n", g.VERSION))) }) http.HandleFunc("/workdir", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte(fmt.Sprintf("%s\n", file.SelfDir()))) }) http.HandleFunc("/config", func(w http.ResponseWriter, r *http.Request) { RenderDataJson(w, g.Config()) }) }
func LastRaw(para cmodel.GraphLastParam) (r *cmodel.GraphLastResp, err error) { endpoint, counter := para.Endpoint, para.Counter pool, addr, err := selectPool(endpoint, counter) if err != nil { return nil, err } conn, err := pool.Fetch() if err != nil { return nil, err } rpcConn := conn.(spool.RpcClient) if rpcConn.Closed() { pool.ForceClose(conn) return nil, errors.New("conn closed") } type ChResult struct { Err error Resp *cmodel.GraphLastResp } ch := make(chan *ChResult, 1) go func() { resp := &cmodel.GraphLastResp{} err := rpcConn.Call("Graph.LastRaw", para, resp) ch <- &ChResult{Err: err, Resp: resp} }() select { case <-time.After(time.Duration(g.Config().Graph.CallTimeout) * time.Millisecond): pool.ForceClose(conn) return nil, fmt.Errorf("%s, call timeout. proc: %s", addr, pool.Proc()) case r := <-ch: if r.Err != nil { pool.ForceClose(conn) return r.Resp, fmt.Errorf("%s, call failed, err %v. proc: %s", addr, r.Err, pool.Proc()) } else { pool.Release(conn) return r.Resp, nil } } }
func selectPool(endpoint, counter string) (rpool *spool.ConnPool, raddr string, rerr error) { pkey := cutils.PK2(endpoint, counter) node, err := GraphNodeRing.GetNode(pkey) if err != nil { return nil, "", err } addr, found := g.Config().Graph.Cluster[node] if !found { return nil, "", errors.New("node not found") } pool, found := GraphConnPools.Get(addr) if !found { return nil, addr, errors.New("addr not found") } return pool, addr, nil }
func QueryOne(para cmodel.GraphQueryParam) (resp *cmodel.GraphQueryResponse, err error) { start, end := para.Start, para.End endpoint, counter := para.Endpoint, para.Counter pool, addr, err := selectPool(endpoint, counter) if err != nil { return nil, err } conn, err := pool.Fetch() if err != nil { return nil, err } rpcConn := conn.(spool.RpcClient) if rpcConn.Closed() { pool.ForceClose(conn) return nil, errors.New("conn closed") } type ChResult struct { Err error Resp *cmodel.GraphQueryResponse } ch := make(chan *ChResult, 1) go func() { resp := &cmodel.GraphQueryResponse{} err := rpcConn.Call("Graph.Query", para, resp) ch <- &ChResult{Err: err, Resp: resp} }() select { case <-time.After(time.Duration(g.Config().Graph.CallTimeout) * time.Millisecond): pool.ForceClose(conn) return nil, fmt.Errorf("%s, call timeout. proc: %s", addr, pool.Proc()) case r := <-ch: if r.Err != nil { pool.ForceClose(conn) return r.Resp, fmt.Errorf("%s, call failed, err %v. proc: %s", addr, r.Err, pool.Proc()) } else { pool.Release(conn) if len(r.Resp.Values) < 1 { return r.Resp, nil } // TODO query不该做这些事情, 说明graph没做好 fixed := []*cmodel.RRDData{} for _, v := range r.Resp.Values { if v == nil || !(v.Timestamp >= start && v.Timestamp <= end) { continue } //FIXME: 查询数据的时候,把所有的负值都过滤掉,因为transfer之前在设置最小值的时候为U if (r.Resp.DsType == "DERIVE" || r.Resp.DsType == "COUNTER") && v.Value < 0 { fixed = append(fixed, &cmodel.RRDData{Timestamp: v.Timestamp, Value: cmodel.JsonFloat(math.NaN())}) } else { fixed = append(fixed, v) } } r.Resp.Values = fixed } return r.Resp, nil } }
func initNodeRings() { cfg := g.Config() GraphNodeRing = rings.NewConsistentHashNodesRing(cfg.Graph.Replicas, cutils.KeysOfMap(cfg.Graph.Cluster)) }
func dashboardChart(rw http.ResponseWriter, req *http.Request) { url := g.Config().Api.Dashboard + "/chart" postByForm(rw, req, url) }
func dashboardCounters(rw http.ResponseWriter, req *http.Request) { url := g.Config().Api.Dashboard + "/api/counters" postByForm(rw, req, url) }
func dashboardEndpoints(rw http.ResponseWriter, req *http.Request) { url := g.Config().Api.Dashboard + req.URL.RequestURI() getRequest(rw, url) }
func queryHistory(rw http.ResponseWriter, req *http.Request) { url := g.Config().Api.Query + "/graph/history" postByJson(rw, req, url) }
func queryInfo(rw http.ResponseWriter, req *http.Request) { url := g.Config().Api.Query + "/graph/info" postByJson(rw, req, url) }
func getMetricValues(req *http.Request, host string, targets []string, result []interface{}) []interface{} { endpoint_counters := []interface{}{} metric := strings.Join(targets, ".") if strings.Contains(host, "{") { // Templating metrics request host = strings.Replace(host, "{", "", -1) host = strings.Replace(host, "}", "", -1) hosts := strings.Split(host, ",") for _, host := range hosts { item := map[string]string{ "endpoint": host, "counter": metric, } endpoint_counters = append(endpoint_counters, item) } } else { item := map[string]string{ "endpoint": host, "counter": metric, } endpoint_counters = append(endpoint_counters, item) } if len(endpoint_counters) > 0 { from, err := strconv.ParseInt(req.PostForm["from"][0], 10, 64) until, err := strconv.ParseInt(req.PostForm["until"][0], 10, 64) url := "/graph/history" if strings.Index(g.Config().Api.Query, req.Host) >= 0 { url = "http://localhost:9966" + url } else { url = g.Config().Api.Query + url } args := map[string]interface{}{ "start": from, "end": until, "cf": "AVERAGE", "endpoint_counters": endpoint_counters, } bs, err := json.Marshal(args) if err != nil { log.Println("Error =", err.Error()) } reqPost, err := http.NewRequest("POST", url, bytes.NewBuffer([]byte(bs))) if err != nil { log.Println("Error =", err.Error()) } reqPost.Header.Set("Content-Type", "application/json") client := &http.Client{} resp, err := client.Do(reqPost) if err != nil { log.Println("Error =", err.Error()) } defer resp.Body.Close() if resp.Status == "200 OK" { body, _ := ioutil.ReadAll(resp.Body) nodes := []interface{}{} if err := json.Unmarshal(body, &nodes); err != nil { log.Println(err.Error()) } for _, node := range nodes { if _, ok := node.(map[string]interface{})["Values"]; ok { result = append(result, node) } } } } return result }
func getMetrics(w http.ResponseWriter, req *http.Request, query string) { result := []interface{}{} query = strings.Replace(query, "#.*", "", -1) arrQuery := strings.Split(query, "#") host, arrMetric := arrQuery[0], arrQuery[1:] maxQuery := strconv.Itoa(g.Config().Api.Max) metric := strings.Join(arrMetric, ".") reg, _ := regexp.Compile("(^{|}$)") host = reg.ReplaceAllString(host, "") host = strings.Replace(host, ",", "\",\"", -1) endpoints := "[\"" + host + "\"]" rand.Seed(time.Now().UTC().UnixNano()) random64 := rand.Float64() _r := strconv.FormatFloat(random64, 'f', -1, 32) form := url.Values{} form.Set("endpoints", endpoints) form.Add("q", metric) form.Add("limit", maxQuery) form.Add("_r", _r) target := "/api/counters" if strings.Index(g.Config().Api.Query, req.Host) >= 0 { target = "http://localhost:9966" + target } else { target = g.Config().Api.Query + target } reqPost, err := http.NewRequest("POST", target, strings.NewReader(form.Encode())) if err != nil { log.Println("Error =", err.Error()) } reqPost.Header.Set("Content-Type", "application/x-www-form-urlencoded") client := &http.Client{} resp, err := client.Do(reqPost) if err != nil { log.Println("Error =", err.Error()) } defer resp.Body.Close() if resp.Status == "200 OK" { body, _ := ioutil.ReadAll(resp.Body) var nodes = make(map[string]interface{}) if err := json.Unmarshal(body, &nodes); err != nil { log.Println(err.Error()) } var segmentPool = make(map[string]int) for _, data := range nodes["data"].([]interface{}) { counter := data.([]interface{})[0].(string) segment := getNextCounterSegment(metric, counter) expandable := checkSegmentExpandable(segment, counter) if _, ok := segmentPool[segment]; !ok { item := map[string]interface{}{ "text": segment, "expandable": expandable, } result = append(result, item) segmentPool[segment] = 1 } } RenderJson(w, result) } else { RenderJson(w, result) } }