github.com/cloudwego/kitex@v0.9.0/pkg/rpcinfo/remoteinfo/remoteInfo.go (about) 1 /* 2 * Copyright 2021 CloudWeGo Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package remoteinfo 18 19 import ( 20 "errors" 21 "net" 22 "sync" 23 24 "github.com/cloudwego/kitex/internal" 25 "github.com/cloudwego/kitex/pkg/discovery" 26 "github.com/cloudwego/kitex/pkg/kerrors" 27 "github.com/cloudwego/kitex/pkg/rpcinfo" 28 ) 29 30 // RemoteInfo implements a rpcinfo.EndpointInfo with mutable address and connection. 31 // It is typically used to represent server info on client side or client info on server side. 32 type RemoteInfo interface { 33 rpcinfo.EndpointInfo 34 SetServiceName(name string) 35 SetTag(key, value string) error 36 ForceSetTag(key, value string) 37 38 // SetTagLock freezes a key of the tags and refuses further modification on its value. 39 SetTagLock(key string) 40 GetInstance() discovery.Instance 41 SetInstance(ins discovery.Instance) 42 43 // SetRemoteAddr tries to set the network address of the discovery.Instance hold by RemoteInfo. 44 // The result indicates whether the modification is successful. 45 SetRemoteAddr(addr net.Addr) (ok bool) 46 ImmutableView() rpcinfo.EndpointInfo 47 } 48 49 // RefreshableInstance declares an interface which can return an instance containing the new address. 50 type RefreshableInstance interface { 51 RefreshInstanceWithAddr(addr net.Addr) (newInstance discovery.Instance) 52 } 53 54 var ( 55 _ RemoteInfo = &remoteInfo{} 56 remoteInfoPool sync.Pool 57 ) 58 59 type remoteInfo struct { 60 sync.RWMutex 61 serviceName string 62 method string 63 tags map[string]string 64 65 instance discovery.Instance 66 tagLocks map[string]struct{} 67 } 68 69 func init() { 70 remoteInfoPool.New = newRemoteInfo 71 } 72 73 func newRemoteInfo() interface{} { 74 return &remoteInfo{ 75 tags: make(map[string]string), 76 tagLocks: make(map[string]struct{}), 77 } 78 } 79 80 // GetInstance implements the RemoteInfo interface. 81 func (ri *remoteInfo) GetInstance() (ins discovery.Instance) { 82 ri.RLock() 83 ins = ri.instance 84 ri.RUnlock() 85 return 86 } 87 88 // SetInstance implements the RemoteInfo interface. 89 func (ri *remoteInfo) SetInstance(ins discovery.Instance) { 90 ri.Lock() 91 ri.instance = ins 92 ri.Unlock() 93 } 94 95 // ServiceName implements the rpcinfo.EndpointInfo interface. 96 func (ri *remoteInfo) ServiceName() string { 97 return ri.serviceName 98 } 99 100 // SetServiceName implements the RemoteInfo interface. 101 func (ri *remoteInfo) SetServiceName(name string) { 102 ri.serviceName = name 103 } 104 105 // Method implements the rpcinfo.EndpointInfo interface. 106 func (ri *remoteInfo) Method() string { 107 return ri.method 108 } 109 110 // Tag implements the rpcinfo.EndpointInfo interface. 111 func (ri *remoteInfo) Tag(key string) (value string, exist bool) { 112 ri.RLock() 113 defer ri.RUnlock() 114 115 if ri.instance != nil { 116 if value, exist = ri.instance.Tag(key); exist { 117 return 118 } 119 } 120 value, exist = ri.tags[key] 121 return 122 } 123 124 // DefaultTag implements the rpcinfo.EndpointInfo interface. 125 func (ri *remoteInfo) DefaultTag(key, def string) string { 126 if value, exist := ri.Tag(key); exist && value != "" { 127 return value 128 } 129 return def 130 } 131 132 // SetRemoteAddr implements the RemoteInfo interface. 133 func (ri *remoteInfo) SetRemoteAddr(addr net.Addr) bool { 134 ri.Lock() 135 defer ri.Unlock() 136 if ins, ok := ri.instance.(RefreshableInstance); ok { 137 ri.instance = ins.RefreshInstanceWithAddr(addr) 138 return true 139 } 140 return false 141 } 142 143 // Address implements the rpcinfo.EndpointInfo interface. 144 func (ri *remoteInfo) Address() net.Addr { 145 ri.RLock() 146 defer ri.RUnlock() 147 if ri.instance != nil { 148 return ri.instance.Address() 149 } 150 return nil 151 } 152 153 // SetTagLocks locks tag. 154 func (ri *remoteInfo) SetTagLock(key string) { 155 ri.Lock() 156 ri.tagLocks[key] = struct{}{} 157 ri.Unlock() 158 } 159 160 // SetTag implements rpcinfo.MutableEndpointInfo. 161 func (ri *remoteInfo) SetTag(key, value string) error { 162 ri.Lock() 163 defer ri.Unlock() 164 if _, exist := ri.tagLocks[key]; exist { 165 return kerrors.ErrNotSupported 166 } 167 ri.tags[key] = value 168 return nil 169 } 170 171 // ForceSetTag is used to set Tag without tag lock. 172 func (ri *remoteInfo) ForceSetTag(key, value string) { 173 ri.Lock() 174 defer ri.Unlock() 175 ri.tags[key] = value 176 } 177 178 // ImmutableView implements rpcinfo.MutableEndpointInfo. 179 func (ri *remoteInfo) ImmutableView() rpcinfo.EndpointInfo { 180 return ri 181 } 182 183 func (ri *remoteInfo) zero() { 184 ri.Lock() 185 defer ri.Unlock() 186 ri.serviceName = "" 187 ri.method = "" 188 ri.instance = nil 189 for k := range ri.tags { 190 delete(ri.tags, k) 191 } 192 for k := range ri.tagLocks { 193 delete(ri.tagLocks, k) 194 } 195 } 196 197 // Recycle is used to recycle the remoteInfo. 198 func (ri *remoteInfo) Recycle() { 199 if r, ok := ri.instance.(internal.Reusable); ok { 200 r.Recycle() 201 } 202 ri.zero() 203 remoteInfoPool.Put(ri) 204 } 205 206 // NewRemoteInfo creates a remoteInfo wrapping the given endpointInfo. 207 // The return value of the created RemoteInfo's Method method will be the `method` argument 208 // instead of the Method field in `basicInfo`. 209 // If the given basicInfo is nil, this function will panic. 210 func NewRemoteInfo(basicInfo *rpcinfo.EndpointBasicInfo, method string) RemoteInfo { 211 if basicInfo == nil { 212 panic(errors.New("nil basicInfo")) 213 } 214 ri := remoteInfoPool.Get().(*remoteInfo) 215 ri.serviceName = basicInfo.ServiceName 216 ri.method = method 217 for k, v := range basicInfo.Tags { 218 ri.tags[k] = v 219 } 220 return ri 221 } 222 223 // AsRemoteInfo converts an rpcinfo.EndpointInfo into a RemoteInfo. Returns nil if impossible. 224 func AsRemoteInfo(r rpcinfo.EndpointInfo) RemoteInfo { 225 if v, ok := r.(RemoteInfo); ok { 226 return v 227 } 228 return nil 229 }