Create & Init Project...

This commit is contained in:
2019-04-22 18:49:16 +08:00
commit fc4fa37393
25440 changed files with 4054998 additions and 0 deletions

View File

@ -0,0 +1,63 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"breaker.go",
"collector.go",
"detect.go",
"operation_name.go",
"process.go",
],
importpath = "go-common/app/service/main/dapper/collector",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/main/dapper/conf:go_default_library",
"//app/service/main/dapper/dao:go_default_library",
"//app/service/main/dapper/model:go_default_library",
"//app/service/main/dapper/pkg/batchwrite:go_default_library",
"//app/service/main/dapper/pkg/collect:go_default_library",
"//app/service/main/dapper/pkg/collect/kafkacollect:go_default_library",
"//app/service/main/dapper/pkg/collect/tcpcollect:go_default_library",
"//app/service/main/dapper/pkg/pointwrite:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
go_test(
name = "go_default_test",
srcs = [
"breaker_test.go",
"collector_test.go",
"detect_test.go",
"operation_name_test.go",
],
embed = [":go_default_library"],
tags = ["automanaged"],
deps = [
"//app/service/main/dapper/model:go_default_library",
"//library/net/trace/proto:go_default_library",
],
)

View File

@ -0,0 +1,69 @@
package collector
import (
"fmt"
"sync"
"go-common/app/service/main/dapper/model"
)
type countBreaker struct {
rmx sync.RWMutex
n int
slot map[string]struct{}
}
func (c *countBreaker) Break(key string) error {
c.rmx.Lock()
_, ok := c.slot[key]
c.rmx.Unlock()
if ok {
return nil
}
c.rmx.Lock()
c.slot[key] = struct{}{}
l := len(c.slot)
c.rmx.Unlock()
if l <= c.n {
return nil
}
return fmt.Errorf("%s reach limit number %d breaked", key, c.n)
}
func newCountBreaker(n int) *countBreaker {
return &countBreaker{n: n, slot: make(map[string]struct{})}
}
type serviceBreaker struct {
rmx sync.RWMutex
n int
slot map[string]*countBreaker
}
func (s *serviceBreaker) Process(span *model.Span) error {
s.rmx.RLock()
operationNameBreaker, ok1 := s.slot[span.ServiceName+"_o"]
peerServiceBreaker, ok2 := s.slot[span.ServiceName+"_p"]
s.rmx.RUnlock()
if !ok1 || !ok2 {
s.rmx.Lock()
if !ok1 {
operationNameBreaker = newCountBreaker(s.n)
s.slot[span.ServiceName+"_o"] = operationNameBreaker
}
if !ok2 {
peerServiceBreaker = newCountBreaker(s.n)
s.slot[span.ServiceName+"_p"] = peerServiceBreaker
}
s.rmx.Unlock()
}
if err := operationNameBreaker.Break(span.OperationName); err != nil {
return err
}
return peerServiceBreaker.Break(span.StringTag("peer.service"))
}
// NewServiceBreakerProcess .
func NewServiceBreakerProcess(n int) Processer {
return &serviceBreaker{n: n, slot: make(map[string]*countBreaker)}
}

View File

@ -0,0 +1,24 @@
package collector
import (
"fmt"
"testing"
"go-common/app/service/main/dapper/model"
)
func TestServiceBreaker(t *testing.T) {
breaker := NewServiceBreakerProcess(10)
for i := 0; i < 20; i++ {
err := breaker.Process(&model.Span{ServiceName: "test", OperationName: fmt.Sprintf("opt_%d", i)})
if i < 10 {
if err != nil {
t.Error(err)
}
} else {
if err == nil {
t.Error("expect breaked")
}
}
}
}

View File

