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  }