github.com/suchongming/fabric@v2.1.1+incompatible/core/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  	"os"
    15  	"strings"
    16  	"time"
    17  
    18  	kitstatsd "github.com/go-kit/kit/metrics/statsd"
    19  	"github.com/hyperledger/fabric-lib-go/healthz"
    20  	"github.com/hyperledger/fabric/common/flogging"
    21  	"github.com/hyperledger/fabric/common/flogging/httpadmin"
    22  	"github.com/hyperledger/fabric/common/metadata"
    23  	"github.com/hyperledger/fabric/common/metrics"
    24  	"github.com/hyperledger/fabric/common/metrics/disabled"
    25  	"github.com/hyperledger/fabric/common/metrics/prometheus"
    26  	"github.com/hyperledger/fabric/common/metrics/statsd"
    27  	"github.com/hyperledger/fabric/common/metrics/statsd/goruntime"
    28  	"github.com/hyperledger/fabric/common/util"
    29  	"github.com/hyperledger/fabric/core/middleware"
    30  	"github.com/prometheus/client_golang/prometheus/promhttp"
    31  )
    32  
    33  //go:generate counterfeiter -o fakes/logger.go -fake-name Logger . Logger
    34  
    35  type Logger interface {
    36  	Warn(args ...interface{})
    37  	Warnf(template string, args ...interface{})
    38  }
    39  
    40  type Statsd struct {
    41  	Network       string
    42  	Address       string
    43  	WriteInterval time.Duration
    44  	Prefix        string
    45  }
    46  
    47  type MetricsOptions struct {
    48  	Provider string
    49  	Statsd   *Statsd
    50  }
    51  
    52  type Options struct {
    53  	Logger        Logger
    54  	ListenAddress string
    55  	Metrics       MetricsOptions
    56  	TLS           TLS
    57  	Version       string
    58  }
    59  
    60  type System struct {
    61  	metrics.Provider
    62  
    63  	logger          Logger
    64  	healthHandler   *healthz.HealthHandler
    65  	options         Options
    66  	statsd          *kitstatsd.Statsd
    67  	collectorTicker *time.Ticker
    68  	sendTicker      *time.Ticker
    69  	httpServer      *http.Server
    70  	mux             *http.ServeMux
    71  	addr            string
    72  	versionGauge    metrics.Gauge
    73  }
    74  
    75  func NewSystem(o Options) *System {
    76  	logger := o.Logger
    77  	if logger == nil {
    78  		logger = flogging.MustGetLogger("operations.runner")
    79  	}
    80  
    81  	system := &System{
    82  		logger:  logger,
    83  		options: o,
    84  	}
    85  
    86  	system.initializeServer()
    87  	system.initializeHealthCheckHandler()
    88  	system.initializeLoggingHandler()
    89  	system.initializeMetricsProvider()
    90  	system.initializeVersionInfoHandler()
    91  
    92  	return system
    93  }
    94  
    95  func (s *System) Run(signals <-chan os.Signal, ready chan<- struct{}) error {
    96  	err := s.Start()
    97  	if err != nil {
    98  		return err
    99  	}
   100  
   101  	close(ready)
   102  
   103  	select {
   104  	case <-signals:
   105  		return s.Stop()
   106  	}
   107  }
   108  
   109  func (s *System) Start() error {
   110  	err := s.startMetricsTickers()
   111  	if err != nil {
   112  		return err
   113  	}
   114  
   115  	s.versionGauge.With("version", s.options.Version).Set(1)
   116  
   117  	listener, err := s.listen()
   118  	if err != nil {
   119  		return err
   120  	}
   121  	s.addr = listener.Addr().String()
   122  
   123  	go s.httpServer.Serve(listener)
   124  
   125  	return nil
   126  }
   127  
   128  func (s *System) Stop() error {
   129  	if s.collectorTicker != nil {
   130  		s.collectorTicker.Stop()
   131  		s.collectorTicker = nil
   132  	}
   133  	if s.sendTicker != nil {
   134  		s.sendTicker.Stop()
   135  		s.sendTicker = nil
   136  	}
   137  	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
   138  	defer cancel()
   139  
   140  	return s.httpServer.Shutdown(ctx)
   141  }
   142  
   143  func (s *System) RegisterChecker(component string, checker healthz.HealthChecker) error {
   144  	return s.healthHandler.RegisterChecker(component, checker)
   145  }
   146  
   147  func (s *System) initializeServer() {
   148  	s.mux = http.NewServeMux()
   149  	s.httpServer = &http.Server{
   150  		Addr:         s.options.ListenAddress,
   151  		Handler:      s.mux,
   152  		ReadTimeout:  10 * time.Second,
   153  		WriteTimeout: 2 * time.Minute,
   154  	}
   155  }
   156  
   157  func (s *System) handlerChain(h http.Handler, secure bool) http.Handler {
   158  	if secure {
   159  		return middleware.NewChain(middleware.RequireCert(), middleware.WithRequestID(util.GenerateUUID)).Handler(h)
   160  	}
   161  	return middleware.NewChain(middleware.WithRequestID(util.GenerateUUID)).Handler(h)
   162  }
   163  
   164  func (s *System) initializeMetricsProvider() error {
   165  	m := s.options.Metrics
   166  	providerType := m.Provider
   167  	switch providerType {
   168  	case "statsd":
   169  		prefix := m.Statsd.Prefix
   170  		if prefix != "" && !strings.HasSuffix(prefix, ".") {
   171  			prefix = prefix + "."
   172  		}
   173  
   174  		ks := kitstatsd.New(prefix, s)
   175  		s.Provider = &statsd.Provider{Statsd: ks}
   176  		s.statsd = ks
   177  		s.versionGauge = versionGauge(s.Provider)
   178  		return nil
   179  
   180  	case "prometheus":
   181  		s.Provider = &prometheus.Provider{}
   182  		s.versionGauge = versionGauge(s.Provider)
   183  		s.mux.Handle("/metrics", s.handlerChain(promhttp.Handler(), s.options.TLS.Enabled))
   184  		return nil
   185  
   186  	default:
   187  		if providerType != "disabled" {
   188  			s.logger.Warnf("Unknown provider type: %s; metrics disabled", providerType)
   189  		}
   190  
   191  		s.Provider = &disabled.Provider{}
   192  		s.versionGauge = versionGauge(s.Provider)
   193  		return nil
   194  	}
   195  }
   196  
   197  func (s *System) initializeLoggingHandler() {
   198  	s.mux.Handle("/logspec", s.handlerChain(httpadmin.NewSpecHandler(), s.options.TLS.Enabled))
   199  }
   200  
   201  func (s *System) initializeHealthCheckHandler() {
   202  	s.healthHandler = healthz.NewHealthHandler()
   203  	s.mux.Handle("/healthz", s.handlerChain(s.healthHandler, false))
   204  }
   205  
   206  func (s *System) initializeVersionInfoHandler() {
   207  	versionInfo := &VersionInfoHandler{
   208  		CommitSHA: metadata.CommitSHA,
   209  		Version:   metadata.Version,
   210  	}
   211  	s.mux.Handle("/version", s.handlerChain(versionInfo, false))
   212  }
   213  
   214  func (s *System) startMetricsTickers() error {
   215  	m := s.options.Metrics
   216  	if s.statsd != nil {
   217  		network := m.Statsd.Network
   218  		address := m.Statsd.Address
   219  		c, err := net.Dial(network, address)
   220  		if err != nil {
   221  			return err
   222  		}
   223  		c.Close()
   224  
   225  		opts := s.options.Metrics.Statsd
   226  		writeInterval := opts.WriteInterval
   227  
   228  		s.collectorTicker = time.NewTicker(writeInterval / 2)
   229  		goCollector := goruntime.NewCollector(s.Provider)
   230  		go goCollector.CollectAndPublish(s.collectorTicker.C)
   231  
   232  		s.sendTicker = time.NewTicker(writeInterval)
   233  		go s.statsd.SendLoop(s.sendTicker.C, network, address)
   234  	}
   235  
   236  	return nil
   237  }
   238  
   239  func (s *System) listen() (net.Listener, error) {
   240  	listener, err := net.Listen("tcp", s.options.ListenAddress)
   241  	if err != nil {
   242  		return nil, err
   243  	}
   244  	tlsConfig, err := s.options.TLS.Config()
   245  	if err != nil {
   246  		return nil, err
   247  	}
   248  	if tlsConfig != nil {
   249  		listener = tls.NewListener(listener, tlsConfig)
   250  	}
   251  	return listener, nil
   252  }
   253  
   254  func (s *System) Addr() string {
   255  	return s.addr
   256  }
   257  
   258  func (s *System) Log(keyvals ...interface{}) error {
   259  	s.logger.Warn(keyvals...)
   260  	return nil
   261  }