diff --git a/conf/mesher.yaml b/conf/mesher.yaml index 024810c..490844f 100644 --- a/conf/mesher.yaml +++ b/conf/mesher.yaml @@ -21,3 +21,8 @@ admin: #admin API # match: # status: 200 # body: ok +#servicecomb: +# apm: +# tracing: +# enable: true +# serverUri: 127.0.0.1:11800 \ No newline at end of file diff --git a/docs/skywalking/skywalking.md b/docs/skywalking/skywalking.md new file mode 100644 index 0000000..d3b3676 --- /dev/null +++ b/docs/skywalking/skywalking.md @@ -0,0 +1,36 @@ +# SkyWalking + +Skywalking-manager is a handler plugin of mesher, it reports tracing data to skywalking server. + +## Configurations +**In conf/mesher.conf** + +**servicecomb.apm.tracing.enable** +> *(optional, bool)* enable application performance manager + +**servicecomb.apm.tracing.serverUri** +> *(optional, string)* server address of skywalking + +## Example +```yaml +servicecomb: + apm: #application performance monitor + tracing: + enable: true #enable tracing ability + serverUri: 127.0.0.1:11800 #url of skywalking +``` +## Step: + +# 1. SkyWawlking-Manager Init +**You must init skywawlking manager pkg which will manage connection and report msg to skywalking** +- For example: +- [1] You can import skywalking manager proxy/pkg/skywalking in file proxy/bootstrap/bootstrap.go. +- [2] Calling function Init() in proxy/pkg/skywalking manually to init skywalking manager. +- [3] Adding skywalking's consumer handler name SkyWalkingConsumer defined in proxy/pkg/skywalking to consumerChain. +- [4] Adding skywalking's provider handler name SkyWalkingProvider defined in proxy/pkg/skywalking to providerChain. +- more details about handler chains in [go-chassis](https://github.com/go-chassis/go-chassis#readme) + +# 2. SkyWalking-Handler Init +- You must import proxy/handler pkg to init skywalking handler. Not only skywalking handler, all the handlers which are customized for mesher are defined here. +- For example you can import handler pkg in file cmd/mesher/mesher.go + diff --git a/go.mod b/go.mod index db2168a..89a7514 100644 --- a/go.mod +++ b/go.mod @@ -21,14 +21,18 @@ require ( github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/testify v1.3.0 + github.com/tetratelabs/go2sky v0.1.1-0.20190703154722-1eaab8035277 github.com/urfave/cli v1.20.1-0.20181029213200-b67dcf995b6a golang.org/x/net v0.0.0-20190311183353-d8887717615a golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 // indirect - google.golang.org/grpc v1.16.0 + google.golang.org/grpc v1.19.1 gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v2 v2.2.1 + gopkg.in/yaml.v2 v2.2.2 k8s.io/apimachinery v0.0.0-20181022183627-f71dbbc36e12 k8s.io/client-go v9.0.0+incompatible ) -replace github.com/openzipkin-contrib/zipkin-go-opentracing v0.3.5 => github.com/go-chassis/zipkin-go-opentracing v0.3.5-0.20190321072447-42cf74fc2a92 +replace ( + github.com/openzipkin-contrib/zipkin-go-opentracing v0.3.5 => github.com/go-chassis/zipkin-go-opentracing v0.3.5-0.20190321072447-42cf74fc2a92 + github.com/tetratelabs/go2sky v0.1.1-0.20190703154722-1eaab8035277 => github.com/SkyAPM/go2sky v0.1.1-0.20190703154722-1eaab8035277 +) diff --git a/proxy/config/struct.go b/proxy/config/struct.go index 87b35f1..a4a4c51 100644 --- a/proxy/config/struct.go +++ b/proxy/config/struct.go @@ -25,6 +25,7 @@ type MesherConfig struct { Admin Admin `yaml:"admin"` HealthCheck []*HealthCheck `yaml:"localHealthCheck"` ProxyedPro string `yaml:"proxyedProtocol"` + ServiceComb *ServiceComb `yaml:"servicecomb"` } //HealthCheck define how to check local ports @@ -66,3 +67,19 @@ type Admin struct { Enable bool `yaml:"enable"` ServerURI string `yaml:"serverUri"` } + +//Tracing has attributes for APM +type Tracing struct { + Enable bool `yaml:"enable"` + ServerURI string `yaml:"serverUri"` +} + +//APM is for Application Performance Management +type APM struct { + Tracing Tracing `yaml:"tracing"` +} + +//ServiceComb is for servicecomb config +type ServiceComb struct { + APM APM `yaml:"apm"` +} diff --git a/proxy/handler/skywalking_handler.go b/proxy/handler/skywalking_handler.go new file mode 100644 index 0000000..afaaec5 --- /dev/null +++ b/proxy/handler/skywalking_handler.go @@ -0,0 +1,122 @@ +/* + * 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 handler + +import ( + "github.com/apache/servicecomb-mesher/proxy/pkg/skywalking" + "github.com/go-chassis/go-chassis/core/handler" + "github.com/go-chassis/go-chassis/core/invocation" + "github.com/go-mesh/openlogging" + "github.com/tetratelabs/go2sky" + skycom "github.com/tetratelabs/go2sky/reporter/grpc/common" + "strconv" +) + +const ( + HTTPPrefix = "http://" +) + +const ( + HTTPClientComponentID = 2 + ServiceCombComponentID = 28 + HTTPServerComponentID = 49 +) + +//SkyWalkingProviderHandler struct +type SkyWalkingProviderHandler struct { +} + +//Handle is for provider +func (sp *SkyWalkingProviderHandler) Handle(chain *handler.Chain, i *invocation.Invocation, cb invocation.ResponseCallBack) { + openlogging.GetLogger().Debugf("SkyWalkingProviderHandler begin. inv:%#v", *i) + span, _, err := skywalking.CreateEntrySpan(i) + if err != nil { + openlogging.GetLogger().Errorf("CreateEntrySpan error:%s", err.Error()) + } + chain.Next(i, func(r *invocation.Response) (err error) { + err = cb(r) + span.Tag(go2sky.TagHTTPMethod, i.Protocol) + span.Tag(go2sky.TagURL, HTTPPrefix+i.MicroServiceName+i.URLPathFormat) + span.Tag(go2sky.TagStatusCode, strconv.Itoa(r.Status)) + span.SetSpanLayer(skycom.SpanLayer_Http) + span.SetComponent(HTTPServerComponentID) + span.End() + return + }) +} + +//Name return provider name +func (sp *SkyWalkingProviderHandler) Name() string { + return skywalking.SkyWalkingProvider +} + +//NewSkyWalkingProvier return provider handler for SkyWalking +func NewSkyWalkingProvier() handler.Handler { + return &SkyWalkingProviderHandler{} +} + +//SkyWalkingConsumerHandler struct +type SkyWalkingConsumerHandler struct { +} + +//Handle is for consumer +func (sc *SkyWalkingConsumerHandler) Handle(chain *handler.Chain, i *invocation.Invocation, cb invocation.ResponseCallBack) { + openlogging.GetLogger().Debugf("SkyWalkingConsumerHandler begin:%#v", *i) + span, ctx, err := skywalking.CreateEntrySpan(i) + if err != nil { + openlogging.GetLogger().Errorf("CreateEntrySpan error:%s", err.Error()) + } + spanExit, err := skywalking.CreateExitSpan(ctx, i) + if err != nil { + openlogging.GetLogger().Errorf("CreateExitSpan error:%s", err.Error()) + } + chain.Next(i, func(r *invocation.Response) (err error) { + err = cb(r) + span.Tag(go2sky.TagHTTPMethod, i.Protocol) + span.Tag(go2sky.TagURL, HTTPPrefix+i.MicroServiceName+i.URLPathFormat) + span.Tag(go2sky.TagStatusCode, strconv.Itoa(r.Status)) + span.SetSpanLayer(skycom.SpanLayer_Http) + span.SetComponent(HTTPServerComponentID) + + spanExit.Tag(go2sky.TagHTTPMethod, i.Protocol) + spanExit.Tag(go2sky.TagURL, HTTPPrefix+i.MicroServiceName+i.URLPathFormat) + spanExit.Tag(go2sky.TagStatusCode, strconv.Itoa(r.Status)) + spanExit.SetSpanLayer(skycom.SpanLayer_Http) + spanExit.SetComponent(HTTPClientComponentID) + + spanExit.End() + span.End() + openlogging.GetLogger().Debugf("SkyWalkingConsumerHandler end.") + return + }) +} + +//Name return consumer name +func (sc *SkyWalkingConsumerHandler) Name() string { + return skywalking.SkyWalkingConsumer +} + +//NewSkyWalkingConsumer return consumer handler for SkyWalking +func NewSkyWalkingConsumer() handler.Handler { + return &SkyWalkingConsumerHandler{} +} + +func init() { + handler.RegisterHandler(skywalking.SkyWalkingProvider, NewSkyWalkingProvier) + handler.RegisterHandler(skywalking.SkyWalkingConsumer, NewSkyWalkingConsumer) +} diff --git a/proxy/handler/skywalking_handler_test.go b/proxy/handler/skywalking_handler_test.go new file mode 100644 index 0000000..3e7a1bf --- /dev/null +++ b/proxy/handler/skywalking_handler_test.go @@ -0,0 +1,120 @@ +/* + * 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 handler_test + +import ( + "context" + "github.com/apache/servicecomb-mesher/proxy/config" + mhandler "github.com/apache/servicecomb-mesher/proxy/handler" + "github.com/apache/servicecomb-mesher/proxy/pkg/skywalking" + gcconfig "github.com/go-chassis/go-chassis/core/config" + "github.com/go-chassis/go-chassis/core/config/model" + "github.com/go-chassis/go-chassis/core/handler" + "github.com/go-chassis/go-chassis/core/invocation" + "github.com/stretchr/testify/assert" + "testing" +) + +const ( + Port = ":49800" + ServerUrl = "127.0.0.1:49800" +) + +//initGcConfig +func initGcConfig() { + var micCfg model.MicroserviceCfg + micCfg.ServiceDescription.Name = "TEST" + gcconfig.MicroserviceDefinition = &micCfg +} + +//initMesherConfig +func initMesherConfig() { + config.SetConfig(&config.MesherConfig{ServiceComb: &config.ServiceComb{config.APM{config.Tracing{Enable: true, ServerURI: "192.168.0.1:17289"}}}}) +} + +//initInv +func initInv() *invocation.Invocation { + var i invocation.Invocation + i.MicroServiceName = "test" + i.Ctx = context.Background() + i.Endpoint = "calculator" + i.URLPathFormat = "/bmi" + return &i +} + +//TestProviderHandlerName +func TestProviderHandlerName(t *testing.T) { + h := mhandler.SkyWalkingProviderHandler{} + assert.Equal(t, h.Name(), skywalking.SkyWalkingProvider) +} + +//TestNewProvier +func TestNewProvier(t *testing.T) { + h := mhandler.NewSkyWalkingProvier() + assert.NotEqual(t, h, nil) + assert.Equal(t, h.Name(), skywalking.SkyWalkingProvider) +} + +//TestProvierHandle +func TestProvierHandle(t *testing.T) { + initGcConfig() + initMesherConfig() + skywalking.Init() + c := handler.Chain{} + c.AddHandler(mhandler.NewSkyWalkingProvier()) + + gcconfig.GlobalDefinition = &model.GlobalCfg{} + gcconfig.GlobalDefinition.Cse.Handler.Chain.Consumer = make(map[string]string) + gcconfig.GlobalDefinition.Cse.Handler.Chain.Consumer["skywalking-provider"] = "skywalking-provider" + + c.Next(initInv(), func(r *invocation.Response) error { + assert.Equal(t, r.Err, nil) + return r.Err + }) +} + +//TestConsumerHandlerName +func TestConsumerHandlerName(t *testing.T) { + c := mhandler.SkyWalkingConsumerHandler{} + assert.Equal(t, c.Name(), skywalking.SkyWalkingConsumer) +} + +//TestNewConsumer +func TestNewConsumer(t *testing.T) { + h := mhandler.NewSkyWalkingConsumer() + assert.NotEqual(t, h, nil) + assert.Equal(t, h.Name(), skywalking.SkyWalkingConsumer) +} + +//TestConsumerHandle +func TestConsumerHandle(t *testing.T) { + initGcConfig() + initMesherConfig() + skywalking.Init() + c := handler.Chain{} + c.AddHandler(mhandler.NewSkyWalkingConsumer()) + + gcconfig.GlobalDefinition = &model.GlobalCfg{} + gcconfig.GlobalDefinition.Cse.Handler.Chain.Consumer = make(map[string]string) + gcconfig.GlobalDefinition.Cse.Handler.Chain.Consumer["skywalking-consumer"] = "skywalking-consumer" + + c.Next(initInv(), func(r *invocation.Response) error { + assert.Equal(t, r.Err, nil) + return r.Err + }) +} diff --git a/proxy/pkg/skywalking/skywalking_manager.go b/proxy/pkg/skywalking/skywalking_manager.go new file mode 100644 index 0000000..27bdf71 --- /dev/null +++ b/proxy/pkg/skywalking/skywalking_manager.go @@ -0,0 +1,79 @@ +/* + * 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 skywalking + +import ( + "context" + "github.com/apache/servicecomb-mesher/proxy/config" + gcconfig "github.com/go-chassis/go-chassis/core/config" + "github.com/go-chassis/go-chassis/core/invocation" + "github.com/go-mesh/openlogging" + "github.com/tetratelabs/go2sky" + "github.com/tetratelabs/go2sky/reporter" +) + +const ( + CrossProcessProtocolV2 = "Sw6" + SkyWalkingConsumer = "skywalking-consumer" + SkyWalkingProvider = "skywalking-provider" + SkyWalkingName = "skywalking" + DeafaultSWServerURI = "127.0.0.1:11800" +) + +var r go2sky.Reporter +var tracer *go2sky.Tracer + +//CreateEntrySpan use tracer to create and start an entry span for incoming request +func CreateEntrySpan(i *invocation.Invocation) (go2sky.Span, context.Context, error) { + return tracer.CreateEntrySpan(i.Ctx, i.MicroServiceName+i.URLPathFormat, func() (string, error) { + return i.Headers()[CrossProcessProtocolV2], nil + }) +} + +//CreateExitSpan use tracer to create and start an exit span for client +func CreateExitSpan(ctx context.Context, i *invocation.Invocation) (go2sky.Span, error) { + return tracer.CreateExitSpan(ctx, i.MicroServiceName+i.URLPathFormat, i.Endpoint+i.URLPathFormat, func(header string) error { + i.SetHeader(CrossProcessProtocolV2, header) + return nil + }) +} + +//CreateLocalSpan use tracer to create and start a span for local usage +func CreateLocalSpan(ctx context.Context, opts ...go2sky.SpanOption) (go2sky.Span, context.Context, error) { + return tracer.CreateLocalSpan(ctx, opts...) +} + +//Init skywalking manager +func Init() { + openlogging.GetLogger().Debugf("SkyWalking manager Init begin config:%#v", config.GetConfig().ServiceComb.APM) + var err error + serverURI := DeafaultSWServerURI + if config.GetConfig().ServiceComb.APM.Tracing.ServerURI != "" && config.GetConfig().ServiceComb.APM.Tracing.Enable { + serverURI = config.GetConfig().ServiceComb.APM.Tracing.ServerURI + } + r, err = reporter.NewGRPCReporter(serverURI) + if err != nil { + openlogging.GetLogger().Errorf("NewGRPCReporter error:%s ", err.Error()) + } + tracer, err = go2sky.NewTracer(gcconfig.MicroserviceDefinition.ServiceDescription.Name, go2sky.WithReporter(r)) + if err != nil { + openlogging.GetLogger().Errorf("NewTracer error " + err.Error()) + } + //tracer.WaitUntilRegister() + openlogging.GetLogger().Debugf("SkyWalking manager Init end") +} diff --git a/proxy/pkg/skywalking/skywalking_manager_test.go b/proxy/pkg/skywalking/skywalking_manager_test.go new file mode 100644 index 0000000..e722c44 --- /dev/null +++ b/proxy/pkg/skywalking/skywalking_manager_test.go @@ -0,0 +1,101 @@ +/* + * 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 skywalking_test + +import ( + "context" + "github.com/apache/servicecomb-mesher/proxy/config" + "github.com/apache/servicecomb-mesher/proxy/pkg/skywalking" + gcconfig "github.com/go-chassis/go-chassis/core/config" + "github.com/go-chassis/go-chassis/core/config/model" + "github.com/go-chassis/go-chassis/core/invocation" + "github.com/stretchr/testify/assert" + "testing" +) + +const ( + Port = ":49800" + ServerUrl = "127.0.0.1:49800" +) + +//initConfig +func initConfig() { + var micCfg model.MicroserviceCfg + micCfg.ServiceDescription.Name = "TEST" + gcconfig.MicroserviceDefinition = &micCfg +} + +//initMesherConfig +func initMesherConfig() { + config.SetConfig(&config.MesherConfig{ServiceComb: &config.ServiceComb{config.APM{config.Tracing{Enable: true, ServerURI: "192.168.0.1:17289"}}}}) +} + +//initInv +func initInv() *invocation.Invocation { + var i invocation.Invocation + i.MicroServiceName = "test" + i.Ctx = context.Background() + i.Endpoint = "calculator" + i.URLPathFormat = "/bmi" + return &i +} + +//TestInit init skywalking manager +func TestInit(t *testing.T) { + initConfig() + initMesherConfig() + skywalking.Init() + assert.NotEqual(t, gcconfig.MicroserviceDefinition, nil) +} + +//TestCreateEntrySpan test skywalking manager creating entryspan +func TestCreateEntrySpan(t *testing.T) { + initConfig() + initMesherConfig() + skywalking.Init() + span, _, err := skywalking.CreateEntrySpan(initInv()) + assert.Equal(t, err, nil) + assert.NotEqual(t, span, nil) + span.End() +} + +//TestCreateExitSpan test skywalking manager creating endspan +func TestCreateExitSpan(t *testing.T) { + initConfig() + initMesherConfig() + skywalking.Init() + inv := initInv() + span, ctx, err := skywalking.CreateEntrySpan(inv) + assert.Equal(t, err, nil) + assert.NotEqual(t, span, nil) + spanExit, err := skywalking.CreateExitSpan(ctx, inv) + assert.Equal(t, err, nil) + assert.NotEqual(t, spanExit, nil) + spanExit.End() + span.End() +} + +//TestCreateLocalSpan test skywalking manager creating localspan +func TestCreateLocalSpan(t *testing.T) { + initConfig() + initMesherConfig() + skywalking.Init() + span, _, err := skywalking.CreateLocalSpan(context.Background()) + assert.Equal(t, err, nil) + assert.NotEqual(t, span, nil) +}