You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by li...@apache.org on 2018/10/26 09:33:10 UTC

[incubator-servicecomb-service-center] branch master updated: SCB-983 Output the QPS per domain (#467)

This is an automated email from the ASF dual-hosted git repository.

littlecui pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-servicecomb-service-center.git


The following commit(s) were added to refs/heads/master by this push:
     new 6595827  SCB-983 Output the QPS per domain (#467)
6595827 is described below

commit 6595827389dea367bc1b81e763a5416c28118f0a
Author: little-cui <su...@qq.com>
AuthorDate: Fri Oct 26 17:33:06 2018 +0800

    SCB-983 Output the QPS per domain (#467)
    
    * SCB-983 Output the QPS per domain
    
    * Add a reporter to calculate qps
    
    * Add UT
---
 server/metric/calculator.go                      |  9 ++--
 server/metric/calculator_test.go                 |  6 +--
 server/metric/metrics.go                         | 23 ++++++--
 server/metric/metrics_test.go                    | 30 +++++++++++
 server/metric/reporter.go                        |  1 +
 server/plugin/pkg/discovery/etcd/metrics_test.go |  4 +-
 server/rest/metrics.go                           | 24 ++++++---
 server/rest/reporter.go                          | 68 ++++++++++++++++++++++++
 8 files changed, 146 insertions(+), 19 deletions(-)

diff --git a/server/metric/calculator.go b/server/metric/calculator.go
index 523f7f5..7f0a890 100644
--- a/server/metric/calculator.go
+++ b/server/metric/calculator.go
@@ -24,6 +24,7 @@ var (
 	DefaultCalculator Calculator = &CommonCalculator{}
 )
 
+// Calculator is the interface to implement customize algorithm of MetricFamily
 type Calculator interface {
 	Calc(mf *dto.MetricFamily) *Details
 }
@@ -53,14 +54,14 @@ func (c *CommonCalculator) Calc(mf *dto.MetricFamily) *Details {
 
 func metricGaugeOf(details *Details, m []*dto.Metric) {
 	for _, d := range m {
-		details.Value += d.GetGauge().GetValue()
+		details.Summary += d.GetGauge().GetValue()
 		details.Put(d.GetLabel(), d.GetGauge().GetValue())
 	}
 }
 
 func metricCounterOf(details *Details, m []*dto.Metric) {
 	for _, d := range m {
-		details.Value += d.GetCounter().GetValue()
+		details.Summary += d.GetCounter().GetValue()
 		details.Put(d.GetLabel(), d.GetCounter().GetValue())
 	}
 }
@@ -80,7 +81,7 @@ func metricSummaryOf(details *Details, m []*dto.Metric) {
 		return
 	}
 
-	details.Value = sum / float64(count)
+	details.Summary = sum / float64(count)
 }
 
 func metricHistogramOf(details *Details, m []*dto.Metric) {
@@ -98,7 +99,7 @@ func metricHistogramOf(details *Details, m []*dto.Metric) {
 		return
 	}
 
-	details.Value = sum / float64(count)
+	details.Summary = sum / float64(count)
 }
 
 func RegisterCalculator(c Calculator) {
diff --git a/server/metric/calculator_test.go b/server/metric/calculator_test.go
index 4634530..41ce510 100644
--- a/server/metric/calculator_test.go
+++ b/server/metric/calculator_test.go
@@ -45,7 +45,7 @@ func TestCommonCalculator_Calc(t *testing.T) {
 	mf = &dto.MetricFamily{Type: &mt, Metric: []*dto.Metric{
 		{Gauge: &dto.Gauge{Value: &v1}}, {Gauge: &dto.Gauge{Value: &v2}}}}
 	details := c.Calc(mf)
-	if details.Value != 3 {
+	if details.Summary != 3 {
 		t.Fatalf("TestCommonCalculator_Calc failed")
 	}
 
@@ -54,7 +54,7 @@ func TestCommonCalculator_Calc(t *testing.T) {
 	mf = &dto.MetricFamily{Type: &mt, Metric: []*dto.Metric{
 		{Counter: &dto.Counter{Value: &v1}}, {Counter: &dto.Counter{Value: &v1}}}}
 	details = c.Calc(mf)
-	if details.Value != 2 {
+	if details.Summary != 2 {
 		t.Fatalf("TestCommonCalculator_Calc failed")
 	}
 
@@ -64,7 +64,7 @@ func TestCommonCalculator_Calc(t *testing.T) {
 	mf = &dto.MetricFamily{Type: &mt, Metric: []*dto.Metric{
 		{Summary: &dto.Summary{SampleCount: &n, SampleSum: &v1}}, {Summary: &dto.Summary{SampleCount: &n, SampleSum: &v1}}}}
 	details = c.Calc(mf)
-	if details.Value != v1/float64(n) {
+	if details.Summary != v1/float64(n) {
 		t.Fatalf("TestCommonCalculator_Calc failed")
 	}
 }
diff --git a/server/metric/metrics.go b/server/metric/metrics.go
index d828cb9..cc42812 100644
--- a/server/metric/metrics.go
+++ b/server/metric/metrics.go
@@ -36,8 +36,10 @@ func NewDetails() *Details {
 	}
 }
 
+// Details is the struct to hold the calculated result and index by metric label
 type Details struct {
-	Value float64
+	// Summary is the calculation results of the details
+	Summary float64
 
 	mapper *util.ConcurrentMap
 	buffer *buffer.Pool
@@ -68,6 +70,13 @@ func (cm *Details) toLabels(key string) (p []*dto.LabelPair) {
 	return
 }
 
+func (cm *Details) Get(labels []*dto.LabelPair) (val float64) {
+	if v, ok := cm.mapper.Get(cm.toKey(labels)); ok {
+		val = v.(float64)
+	}
+	return
+}
+
 func (cm *Details) Put(labels []*dto.LabelPair, val float64) {
 	cm.mapper.Put(cm.toKey(labels), val)
 	return
@@ -81,6 +90,7 @@ func (cm *Details) ForEach(f func(labels []*dto.LabelPair, v float64) (next bool
 	})
 }
 
+// Metrics is the struct to hold the Details objects store and index by metric name
 type Metrics struct {
 	mapper *util.ConcurrentMap
 }
@@ -89,9 +99,9 @@ func (cm *Metrics) Put(key string, val *Details) {
 	cm.mapper.Put(key, val)
 }
 
-func (cm *Metrics) Get(key string) (val float64) {
+func (cm *Metrics) Get(key string) (val *Details) {
 	if v, ok := cm.mapper.Get(key); ok {
-		val = v.(*Details).Value
+		val = v.(*Details)
 	}
 	return
 }
@@ -103,3 +113,10 @@ func (cm *Metrics) ForEach(f func(k string, v *Details) (next bool)) {
 		return f(k, v)
 	})
 }
+
+func (cm *Metrics) Summary(key string) (sum float64) {
+	if v, ok := cm.mapper.Get(key); ok {
+		sum = v.(*Details).Summary
+	}
+	return
+}
diff --git a/server/metric/metrics_test.go b/server/metric/metrics_test.go
index 1e4a11d..2f3b4b1 100644
--- a/server/metric/metrics_test.go
+++ b/server/metric/metrics_test.go
@@ -62,4 +62,34 @@ func TestDetails_ForEach(t *testing.T) {
 	if l != 3 {
 		t.Fatalf("TestDetails_ForEach failed")
 	}
+
+	ms := NewMetrics()
+	if ms.Summary("x") != 0 {
+		t.Fatalf("TestMetrics_Summary failed")
+	}
+	if ms.Get("x") != nil {
+		t.Fatalf("TestMetrics_Details failed")
+	}
+	find := false
+	ms.ForEach(func(k string, v *Details) (next bool) {
+		find = true
+		return true
+	})
+	if find {
+		t.Fatalf("TestMetrics_ForEach failed")
+	}
+	ms.Put("a", d)
+	if ms.Summary("a") != 0 {
+		t.Fatalf("TestMetrics_Summary failed")
+	}
+	if ms.Get("a") != d {
+		t.Fatalf("TestMetrics_Get failed")
+	}
+	ms.ForEach(func(k string, v *Details) (next bool) {
+		find = true
+		return true
+	})
+	if !find {
+		t.Fatalf("TestMetrics_ForEach failed")
+	}
 }
diff --git a/server/metric/reporter.go b/server/metric/reporter.go
index d19a849..bb67dc2 100644
--- a/server/metric/reporter.go
+++ b/server/metric/reporter.go
@@ -20,6 +20,7 @@ import "github.com/apache/incubator-servicecomb-service-center/pkg/log"
 
 var reporters = make(map[string]Reporter)
 
+// Reporter is the interface to implement handler to process metrics after calculate
 type Reporter interface {
 	Report()
 }
diff --git a/server/plugin/pkg/discovery/etcd/metrics_test.go b/server/plugin/pkg/discovery/etcd/metrics_test.go
index a64b434..5f3b5b4 100644
--- a/server/plugin/pkg/discovery/etcd/metrics_test.go
+++ b/server/plugin/pkg/discovery/etcd/metrics_test.go
@@ -32,13 +32,13 @@ func TestReportCacheSize(t *testing.T) {
 	if err != nil {
 		t.Fatalf("TestReportCacheSize failed")
 	}
-	if metric.Gatherer.Records.Get("local_cache_size_bytes") != 100 {
+	if metric.Gatherer.Records.Summary("local_cache_size_bytes") != 100 {
 		t.Fatalf("TestReportCacheSize failed")
 	}
 
 	ReportCacheSize("", "b", 200)
 	err = metric.Gatherer.Collect()
-	if metric.Gatherer.Records.Get("local_cache_size_bytes") != 100 {
+	if metric.Gatherer.Records.Summary("local_cache_size_bytes") != 100 {
 		t.Fatalf("TestReportCacheSize failed")
 	}
 }
diff --git a/server/rest/metrics.go b/server/rest/metrics.go
index 2aabd45..78717a3 100644
--- a/server/rest/metrics.go
+++ b/server/rest/metrics.go
@@ -18,6 +18,7 @@ package rest
 
 import (
 	"github.com/apache/incubator-servicecomb-service-center/pkg/rest"
+	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
 	"github.com/apache/incubator-servicecomb-service-center/server/metric"
 	"github.com/prometheus/client_golang/prometheus"
 	"net/http"
@@ -33,7 +34,7 @@ var (
 			Subsystem: "http",
 			Name:      "request_total",
 			Help:      "Counter of requests received into ROA handler",
-		}, []string{"method", "code", "instance", "api"})
+		}, []string{"method", "code", "instance", "api", "domain"})
 
 	successfulRequests = prometheus.NewCounterVec(
 		prometheus.CounterOpts{
@@ -41,7 +42,7 @@ var (
 			Subsystem: "http",
 			Name:      "success_total",
 			Help:      "Counter of successful requests processed by ROA handler",
-		}, []string{"method", "code", "instance", "api"})
+		}, []string{"method", "code", "instance", "api", "domain"})
 
 	reqDurations = prometheus.NewSummaryVec(
 		prometheus.SummaryOpts{
@@ -50,11 +51,19 @@ var (
 			Name:       "request_durations_microseconds",
 			Help:       "HTTP request latency summary of ROA handler",
 			Objectives: prometheus.DefObjectives,
-		}, []string{"method", "instance", "api"})
+		}, []string{"method", "instance", "api", "domain"})
+
+	queryPerSeconds = prometheus.NewGaugeVec(
+		prometheus.GaugeOpts{
+			Namespace: metric.FamilyName,
+			Subsystem: "http",
+			Name:      "query_per_seconds",
+			Help:      "HTTP requests per seconds of ROA handler",
+		}, []string{"method", "instance", "api", "domain"})
 )
 
 func init() {
-	prometheus.MustRegister(incomingRequests, successfulRequests, reqDurations)
+	prometheus.MustRegister(incomingRequests, successfulRequests, reqDurations, queryPerSeconds)
 
 	RegisterServerHandler("/metrics", prometheus.Handler())
 }
@@ -63,17 +72,18 @@ func ReportRequestCompleted(w http.ResponseWriter, r *http.Request, start time.T
 	instance := metric.InstanceName()
 	elapsed := float64(time.Since(start).Nanoseconds()) / float64(time.Microsecond)
 	route, _ := r.Context().Value(rest.CTX_MATCH_FUNC).(string)
+	domain := util.ParseDomain(r.Context())
 
 	if strings.Index(r.Method, "WATCH") != 0 {
-		reqDurations.WithLabelValues(r.Method, instance, route).Observe(elapsed)
+		reqDurations.WithLabelValues(r.Method, instance, route, domain).Observe(elapsed)
 	}
 
 	success, code := codeOf(w.Header())
 
-	incomingRequests.WithLabelValues(r.Method, code, instance, route).Inc()
+	incomingRequests.WithLabelValues(r.Method, code, instance, route, domain).Inc()
 
 	if success {
-		successfulRequests.WithLabelValues(r.Method, code, instance, route).Inc()
+		successfulRequests.WithLabelValues(r.Method, code, instance, route, domain).Inc()
 	}
 }
 
diff --git a/server/rest/reporter.go b/server/rest/reporter.go
new file mode 100644
index 0000000..d103ade
--- /dev/null
+++ b/server/rest/reporter.go
@@ -0,0 +1,68 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements.  See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rest
+
+import (
+	"github.com/apache/incubator-servicecomb-service-center/server/metric"
+	dto "github.com/prometheus/client_model/go"
+)
+
+const (
+	httpRequestTotal = "http_request_total"
+)
+
+var qpsLabelMap = map[string]int{
+	"method":   0,
+	"instance": 1,
+	"api":      2,
+	"domain":   3,
+}
+
+type APIReporter struct {
+	cache *metric.Details
+}
+
+func (r *APIReporter) Report() {
+	details := metric.Gatherer.Records.Get(httpRequestTotal)
+	defer func() { r.cache = details }()
+
+	if r.cache == nil {
+		return
+	}
+	details.ForEach(func(labels []*dto.LabelPair, v float64) (next bool) {
+		old := r.cache.Get(labels)
+		queryPerSeconds.WithLabelValues(r.toLabels(labels)...).Set((v - old) / metric.Period.Seconds())
+		return true
+	})
+}
+
+func (r *APIReporter) toLabels(pairs []*dto.LabelPair) (labels []string) {
+	labels = make([]string, len(qpsLabelMap))
+	for _, pair := range pairs {
+		if i, ok := qpsLabelMap[pair.GetName()]; ok {
+			labels[i] = pair.GetValue()
+		}
+	}
+	return
+}
+
+func init() {
+	metric.RegisterReporter("rest", NewAPIReporter())
+}
+
+func NewAPIReporter() *APIReporter {
+	return &APIReporter{}
+}