github.com/m3db/m3@v1.5.0/src/aggregator/server/server.go (about) 1 // Copyright (c) 2020 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 server 22 23 import ( 24 "log" 25 "net/http" 26 "os" 27 "os/signal" 28 "syscall" 29 "time" 30 31 m3aggregator "github.com/m3db/m3/src/aggregator/aggregator" 32 "github.com/m3db/m3/src/cmd/services/m3aggregator/config" 33 "github.com/m3db/m3/src/cmd/services/m3aggregator/serve" 34 "github.com/m3db/m3/src/x/clock" 35 xconfig "github.com/m3db/m3/src/x/config" 36 "github.com/m3db/m3/src/x/instrument" 37 xos "github.com/m3db/m3/src/x/os" 38 39 "go.uber.org/zap" 40 ) 41 42 const ( 43 gracefulShutdownTimeout = 15 * time.Second 44 ) 45 46 // RunOptions are the server options for running the aggregator server. 47 type RunOptions struct { 48 // Config is the aggregator configuration. 49 Config config.Configuration 50 51 // AdminOptions are additional options to apply to the aggregator server. 52 AdminOptions []AdminOption 53 54 // CustomBuildTags are additional tags to be added to the instrument build 55 // reporter. 56 CustomBuildTags map[string]string 57 58 // InterruptCh is a programmatic interrupt channel to supply to 59 // interrupt and shutdown the server. 60 InterruptCh <-chan error 61 62 // ShutdownCh is an optional channel to supply if interested in receiving 63 // a notification that the server has shutdown. 64 ShutdownCh chan<- struct{} 65 } 66 67 // AdminOption is an additional option to apply to the aggregator server. 68 type AdminOption func(opts serve.Options) (serve.Options, error) 69 70 // Run runs the aggregator server. 71 func Run(opts RunOptions) { 72 cfg := opts.Config 73 74 // Create logger and metrics scope. 75 logger, err := cfg.LoggingOrDefault().BuildLogger() 76 if err != nil { 77 log.Fatalf("error creating logger: %v", err) 78 } 79 80 // NB(nate): Register shutdown notification defer function first so that 81 // it's the last defer to fire before terminating. This allows other defer methods 82 // that clean up resources to execute first. 83 if opts.ShutdownCh != nil { 84 defer func() { 85 select { 86 case opts.ShutdownCh <- struct{}{}: 87 break 88 default: 89 logger.Warn("could not send shutdown notification as channel was full") 90 } 91 }() 92 } 93 94 defer logger.Sync() 95 96 cfg.Debug.SetRuntimeValues(logger) 97 98 xconfig.WarnOnDeprecation(cfg, logger) 99 100 defaultServeMux := http.NewServeMux() 101 metricsCfg := cfg.MetricsOrDefault() 102 scope, closer, _, err := metricsCfg.NewRootScopeAndReporters( 103 instrument.NewRootScopeAndReportersOptions{ 104 PrometheusDefaultServeMux: defaultServeMux, 105 }) 106 if err != nil { 107 logger.Fatal("error creating metrics root scope", zap.Error(err)) 108 } 109 defer closer.Close() 110 instrumentOpts := instrument.NewOptions(). 111 SetLogger(logger). 112 SetMetricsScope(scope). 113 SetTimerOptions(instrument.TimerOptions{StandardSampleRate: metricsCfg.SampleRate()}). 114 SetReportInterval(metricsCfg.ReportInterval()). 115 SetCustomBuildTags(opts.CustomBuildTags) 116 117 buildReporter := instrument.NewBuildReporter(instrumentOpts) 118 if err := buildReporter.Start(); err != nil { 119 logger.Fatal("could not start build reporter", zap.Error(err)) 120 } 121 122 defer buildReporter.Stop() 123 124 if cfg.M3Msg == nil && cfg.RawTCP == nil { 125 m3MsgCfg := cfg.M3MsgOrDefault() 126 cfg.M3Msg = &m3MsgCfg 127 } 128 129 serverOptions := serve.NewOptions(instrumentOpts) 130 if cfg.M3Msg != nil { 131 // Create the M3Msg server options. 132 m3msgInsrumentOpts := instrumentOpts. 133 SetMetricsScope(scope. 134 SubScope("m3msg-server"). 135 Tagged(map[string]string{"server": "m3msg"})) 136 m3msgServerOpts, err := cfg.M3Msg.NewServerOptions(m3msgInsrumentOpts) 137 if err != nil { 138 logger.Fatal("could not create m3msg server options", zap.Error(err)) 139 } 140 141 serverOptions = serverOptions. 142 SetM3MsgAddr(cfg.M3Msg.Server.ListenAddress). 143 SetM3MsgServerOpts(m3msgServerOpts) 144 } 145 146 if cfg.RawTCP != nil { 147 // Create the raw TCP server options. 148 rawTCPInstrumentOpts := instrumentOpts. 149 SetMetricsScope(scope. 150 SubScope("rawtcp-server"). 151 Tagged(map[string]string{"server": "rawtcp"})) 152 153 serverOptions = serverOptions. 154 SetRawTCPAddr(cfg.RawTCP.ListenAddress). 155 SetRawTCPServerOpts(cfg.RawTCP.NewServerOptions(rawTCPInstrumentOpts)) 156 } 157 158 // Create the http server options. 159 httpCfg := cfg.HTTPOrDefault() 160 serverOptions = serverOptions. 161 SetHTTPAddr(httpCfg.ListenAddress). 162 SetHTTPServerOpts(httpCfg.NewServerOptions().SetMux(defaultServeMux)) 163 164 for i, transform := range opts.AdminOptions { 165 if opts, err := transform(serverOptions); err != nil { 166 logger.Fatal("could not apply transform", 167 zap.Int("index", i), zap.Error(err)) 168 } else { 169 serverOptions = opts 170 } 171 } 172 173 // Create the kv client. 174 kvCfg := cfg.KVClientOrDefault() 175 client, err := kvCfg.NewKVClient(instrumentOpts. 176 SetMetricsScope(scope.SubScope("kv-client"))) 177 if err != nil { 178 logger.Fatal("error creating the kv client", zap.Error(err)) 179 } 180 181 // Create the runtime options manager. 182 runtimeCfg := cfg.RuntimeOptionsOrDefault() 183 runtimeOptsManager := runtimeCfg.NewRuntimeOptionsManager() 184 185 // Create the aggregator. 186 aggCfg := cfg.AggregatorOrDefault() 187 aggregatorOpts, err := aggCfg.NewAggregatorOptions( 188 serverOptions.RawTCPAddr(), 189 client, serverOptions, runtimeOptsManager, clock.NewOptions(), 190 instrumentOpts.SetMetricsScope(scope.SubScope("aggregator"))) 191 if err != nil { 192 logger.Fatal("error creating aggregator options", zap.Error(err)) 193 } 194 aggregator := m3aggregator.NewAggregator(aggregatorOpts) 195 if err := aggregator.Open(); err != nil { 196 logger.Fatal("error opening the aggregator", zap.Error(err)) 197 } 198 199 // Watch runtime option changes after aggregator is open. 200 placementManager := aggregatorOpts.PlacementManager() 201 runtimeCfg.WatchRuntimeOptionChanges(client, runtimeOptsManager, placementManager, logger) 202 203 doneCh := make(chan struct{}) 204 closedCh := make(chan struct{}) 205 go func() { 206 if err := serve.Serve( 207 aggregator, 208 doneCh, 209 serverOptions, 210 ); err != nil { 211 logger.Fatal("could not start serving traffic", zap.Error(err)) 212 } 213 logger.Debug("server closed") 214 close(closedCh) 215 }() 216 217 // Handle interrupts. 218 xos.WaitForInterrupt(logger, xos.InterruptOptions{ 219 InterruptCh: opts.InterruptCh, 220 }) 221 222 if s := aggCfg.ShutdownWaitTimeout; s != 0 { 223 sigC := make(chan os.Signal, 1) 224 signal.Notify(sigC, syscall.SIGINT, syscall.SIGTERM) 225 226 logger.Info("waiting intentional shutdown period", zap.Duration("waitTimeout", s)) 227 select { 228 case sig := <-sigC: 229 logger.Info("second signal received, skipping shutdown wait", zap.String("signal", sig.String())) 230 case <-time.After(aggCfg.ShutdownWaitTimeout): 231 logger.Info("shutdown period elapsed") 232 } 233 } 234 235 close(doneCh) 236 237 select { 238 case <-closedCh: 239 logger.Info("server closed clean") 240 case <-time.After(gracefulShutdownTimeout): 241 logger.Info("server closed due to timeout", zap.Duration("timeout", gracefulShutdownTimeout)) 242 scope.SubScope("aggregator-server-close").Counter("timeout").Inc(1) 243 } 244 }