func CreateMsg(metrics []*schema.MetricData, id int64, version Format) ([]byte, error) { buf := new(bytes.Buffer) err := binary.Write(buf, binary.LittleEndian, uint8(version)) if err != nil { return nil, fmt.Errorf(errFmtBinWriteFailed, err) } err = binary.Write(buf, binary.BigEndian, id) if err != nil { return nil, fmt.Errorf(errFmtBinWriteFailed, err) } var msg []byte switch version { case FormatMetricDataArrayJson: msg, err = json.Marshal(metrics) case FormatMetricDataArrayMsgp: m := schema.MetricDataArray(metrics) msg, err = m.MarshalMsg(nil) default: return nil, fmt.Errorf(errFmtUnknownFormat, version) } if err != nil { return nil, fmt.Errorf("Failed to marshal metrics payload: %s", err) } _, err = buf.Write(msg) if err != nil { return nil, fmt.Errorf(errFmtBinWriteFailed, err) } return buf.Bytes(), nil }
// sets m.Metrics to a []*schema.MetricData // any subsequent call may however put different MetricData into our m.Metrics array func (m *MetricData) DecodeMetricData() error { var err error switch m.Format { case FormatMetricDataArrayJson: err = json.Unmarshal(m.Msg[9:], &m.Metrics) case FormatMetricDataArrayMsgp: out := schema.MetricDataArray(m.Metrics) _, err = out.UnmarshalMsg(m.Msg[9:]) m.Metrics = []*schema.MetricData(out) default: return fmt.Errorf("unrecognized format %d", m.Msg[0]) } if err != nil { return fmt.Errorf("ERROR: failure to unmarshal message body via format %q: %s", m.Format, err) } m.Msg = nil // no more need for the original input return nil }
func (route *RouteGrafanaNet) run() { metrics := make([]*schema.MetricData, 0, route.flushMaxNum) ticker := time.NewTicker(route.flushMaxWait) client := &http.Client{ Timeout: time.Duration(2 * time.Second), } b := &backoff.Backoff{ Min: 100 * time.Millisecond, Max: time.Minute, Factor: 1.5, Jitter: true, } flush := func() { if len(metrics) == 0 { return } for { pre := time.Now() mda := schema.MetricDataArray(metrics) data, err := msg.CreateMsg(mda, 0, msg.FormatMetricDataArrayMsgp) if err != nil { panic(err) } req, err := http.NewRequest("POST", route.addr, bytes.NewBuffer(data)) if err != nil { panic(err) } req.Header.Add("Authorization", "Bearer "+route.apiKey) req.Header.Add("Content-Type", "rt-metric-binary") resp, err := client.Do(req) diff := time.Since(pre) if err == nil && resp.StatusCode >= 200 && resp.StatusCode < 300 { b.Reset() log.Info("GrafanaNet sent %d metrics in %s -msg size %d", len(metrics), diff, len(data)) route.numOut.Inc(int64(len(metrics))) route.tickFlushSize.Update(int64(len(data))) route.durationTickFlush.Update(diff) metrics = metrics[:0] resp.Body.Close() break } route.numErrFlush.Inc(1) dur := b.Duration() if err != nil { log.Warning("GrafanaNet failed to submit data: %s will try again in %s (this attempt took %s)", err, dur, diff) } else { buf := make([]byte, 300) n, _ := resp.Body.Read(buf) log.Warning("GrafanaNet failed to submit data: http %d - %s will try again in %s (this attempt took %s)", resp.StatusCode, buf[:n], dur, diff) resp.Body.Close() } time.Sleep(dur) } } for { select { case buf := <-route.buf: route.numBuffered.Dec(1) md := parseMetric(buf, route.schemas) if md == nil { continue } md.SetId() metrics = append(metrics, md) if len(metrics) == route.flushMaxNum { flush() } case <-ticker.C: flush() } } }