diff --git a/common/extension/metrics.go b/common/extension/metrics.go index 8f3234117657667831149a92a8fb7c6fddc7ecd6..d78907ee484053ac3623568e73c46acd7d1b3f3b 100644 --- a/common/extension/metrics.go +++ b/common/extension/metrics.go @@ -21,7 +21,7 @@ import ( "github.com/apache/dubbo-go/metrics" ) -var( +var ( metricReporterMap = make(map[string]metrics.Reporter, 4) ) diff --git a/config/config_loader.go b/config/config_loader.go index 73bab42b6a9f086d2c31c6cdec47bc3e798b8fcb..fa0d3da433dd91451ded13d0191ffe7f0181a879 100644 --- a/config/config_loader.go +++ b/config/config_loader.go @@ -33,7 +33,7 @@ import ( var ( consumerConfig *ConsumerConfig providerConfig *ProviderConfig - metricConfig *MetricConfig + metricConfig *MetricConfig maxWait = 3 ) diff --git a/config/metric_config.go b/config/metric_config.go index 71071b289b2be0a174fbbb12f5d7c08cceda57b6..1b0f234f8a0d6209ed4e1b0c366106b45437dcdf 100644 --- a/config/metric_config.go +++ b/config/metric_config.go @@ -23,7 +23,7 @@ import ( // This is the config struct for all metrics implementation type MetricConfig struct { - Reporters []string `yaml:"reporters" json:"reporters,omitempty"` + Reporters []string `yaml:"reporters" json:"reporters,omitempty"` } // parse the config from yml @@ -46,5 +46,3 @@ func GetMetricConfig() *MetricConfig { } return metricConfig } - - diff --git a/config/metric_config_test.go b/config/metric_config_test.go index ff8b795506f4fe5d408ca4330186cf0b1ff4ae27..fe9d2493f37c0bd563931f5acf133105d72d0e53 100644 --- a/config/metric_config_test.go +++ b/config/metric_config_test.go @@ -25,7 +25,6 @@ import ( "github.com/stretchr/testify/assert" ) - func TestGetMetricConfig(t *testing.T) { empty := GetMetricConfig() assert.NotNil(t, empty) diff --git a/filter/filter_impl/metrics_filter.go b/filter/filter_impl/metrics_filter.go index e3db21a979f4e9433d420a633c206e0434138a78..e35a97d198043df8d6837629435533962f718cbf 100644 --- a/filter/filter_impl/metrics_filter.go +++ b/filter/filter_impl/metrics_filter.go @@ -72,6 +72,9 @@ func (p *metricsFilter) OnResponse(ctx context.Context, res protocol.Result, inv return res } +// the metricsFilter is singleton. +// it's lazy initialization +// make sure that the configuration had been loaded before invoking this method. func newMetricsFilter() filter.Filter { if metricFilterInstance == nil { reporterNames := config.GetMetricConfig().Reporters diff --git a/go.mod b/go.mod index db6dc92c63176334f6dfa0436889ffab6f3c9c53..58932cf84a061960f9bc768b7ac14e8259bcecaf 100644 --- a/go.mod +++ b/go.mod @@ -37,7 +37,7 @@ require ( github.com/nacos-group/nacos-sdk-go v0.0.0-20190723125407-0242d42e3dbb github.com/opentracing/opentracing-go v1.1.0 github.com/pkg/errors v0.8.1 - github.com/prometheus/client_golang v1.1.0 // indirect + github.com/prometheus/client_golang v1.1.0 github.com/samuel/go-zookeeper v0.0.0-20180130194729-c4fab1ac1bec github.com/satori/go.uuid v1.2.0 github.com/smartystreets/goconvey v0.0.0-20190710185942-9d28bd7c0945 // indirect diff --git a/metrics/prometheus/prometheus_reporter.go b/metrics/prometheus/reporter.go similarity index 75% rename from metrics/prometheus/prometheus_reporter.go rename to metrics/prometheus/reporter.go index 245e2b0c6730fa44443a2b05d36a96ead1d19a2e..8b16ff5e712b7b2adac9d1ecab397bd75fb0ee2e 100644 --- a/metrics/prometheus/prometheus_reporter.go +++ b/metrics/prometheus/reporter.go @@ -22,6 +22,8 @@ import ( "strconv" "strings" "time" + + "github.com/apache/dubbo-go/common/logger" ) import ( "github.com/prometheus/client_golang/prometheus" @@ -49,11 +51,19 @@ const ( consumerKey = "consumer" ) +// should initialize after loading configuration func init() { - extension.SetMetricReporter(reporterName, newPrometheus()) + rpt := &PrometheusReporter{ + consumerVec: newSummaryVec(consumerKey), + providerVec: newSummaryVec(providerKey), + } + prometheus.MustRegister(rpt.consumerVec, rpt.providerVec) + extension.SetMetricReporter(reporterName, rpt) } // it will collect the data for Prometheus +// if you want to use this, you should initialize your prometheus. +// https://prometheus.io/docs/guides/go-application/ type PrometheusReporter struct { // report the consumer-side's data @@ -68,8 +78,12 @@ func (reporter *PrometheusReporter) Report(ctx context.Context, invoker protocol var sumVec *prometheus.SummaryVec if isProvider(url) { sumVec = reporter.providerVec - } else { + } else if isConsumer(url) { sumVec = reporter.consumerVec + } else { + logger.Warnf("The url is not the consumer's or provider's, "+ + "so the invocation will be ignored. url: %s", url.String()) + return } sumVec.With(prometheus.Labels{ @@ -83,21 +97,20 @@ func (reporter *PrometheusReporter) Report(ctx context.Context, invoker protocol }).Observe(float64(cost.Nanoseconds() / constant.MsToNanoRate)) } +// whether this url represents the application received the request as server func isProvider(url common.URL) bool { - side := url.GetParam(constant.ROLE_KEY, "") - return strings.EqualFold(side, strconv.Itoa(common.PROVIDER)) + role := url.GetParam(constant.ROLE_KEY, "") + return strings.EqualFold(role, strconv.Itoa(common.PROVIDER)) } -func newPrometheus() metrics.Reporter { - // cfg := *config.GetMetricConfig().GetPrometheusConfig() - result := &PrometheusReporter{ - consumerVec: newSummaryVec(consumerKey), - providerVec: newSummaryVec(providerKey), - } - prometheus.MustRegister(result.consumerVec, result.providerVec) - return result +// whether this url represents the application sent then request as client +func isConsumer(url common.URL) bool { + role := url.GetParam(constant.ROLE_KEY, "") + return strings.EqualFold(role, strconv.Itoa(common.CONSUMER)) } +// create SummaryVec, the Namespace is dubbo +// the objectives is from my experience. func newSummaryVec(side string) *prometheus.SummaryVec { return prometheus.NewSummaryVec( diff --git a/metrics/prometheus/reporter_test.go b/metrics/prometheus/reporter_test.go new file mode 100644 index 0000000000000000000000000000000000000000..bc0a14c354b0155aefa346074de753c367f34160 --- /dev/null +++ b/metrics/prometheus/reporter_test.go @@ -0,0 +1,48 @@ +/* + * 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 prometheus + +import ( + "context" + "testing" + "time" +) + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/extension" + "github.com/apache/dubbo-go/protocol" + "github.com/apache/dubbo-go/protocol/invocation" +) + +func TestPrometheusReporter_Report(t *testing.T) { + reporter := extension.GetMetricReporter(reporterName) + url, _ := common.NewURL(context.Background(), + "dubbo://:20000/UserProvider?app.version=0.0.1&application=BDTService&bean.name=UserProvider"+ + "&cluster=failover&environment=dev&group=&interface=com.ikurento.user.UserProvider&loadbalance=random&methods.GetUser."+ + "loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name="+ + "BDTService&organization=ikurento.com&owner=ZX®istry.role=3&retries=&"+ + "service.filter=echo%2Ctoken%2Caccesslog×tamp=1569153406&token=934804bf-b007-4174-94eb-96e3e1d60cc7&version=&warmup=100") + invoker := protocol.NewBaseInvoker(url) + + attach := make(map[string]string, 10) + inv := invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach) + + ctx := context.Background() + reporter.Report(ctx, invoker, inv, 100*time.Millisecond) +}