@ -0,0 +1,195 @@
package collector
import (
"context"
"fmt"
"time"
"go-common/app/service/main/dapper/conf"
"go-common/app/service/main/dapper/dao"
"go-common/app/service/main/dapper/model"
"go-common/app/service/main/dapper/pkg/batchwrite"
"go-common/app/service/main/dapper/pkg/collect"
"go-common/app/service/main/dapper/pkg/collect/kafkacollect"
"go-common/app/service/main/dapper/pkg/collect/tcpcollect"
"go-common/app/service/main/dapper/pkg/pointwrite"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
)
// Collector dapper collector receving trace data from tcp and write
// to hbase and influxdb
type Collector struct {
daoImpl dao.Dao
cfg *conf.Config
bw batchwrite.BatchWriter
pw pointwrite.PointWriter
process []Processer
clts []collect.Collecter
tcpClt *tcpcollect.TCPCollect
}
func (c *Collector) checkRetention(span *model.Span) error {
if c.cfg.Dapper.RetentionDay != 0 {
retentionSecond := c.cfg.Dapper.RetentionDay * 3600 * 24
if (time.Now().Unix() - span.StartTime.Unix()) > int64(retentionSecond) {
return fmt.Errorf("span beyond retention policy ignored")
}
}
return nil
}
// Process dispatch protoSpan
func (c *Collector) Process(ctx context.Context, protoSpan *model.ProtoSpan) error {
log.V(5).Info("dispatch span form serviceName: %s, operationName: %s", protoSpan.ServiceName, protoSpan.OperationName)
// ignored serviceName empry span
if protoSpan.ServiceName == "" {
log.Warn("span miss servicename ignored")
return nil
}
// fix operationName
if protoSpan.OperationName == "" {
protoSpan.OperationName = "missing operationname !!"
}
if protoSpan.ServiceName == "" {
log.Warn("span miss servicename ignored")
return nil
}
// convert to model.Span
span, err := model.FromProtoSpan(protoSpan, false)
if err != nil {
return err
}
// run process
for _, p := range c.process {
if err := p.Process(span); err != nil {
return err
}
}
if c.writeSamplePoint(span); err != nil {
log.Error("write sample point error: %s", err)
}
if c.writeRawTrace(span); err != nil {
log.Error("write sample point error: %s", err)
}
return nil
}
func (c *Collector) writeSamplePoint(span *model.Span) error {
return c.pw.WriteSpan(span)
}
func (c *Collector) writeRawTrace(span *model.Span) error {
return c.bw.WriteSpan(span)
}
// ListenAndStart listen and start collector server
func (c *Collector) ListenAndStart() error {
tcpClt := tcpcollect.New(c.cfg.Collect)
// NOTE: remove this future
c.tcpClt = tcpClt
c.clts = append(c.clts, tcpClt)
// if KafkaCollect is configured enable kafka collect
if c.cfg.KafkaCollect != nil {
kafkaClt, err := kafkacollect.New(c.cfg.KafkaCollect.Topic, c.cfg.KafkaCollect.Addrs)
if err != nil {
return err
}
c.clts = append(c.clts, kafkaClt)
}
for _, clt := range c.clts {
clt.RegisterProcess(c)
if err := clt.Start(); err != nil {
return err
}
}
if c.cfg.Dapper.APIListen != "" {
c.listenAndStartAPI()
}
return nil
}
func (c *Collector) listenAndStartAPI() {
engine := bm.NewServer(nil)
engine.GET("/x/internal/dapper-collector/client-status", c.clientStatusHandler)
engine.Start()
}
// Close collector
func (c *Collector) Close() error {
for _, clt := range c.clts {
if err := clt.Close(); err != nil {
log.Error("close collect error: %s", err)
}
}
c.bw.Close()
c.pw.Close()
return nil
}
// ClientStatus .
func (c *Collector) clientStatusHandler(bc *bm.Context) {
resp := &model.ClientStatusResp{QueueLen: c.bw.QueueLen()}
for _, cs := range c.tcpClt.ClientStatus() {
resp.Clients = append(resp.Clients, &model.ClientStatus{
Addr: cs.Addr,
UpTime: cs.UpTime,
ErrCount: cs.ErrorCounter.Value(),
Rate: cs.Counter.Value(),
})
}
bc.JSON(resp, nil)
}
// New new dapper collector
func New(cfg *conf.Config) (*Collector, error) {
daoImpl, err := dao.New(cfg)
if err != nil {
return nil, err
}
bw := batchwrite.NewRawDataBatchWriter(
daoImpl.WriteRawTrace,
cfg.BatchWriter.RawBufSize,
cfg.BatchWriter.RawChanSize,
cfg.BatchWriter.RawWorkers,
0,
)
detectData := make(map[string]map[string]struct{})
serviceNames, err := daoImpl.FetchServiceName(context.Background())
if err != nil {
return nil, nil
}
log.V(10).Info("fetch serviceNames get %d", len(serviceNames))
for _, serviceName := range serviceNames {
operationNames, err := daoImpl.FetchOperationName(context.Background(), serviceName)
if err != nil {
log.Error("fetch operationName for %s error: %s", serviceName, err)
continue
}
log.V(10).Info("fetch operationName for %s get %d", serviceName, len(operationNames))
detectData[serviceName] = make(map[string]struct{})
for _, operationName := range operationNames {
detectData[serviceName][operationName] = struct{}{}
}
}
// TODO configable
pw := pointwrite.New(daoImpl.BatchWriteSpanPoint, 5, 10*time.Second)
// register processer
var process []Processer
process = append(process, NewOperationNameProcess())
// FIXME: breaker not work
process = append(process, NewServiceBreakerProcess(4096))
process = append(process, NewPeerServiceDetectProcesser(detectData))
return &Collector{
cfg: cfg,
daoImpl: daoImpl,
bw: bw,
pw: pw,
process: process,
}, nil
}

View File

@ -0,0 +1 @@
package collector

View File

