cubefs
246 строк · 6.6 Кб
1/*
2*
3* Copyright 2017 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
19package grpc
20
21import (
22"fmt"
23"sync"
24
25"google.golang.org/grpc/balancer"
26"google.golang.org/grpc/connectivity"
27"google.golang.org/grpc/internal/buffer"
28"google.golang.org/grpc/internal/channelz"
29"google.golang.org/grpc/internal/grpcsync"
30"google.golang.org/grpc/resolver"
31)
32
33// scStateUpdate contains the subConn and the new state it changed to.
34type scStateUpdate struct {
35sc balancer.SubConn
36state connectivity.State
37err error
38}
39
40// ccBalancerWrapper is a wrapper on top of cc for balancers.
41// It implements balancer.ClientConn interface.
42type ccBalancerWrapper struct {
43cc *ClientConn
44balancerMu sync.Mutex // synchronizes calls to the balancer
45balancer balancer.Balancer
46scBuffer *buffer.Unbounded
47done *grpcsync.Event
48
49mu sync.Mutex
50subConns map[*acBalancerWrapper]struct{}
51}
52
53func newCCBalancerWrapper(cc *ClientConn, b balancer.Builder, bopts balancer.BuildOptions) *ccBalancerWrapper {
54ccb := &ccBalancerWrapper{
55cc: cc,
56scBuffer: buffer.NewUnbounded(),
57done: grpcsync.NewEvent(),
58subConns: make(map[*acBalancerWrapper]struct{}),
59}
60go ccb.watcher()
61ccb.balancer = b.Build(ccb, bopts)
62return ccb
63}
64
65// watcher balancer functions sequentially, so the balancer can be implemented
66// lock-free.
67func (ccb *ccBalancerWrapper) watcher() {
68for {
69select {
70case t := <-ccb.scBuffer.Get():
71ccb.scBuffer.Load()
72if ccb.done.HasFired() {
73break
74}
75ccb.balancerMu.Lock()
76su := t.(*scStateUpdate)
77ccb.balancer.UpdateSubConnState(su.sc, balancer.SubConnState{ConnectivityState: su.state, ConnectionError: su.err})
78ccb.balancerMu.Unlock()
79case <-ccb.done.Done():
80}
81
82if ccb.done.HasFired() {
83ccb.balancer.Close()
84ccb.mu.Lock()
85scs := ccb.subConns
86ccb.subConns = nil
87ccb.mu.Unlock()
88for acbw := range scs {
89ccb.cc.removeAddrConn(acbw.getAddrConn(), errConnDrain)
90}
91ccb.UpdateState(balancer.State{ConnectivityState: connectivity.Connecting, Picker: nil})
92return
93}
94}
95}
96
97func (ccb *ccBalancerWrapper) close() {
98ccb.done.Fire()
99}
100
101func (ccb *ccBalancerWrapper) handleSubConnStateChange(sc balancer.SubConn, s connectivity.State, err error) {
102// When updating addresses for a SubConn, if the address in use is not in
103// the new addresses, the old ac will be tearDown() and a new ac will be
104// created. tearDown() generates a state change with Shutdown state, we
105// don't want the balancer to receive this state change. So before
106// tearDown() on the old ac, ac.acbw (acWrapper) will be set to nil, and
107// this function will be called with (nil, Shutdown). We don't need to call
108// balancer method in this case.
109if sc == nil {
110return
111}
112ccb.scBuffer.Put(&scStateUpdate{
113sc: sc,
114state: s,
115err: err,
116})
117}
118
119func (ccb *ccBalancerWrapper) updateClientConnState(ccs *balancer.ClientConnState) error {
120ccb.balancerMu.Lock()
121defer ccb.balancerMu.Unlock()
122return ccb.balancer.UpdateClientConnState(*ccs)
123}
124
125func (ccb *ccBalancerWrapper) resolverError(err error) {
126ccb.balancerMu.Lock()
127ccb.balancer.ResolverError(err)
128ccb.balancerMu.Unlock()
129}
130
131func (ccb *ccBalancerWrapper) NewSubConn(addrs []resolver.Address, opts balancer.NewSubConnOptions) (balancer.SubConn, error) {
132if len(addrs) <= 0 {
133return nil, fmt.Errorf("grpc: cannot create SubConn with empty address list")
134}
135ccb.mu.Lock()
136defer ccb.mu.Unlock()
137if ccb.subConns == nil {
138return nil, fmt.Errorf("grpc: ClientConn balancer wrapper was closed")
139}
140ac, err := ccb.cc.newAddrConn(addrs, opts)
141if err != nil {
142return nil, err
143}
144acbw := &acBalancerWrapper{ac: ac}
145acbw.ac.mu.Lock()
146ac.acbw = acbw
147acbw.ac.mu.Unlock()
148ccb.subConns[acbw] = struct{}{}
149return acbw, nil
150}
151
152func (ccb *ccBalancerWrapper) RemoveSubConn(sc balancer.SubConn) {
153acbw, ok := sc.(*acBalancerWrapper)
154if !ok {
155return
156}
157ccb.mu.Lock()
158defer ccb.mu.Unlock()
159if ccb.subConns == nil {
160return
161}
162delete(ccb.subConns, acbw)
163ccb.cc.removeAddrConn(acbw.getAddrConn(), errConnDrain)
164}
165
166func (ccb *ccBalancerWrapper) UpdateState(s balancer.State) {
167ccb.mu.Lock()
168defer ccb.mu.Unlock()
169if ccb.subConns == nil {
170return
171}
172// Update picker before updating state. Even though the ordering here does
173// not matter, it can lead to multiple calls of Pick in the common start-up
174// case where we wait for ready and then perform an RPC. If the picker is
175// updated later, we could call the "connecting" picker when the state is
176// updated, and then call the "ready" picker after the picker gets updated.
177ccb.cc.blockingpicker.updatePicker(s.Picker)
178ccb.cc.csMgr.updateState(s.ConnectivityState)
179}
180
181func (ccb *ccBalancerWrapper) ResolveNow(o resolver.ResolveNowOptions) {
182ccb.cc.resolveNow(o)
183}
184
185func (ccb *ccBalancerWrapper) Target() string {
186return ccb.cc.target
187}
188
189// acBalancerWrapper is a wrapper on top of ac for balancers.
190// It implements balancer.SubConn interface.
191type acBalancerWrapper struct {
192mu sync.Mutex
193ac *addrConn
194}
195
196func (acbw *acBalancerWrapper) UpdateAddresses(addrs []resolver.Address) {
197acbw.mu.Lock()
198defer acbw.mu.Unlock()
199if len(addrs) <= 0 {
200acbw.ac.tearDown(errConnDrain)
201return
202}
203if !acbw.ac.tryUpdateAddrs(addrs) {
204cc := acbw.ac.cc
205opts := acbw.ac.scopts
206acbw.ac.mu.Lock()
207// Set old ac.acbw to nil so the Shutdown state update will be ignored
208// by balancer.
209//
210// TODO(bar) the state transition could be wrong when tearDown() old ac
211// and creating new ac, fix the transition.
212acbw.ac.acbw = nil
213acbw.ac.mu.Unlock()
214acState := acbw.ac.getState()
215acbw.ac.tearDown(errConnDrain)
216
217if acState == connectivity.Shutdown {
218return
219}
220
221ac, err := cc.newAddrConn(addrs, opts)
222if err != nil {
223channelz.Warningf(logger, acbw.ac.channelzID, "acBalancerWrapper: UpdateAddresses: failed to newAddrConn: %v", err)
224return
225}
226acbw.ac = ac
227ac.mu.Lock()
228ac.acbw = acbw
229ac.mu.Unlock()
230if acState != connectivity.Idle {
231ac.connect()
232}
233}
234}
235
236func (acbw *acBalancerWrapper) Connect() {
237acbw.mu.Lock()
238defer acbw.mu.Unlock()
239acbw.ac.connect()
240}
241
242func (acbw *acBalancerWrapper) getAddrConn() *addrConn {
243acbw.mu.Lock()
244defer acbw.mu.Unlock()
245return acbw.ac
246}
247