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