1 /* 2 * Copyright 2016 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.grpclb; 18 19 import static com.google.common.base.Preconditions.checkNotNull; 20 import static com.google.common.base.Preconditions.checkState; 21 22 import com.google.common.annotations.VisibleForTesting; 23 import com.google.common.base.Stopwatch; 24 import io.grpc.Attributes; 25 import io.grpc.ChannelLogger.ChannelLogLevel; 26 import io.grpc.Context; 27 import io.grpc.EquivalentAddressGroup; 28 import io.grpc.LoadBalancer; 29 import io.grpc.Status; 30 import io.grpc.grpclb.GrpclbState.Mode; 31 import io.grpc.internal.BackoffPolicy; 32 import io.grpc.internal.TimeProvider; 33 import java.util.ArrayList; 34 import java.util.Collections; 35 import java.util.List; 36 import javax.annotation.Nullable; 37 38 /** 39 * A {@link LoadBalancer} that uses the GRPCLB protocol. 40 * 41 * <p>Optionally, when requested by the naming system, will delegate the work to a local pick-first 42 * or round-robin balancer. 43 */ 44 class GrpclbLoadBalancer extends LoadBalancer { 45 46 private static final GrpclbConfig DEFAULT_CONFIG = GrpclbConfig.create(Mode.ROUND_ROBIN); 47 48 private final Helper helper; 49 private final Context context; 50 private final TimeProvider time; 51 private final Stopwatch stopwatch; 52 private final SubchannelPool subchannelPool; 53 private final BackoffPolicy.Provider backoffPolicyProvider; 54 55 private GrpclbConfig config = DEFAULT_CONFIG; 56 57 // All mutable states in this class are mutated ONLY from Channel Executor 58 @Nullable 59 private GrpclbState grpclbState; 60 GrpclbLoadBalancer( Helper helper, Context context, SubchannelPool subchannelPool, TimeProvider time, Stopwatch stopwatch, BackoffPolicy.Provider backoffPolicyProvider)61 GrpclbLoadBalancer( 62 Helper helper, 63 Context context, 64 SubchannelPool subchannelPool, 65 TimeProvider time, 66 Stopwatch stopwatch, 67 BackoffPolicy.Provider backoffPolicyProvider) { 68 this.helper = checkNotNull(helper, "helper"); 69 this.context = checkNotNull(context, "context"); 70 this.time = checkNotNull(time, "time provider"); 71 this.stopwatch = checkNotNull(stopwatch, "stopwatch"); 72 this.backoffPolicyProvider = checkNotNull(backoffPolicyProvider, "backoffPolicyProvider"); 73 this.subchannelPool = checkNotNull(subchannelPool, "subchannelPool"); 74 recreateStates(); 75 checkNotNull(grpclbState, "grpclbState"); 76 } 77 78 @Override acceptResolvedAddresses(ResolvedAddresses resolvedAddresses)79 public boolean acceptResolvedAddresses(ResolvedAddresses resolvedAddresses) { 80 Attributes attributes = resolvedAddresses.getAttributes(); 81 List<EquivalentAddressGroup> newLbAddresses = attributes.get(GrpclbConstants.ATTR_LB_ADDRS); 82 if (newLbAddresses == null) { 83 newLbAddresses = Collections.emptyList(); 84 } 85 if (newLbAddresses.isEmpty() && resolvedAddresses.getAddresses().isEmpty()) { 86 handleNameResolutionError( 87 Status.UNAVAILABLE.withDescription("No backend or balancer addresses found")); 88 return false; 89 } 90 List<EquivalentAddressGroup> overrideAuthorityLbAddresses = 91 new ArrayList<>(newLbAddresses.size()); 92 for (EquivalentAddressGroup lbAddr : newLbAddresses) { 93 String lbAddrAuthority = lbAddr.getAttributes().get(GrpclbConstants.ATTR_LB_ADDR_AUTHORITY); 94 if (lbAddrAuthority == null) { 95 throw new AssertionError( 96 "This is a bug: LB address " + lbAddr + " does not have an authority."); 97 } 98 Attributes attrs = lbAddr.getAttributes().toBuilder() 99 .set(EquivalentAddressGroup.ATTR_AUTHORITY_OVERRIDE, lbAddrAuthority) 100 .build(); 101 overrideAuthorityLbAddresses.add(new EquivalentAddressGroup(lbAddr.getAddresses(), attrs)); 102 } 103 104 List<EquivalentAddressGroup> newBackendServers = 105 Collections.unmodifiableList(resolvedAddresses.getAddresses()); 106 GrpclbConfig newConfig = (GrpclbConfig) resolvedAddresses.getLoadBalancingPolicyConfig(); 107 if (newConfig == null) { 108 newConfig = DEFAULT_CONFIG; 109 } 110 if (!config.equals(newConfig)) { 111 config = newConfig; 112 helper.getChannelLogger().log(ChannelLogLevel.INFO, "Config: " + newConfig); 113 recreateStates(); 114 } 115 grpclbState.handleAddresses(Collections.unmodifiableList(overrideAuthorityLbAddresses), 116 newBackendServers); 117 118 return true; 119 } 120 121 @Override requestConnection()122 public void requestConnection() { 123 if (grpclbState != null) { 124 grpclbState.requestConnection(); 125 } 126 } 127 resetStates()128 private void resetStates() { 129 if (grpclbState != null) { 130 grpclbState.shutdown(); 131 grpclbState = null; 132 } 133 } 134 recreateStates()135 private void recreateStates() { 136 resetStates(); 137 checkState(grpclbState == null, "Should've been cleared"); 138 grpclbState = 139 new GrpclbState( 140 config, helper, context, subchannelPool, time, stopwatch, backoffPolicyProvider); 141 } 142 143 @Override shutdown()144 public void shutdown() { 145 resetStates(); 146 } 147 148 @Override handleNameResolutionError(Status error)149 public void handleNameResolutionError(Status error) { 150 if (grpclbState != null) { 151 grpclbState.propagateError(error); 152 } 153 } 154 155 @Override canHandleEmptyAddressListFromNameResolution()156 public boolean canHandleEmptyAddressListFromNameResolution() { 157 return true; 158 } 159 160 @VisibleForTesting 161 @Nullable getGrpclbState()162 GrpclbState getGrpclbState() { 163 return grpclbState; 164 } 165 } 166