go.uber.org/yarpc@v1.72.1/transport/grpc/transport.go (about) 1 // Copyright (c) 2022 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package grpc 22 23 import ( 24 "net" 25 "sync" 26 27 "go.uber.org/yarpc/api/peer" 28 "go.uber.org/yarpc/pkg/lifecycle" 29 ) 30 31 var emptyDialOpts = &dialOptions{} 32 33 // Transport is a grpc transport.Transport. 34 // 35 // This currently does not have any additional functionality over creating 36 // an Inbound or Outbound separately, but may in the future. 37 type Transport struct { 38 lock sync.Mutex 39 once *lifecycle.Once 40 options *transportOptions 41 addressToPeer map[string]*grpcPeer 42 } 43 44 // NewTransport returns a new Transport. 45 func NewTransport(options ...TransportOption) *Transport { 46 return newTransport(newTransportOptions(options)) 47 } 48 49 func newTransport(transportOptions *transportOptions) *Transport { 50 return &Transport{ 51 once: lifecycle.NewOnce(), 52 options: transportOptions, 53 addressToPeer: make(map[string]*grpcPeer), 54 } 55 } 56 57 // Start implements transport.Lifecycle#Start. 58 func (t *Transport) Start() error { 59 return t.once.Start(nil) 60 } 61 62 // Stop implements transport.Lifecycle#Stop. 63 func (t *Transport) Stop() error { 64 return t.once.Stop(func() error { 65 t.lock.Lock() 66 defer t.lock.Unlock() 67 68 for _, grpcPeer := range t.addressToPeer { 69 grpcPeer.stop() 70 } 71 for _, grpcPeer := range t.addressToPeer { 72 grpcPeer.wait() 73 } 74 return nil 75 }) 76 } 77 78 // IsRunning implements transport.Lifecycle#IsRunning. 79 func (t *Transport) IsRunning() bool { 80 return t.once.IsRunning() 81 } 82 83 // NewInbound returns a new Inbound for the given listener. 84 func (t *Transport) NewInbound(listener net.Listener, options ...InboundOption) *Inbound { 85 return newInbound(t, listener, options...) 86 } 87 88 // NewSingleOutbound returns a new Outbound for the given adrress. 89 // Note: This does not support TLS. See TLS example in doc.go. 90 func (t *Transport) NewSingleOutbound(address string, options ...OutboundOption) *Outbound { 91 return newSingleOutbound(t, address, options...) 92 } 93 94 // NewOutbound returns a new Outbound for the given peer.Chooser. 95 func (t *Transport) NewOutbound(peerChooser peer.Chooser, options ...OutboundOption) *Outbound { 96 return newOutbound(t, peerChooser, options...) 97 } 98 99 // RetainPeer retains the peer. 100 // 101 // Deprecated: use grpcTransport.NewDialer(...grpc.DialOption) to create a 102 // peer.Transport that supports custom DialOptions instead of using the 103 // grpc.Transport as a peer.Transport. 104 func (t *Transport) RetainPeer(pid peer.Identifier, ps peer.Subscriber) (peer.Peer, error) { 105 return t.retainPeer(pid, emptyDialOpts, ps) 106 } 107 108 func (t *Transport) retainPeer(pid peer.Identifier, options *dialOptions, ps peer.Subscriber) (peer.Peer, error) { 109 t.lock.Lock() 110 defer t.lock.Unlock() 111 address := pid.Identifier() 112 p, ok := t.addressToPeer[address] 113 if !ok { 114 var err error 115 p, err = t.newPeer(address, options) 116 if err != nil { 117 return nil, err 118 } 119 t.addressToPeer[address] = p 120 } 121 p.Subscribe(ps) 122 return p, nil 123 } 124 125 // ReleasePeer releases the peer. 126 // 127 // Deprecated: use grpcTransport.NewDialer(...grpc.DialOption) to create a 128 // peer.Transport that supports custom DialOptions instead of using the 129 // grpc.Transport as a peer.Transport. 130 func (t *Transport) ReleasePeer(pid peer.Identifier, ps peer.Subscriber) error { 131 t.lock.Lock() 132 defer t.lock.Unlock() 133 address := pid.Identifier() 134 p, ok := t.addressToPeer[address] 135 if !ok { 136 return peer.ErrTransportHasNoReferenceToPeer{ 137 TransportName: "grpc.Transport", 138 PeerIdentifier: address, 139 } 140 } 141 if err := p.Unsubscribe(ps); err != nil { 142 return err 143 } 144 if p.NumSubscribers() == 0 { 145 delete(t.addressToPeer, address) 146 p.stop() 147 } 148 return nil 149 }