github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/xds/internal/xdsclient/pubsub/pubsub.go (about) 1 /* 2 * 3 * Copyright 2021 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 // Package pubsub implements a utility type to maintain resource watchers and 19 // the updates. 20 // 21 // This package is designed to work with the xds resources. It could be made a 22 // general system that works with all types. 23 package pubsub 24 25 import ( 26 "sync" 27 "time" 28 29 "github.com/hxx258456/ccgo/grpc/internal/buffer" 30 "github.com/hxx258456/ccgo/grpc/internal/grpclog" 31 "github.com/hxx258456/ccgo/grpc/internal/grpcsync" 32 "github.com/hxx258456/ccgo/grpc/xds/internal/xdsclient/xdsresource" 33 ) 34 35 // Pubsub maintains resource watchers and resource updates. 36 // 37 // There can be multiple watchers for the same resource. An update to a resource 38 // triggers updates to all the existing watchers. Watchers can be canceled at 39 // any time. 40 type Pubsub struct { 41 done *grpcsync.Event 42 logger *grpclog.PrefixLogger 43 watchExpiryTimeout time.Duration 44 45 updateCh *buffer.Unbounded // chan *watcherInfoWithUpdate 46 // All the following maps are to keep the updates/metadata in a cache. 47 mu sync.Mutex 48 ldsWatchers map[string]map[*watchInfo]bool 49 ldsCache map[string]xdsresource.ListenerUpdate 50 ldsMD map[string]xdsresource.UpdateMetadata 51 rdsWatchers map[string]map[*watchInfo]bool 52 rdsCache map[string]xdsresource.RouteConfigUpdate 53 rdsMD map[string]xdsresource.UpdateMetadata 54 cdsWatchers map[string]map[*watchInfo]bool 55 cdsCache map[string]xdsresource.ClusterUpdate 56 cdsMD map[string]xdsresource.UpdateMetadata 57 edsWatchers map[string]map[*watchInfo]bool 58 edsCache map[string]xdsresource.EndpointsUpdate 59 edsMD map[string]xdsresource.UpdateMetadata 60 } 61 62 // New creates a new Pubsub. 63 func New(watchExpiryTimeout time.Duration, logger *grpclog.PrefixLogger) *Pubsub { 64 pb := &Pubsub{ 65 done: grpcsync.NewEvent(), 66 logger: logger, 67 watchExpiryTimeout: watchExpiryTimeout, 68 69 updateCh: buffer.NewUnbounded(), 70 ldsWatchers: make(map[string]map[*watchInfo]bool), 71 ldsCache: make(map[string]xdsresource.ListenerUpdate), 72 ldsMD: make(map[string]xdsresource.UpdateMetadata), 73 rdsWatchers: make(map[string]map[*watchInfo]bool), 74 rdsCache: make(map[string]xdsresource.RouteConfigUpdate), 75 rdsMD: make(map[string]xdsresource.UpdateMetadata), 76 cdsWatchers: make(map[string]map[*watchInfo]bool), 77 cdsCache: make(map[string]xdsresource.ClusterUpdate), 78 cdsMD: make(map[string]xdsresource.UpdateMetadata), 79 edsWatchers: make(map[string]map[*watchInfo]bool), 80 edsCache: make(map[string]xdsresource.EndpointsUpdate), 81 edsMD: make(map[string]xdsresource.UpdateMetadata), 82 } 83 go pb.run() 84 return pb 85 } 86 87 // WatchListener registers a watcher for the LDS resource. 88 // 89 // It also returns whether this is the first watch for this resource. 90 func (pb *Pubsub) WatchListener(serviceName string, cb func(xdsresource.ListenerUpdate, error)) (first bool, cancel func() bool) { 91 wi := &watchInfo{ 92 c: pb, 93 rType: xdsresource.ListenerResource, 94 target: serviceName, 95 ldsCallback: cb, 96 } 97 98 wi.expiryTimer = time.AfterFunc(pb.watchExpiryTimeout, func() { 99 wi.timeout() 100 }) 101 return pb.watch(wi) 102 } 103 104 // WatchRouteConfig register a watcher for the RDS resource. 105 // 106 // It also returns whether this is the first watch for this resource. 107 func (pb *Pubsub) WatchRouteConfig(routeName string, cb func(xdsresource.RouteConfigUpdate, error)) (first bool, cancel func() bool) { 108 wi := &watchInfo{ 109 c: pb, 110 rType: xdsresource.RouteConfigResource, 111 target: routeName, 112 rdsCallback: cb, 113 } 114 115 wi.expiryTimer = time.AfterFunc(pb.watchExpiryTimeout, func() { 116 wi.timeout() 117 }) 118 return pb.watch(wi) 119 } 120 121 // WatchCluster register a watcher for the CDS resource. 122 // 123 // It also returns whether this is the first watch for this resource. 124 func (pb *Pubsub) WatchCluster(clusterName string, cb func(xdsresource.ClusterUpdate, error)) (first bool, cancel func() bool) { 125 wi := &watchInfo{ 126 c: pb, 127 rType: xdsresource.ClusterResource, 128 target: clusterName, 129 cdsCallback: cb, 130 } 131 132 wi.expiryTimer = time.AfterFunc(pb.watchExpiryTimeout, func() { 133 wi.timeout() 134 }) 135 return pb.watch(wi) 136 } 137 138 // WatchEndpoints registers a watcher for the EDS resource. 139 // 140 // It also returns whether this is the first watch for this resource. 141 func (pb *Pubsub) WatchEndpoints(clusterName string, cb func(xdsresource.EndpointsUpdate, error)) (first bool, cancel func() bool) { 142 wi := &watchInfo{ 143 c: pb, 144 rType: xdsresource.EndpointsResource, 145 target: clusterName, 146 edsCallback: cb, 147 } 148 149 wi.expiryTimer = time.AfterFunc(pb.watchExpiryTimeout, func() { 150 wi.timeout() 151 }) 152 return pb.watch(wi) 153 } 154 155 // Close closes the pubsub. 156 func (pb *Pubsub) Close() { 157 if pb.done.HasFired() { 158 return 159 } 160 pb.done.Fire() 161 } 162 163 // run is a goroutine for all the callbacks. 164 // 165 // Callback can be called in watch(), if an item is found in cache. Without this 166 // goroutine, the callback will be called inline, which might cause a deadlock 167 // in user's code. Callbacks also cannot be simple `go callback()` because the 168 // order matters. 169 func (pb *Pubsub) run() { 170 for { 171 select { 172 case t := <-pb.updateCh.Get(): 173 pb.updateCh.Load() 174 if pb.done.HasFired() { 175 return 176 } 177 pb.callCallback(t.(*watcherInfoWithUpdate)) 178 case <-pb.done.Done(): 179 return 180 } 181 } 182 }