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{}
+}