blob: cd1807543eee34a97c05d3565066d410b2fd0c3a [file] [log] [blame]
khenaidoo5fc5cea2021-08-11 17:39:16 -04001/*
2 *
3 * Copyright 2018 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 channelz defines APIs for enabling channelz service, entry
20// registration/deletion, and accessing channelz data. It also defines channelz
21// metric struct formats.
22//
23// All APIs in this package are experimental.
24package channelz
25
26import (
27 "fmt"
28 "sort"
29 "sync"
30 "sync/atomic"
31 "time"
32
33 "google.golang.org/grpc/grpclog"
34)
35
36const (
37 defaultMaxTraceEntry int32 = 30
38)
39
40var (
41 db dbWrapper
42 idGen idGenerator
43 // EntryPerPage defines the number of channelz entries to be shown on a web page.
44 EntryPerPage = int64(50)
45 curState int32
46 maxTraceEntry = defaultMaxTraceEntry
47)
48
49// TurnOn turns on channelz data collection.
50func TurnOn() {
51 if !IsOn() {
52 NewChannelzStorage()
53 atomic.StoreInt32(&curState, 1)
54 }
55}
56
57// IsOn returns whether channelz data collection is on.
58func IsOn() bool {
59 return atomic.CompareAndSwapInt32(&curState, 1, 1)
60}
61
62// SetMaxTraceEntry sets maximum number of trace entry per entity (i.e. channel/subchannel).
63// Setting it to 0 will disable channel tracing.
64func SetMaxTraceEntry(i int32) {
65 atomic.StoreInt32(&maxTraceEntry, i)
66}
67
68// ResetMaxTraceEntryToDefault resets the maximum number of trace entry per entity to default.
69func ResetMaxTraceEntryToDefault() {
70 atomic.StoreInt32(&maxTraceEntry, defaultMaxTraceEntry)
71}
72
73func getMaxTraceEntry() int {
74 i := atomic.LoadInt32(&maxTraceEntry)
75 return int(i)
76}
77
78// dbWarpper wraps around a reference to internal channelz data storage, and
79// provide synchronized functionality to set and get the reference.
80type dbWrapper struct {
81 mu sync.RWMutex
82 DB *channelMap
83}
84
85func (d *dbWrapper) set(db *channelMap) {
86 d.mu.Lock()
87 d.DB = db
88 d.mu.Unlock()
89}
90
91func (d *dbWrapper) get() *channelMap {
92 d.mu.RLock()
93 defer d.mu.RUnlock()
94 return d.DB
95}
96
97// NewChannelzStorage initializes channelz data storage and id generator.
98//
99// This function returns a cleanup function to wait for all channelz state to be reset by the
100// grpc goroutines when those entities get closed. By using this cleanup function, we make sure tests
101// don't mess up each other, i.e. lingering goroutine from previous test doing entity removal happen
102// to remove some entity just register by the new test, since the id space is the same.
103//
104// Note: This function is exported for testing purpose only. User should not call
105// it in most cases.
106func NewChannelzStorage() (cleanup func() error) {
107 db.set(&channelMap{
108 topLevelChannels: make(map[int64]struct{}),
109 channels: make(map[int64]*channel),
110 listenSockets: make(map[int64]*listenSocket),
111 normalSockets: make(map[int64]*normalSocket),
112 servers: make(map[int64]*server),
113 subChannels: make(map[int64]*subChannel),
114 })
115 idGen.reset()
116 return func() error {
117 var err error
118 cm := db.get()
119 if cm == nil {
120 return nil
121 }
122 for i := 0; i < 1000; i++ {
123 cm.mu.Lock()
124 if len(cm.topLevelChannels) == 0 && len(cm.servers) == 0 && len(cm.channels) == 0 && len(cm.subChannels) == 0 && len(cm.listenSockets) == 0 && len(cm.normalSockets) == 0 {
125 cm.mu.Unlock()
126 // all things stored in the channelz map have been cleared.
127 return nil
128 }
129 cm.mu.Unlock()
130 time.Sleep(10 * time.Millisecond)
131 }
132
133 cm.mu.Lock()
134 err = fmt.Errorf("after 10s the channelz map has not been cleaned up yet, topchannels: %d, servers: %d, channels: %d, subchannels: %d, listen sockets: %d, normal sockets: %d", len(cm.topLevelChannels), len(cm.servers), len(cm.channels), len(cm.subChannels), len(cm.listenSockets), len(cm.normalSockets))
135 cm.mu.Unlock()
136 return err
137 }
138}
139
140// GetTopChannels returns a slice of top channel's ChannelMetric, along with a
141// boolean indicating whether there's more top channels to be queried for.
142//
143// The arg id specifies that only top channel with id at or above it will be included
144// in the result. The returned slice is up to a length of the arg maxResults or
145// EntryPerPage if maxResults is zero, and is sorted in ascending id order.
146func GetTopChannels(id int64, maxResults int64) ([]*ChannelMetric, bool) {
147 return db.get().GetTopChannels(id, maxResults)
148}
149
150// GetServers returns a slice of server's ServerMetric, along with a
151// boolean indicating whether there's more servers to be queried for.
152//
153// The arg id specifies that only server with id at or above it will be included
154// in the result. The returned slice is up to a length of the arg maxResults or
155// EntryPerPage if maxResults is zero, and is sorted in ascending id order.
156func GetServers(id int64, maxResults int64) ([]*ServerMetric, bool) {
157 return db.get().GetServers(id, maxResults)
158}
159
160// GetServerSockets returns a slice of server's (identified by id) normal socket's
161// SocketMetric, along with a boolean indicating whether there's more sockets to
162// be queried for.
163//
164// The arg startID specifies that only sockets with id at or above it will be
165// included in the result. The returned slice is up to a length of the arg maxResults
166// or EntryPerPage if maxResults is zero, and is sorted in ascending id order.
167func GetServerSockets(id int64, startID int64, maxResults int64) ([]*SocketMetric, bool) {
168 return db.get().GetServerSockets(id, startID, maxResults)
169}
170
171// GetChannel returns the ChannelMetric for the channel (identified by id).
172func GetChannel(id int64) *ChannelMetric {
173 return db.get().GetChannel(id)
174}
175
176// GetSubChannel returns the SubChannelMetric for the subchannel (identified by id).
177func GetSubChannel(id int64) *SubChannelMetric {
178 return db.get().GetSubChannel(id)
179}
180
181// GetSocket returns the SocketInternalMetric for the socket (identified by id).
182func GetSocket(id int64) *SocketMetric {
183 return db.get().GetSocket(id)
184}
185
186// GetServer returns the ServerMetric for the server (identified by id).
187func GetServer(id int64) *ServerMetric {
188 return db.get().GetServer(id)
189}
190
191// RegisterChannel registers the given channel c in channelz database with ref
192// as its reference name, and add it to the child list of its parent (identified
193// by pid). pid = 0 means no parent. It returns the unique channelz tracking id
194// assigned to this channel.
195func RegisterChannel(c Channel, pid int64, ref string) int64 {
196 id := idGen.genID()
197 cn := &channel{
198 refName: ref,
199 c: c,
200 subChans: make(map[int64]string),
201 nestedChans: make(map[int64]string),
202 id: id,
203 pid: pid,
204 trace: &channelTrace{createdTime: time.Now(), events: make([]*TraceEvent, 0, getMaxTraceEntry())},
205 }
206 if pid == 0 {
khenaidoo257f3192021-12-15 16:46:37 -0500207 db.get().addChannel(id, cn, true, pid)
khenaidoo5fc5cea2021-08-11 17:39:16 -0400208 } else {
khenaidoo257f3192021-12-15 16:46:37 -0500209 db.get().addChannel(id, cn, false, pid)
khenaidoo5fc5cea2021-08-11 17:39:16 -0400210 }
211 return id
212}
213
214// RegisterSubChannel registers the given channel c in channelz database with ref
215// as its reference name, and add it to the child list of its parent (identified
216// by pid). It returns the unique channelz tracking id assigned to this subchannel.
217func RegisterSubChannel(c Channel, pid int64, ref string) int64 {
218 if pid == 0 {
219 logger.Error("a SubChannel's parent id cannot be 0")
220 return 0
221 }
222 id := idGen.genID()
223 sc := &subChannel{
224 refName: ref,
225 c: c,
226 sockets: make(map[int64]string),
227 id: id,
228 pid: pid,
229 trace: &channelTrace{createdTime: time.Now(), events: make([]*TraceEvent, 0, getMaxTraceEntry())},
230 }
khenaidoo257f3192021-12-15 16:46:37 -0500231 db.get().addSubChannel(id, sc, pid)
khenaidoo5fc5cea2021-08-11 17:39:16 -0400232 return id
233}
234
235// RegisterServer registers the given server s in channelz database. It returns
236// the unique channelz tracking id assigned to this server.
237func RegisterServer(s Server, ref string) int64 {
238 id := idGen.genID()
239 svr := &server{
240 refName: ref,
241 s: s,
242 sockets: make(map[int64]string),
243 listenSockets: make(map[int64]string),
244 id: id,
245 }
246 db.get().addServer(id, svr)
247 return id
248}
249
250// RegisterListenSocket registers the given listen socket s in channelz database
251// with ref as its reference name, and add it to the child list of its parent
252// (identified by pid). It returns the unique channelz tracking id assigned to
253// this listen socket.
254func RegisterListenSocket(s Socket, pid int64, ref string) int64 {
255 if pid == 0 {
256 logger.Error("a ListenSocket's parent id cannot be 0")
257 return 0
258 }
259 id := idGen.genID()
260 ls := &listenSocket{refName: ref, s: s, id: id, pid: pid}
khenaidoo257f3192021-12-15 16:46:37 -0500261 db.get().addListenSocket(id, ls, pid)
khenaidoo5fc5cea2021-08-11 17:39:16 -0400262 return id
263}
264
265// RegisterNormalSocket registers the given normal socket s in channelz database
266// with ref as its reference name, and add it to the child list of its parent
267// (identified by pid). It returns the unique channelz tracking id assigned to
268// this normal socket.
269func RegisterNormalSocket(s Socket, pid int64, ref string) int64 {
270 if pid == 0 {
271 logger.Error("a NormalSocket's parent id cannot be 0")
272 return 0
273 }
274 id := idGen.genID()
275 ns := &normalSocket{refName: ref, s: s, id: id, pid: pid}
khenaidoo257f3192021-12-15 16:46:37 -0500276 db.get().addNormalSocket(id, ns, pid)
khenaidoo5fc5cea2021-08-11 17:39:16 -0400277 return id
278}
279
khenaidoo257f3192021-12-15 16:46:37 -0500280// RemoveEntry removes an entry with unique channelz tracking id to be id from
khenaidoo5fc5cea2021-08-11 17:39:16 -0400281// channelz database.
282func RemoveEntry(id int64) {
283 db.get().removeEntry(id)
284}
285
286// TraceEventDesc is what the caller of AddTraceEvent should provide to describe the event to be added
287// to the channel trace.
288// The Parent field is optional. It is used for event that will be recorded in the entity's parent
289// trace also.
290type TraceEventDesc struct {
291 Desc string
292 Severity Severity
293 Parent *TraceEventDesc
294}
295
296// AddTraceEvent adds trace related to the entity with specified id, using the provided TraceEventDesc.
297func AddTraceEvent(l grpclog.DepthLoggerV2, id int64, depth int, desc *TraceEventDesc) {
298 for d := desc; d != nil; d = d.Parent {
299 switch d.Severity {
300 case CtUnknown, CtInfo:
301 l.InfoDepth(depth+1, d.Desc)
302 case CtWarning:
303 l.WarningDepth(depth+1, d.Desc)
304 case CtError:
305 l.ErrorDepth(depth+1, d.Desc)
306 }
307 }
308 if getMaxTraceEntry() == 0 {
309 return
310 }
311 db.get().traceEvent(id, desc)
312}
313
314// channelMap is the storage data structure for channelz.
315// Methods of channelMap can be divided in two two categories with respect to locking.
316// 1. Methods acquire the global lock.
317// 2. Methods that can only be called when global lock is held.
318// A second type of method need always to be called inside a first type of method.
319type channelMap struct {
320 mu sync.RWMutex
321 topLevelChannels map[int64]struct{}
322 servers map[int64]*server
323 channels map[int64]*channel
324 subChannels map[int64]*subChannel
325 listenSockets map[int64]*listenSocket
326 normalSockets map[int64]*normalSocket
327}
328
329func (c *channelMap) addServer(id int64, s *server) {
330 c.mu.Lock()
331 s.cm = c
332 c.servers[id] = s
333 c.mu.Unlock()
334}
335
khenaidoo257f3192021-12-15 16:46:37 -0500336func (c *channelMap) addChannel(id int64, cn *channel, isTopChannel bool, pid int64) {
khenaidoo5fc5cea2021-08-11 17:39:16 -0400337 c.mu.Lock()
338 cn.cm = c
339 cn.trace.cm = c
340 c.channels[id] = cn
341 if isTopChannel {
342 c.topLevelChannels[id] = struct{}{}
343 } else {
344 c.findEntry(pid).addChild(id, cn)
345 }
346 c.mu.Unlock()
347}
348
khenaidoo257f3192021-12-15 16:46:37 -0500349func (c *channelMap) addSubChannel(id int64, sc *subChannel, pid int64) {
khenaidoo5fc5cea2021-08-11 17:39:16 -0400350 c.mu.Lock()
351 sc.cm = c
352 sc.trace.cm = c
353 c.subChannels[id] = sc
354 c.findEntry(pid).addChild(id, sc)
355 c.mu.Unlock()
356}
357
khenaidoo257f3192021-12-15 16:46:37 -0500358func (c *channelMap) addListenSocket(id int64, ls *listenSocket, pid int64) {
khenaidoo5fc5cea2021-08-11 17:39:16 -0400359 c.mu.Lock()
360 ls.cm = c
361 c.listenSockets[id] = ls
362 c.findEntry(pid).addChild(id, ls)
363 c.mu.Unlock()
364}
365
khenaidoo257f3192021-12-15 16:46:37 -0500366func (c *channelMap) addNormalSocket(id int64, ns *normalSocket, pid int64) {
khenaidoo5fc5cea2021-08-11 17:39:16 -0400367 c.mu.Lock()
368 ns.cm = c
369 c.normalSockets[id] = ns
370 c.findEntry(pid).addChild(id, ns)
371 c.mu.Unlock()
372}
373
374// removeEntry triggers the removal of an entry, which may not indeed delete the entry, if it has to
375// wait on the deletion of its children and until no other entity's channel trace references it.
376// It may lead to a chain of entry deletion. For example, deleting the last socket of a gracefully
377// shutting down server will lead to the server being also deleted.
378func (c *channelMap) removeEntry(id int64) {
379 c.mu.Lock()
380 c.findEntry(id).triggerDelete()
381 c.mu.Unlock()
382}
383
384// c.mu must be held by the caller
385func (c *channelMap) decrTraceRefCount(id int64) {
386 e := c.findEntry(id)
387 if v, ok := e.(tracedChannel); ok {
388 v.decrTraceRefCount()
389 e.deleteSelfIfReady()
390 }
391}
392
393// c.mu must be held by the caller.
394func (c *channelMap) findEntry(id int64) entry {
395 var v entry
396 var ok bool
397 if v, ok = c.channels[id]; ok {
398 return v
399 }
400 if v, ok = c.subChannels[id]; ok {
401 return v
402 }
403 if v, ok = c.servers[id]; ok {
404 return v
405 }
406 if v, ok = c.listenSockets[id]; ok {
407 return v
408 }
409 if v, ok = c.normalSockets[id]; ok {
410 return v
411 }
412 return &dummyEntry{idNotFound: id}
413}
414
415// c.mu must be held by the caller
416// deleteEntry simply deletes an entry from the channelMap. Before calling this
417// method, caller must check this entry is ready to be deleted, i.e removeEntry()
418// has been called on it, and no children still exist.
419// Conditionals are ordered by the expected frequency of deletion of each entity
420// type, in order to optimize performance.
421func (c *channelMap) deleteEntry(id int64) {
422 var ok bool
423 if _, ok = c.normalSockets[id]; ok {
424 delete(c.normalSockets, id)
425 return
426 }
427 if _, ok = c.subChannels[id]; ok {
428 delete(c.subChannels, id)
429 return
430 }
431 if _, ok = c.channels[id]; ok {
432 delete(c.channels, id)
433 delete(c.topLevelChannels, id)
434 return
435 }
436 if _, ok = c.listenSockets[id]; ok {
437 delete(c.listenSockets, id)
438 return
439 }
440 if _, ok = c.servers[id]; ok {
441 delete(c.servers, id)
442 return
443 }
444}
445
446func (c *channelMap) traceEvent(id int64, desc *TraceEventDesc) {
447 c.mu.Lock()
448 child := c.findEntry(id)
449 childTC, ok := child.(tracedChannel)
450 if !ok {
451 c.mu.Unlock()
452 return
453 }
454 childTC.getChannelTrace().append(&TraceEvent{Desc: desc.Desc, Severity: desc.Severity, Timestamp: time.Now()})
455 if desc.Parent != nil {
456 parent := c.findEntry(child.getParentID())
457 var chanType RefChannelType
458 switch child.(type) {
459 case *channel:
460 chanType = RefChannel
461 case *subChannel:
462 chanType = RefSubChannel
463 }
464 if parentTC, ok := parent.(tracedChannel); ok {
465 parentTC.getChannelTrace().append(&TraceEvent{
466 Desc: desc.Parent.Desc,
467 Severity: desc.Parent.Severity,
468 Timestamp: time.Now(),
469 RefID: id,
470 RefName: childTC.getRefName(),
471 RefType: chanType,
472 })
473 childTC.incrTraceRefCount()
474 }
475 }
476 c.mu.Unlock()
477}
478
479type int64Slice []int64
480
481func (s int64Slice) Len() int { return len(s) }
482func (s int64Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
483func (s int64Slice) Less(i, j int) bool { return s[i] < s[j] }
484
485func copyMap(m map[int64]string) map[int64]string {
486 n := make(map[int64]string)
487 for k, v := range m {
488 n[k] = v
489 }
490 return n
491}
492
493func min(a, b int64) int64 {
494 if a < b {
495 return a
496 }
497 return b
498}
499
500func (c *channelMap) GetTopChannels(id int64, maxResults int64) ([]*ChannelMetric, bool) {
501 if maxResults <= 0 {
502 maxResults = EntryPerPage
503 }
504 c.mu.RLock()
505 l := int64(len(c.topLevelChannels))
506 ids := make([]int64, 0, l)
507 cns := make([]*channel, 0, min(l, maxResults))
508
509 for k := range c.topLevelChannels {
510 ids = append(ids, k)
511 }
512 sort.Sort(int64Slice(ids))
513 idx := sort.Search(len(ids), func(i int) bool { return ids[i] >= id })
514 count := int64(0)
515 var end bool
516 var t []*ChannelMetric
517 for i, v := range ids[idx:] {
518 if count == maxResults {
519 break
520 }
521 if cn, ok := c.channels[v]; ok {
522 cns = append(cns, cn)
523 t = append(t, &ChannelMetric{
524 NestedChans: copyMap(cn.nestedChans),
525 SubChans: copyMap(cn.subChans),
526 })
527 count++
528 }
529 if i == len(ids[idx:])-1 {
530 end = true
531 break
532 }
533 }
534 c.mu.RUnlock()
535 if count == 0 {
536 end = true
537 }
538
539 for i, cn := range cns {
540 t[i].ChannelData = cn.c.ChannelzMetric()
541 t[i].ID = cn.id
542 t[i].RefName = cn.refName
543 t[i].Trace = cn.trace.dumpData()
544 }
545 return t, end
546}
547
548func (c *channelMap) GetServers(id, maxResults int64) ([]*ServerMetric, bool) {
549 if maxResults <= 0 {
550 maxResults = EntryPerPage
551 }
552 c.mu.RLock()
553 l := int64(len(c.servers))
554 ids := make([]int64, 0, l)
555 ss := make([]*server, 0, min(l, maxResults))
556 for k := range c.servers {
557 ids = append(ids, k)
558 }
559 sort.Sort(int64Slice(ids))
560 idx := sort.Search(len(ids), func(i int) bool { return ids[i] >= id })
561 count := int64(0)
562 var end bool
563 var s []*ServerMetric
564 for i, v := range ids[idx:] {
565 if count == maxResults {
566 break
567 }
568 if svr, ok := c.servers[v]; ok {
569 ss = append(ss, svr)
570 s = append(s, &ServerMetric{
571 ListenSockets: copyMap(svr.listenSockets),
572 })
573 count++
574 }
575 if i == len(ids[idx:])-1 {
576 end = true
577 break
578 }
579 }
580 c.mu.RUnlock()
581 if count == 0 {
582 end = true
583 }
584
585 for i, svr := range ss {
586 s[i].ServerData = svr.s.ChannelzMetric()
587 s[i].ID = svr.id
588 s[i].RefName = svr.refName
589 }
590 return s, end
591}
592
593func (c *channelMap) GetServerSockets(id int64, startID int64, maxResults int64) ([]*SocketMetric, bool) {
594 if maxResults <= 0 {
595 maxResults = EntryPerPage
596 }
597 var svr *server
598 var ok bool
599 c.mu.RLock()
600 if svr, ok = c.servers[id]; !ok {
601 // server with id doesn't exist.
602 c.mu.RUnlock()
603 return nil, true
604 }
605 svrskts := svr.sockets
606 l := int64(len(svrskts))
607 ids := make([]int64, 0, l)
608 sks := make([]*normalSocket, 0, min(l, maxResults))
609 for k := range svrskts {
610 ids = append(ids, k)
611 }
612 sort.Sort(int64Slice(ids))
613 idx := sort.Search(len(ids), func(i int) bool { return ids[i] >= startID })
614 count := int64(0)
615 var end bool
616 for i, v := range ids[idx:] {
617 if count == maxResults {
618 break
619 }
620 if ns, ok := c.normalSockets[v]; ok {
621 sks = append(sks, ns)
622 count++
623 }
624 if i == len(ids[idx:])-1 {
625 end = true
626 break
627 }
628 }
629 c.mu.RUnlock()
630 if count == 0 {
631 end = true
632 }
633 s := make([]*SocketMetric, 0, len(sks))
634 for _, ns := range sks {
635 sm := &SocketMetric{}
636 sm.SocketData = ns.s.ChannelzMetric()
637 sm.ID = ns.id
638 sm.RefName = ns.refName
639 s = append(s, sm)
640 }
641 return s, end
642}
643
644func (c *channelMap) GetChannel(id int64) *ChannelMetric {
645 cm := &ChannelMetric{}
646 var cn *channel
647 var ok bool
648 c.mu.RLock()
649 if cn, ok = c.channels[id]; !ok {
650 // channel with id doesn't exist.
651 c.mu.RUnlock()
652 return nil
653 }
654 cm.NestedChans = copyMap(cn.nestedChans)
655 cm.SubChans = copyMap(cn.subChans)
656 // cn.c can be set to &dummyChannel{} when deleteSelfFromMap is called. Save a copy of cn.c when
657 // holding the lock to prevent potential data race.
658 chanCopy := cn.c
659 c.mu.RUnlock()
660 cm.ChannelData = chanCopy.ChannelzMetric()
661 cm.ID = cn.id
662 cm.RefName = cn.refName
663 cm.Trace = cn.trace.dumpData()
664 return cm
665}
666
667func (c *channelMap) GetSubChannel(id int64) *SubChannelMetric {
668 cm := &SubChannelMetric{}
669 var sc *subChannel
670 var ok bool
671 c.mu.RLock()
672 if sc, ok = c.subChannels[id]; !ok {
673 // subchannel with id doesn't exist.
674 c.mu.RUnlock()
675 return nil
676 }
677 cm.Sockets = copyMap(sc.sockets)
678 // sc.c can be set to &dummyChannel{} when deleteSelfFromMap is called. Save a copy of sc.c when
679 // holding the lock to prevent potential data race.
680 chanCopy := sc.c
681 c.mu.RUnlock()
682 cm.ChannelData = chanCopy.ChannelzMetric()
683 cm.ID = sc.id
684 cm.RefName = sc.refName
685 cm.Trace = sc.trace.dumpData()
686 return cm
687}
688
689func (c *channelMap) GetSocket(id int64) *SocketMetric {
690 sm := &SocketMetric{}
691 c.mu.RLock()
692 if ls, ok := c.listenSockets[id]; ok {
693 c.mu.RUnlock()
694 sm.SocketData = ls.s.ChannelzMetric()
695 sm.ID = ls.id
696 sm.RefName = ls.refName
697 return sm
698 }
699 if ns, ok := c.normalSockets[id]; ok {
700 c.mu.RUnlock()
701 sm.SocketData = ns.s.ChannelzMetric()
702 sm.ID = ns.id
703 sm.RefName = ns.refName
704 return sm
705 }
706 c.mu.RUnlock()
707 return nil
708}
709
710func (c *channelMap) GetServer(id int64) *ServerMetric {
711 sm := &ServerMetric{}
712 var svr *server
713 var ok bool
714 c.mu.RLock()
715 if svr, ok = c.servers[id]; !ok {
716 c.mu.RUnlock()
717 return nil
718 }
719 sm.ListenSockets = copyMap(svr.listenSockets)
720 c.mu.RUnlock()
721 sm.ID = svr.id
722 sm.RefName = svr.refName
723 sm.ServerData = svr.s.ChannelzMetric()
724 return sm
725}
726
727type idGenerator struct {
728 id int64
729}
730
731func (i *idGenerator) reset() {
732 atomic.StoreInt64(&i.id, 0)
733}
734
735func (i *idGenerator) genID() int64 {
736 return atomic.AddInt64(&i.id, 1)
737}