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 }