blob: e4252e5be9f277a3026e67c1bc118e97b0752fad [file] [log] [blame]
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -07001/*
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
amit.ghosh258d14c2020-10-02 15:13:38 +020033 "google.golang.org/grpc/internal/grpclog"
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -070034)
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 {
207 db.get().addChannel(id, cn, true, pid, ref)
208 } else {
209 db.get().addChannel(id, cn, false, pid, ref)
210 }
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 {
amit.ghosh258d14c2020-10-02 15:13:38 +0200219 grpclog.ErrorDepth(0, "a SubChannel's parent id cannot be 0")
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700220 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 }
231 db.get().addSubChannel(id, sc, pid, ref)
232 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 {
amit.ghosh258d14c2020-10-02 15:13:38 +0200256 grpclog.ErrorDepth(0, "a ListenSocket's parent id cannot be 0")
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700257 return 0
258 }
259 id := idGen.genID()
260 ls := &listenSocket{refName: ref, s: s, id: id, pid: pid}
261 db.get().addListenSocket(id, ls, pid, ref)
262 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 {
amit.ghosh258d14c2020-10-02 15:13:38 +0200271 grpclog.ErrorDepth(0, "a NormalSocket's parent id cannot be 0")
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700272 return 0
273 }
274 id := idGen.genID()
275 ns := &normalSocket{refName: ref, s: s, id: id, pid: pid}
276 db.get().addNormalSocket(id, ns, pid, ref)
277 return id
278}
279
280// RemoveEntry removes an entry with unique channelz trakcing id to be id from
281// 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.
amit.ghosh258d14c2020-10-02 15:13:38 +0200297func AddTraceEvent(id int64, depth int, desc *TraceEventDesc) {
298 for d := desc; d != nil; d = d.Parent {
299 switch d.Severity {
300 case CtUNKNOWN:
301 grpclog.InfoDepth(depth+1, d.Desc)
302 case CtINFO:
303 grpclog.InfoDepth(depth+1, d.Desc)
304 case CtWarning:
305 grpclog.WarningDepth(depth+1, d.Desc)
306 case CtError:
307 grpclog.ErrorDepth(depth+1, d.Desc)
308 }
309 }
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700310 if getMaxTraceEntry() == 0 {
311 return
312 }
313 db.get().traceEvent(id, desc)
314}
315
316// channelMap is the storage data structure for channelz.
317// Methods of channelMap can be divided in two two categories with respect to locking.
318// 1. Methods acquire the global lock.
319// 2. Methods that can only be called when global lock is held.
320// A second type of method need always to be called inside a first type of method.
321type channelMap struct {
322 mu sync.RWMutex
323 topLevelChannels map[int64]struct{}
324 servers map[int64]*server
325 channels map[int64]*channel
326 subChannels map[int64]*subChannel
327 listenSockets map[int64]*listenSocket
328 normalSockets map[int64]*normalSocket
329}
330
331func (c *channelMap) addServer(id int64, s *server) {
332 c.mu.Lock()
333 s.cm = c
334 c.servers[id] = s
335 c.mu.Unlock()
336}
337
338func (c *channelMap) addChannel(id int64, cn *channel, isTopChannel bool, pid int64, ref string) {
339 c.mu.Lock()
340 cn.cm = c
341 cn.trace.cm = c
342 c.channels[id] = cn
343 if isTopChannel {
344 c.topLevelChannels[id] = struct{}{}
345 } else {
346 c.findEntry(pid).addChild(id, cn)
347 }
348 c.mu.Unlock()
349}
350
351func (c *channelMap) addSubChannel(id int64, sc *subChannel, pid int64, ref string) {
352 c.mu.Lock()
353 sc.cm = c
354 sc.trace.cm = c
355 c.subChannels[id] = sc
356 c.findEntry(pid).addChild(id, sc)
357 c.mu.Unlock()
358}
359
360func (c *channelMap) addListenSocket(id int64, ls *listenSocket, pid int64, ref string) {
361 c.mu.Lock()
362 ls.cm = c
363 c.listenSockets[id] = ls
364 c.findEntry(pid).addChild(id, ls)
365 c.mu.Unlock()
366}
367
368func (c *channelMap) addNormalSocket(id int64, ns *normalSocket, pid int64, ref string) {
369 c.mu.Lock()
370 ns.cm = c
371 c.normalSockets[id] = ns
372 c.findEntry(pid).addChild(id, ns)
373 c.mu.Unlock()
374}
375
376// removeEntry triggers the removal of an entry, which may not indeed delete the entry, if it has to
377// wait on the deletion of its children and until no other entity's channel trace references it.
378// It may lead to a chain of entry deletion. For example, deleting the last socket of a gracefully
379// shutting down server will lead to the server being also deleted.
380func (c *channelMap) removeEntry(id int64) {
381 c.mu.Lock()
382 c.findEntry(id).triggerDelete()
383 c.mu.Unlock()
384}
385
386// c.mu must be held by the caller
387func (c *channelMap) decrTraceRefCount(id int64) {
388 e := c.findEntry(id)
389 if v, ok := e.(tracedChannel); ok {
390 v.decrTraceRefCount()
391 e.deleteSelfIfReady()
392 }
393}
394
395// c.mu must be held by the caller.
396func (c *channelMap) findEntry(id int64) entry {
397 var v entry
398 var ok bool
399 if v, ok = c.channels[id]; ok {
400 return v
401 }
402 if v, ok = c.subChannels[id]; ok {
403 return v
404 }
405 if v, ok = c.servers[id]; ok {
406 return v
407 }
408 if v, ok = c.listenSockets[id]; ok {
409 return v
410 }
411 if v, ok = c.normalSockets[id]; ok {
412 return v
413 }
414 return &dummyEntry{idNotFound: id}
415}
416
417// c.mu must be held by the caller
418// deleteEntry simply deletes an entry from the channelMap. Before calling this
419// method, caller must check this entry is ready to be deleted, i.e removeEntry()
420// has been called on it, and no children still exist.
421// Conditionals are ordered by the expected frequency of deletion of each entity
422// type, in order to optimize performance.
423func (c *channelMap) deleteEntry(id int64) {
424 var ok bool
425 if _, ok = c.normalSockets[id]; ok {
426 delete(c.normalSockets, id)
427 return
428 }
429 if _, ok = c.subChannels[id]; ok {
430 delete(c.subChannels, id)
431 return
432 }
433 if _, ok = c.channels[id]; ok {
434 delete(c.channels, id)
435 delete(c.topLevelChannels, id)
436 return
437 }
438 if _, ok = c.listenSockets[id]; ok {
439 delete(c.listenSockets, id)
440 return
441 }
442 if _, ok = c.servers[id]; ok {
443 delete(c.servers, id)
444 return
445 }
446}
447
448func (c *channelMap) traceEvent(id int64, desc *TraceEventDesc) {
449 c.mu.Lock()
450 child := c.findEntry(id)
451 childTC, ok := child.(tracedChannel)
452 if !ok {
453 c.mu.Unlock()
454 return
455 }
456 childTC.getChannelTrace().append(&TraceEvent{Desc: desc.Desc, Severity: desc.Severity, Timestamp: time.Now()})
457 if desc.Parent != nil {
458 parent := c.findEntry(child.getParentID())
459 var chanType RefChannelType
460 switch child.(type) {
461 case *channel:
462 chanType = RefChannel
463 case *subChannel:
464 chanType = RefSubChannel
465 }
466 if parentTC, ok := parent.(tracedChannel); ok {
467 parentTC.getChannelTrace().append(&TraceEvent{
468 Desc: desc.Parent.Desc,
469 Severity: desc.Parent.Severity,
470 Timestamp: time.Now(),
471 RefID: id,
472 RefName: childTC.getRefName(),
473 RefType: chanType,
474 })
475 childTC.incrTraceRefCount()
476 }
477 }
478 c.mu.Unlock()
479}
480
481type int64Slice []int64
482
483func (s int64Slice) Len() int { return len(s) }
484func (s int64Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
485func (s int64Slice) Less(i, j int) bool { return s[i] < s[j] }
486
487func copyMap(m map[int64]string) map[int64]string {
488 n := make(map[int64]string)
489 for k, v := range m {
490 n[k] = v
491 }
492 return n
493}
494
495func min(a, b int64) int64 {
496 if a < b {
497 return a
498 }
499 return b
500}
501
502func (c *channelMap) GetTopChannels(id int64, maxResults int64) ([]*ChannelMetric, bool) {
503 if maxResults <= 0 {
504 maxResults = EntryPerPage
505 }
506 c.mu.RLock()
507 l := int64(len(c.topLevelChannels))
508 ids := make([]int64, 0, l)
509 cns := make([]*channel, 0, min(l, maxResults))
510
511 for k := range c.topLevelChannels {
512 ids = append(ids, k)
513 }
514 sort.Sort(int64Slice(ids))
515 idx := sort.Search(len(ids), func(i int) bool { return ids[i] >= id })
516 count := int64(0)
517 var end bool
518 var t []*ChannelMetric
519 for i, v := range ids[idx:] {
520 if count == maxResults {
521 break
522 }
523 if cn, ok := c.channels[v]; ok {
524 cns = append(cns, cn)
525 t = append(t, &ChannelMetric{
526 NestedChans: copyMap(cn.nestedChans),
527 SubChans: copyMap(cn.subChans),
528 })
529 count++
530 }
531 if i == len(ids[idx:])-1 {
532 end = true
533 break
534 }
535 }
536 c.mu.RUnlock()
537 if count == 0 {
538 end = true
539 }
540
541 for i, cn := range cns {
542 t[i].ChannelData = cn.c.ChannelzMetric()
543 t[i].ID = cn.id
544 t[i].RefName = cn.refName
545 t[i].Trace = cn.trace.dumpData()
546 }
547 return t, end
548}
549
550func (c *channelMap) GetServers(id, maxResults int64) ([]*ServerMetric, bool) {
551 if maxResults <= 0 {
552 maxResults = EntryPerPage
553 }
554 c.mu.RLock()
555 l := int64(len(c.servers))
556 ids := make([]int64, 0, l)
557 ss := make([]*server, 0, min(l, maxResults))
558 for k := range c.servers {
559 ids = append(ids, k)
560 }
561 sort.Sort(int64Slice(ids))
562 idx := sort.Search(len(ids), func(i int) bool { return ids[i] >= id })
563 count := int64(0)
564 var end bool
565 var s []*ServerMetric
566 for i, v := range ids[idx:] {
567 if count == maxResults {
568 break
569 }
570 if svr, ok := c.servers[v]; ok {
571 ss = append(ss, svr)
572 s = append(s, &ServerMetric{
573 ListenSockets: copyMap(svr.listenSockets),
574 })
575 count++
576 }
577 if i == len(ids[idx:])-1 {
578 end = true
579 break
580 }
581 }
582 c.mu.RUnlock()
583 if count == 0 {
584 end = true
585 }
586
587 for i, svr := range ss {
588 s[i].ServerData = svr.s.ChannelzMetric()
589 s[i].ID = svr.id
590 s[i].RefName = svr.refName
591 }
592 return s, end
593}
594
595func (c *channelMap) GetServerSockets(id int64, startID int64, maxResults int64) ([]*SocketMetric, bool) {
596 if maxResults <= 0 {
597 maxResults = EntryPerPage
598 }
599 var svr *server
600 var ok bool
601 c.mu.RLock()
602 if svr, ok = c.servers[id]; !ok {
603 // server with id doesn't exist.
604 c.mu.RUnlock()
605 return nil, true
606 }
607 svrskts := svr.sockets
608 l := int64(len(svrskts))
609 ids := make([]int64, 0, l)
610 sks := make([]*normalSocket, 0, min(l, maxResults))
611 for k := range svrskts {
612 ids = append(ids, k)
613 }
614 sort.Sort(int64Slice(ids))
615 idx := sort.Search(len(ids), func(i int) bool { return ids[i] >= startID })
616 count := int64(0)
617 var end bool
618 for i, v := range ids[idx:] {
619 if count == maxResults {
620 break
621 }
622 if ns, ok := c.normalSockets[v]; ok {
623 sks = append(sks, ns)
624 count++
625 }
626 if i == len(ids[idx:])-1 {
627 end = true
628 break
629 }
630 }
631 c.mu.RUnlock()
632 if count == 0 {
633 end = true
634 }
635 var s []*SocketMetric
636 for _, ns := range sks {
637 sm := &SocketMetric{}
638 sm.SocketData = ns.s.ChannelzMetric()
639 sm.ID = ns.id
640 sm.RefName = ns.refName
641 s = append(s, sm)
642 }
643 return s, end
644}
645
646func (c *channelMap) GetChannel(id int64) *ChannelMetric {
647 cm := &ChannelMetric{}
648 var cn *channel
649 var ok bool
650 c.mu.RLock()
651 if cn, ok = c.channels[id]; !ok {
652 // channel with id doesn't exist.
653 c.mu.RUnlock()
654 return nil
655 }
656 cm.NestedChans = copyMap(cn.nestedChans)
657 cm.SubChans = copyMap(cn.subChans)
658 // cn.c can be set to &dummyChannel{} when deleteSelfFromMap is called. Save a copy of cn.c when
659 // holding the lock to prevent potential data race.
660 chanCopy := cn.c
661 c.mu.RUnlock()
662 cm.ChannelData = chanCopy.ChannelzMetric()
663 cm.ID = cn.id
664 cm.RefName = cn.refName
665 cm.Trace = cn.trace.dumpData()
666 return cm
667}
668
669func (c *channelMap) GetSubChannel(id int64) *SubChannelMetric {
670 cm := &SubChannelMetric{}
671 var sc *subChannel
672 var ok bool
673 c.mu.RLock()
674 if sc, ok = c.subChannels[id]; !ok {
675 // subchannel with id doesn't exist.
676 c.mu.RUnlock()
677 return nil
678 }
679 cm.Sockets = copyMap(sc.sockets)
680 // sc.c can be set to &dummyChannel{} when deleteSelfFromMap is called. Save a copy of sc.c when
681 // holding the lock to prevent potential data race.
682 chanCopy := sc.c
683 c.mu.RUnlock()
684 cm.ChannelData = chanCopy.ChannelzMetric()
685 cm.ID = sc.id
686 cm.RefName = sc.refName
687 cm.Trace = sc.trace.dumpData()
688 return cm
689}
690
691func (c *channelMap) GetSocket(id int64) *SocketMetric {
692 sm := &SocketMetric{}
693 c.mu.RLock()
694 if ls, ok := c.listenSockets[id]; ok {
695 c.mu.RUnlock()
696 sm.SocketData = ls.s.ChannelzMetric()
697 sm.ID = ls.id
698 sm.RefName = ls.refName
699 return sm
700 }
701 if ns, ok := c.normalSockets[id]; ok {
702 c.mu.RUnlock()
703 sm.SocketData = ns.s.ChannelzMetric()
704 sm.ID = ns.id
705 sm.RefName = ns.refName
706 return sm
707 }
708 c.mu.RUnlock()
709 return nil
710}
711
712func (c *channelMap) GetServer(id int64) *ServerMetric {
713 sm := &ServerMetric{}
714 var svr *server
715 var ok bool
716 c.mu.RLock()
717 if svr, ok = c.servers[id]; !ok {
718 c.mu.RUnlock()
719 return nil
720 }
721 sm.ListenSockets = copyMap(svr.listenSockets)
722 c.mu.RUnlock()
723 sm.ID = svr.id
724 sm.RefName = svr.refName
725 sm.ServerData = svr.s.ChannelzMetric()
726 return sm
727}
728
729type idGenerator struct {
730 id int64
731}
732
733func (i *idGenerator) reset() {
734 atomic.StoreInt64(&i.id, 0)
735}
736
737func (i *idGenerator) genID() int64 {
738 return atomic.AddInt64(&i.id, 1)
739}