@ -0,0 +1,73 @@
package collector
import (
"sync"
"go-common/app/service/main/dapper/model"
)
type peerServiceDetect struct {
rmx sync.RWMutex
pair map[string]string
}
func (p *peerServiceDetect) detect(operationName string) (string, bool) {
p.rmx.RLock()
serviceName, ok := p.pair[operationName]
p.rmx.RUnlock()
return serviceName, ok
}
func (p *peerServiceDetect) add(serviceName, operationName string) {
if operationName == "" || serviceName == "" {
// ignored empty
return
}
p.rmx.RLock()
val, ok := p.pair[operationName]
p.rmx.RUnlock()
if !ok || serviceName != val {
p.rmx.Lock()
p.pair[operationName] = serviceName
p.rmx.Unlock()
}
}
func (p *peerServiceDetect) process(span *model.Span) {
if span.IsServer() {
p.add(span.ServiceName, span.OperationName)
return
}
if span.GetTagString("peer.service") != "" {
return
}
peerService, ok := p.detect(span.OperationName)
if ok {
span.SetTag("peer.service", peerService)
span.SetTag("_auto.peer.service", true)
return
}
if peerSign := span.StringTag("_peer.sign"); peerSign != "" {
peerService, ok := p.detect(peerSign)
if ok {
span.SetTag("peer.service", peerService)
span.SetTag("_auto.peer.service", true)
}
}
}
func (p *peerServiceDetect) Process(span *model.Span) error {
p.process(span)
return nil
}
// NewPeerServiceDetectProcesser .
func NewPeerServiceDetectProcesser(data map[string]map[string]struct{}) Processer {
p := &peerServiceDetect{pair: make(map[string]string)}
for serviceName, operationNames := range data {
for operationName := range operationNames {
p.add(serviceName, operationName)
}
}
return p
}

View File

@ -0,0 +1,26 @@
package collector
import (
"testing"
"go-common/app/service/main/dapper/model"
)
func TestPeerServiceDetect(t *testing.T) {
detect := NewPeerServiceDetectProcesser(map[string]map[string]struct{}{
"s_a": {
"o_1": struct{}{},
"o_11": struct{}{},
},
"s_b": {"o_2": struct{}{}},
"s_c": {"o_21": struct{}{}},
})
sp1, _ := model.FromProtoSpan(&model.ProtoSpan{ServiceName: "xxx", OperationName: "o_1"}, false)
detect.Process(sp1)
if sp1.StringTag("peer.service") != "s_a" && sp1.BoolTag("_auto.peer.service") {
t.Errorf("expect get s_a get %s", sp1.StringTag("peer.service"))
}
if err := detect.Process(&model.Span{ServiceName: "hh", OperationName: ""}); err != nil {
t.Errorf("expect get noting")
}
}

View File

@ -0,0 +1,46 @@
package collector
import (
"net/url"
"strings"
)
import (
"go-common/app/service/main/dapper/model"
)
// OperationNameProcess fix operation name so sad!
type OperationNameProcess struct{}
// Process implement operation name
func (o *OperationNameProcess) Process(span *model.Span) error {
switch {
case !span.IsServer() && strings.HasPrefix(span.OperationName, "http://"):
o.fixHTTP(span)
}
return nil
}
func (o *OperationNameProcess) fixHTTP(span *model.Span) {
oldOperationName := span.OperationName
method := "UNKONWN"
if methodTag := span.GetTagString("http.method"); methodTag != "" {
method = methodTag
}
operationName := "HTTP:" + method
span.SetOperationName(operationName)
peerSign := oldOperationName
if strings.HasPrefix(oldOperationName, "http://") {
if reqURL, err := url.Parse(oldOperationName); err == nil {
peerSign = reqURL.Path
span.SetTag("http.url", oldOperationName)
}
}
span.SetTag("_peer.sign", peerSign)
}
// NewOperationNameProcess .
func NewOperationNameProcess() Processer {
return &OperationNameProcess{}
}

View File

@ -0,0 +1,27 @@
package collector
import (
"testing"
"go-common/app/service/main/dapper/model"
protogen "go-common/library/net/trace/proto"
)
func TestOperationNameProcess(t *testing.T) {
p := NewOperationNameProcess()
sp1, _ := model.FromProtoSpan(&model.ProtoSpan{
ServiceName: "http",
OperationName: "http://www.example.com/echo",
Tags: []*protogen.Tag{&protogen.Tag{Key: "span.kind", Kind: protogen.Tag_STRING, Value: []byte("client")}},
}, false)
p.Process(sp1)
if sp1.OperationName != "HTTP:UNKONWN" || sp1.ProtoSpan.OperationName != "HTTP:UNKONWN" {
t.Errorf("expect operationName == , get %s %s", sp1.OperationName, sp1.ProtoSpan.OperationName)
}
if sp1.StringTag("http.url") != "http://www.example.com/echo" {
t.Errorf("expect http.url be set")
}
if sp1.StringTag("_peer.sign") != "/echo" {
t.Errorf("expect _peer.sign be set")
}
}

View File

@ -0,0 +1,16 @@
package collector
import (
"go-common/app/service/main/dapper/model"
)
// Processer span processer
type Processer interface {
Process(span *model.Span) error
}
// ProcessFunc implement Processer
type ProcessFunc func(*model.Span) error
// Process implement Processer
func (p ProcessFunc) Process(span *model.Span) error { return p(span) }