/* * Copyright 2016 The gRPC Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package io.grpc.grpclb; import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.VisibleForTesting; import io.grpc.Attributes; import io.grpc.ConnectivityStateInfo; import io.grpc.EquivalentAddressGroup; import io.grpc.InternalLogId; import io.grpc.InternalWithLogId; import io.grpc.LoadBalancer; import io.grpc.Status; import io.grpc.internal.BackoffPolicy; import io.grpc.internal.GrpcAttributes; import io.grpc.internal.ObjectPool; import io.grpc.internal.TimeProvider; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.ScheduledExecutorService; import javax.annotation.Nullable; /** * A {@link LoadBalancer} that uses the GRPCLB protocol. * *

Optionally, when requested by the naming system, will delegate the work to a local pick-first * or round-robin balancer. */ class GrpclbLoadBalancer extends LoadBalancer implements InternalWithLogId { private final InternalLogId logId = InternalLogId.allocate(getClass().getName()); private final SubchannelPool subchannelPool; private final ObjectPool timerServicePool; // All mutable states in this class are mutated ONLY from Channel Executor private ScheduledExecutorService timerService; @Nullable private GrpclbState grpclbState; GrpclbLoadBalancer( Helper helper, SubchannelPool subchannelPool, ObjectPool timerServicePool, TimeProvider time, BackoffPolicy.Provider backoffPolicyProvider) { checkNotNull(helper, "helper"); this.timerServicePool = checkNotNull(timerServicePool, "timerServicePool"); this.timerService = checkNotNull(timerServicePool.getObject(), "timerService"); checkNotNull(time, "time provider"); checkNotNull(backoffPolicyProvider, "backoffPolicyProvider"); this.subchannelPool = checkNotNull(subchannelPool, "subchannelPool"); this.subchannelPool.init(helper, timerService); grpclbState = new GrpclbState(helper, subchannelPool, time, timerService, backoffPolicyProvider, logId); } @Override public InternalLogId getLogId() { return logId; } @Override public void handleSubchannelState(Subchannel subchannel, ConnectivityStateInfo newState) { // grpclbState should never be null here since handleSubchannelState cannot be called while the // lb is shutdown. grpclbState.handleSubchannelState(subchannel, newState); } @Override public void handleResolvedAddressGroups( List updatedServers, Attributes attributes) { // LB addresses and backend addresses are treated separately List newLbAddressGroups = new ArrayList<>(); List newBackendServers = new ArrayList<>(); for (EquivalentAddressGroup server : updatedServers) { String lbAddrAuthority = server.getAttributes().get(GrpcAttributes.ATTR_LB_ADDR_AUTHORITY); if (lbAddrAuthority != null) { newLbAddressGroups.add(new LbAddressGroup(server, lbAddrAuthority)); } else { newBackendServers.add(server); } } newLbAddressGroups = Collections.unmodifiableList(newLbAddressGroups); newBackendServers = Collections.unmodifiableList(newBackendServers); grpclbState.handleAddresses(newLbAddressGroups, newBackendServers); } private void resetStates() { if (grpclbState != null) { grpclbState.shutdown(); grpclbState = null; } } @Override public void shutdown() { resetStates(); timerService = timerServicePool.returnObject(timerService); } @Override public void handleNameResolutionError(Status error) { if (grpclbState != null) { grpclbState.propagateError(error); } } @VisibleForTesting @Nullable GrpclbState getGrpclbState() { return grpclbState; } }