github.com/canhui/fabric_ca2_2@v2.0.0-alpha+incompatible/lib/server/operations/system.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package operations 8 9 import ( 10 "context" 11 "crypto/tls" 12 "net" 13 "net/http" 14 "strings" 15 "time" 16 17 "github.com/cloudflare/cfssl/log" 18 kitstatsd "github.com/go-kit/kit/metrics/statsd" 19 "github.com/gorilla/mux" 20 "github.com/hyperledger/fabric-lib-go/healthz" 21 "github.com/hyperledger/fabric/common/metrics" 22 "github.com/hyperledger/fabric/common/metrics/disabled" 23 "github.com/hyperledger/fabric/common/metrics/prometheus" 24 "github.com/hyperledger/fabric/common/metrics/statsd" 25 prom "github.com/prometheus/client_golang/prometheus" 26 ) 27 28 // System is an operations server that is responsible for metrics and health checks 29 type System struct { 30 metrics.Provider 31 healthHandler *healthz.HealthHandler 32 33 options Options 34 statsd *kitstatsd.Statsd 35 sendTicker *time.Ticker 36 httpServer *http.Server 37 mux *mux.Router 38 addr string 39 } 40 41 // Options contains configuration for the operations system 42 type Options struct { 43 ListenAddress string 44 Metrics MetricsOptions 45 TLS TLS 46 } 47 48 // MetricsOptions contains the information on providers 49 type MetricsOptions struct { 50 Provider string 51 Statsd *Statsd 52 } 53 54 // Statsd contains configuration of statsd 55 type Statsd struct { 56 Network string 57 Address string 58 WriteInterval time.Duration 59 Prefix string 60 } 61 62 // NewSystem creates a System struct 63 func NewSystem(o Options) *System { 64 system := &System{ 65 options: o, 66 } 67 68 system.initializeServer() 69 system.initializeHealthCheckHandler() 70 system.initializeMetricsProvider() 71 72 return system 73 } 74 75 // Start starts the operations system server 76 func (s *System) Start() error { 77 err := s.startMetricsTickers() 78 if err != nil { 79 return err 80 } 81 82 listener, err := s.listen() 83 if err != nil { 84 return err 85 } 86 s.addr = listener.Addr().String() 87 88 log.Infof("Operation Server Listening on %s", listener.Addr()) 89 go s.httpServer.Serve(listener) 90 91 return nil 92 } 93 94 // Stop stop the operations system server 95 func (s *System) Stop() error { 96 if s.sendTicker != nil { 97 s.sendTicker.Stop() 98 s.sendTicker = nil 99 } 100 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 101 defer cancel() 102 103 return s.httpServer.Shutdown(ctx) 104 } 105 106 func (s *System) initializeServer() { 107 s.mux = mux.NewRouter() 108 s.httpServer = &http.Server{ 109 Addr: s.options.ListenAddress, 110 Handler: s.mux, 111 ReadTimeout: 10 * time.Second, 112 WriteTimeout: 2 * time.Minute, 113 } 114 } 115 116 func (s *System) initializeMetricsProvider() { 117 m := s.options.Metrics 118 providerType := m.Provider 119 switch providerType { 120 case "statsd": 121 prefix := m.Statsd.Prefix 122 if prefix != "" && !strings.HasSuffix(prefix, ".") { 123 prefix = prefix + "." 124 } 125 126 ks := kitstatsd.New(prefix, s) 127 s.Provider = &statsd.Provider{Statsd: ks} 128 s.statsd = ks 129 130 case "prometheus": 131 s.Provider = &prometheus.Provider{} 132 s.mux.Handle("/metrics", prom.Handler()) 133 134 default: 135 if providerType != "disabled" { 136 log.Warningf("Unknown provider type: %s; metrics disabled", providerType) 137 } 138 139 s.Provider = &disabled.Provider{} 140 } 141 } 142 143 func (s *System) initializeHealthCheckHandler() { 144 s.healthHandler = healthz.NewHealthHandler() 145 s.mux.Handle("/healthz", s.healthHandler) 146 } 147 148 func (s *System) startMetricsTickers() error { 149 m := s.options.Metrics 150 if s.statsd != nil { 151 network := m.Statsd.Network 152 address := m.Statsd.Address 153 c, err := net.Dial(network, address) 154 if err != nil { 155 return err 156 } 157 c.Close() 158 159 writeInterval := s.options.Metrics.Statsd.WriteInterval 160 161 s.sendTicker = time.NewTicker(writeInterval) 162 go s.statsd.SendLoop(s.sendTicker.C, network, address) 163 } 164 165 return nil 166 } 167 168 // Log is a function required to meet the interface required by statsd 169 func (s *System) Log(keyvals ...interface{}) error { 170 log.Warning(keyvals...) 171 return nil 172 } 173 174 // RegisterChecker registers the HealthCheck with Healthchecker server 175 func (s *System) RegisterChecker(component string, checker healthz.HealthChecker) error { 176 return s.healthHandler.RegisterChecker(component, checker) 177 } 178 179 func (s *System) listen() (net.Listener, error) { 180 listener, err := net.Listen("tcp", s.options.ListenAddress) 181 if err != nil { 182 return nil, err 183 } 184 tlsConfig, err := s.options.TLS.Config() 185 if err != nil { 186 return nil, err 187 } 188 if tlsConfig != nil { 189 listener = tls.NewListener(listener, tlsConfig) 190 } 191 return listener, nil 192 } 193 194 // Addr returns the address of the listener 195 func (s *System) Addr() string { 196 return s.addr 197 }