• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2020 The gRPC 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 io.grpc.xds;
18 
19 import static com.google.common.base.Preconditions.checkNotNull;
20 
21 import com.google.common.annotations.VisibleForTesting;
22 import io.grpc.Context;
23 import io.grpc.internal.ExponentialBackoffPolicy;
24 import io.grpc.internal.GrpcUtil;
25 import io.grpc.internal.ObjectPool;
26 import io.grpc.internal.SharedResourceHolder;
27 import io.grpc.internal.TimeProvider;
28 import io.grpc.xds.Bootstrapper.BootstrapInfo;
29 import io.grpc.xds.XdsClientImpl.XdsChannelFactory;
30 import io.grpc.xds.XdsNameResolverProvider.XdsClientPoolFactory;
31 import io.grpc.xds.internal.security.TlsContextManagerImpl;
32 import java.util.Map;
33 import java.util.concurrent.ScheduledExecutorService;
34 import java.util.concurrent.atomic.AtomicReference;
35 import javax.annotation.Nullable;
36 import javax.annotation.concurrent.GuardedBy;
37 import javax.annotation.concurrent.ThreadSafe;
38 
39 /**
40  * The global factory for creating a singleton {@link XdsClient} instance to be used by all gRPC
41  * clients in the process.
42  */
43 @ThreadSafe
44 final class SharedXdsClientPoolProvider implements XdsClientPoolFactory {
45 
46   private final Bootstrapper bootstrapper;
47   private final Object lock = new Object();
48   private final AtomicReference<Map<String, ?>> bootstrapOverride = new AtomicReference<>();
49   private volatile ObjectPool<XdsClient> xdsClientPool;
50 
SharedXdsClientPoolProvider()51   SharedXdsClientPoolProvider() {
52     this(new BootstrapperImpl());
53   }
54 
55   @VisibleForTesting
SharedXdsClientPoolProvider(Bootstrapper bootstrapper)56   SharedXdsClientPoolProvider(Bootstrapper bootstrapper) {
57     this.bootstrapper = checkNotNull(bootstrapper, "bootstrapper");
58   }
59 
getDefaultProvider()60   static SharedXdsClientPoolProvider getDefaultProvider() {
61     return SharedXdsClientPoolProviderHolder.instance;
62   }
63 
64   @Override
setBootstrapOverride(Map<String, ?> bootstrap)65   public void setBootstrapOverride(Map<String, ?> bootstrap) {
66     bootstrapOverride.set(bootstrap);
67   }
68 
69   @Override
70   @Nullable
get()71   public ObjectPool<XdsClient> get() {
72     return xdsClientPool;
73   }
74 
75   @Override
getOrCreate()76   public ObjectPool<XdsClient> getOrCreate() throws XdsInitializationException {
77     ObjectPool<XdsClient> ref = xdsClientPool;
78     if (ref == null) {
79       synchronized (lock) {
80         ref = xdsClientPool;
81         if (ref == null) {
82           BootstrapInfo bootstrapInfo;
83           Map<String, ?> rawBootstrap = bootstrapOverride.get();
84           if (rawBootstrap != null) {
85             bootstrapInfo = bootstrapper.bootstrap(rawBootstrap);
86           } else {
87             bootstrapInfo = bootstrapper.bootstrap();
88           }
89           if (bootstrapInfo.servers().isEmpty()) {
90             throw new XdsInitializationException("No xDS server provided");
91           }
92           ref = xdsClientPool = new RefCountedXdsClientObjectPool(bootstrapInfo);
93         }
94       }
95     }
96     return ref;
97   }
98 
99   private static class SharedXdsClientPoolProviderHolder {
100     private static final SharedXdsClientPoolProvider instance = new SharedXdsClientPoolProvider();
101   }
102 
103   @ThreadSafe
104   @VisibleForTesting
105   static class RefCountedXdsClientObjectPool implements ObjectPool<XdsClient> {
106     private final Context context = Context.ROOT;
107     private final BootstrapInfo bootstrapInfo;
108     private final Object lock = new Object();
109     @GuardedBy("lock")
110     private ScheduledExecutorService scheduler;
111     @GuardedBy("lock")
112     private XdsClient xdsClient;
113     @GuardedBy("lock")
114     private int refCount;
115 
116     @VisibleForTesting
RefCountedXdsClientObjectPool(BootstrapInfo bootstrapInfo)117     RefCountedXdsClientObjectPool(BootstrapInfo bootstrapInfo) {
118       this.bootstrapInfo = checkNotNull(bootstrapInfo);
119     }
120 
121     @Override
getObject()122     public XdsClient getObject() {
123       synchronized (lock) {
124         if (refCount == 0) {
125           scheduler = SharedResourceHolder.get(GrpcUtil.TIMER_SERVICE);
126           xdsClient = new XdsClientImpl(
127               XdsChannelFactory.DEFAULT_XDS_CHANNEL_FACTORY,
128               bootstrapInfo,
129               context,
130               scheduler,
131               new ExponentialBackoffPolicy.Provider(),
132               GrpcUtil.STOPWATCH_SUPPLIER,
133               TimeProvider.SYSTEM_TIME_PROVIDER,
134               new TlsContextManagerImpl(bootstrapInfo));
135         }
136         refCount++;
137         return xdsClient;
138       }
139     }
140 
141     @Override
returnObject(Object object)142     public XdsClient returnObject(Object object) {
143       synchronized (lock) {
144         refCount--;
145         if (refCount == 0) {
146           xdsClient.shutdown();
147           xdsClient = null;
148           scheduler = SharedResourceHolder.release(GrpcUtil.TIMER_SERVICE, scheduler);
149         }
150         return null;
151       }
152     }
153 
154     @VisibleForTesting
155     @Nullable
getXdsClientForTest()156     XdsClient getXdsClientForTest() {
157       synchronized (lock) {
158         return xdsClient;
159       }
160     }
161   }
162 }
163