github.com/m3db/m3@v1.5.0/src/aggregator/server/rawtcp/server.go (about) 1 // Copyright (c) 2016 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package rawtcp 22 23 import ( 24 "bufio" 25 "fmt" 26 "io" 27 "net" 28 "sync" 29 "time" 30 31 "github.com/m3db/m3/src/aggregator/aggregator" 32 "github.com/m3db/m3/src/aggregator/rate" 33 "github.com/m3db/m3/src/metrics/encoding" 34 "github.com/m3db/m3/src/metrics/encoding/protobuf" 35 "github.com/m3db/m3/src/metrics/metadata" 36 "github.com/m3db/m3/src/metrics/metric/aggregated" 37 "github.com/m3db/m3/src/metrics/metric/unaggregated" 38 "github.com/m3db/m3/src/metrics/policy" 39 xio "github.com/m3db/m3/src/x/io" 40 xserver "github.com/m3db/m3/src/x/server" 41 xtime "github.com/m3db/m3/src/x/time" 42 43 "github.com/uber-go/tally" 44 "go.uber.org/zap" 45 ) 46 47 const ( 48 unknownRemoteHostAddress = "<unknown>" 49 ) 50 51 // NewServer creates a new raw TCP server. 52 func NewServer(address string, aggregator aggregator.Aggregator, opts Options) xserver.Server { 53 iOpts := opts.InstrumentOptions() 54 handlerScope := iOpts.MetricsScope().Tagged(map[string]string{"handler": "rawtcp"}) 55 handler := NewHandler(aggregator, opts.SetInstrumentOptions(iOpts.SetMetricsScope(handlerScope))) 56 return xserver.NewServer(address, handler, opts.ServerOptions()) 57 } 58 59 type handlerMetrics struct { 60 unknownMessageTypeErrors tally.Counter 61 addUntimedErrors tally.Counter 62 addTimedErrors tally.Counter 63 addForwardedErrors tally.Counter 64 addPassthroughErrors tally.Counter 65 unknownErrorTypeErrors tally.Counter 66 decodeErrors tally.Counter 67 errLogRateLimited tally.Counter 68 } 69 70 func newHandlerMetrics(scope tally.Scope) handlerMetrics { 71 return handlerMetrics{ 72 unknownMessageTypeErrors: scope.Counter("unknown-message-type-errors"), 73 addUntimedErrors: scope.Counter("add-untimed-errors"), 74 addTimedErrors: scope.Counter("add-timed-errors"), 75 addForwardedErrors: scope.Counter("add-forwarded-errors"), 76 addPassthroughErrors: scope.Counter("add-passthrough-errors"), 77 unknownErrorTypeErrors: scope.Counter("unknown-error-type-errors"), 78 decodeErrors: scope.Counter("decode-errors"), 79 errLogRateLimited: scope.Counter("error-log-rate-limited"), 80 } 81 } 82 83 type handler struct { 84 sync.Mutex 85 86 aggregator aggregator.Aggregator 87 log *zap.Logger 88 readBufferSize int 89 protobufItOpts protobuf.UnaggregatedOptions 90 91 errLogRateLimiter *rate.Limiter 92 metrics handlerMetrics 93 94 opts Options 95 } 96 97 // NewHandler creates a new raw TCP handler. 98 func NewHandler(aggregator aggregator.Aggregator, opts Options) xserver.Handler { 99 iOpts := opts.InstrumentOptions() 100 var limiter *rate.Limiter 101 if rateLimit := opts.ErrorLogLimitPerSecond(); rateLimit != 0 { 102 limiter = rate.NewLimiter(rateLimit) 103 } 104 return &handler{ 105 aggregator: aggregator, 106 log: iOpts.Logger(), 107 readBufferSize: opts.ReadBufferSize(), 108 protobufItOpts: opts.ProtobufUnaggregatedIteratorOptions(), 109 errLogRateLimiter: limiter, 110 metrics: newHandlerMetrics(iOpts.MetricsScope()), 111 opts: opts, 112 } 113 } 114 115 func (s *handler) Handle(conn net.Conn) { 116 remoteAddress := unknownRemoteHostAddress 117 if remoteAddr := conn.RemoteAddr(); remoteAddr != nil { 118 remoteAddress = remoteAddr.String() 119 } 120 121 nowFn := s.opts.ClockOptions().NowFn() 122 rOpts := xio.ResettableReaderOptions{ReadBufferSize: s.readBufferSize} 123 read := s.opts.RWOptions().ResettableReaderFn()(conn, rOpts) 124 reader := bufio.NewReaderSize(read, s.readBufferSize) 125 it := protobuf.NewUnaggregatedIterator(reader, s.protobufItOpts) 126 defer it.Close() 127 128 // Iterate over the incoming metrics stream and queue up metrics. 129 var ( 130 untimedMetric unaggregated.MetricUnion 131 stagedMetadatas metadata.StagedMetadatas 132 forwardedMetric aggregated.ForwardedMetric 133 forwardMetadata metadata.ForwardMetadata 134 timedMetric aggregated.Metric 135 timedMetadata metadata.TimedMetadata 136 passthroughMetric aggregated.Metric 137 passthroughMetadata policy.StoragePolicy 138 err error 139 ) 140 for it.Next() { 141 current := it.Current() 142 switch current.Type { 143 case encoding.CounterWithMetadatasType: 144 untimedMetric = current.CounterWithMetadatas.Counter.ToUnion() 145 untimedMetric.Annotation = current.CounterWithMetadatas.Annotation 146 stagedMetadatas = current.CounterWithMetadatas.StagedMetadatas 147 err = s.aggregator.AddUntimed(untimedMetric, stagedMetadatas) 148 case encoding.BatchTimerWithMetadatasType: 149 untimedMetric = current.BatchTimerWithMetadatas.BatchTimer.ToUnion() 150 untimedMetric.Annotation = current.BatchTimerWithMetadatas.Annotation 151 stagedMetadatas = current.BatchTimerWithMetadatas.StagedMetadatas 152 err = s.aggregator.AddUntimed(untimedMetric, stagedMetadatas) 153 case encoding.GaugeWithMetadatasType: 154 untimedMetric = current.GaugeWithMetadatas.Gauge.ToUnion() 155 untimedMetric.Annotation = current.GaugeWithMetadatas.Annotation 156 stagedMetadatas = current.GaugeWithMetadatas.StagedMetadatas 157 err = s.aggregator.AddUntimed(untimedMetric, stagedMetadatas) 158 case encoding.ForwardedMetricWithMetadataType: 159 forwardedMetric = current.ForwardedMetricWithMetadata.ForwardedMetric 160 untimedMetric.Annotation = current.ForwardedMetricWithMetadata.Annotation 161 forwardMetadata = current.ForwardedMetricWithMetadata.ForwardMetadata 162 err = s.aggregator.AddForwarded(forwardedMetric, forwardMetadata) 163 case encoding.TimedMetricWithMetadataType: 164 timedMetric = current.TimedMetricWithMetadata.Metric 165 timedMetric.Annotation = current.TimedMetricWithMetadata.Annotation 166 timedMetadata = current.TimedMetricWithMetadata.TimedMetadata 167 err = s.aggregator.AddTimed(timedMetric, timedMetadata) 168 case encoding.TimedMetricWithMetadatasType: 169 timedMetric = current.TimedMetricWithMetadatas.Metric 170 timedMetric.Annotation = current.TimedMetricWithMetadatas.Annotation 171 stagedMetadatas = current.TimedMetricWithMetadatas.StagedMetadatas 172 err = s.aggregator.AddTimedWithStagedMetadatas(timedMetric, stagedMetadatas) 173 case encoding.PassthroughMetricWithMetadataType: 174 passthroughMetric = current.PassthroughMetricWithMetadata.Metric 175 passthroughMetric.Annotation = current.PassthroughMetricWithMetadata.Annotation 176 passthroughMetadata = current.PassthroughMetricWithMetadata.StoragePolicy 177 err = s.aggregator.AddPassthrough(passthroughMetric, passthroughMetadata) 178 default: 179 err = newUnknownMessageTypeError(current.Type) 180 } 181 182 if err == nil { 183 continue 184 } 185 186 // We rate limit the error log here because the error rate may scale with 187 // the metrics incoming rate and consume lots of cpu cycles. 188 if s.errLogRateLimiter != nil && !s.errLogRateLimiter.IsAllowed(1, xtime.ToUnixNano(nowFn())) { 189 s.metrics.errLogRateLimited.Inc(1) 190 continue 191 } 192 switch err.(type) { 193 case unknownMessageTypeError: 194 s.metrics.unknownMessageTypeErrors.Inc(1) 195 s.log.Error("unexpected message type", 196 zap.String("remoteAddress", remoteAddress), 197 zap.Error(err), 198 ) 199 default: 200 switch current.Type { 201 case encoding.CounterWithMetadatasType: 202 fallthrough 203 case encoding.BatchTimerWithMetadatasType: 204 fallthrough 205 case encoding.GaugeWithMetadatasType: 206 s.metrics.addUntimedErrors.Inc(1) 207 s.log.Error("error adding untimed metric", 208 zap.String("remoteAddress", remoteAddress), 209 zap.Stringer("type", untimedMetric.Type), 210 zap.Stringer("id", untimedMetric.ID), 211 zap.Any("metadatas", stagedMetadatas), 212 zap.Error(err), 213 ) 214 case encoding.ForwardedMetricWithMetadataType: 215 s.metrics.addForwardedErrors.Inc(1) 216 s.log.Error("error adding forwarded metric", 217 zap.String("remoteAddress", remoteAddress), 218 zap.Stringer("id", forwardedMetric.ID), 219 zap.Time("timestamp", time.Unix(0, forwardedMetric.TimeNanos)), 220 zap.Float64s("values", forwardedMetric.Values), 221 zap.Error(err), 222 ) 223 case encoding.TimedMetricWithMetadataType: 224 fallthrough 225 case encoding.TimedMetricWithMetadatasType: 226 s.metrics.addTimedErrors.Inc(1) 227 s.log.Error("error adding timed metric", 228 zap.String("remoteAddress", remoteAddress), 229 zap.Stringer("id", timedMetric.ID), 230 zap.Time("timestamp", time.Unix(0, timedMetric.TimeNanos)), 231 zap.Float64("value", timedMetric.Value), 232 zap.Any("metadatas", stagedMetadatas), 233 zap.Error(err), 234 ) 235 case encoding.PassthroughMetricWithMetadataType: 236 s.metrics.addPassthroughErrors.Inc(1) 237 s.log.Error("error adding passthrough metric", 238 zap.String("remoteAddress", remoteAddress), 239 zap.Stringer("id", timedMetric.ID), 240 zap.Time("timestamp", time.Unix(0, timedMetric.TimeNanos)), 241 zap.Float64("value", timedMetric.Value), 242 zap.Error(err), 243 ) 244 default: 245 // make the linter happy. 246 s.log.Error("unknown message type for error. this cannot happen") 247 } 248 } 249 } 250 251 // If there is an error during decoding, it's likely due to a broken connection 252 // and therefore we ignore the EOF error. 253 if err := it.Err(); err != nil && err != io.EOF { 254 s.log.Error("decode error", 255 zap.String("remoteAddress", remoteAddress), 256 zap.Error(err), 257 ) 258 s.metrics.decodeErrors.Inc(1) 259 } 260 } 261 262 func (s *handler) Close() { 263 // NB(cw) Do not close s.aggregator here because it's shared between 264 // the raw TCP server and the http server, and it will be closed on 265 // exit signal. 266 } 267 268 type unknownMessageTypeError struct { 269 msgType encoding.UnaggregatedMessageType 270 } 271 272 func newUnknownMessageTypeError( 273 msgType encoding.UnaggregatedMessageType, 274 ) unknownMessageTypeError { 275 return unknownMessageTypeError{msgType: msgType} 276 } 277 278 func (e unknownMessageTypeError) Error() string { 279 return fmt.Sprintf("unknown message type %v", e.msgType) 280 }