gitee.com/zhaochuninhefei/gmgo@v0.0.31-0.20240209061119-069254a02979/grpc/profiling/service/service.go (about)

     1  /*
     2   *
     3   * Copyright 2019 gRPC authors.
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   *
    17   */
    18  
    19  // Package service defines methods to register a gRPC client/service for a
    20  // profiling service that is exposed in the same server. This service can be
    21  // queried by a client to remotely manage the gRPC profiling behaviour of an
    22  // application.
    23  //
    24  // Experimental
    25  //
    26  // Notice: This package is EXPERIMENTAL and may be changed or removed in a
    27  // later release.
    28  package service
    29  
    30  import (
    31  	"context"
    32  	"errors"
    33  	"sync"
    34  
    35  	"gitee.com/zhaochuninhefei/gmgo/grpc"
    36  	"gitee.com/zhaochuninhefei/gmgo/grpc/grpclog"
    37  	"gitee.com/zhaochuninhefei/gmgo/grpc/internal/profiling"
    38  	ppb "gitee.com/zhaochuninhefei/gmgo/grpc/profiling/proto"
    39  )
    40  
    41  var logger = grpclog.Component("profiling")
    42  
    43  // ProfilingConfig defines configuration options for the Init method.
    44  type ProfilingConfig struct {
    45  	// Setting this to true will enable profiling.
    46  	Enabled bool
    47  
    48  	// Profiling uses a circular buffer (ring buffer) to store statistics for
    49  	// only the last few RPCs so that profiling stats do not grow unbounded. This
    50  	// parameter defines the upper limit on the number of RPCs for which
    51  	// statistics should be stored at any given time. An average RPC requires
    52  	// approximately 2-3 KiB of memory for profiling-related statistics, so
    53  	// choose an appropriate number based on the amount of memory you can afford.
    54  	StreamStatsSize uint32
    55  
    56  	// To expose the profiling service and its methods, a *grpc.Server must be
    57  	// provided.
    58  	Server *grpc.Server
    59  }
    60  
    61  var errorNilServer = errors.New("profiling: no grpc.Server provided")
    62  
    63  // Init takes a *ProfilingConfig to initialize profiling (turned on/off
    64  // depending on the value set in pc.Enabled) and register the profiling service
    65  // in the server provided in pc.Server.
    66  //goland:noinspection GoUnusedExportedFunction
    67  func Init(pc *ProfilingConfig) error {
    68  	if pc.Server == nil {
    69  		return errorNilServer
    70  	}
    71  
    72  	if err := profiling.InitStats(pc.StreamStatsSize); err != nil {
    73  		return err
    74  	}
    75  
    76  	ppb.RegisterProfilingServer(pc.Server, getProfilingServerInstance())
    77  
    78  	// Do this last after everything has been initialized and allocated.
    79  	profiling.Enable(pc.Enabled)
    80  
    81  	return nil
    82  }
    83  
    84  type profilingServer struct {
    85  	ppb.UnimplementedProfilingServer
    86  	drainMutex sync.Mutex
    87  }
    88  
    89  var profilingServerInstance *profilingServer
    90  var profilingServerOnce sync.Once
    91  
    92  // getProfilingServerInstance creates and returns a singleton instance of
    93  // profilingServer. Only one instance of profilingServer is created to use a
    94  // shared mutex across all profilingServer instances.
    95  func getProfilingServerInstance() *profilingServer {
    96  	profilingServerOnce.Do(func() {
    97  		profilingServerInstance = &profilingServer{}
    98  	})
    99  
   100  	return profilingServerInstance
   101  }
   102  
   103  //goland:noinspection GoUnusedParameter
   104  func (s *profilingServer) Enable(ctx context.Context, req *ppb.EnableRequest) (*ppb.EnableResponse, error) {
   105  	if req.Enabled {
   106  		logger.Infof("profilingServer: Enable: enabling profiling")
   107  	} else {
   108  		logger.Infof("profilingServer: Enable: disabling profiling")
   109  	}
   110  	profiling.Enable(req.Enabled)
   111  
   112  	return &ppb.EnableResponse{}, nil
   113  }
   114  
   115  func timerToProtoTimer(timer *profiling.Timer) *ppb.Timer {
   116  	return &ppb.Timer{
   117  		Tags:      timer.Tags,
   118  		BeginSec:  timer.Begin.Unix(),
   119  		BeginNsec: int32(timer.Begin.Nanosecond()),
   120  		EndSec:    timer.End.Unix(),
   121  		EndNsec:   int32(timer.End.Nanosecond()),
   122  		GoId:      timer.GoID,
   123  	}
   124  }
   125  
   126  func statToProtoStat(stat *profiling.Stat) *ppb.Stat {
   127  	protoStat := &ppb.Stat{
   128  		Tags:     stat.Tags,
   129  		Timers:   make([]*ppb.Timer, 0, len(stat.Timers)),
   130  		Metadata: stat.Metadata,
   131  	}
   132  	for _, t := range stat.Timers {
   133  		protoStat.Timers = append(protoStat.Timers, timerToProtoTimer(t))
   134  	}
   135  	return protoStat
   136  }
   137  
   138  //goland:noinspection GoUnusedParameter
   139  func (s *profilingServer) GetStreamStats(ctx context.Context, req *ppb.GetStreamStatsRequest) (*ppb.GetStreamStatsResponse, error) {
   140  	// Since the drain operation is destructive, only one client request should
   141  	// be served at a time.
   142  	logger.Infof("profilingServer: GetStreamStats: processing request")
   143  	s.drainMutex.Lock()
   144  	results := profiling.StreamStats.Drain()
   145  	s.drainMutex.Unlock()
   146  
   147  	logger.Infof("profilingServer: GetStreamStats: returning %v records", len(results))
   148  	streamStats := make([]*ppb.Stat, 0)
   149  	for _, stat := range results {
   150  		streamStats = append(streamStats, statToProtoStat(stat.(*profiling.Stat)))
   151  	}
   152  	return &ppb.GetStreamStatsResponse{StreamStats: streamStats}, nil
   153  }