github.com/artisanhe/tools@v1.0.1-0.20210607022958-19a8fef2eb04/catgo/cat-go/cat/aggregator_transaction.go (about) 1 package cat 2 3 import ( 4 "bytes" 5 "fmt" 6 "time" 7 8 "github.com/artisanhe/tools/catgo/cat-go/message" 9 ) 10 11 type transactionData struct { 12 mtype, name string 13 14 count, fail int 15 16 sum int64 17 18 durations map[int]int 19 } 20 21 //noinspection GoUnhandledErrorResult 22 func encodeTransactionData(data *transactionData) *bytes.Buffer { 23 buf := newBuf() 24 25 buf.WriteRune(batchFlag) 26 buf.WriteInt(data.count) 27 buf.WriteRune(batchSplit) 28 buf.WriteInt(data.fail) 29 buf.WriteRune(batchSplit) 30 buf.WriteUInt64(uint64(data.sum)) 31 buf.WriteRune(batchSplit) 32 33 i := 0 34 for k, v := range data.durations { 35 if i > 0 { 36 buf.WriteRune('|') 37 } 38 buf.WriteInt(k) 39 buf.WriteRune(',') 40 buf.WriteInt(v) 41 i++ 42 } 43 buf.WriteRune(batchSplit) 44 45 return &buf.Buffer 46 } 47 48 func (p *transactionAggregator) GetName() string { 49 return "TransactionAggregator" 50 } 51 52 type transactionAggregator struct { 53 scheduleMixin 54 ch chan *message.Transaction 55 dataMap map[string]*transactionData 56 ticker *time.Ticker 57 } 58 59 func (p *transactionAggregator) collectAndSend() { 60 dataMap := p.dataMap 61 p.dataMap = make(map[string]*transactionData) 62 p.send(dataMap) 63 } 64 65 func (p *transactionAggregator) send(dataMap map[string]*transactionData) { 66 if len(dataMap) == 0 { 67 return 68 } 69 t := message.NewTransaction(typeSystem, nameTransactionAggregator, aggregator.flush) 70 defer t.Complete() 71 72 for _, data := range dataMap { 73 trans := message.NewTransaction(data.mtype, data.name, nil) 74 trans.SetData(encodeTransactionData(data).String()) 75 trans.Complete() 76 t.AddChild(trans) 77 } 78 } 79 80 func (p *transactionAggregator) getOrDefault(transaction *message.Transaction) *transactionData { 81 key := fmt.Sprintf("%s,%s", transaction.Type, transaction.Name) 82 83 if data, ok := p.dataMap[key]; ok { 84 return data 85 } else { 86 p.dataMap[key] = &transactionData{ 87 mtype: transaction.GetType(), 88 name: transaction.GetName(), 89 count: 0, 90 fail: 0, 91 sum: 0, 92 durations: make(map[int]int), 93 } 94 return p.dataMap[key] 95 } 96 } 97 98 func (p *transactionAggregator) afterStart() { 99 p.ticker = time.NewTicker(transactionAggregatorInterval) 100 } 101 102 func (p *transactionAggregator) beforeStop() { 103 close(p.ch) 104 105 for t := range p.ch { 106 p.getOrDefault(t).add(t) 107 } 108 p.collectAndSend() 109 110 p.ticker.Stop() 111 } 112 113 func (p *transactionAggregator) process() { 114 select { 115 case sig := <-p.signals: 116 p.handle(sig) 117 case t := <-p.ch: 118 p.getOrDefault(t).add(t) 119 case <-p.ticker.C: 120 p.collectAndSend() 121 } 122 } 123 124 func (p *transactionAggregator) Put(t *message.Transaction) { 125 if !IsEnabled() { 126 return 127 } 128 129 select { 130 case p.ch <- t: 131 default: 132 logger.Warning("Transaction aggregator is full") 133 } 134 } 135 136 func (data *transactionData) add(transaction *message.Transaction) { 137 data.count++ 138 139 if transaction.GetStatus() != SUCCESS { 140 data.fail++ 141 } 142 143 millis := duration2Millis(transaction.GetDuration()) 144 data.sum += millis 145 146 duration := computeDuration(int(millis)) 147 if _, ok := data.durations[duration]; ok { 148 data.durations[duration]++ 149 } else { 150 data.durations[duration] = 1 151 } 152 } 153 154 func newTransactionAggregator() *transactionAggregator { 155 return &transactionAggregator{ 156 scheduleMixin: makeScheduleMixedIn(signalTransactionAggregatorExit), 157 ch: make(chan *message.Transaction, transactionAggregatorChannelCapacity), 158 dataMap: make(map[string]*transactionData), 159 } 160 }