All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.servicecomb.loadbalance.LoadbalanceHandler Maven / Gradle / Ivy

/*
 * Copyright 2017 Huawei Technologies Co., Ltd
 *
 * 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.servicecomb.loadbalance;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.servicecomb.core.Invocation;
import io.servicecomb.core.exception.ExceptionUtils;
import io.servicecomb.core.handler.impl.AbstractHandler;
import io.servicecomb.core.provider.consumer.SyncResponseExecutor;
import io.servicecomb.loadbalance.filter.IsolationServerListFilter;
import io.servicecomb.loadbalance.filter.TransactionControlFilter;
import io.servicecomb.swagger.invocation.AsyncResponse;
import io.servicecomb.swagger.invocation.Response;

import com.netflix.client.DefaultLoadBalancerRetryHandler;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RoundRobinRule;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.reactive.ExecutionContext;
import com.netflix.loadbalancer.reactive.ExecutionInfo;
import com.netflix.loadbalancer.reactive.ExecutionListener;
import com.netflix.loadbalancer.reactive.LoadBalancerCommand;
import com.netflix.loadbalancer.reactive.ServerOperation;

import rx.Observable;

/**
 * 负载均衡处理链
 *
 */
public class LoadbalanceHandler extends AbstractHandler {
    private static final Logger LOGGER = LoggerFactory.getLogger(LoadbalanceHandler.class);

    // 会给每个Microservice创建一个handler实例,因此这里的key为transportName,保证每个通道使用一个负载均衡策略
    private volatile Map loadBalancerMap = new ConcurrentHashMap<>();

    private final Object lock = new Object();

    private String policy = null;

    @Override
    public void handle(Invocation invocation, AsyncResponse asyncResp) throws Exception {
        String p = Configuration.INSTANCE.getPolicy(invocation.getMicroserviceName());
        if (this.policy != null && !this.policy.equals(p)) {
            //配置变化,需要重新生成所有的lb实例
            synchronized (lock) {
                loadBalancerMap.clear();
            }
        }
        this.policy = p;

        String transportName = invocation.getConfigTransportName();
        LoadBalancer lb = loadBalancerMap.get(transportName);
        if (null == lb) {
            synchronized (lock) {
                lb = loadBalancerMap.get(transportName);
                if (null == lb) {
                    lb = createLoadBalancer(invocation.getAppId(),
                            invocation.getMicroserviceName(),
                            invocation.getMicroserviceVersionRule(),
                            transportName);
                    loadBalancerMap.put(transportName, lb);
                }
            }
        }

        final LoadBalancer choosenLB = lb;

        setIsolationFilter(choosenLB, invocation);
        setTransactionControlFilter(choosenLB, invocation);

        if (!Configuration.INSTANCE.isRetryEnabled(invocation.getMicroserviceName())) {
            send(invocation, asyncResp, choosenLB);
        } else {
            sendWithRetry(invocation, asyncResp, choosenLB);
        }
    }

    protected void setIsolationFilter(LoadBalancer lb, Invocation invocation) {
        final String filterName = IsolationServerListFilter.class.getName();
        boolean isIsolationOpen = Configuration.INSTANCE.isIsolationFilterOpen(invocation.getMicroserviceName());
        if (!isIsolationOpen) {
            lb.removeFilter(filterName);
            return;
        }
        if (lb.containsFilter(filterName)) {
            return;
        }
        IsolationServerListFilter isolationListFilter = new IsolationServerListFilter();
        isolationListFilter.setMicroserviceName(invocation.getMicroserviceName());
        isolationListFilter.setLoadBalancerStats(lb.getLoadBalancerStats());
        lb.putFilter(filterName, isolationListFilter);
    }

    protected void setTransactionControlFilter(LoadBalancer lb, Invocation invocation) {
        final String filterName = TransactionControlFilter.class.getName();
        String policyClsName = Configuration.INSTANCE.getFlowsplitFilterPolicy(invocation.getMicroserviceName());
        if (policyClsName.isEmpty()) {
            lb.removeFilter(filterName);
            return;
        }
        if (lb.containsFilter(filterName)) {
            return;
        }
        try {
            Class policyCls = Class.forName(policyClsName);
            if (!TransactionControlFilter.class.isAssignableFrom(policyCls)) {
                String errMsg = String.format(
                        "Define instance filter %s in yaml, but not extends abstract class TransactionControlFilter.",
                        policyClsName);
                LOGGER.error(errMsg);
                throw new Error(errMsg);
            }
            TransactionControlFilter transactionControlFilter = (TransactionControlFilter) policyCls.newInstance();
            transactionControlFilter.setInvocation(invocation);
            transactionControlFilter.setLoadBalancerStats(lb.getLoadBalancerStats());
            lb.putFilter(filterName, transactionControlFilter);
        } catch (Throwable e) {
            String errMsg = "Fail to create instance of class: " + policyClsName;
            LOGGER.error(errMsg);
            throw new Error(errMsg, e);
        }
    }

    private void send(Invocation invocation, AsyncResponse asyncResp, final LoadBalancer choosenLB) throws Exception {
        long time = System.currentTimeMillis();
        CseServer server = (CseServer) choosenLB.chooseServer(invocation);
        if (null == server) {
            asyncResp.consumerFail(ExceptionUtils.lbAddressNotFound(invocation.getMicroserviceName(),
                    invocation.getMicroserviceVersionRule(),
                    invocation.getConfigTransportName()));
            return;
        }
        server.setLastVisitTime(time);
        choosenLB.getLoadBalancerStats().incrementNumRequests(server);
        invocation.setEndpoint(server.getEndpoint());
        invocation.next(resp -> {
            // this stats is for WeightedResponseTimeRule
            choosenLB.getLoadBalancerStats().noteResponseTime(server, (System.currentTimeMillis() - time));
            if (resp.isFailed()) {
                choosenLB.getLoadBalancerStats().incrementSuccessiveConnectionFailureCount(server);
            } else {
                choosenLB.getLoadBalancerStats().incrementActiveRequestsCount(server);
            }
            asyncResp.handle(resp);
        });
    }

    private void sendWithRetry(Invocation invocation, AsyncResponse asyncResp,
            final LoadBalancer choosenLB) throws Exception {
        long time = System.currentTimeMillis();
        // retry in loadbalance, 2.0 feature
        final int currentHandler = invocation.getHandlerIndex();

        final SyncResponseExecutor orginExecutor;
        final Executor newExecutor;
        if (invocation.getResponseExecutor() instanceof SyncResponseExecutor) {
            orginExecutor = (SyncResponseExecutor) invocation.getResponseExecutor();
            newExecutor = new Executor() {
                @Override
                public void execute(Runnable command) {
                    command.run(); // retry的场景,对于同步调用, 需要在网络线程中进行。同步调用的主线程已经被挂起,无法再主线程中进行重试。
                }
            };
            invocation.setResponseExecutor(newExecutor);
        } else {
            orginExecutor = null;
            newExecutor = null;
        }

        ExecutionListener listener = new ExecutionListener() {
            @Override
            public void onExecutionStart(ExecutionContext context) throws AbortExecutionException {
            }

            @Override
            public void onStartWithServer(ExecutionContext context,
                    ExecutionInfo info) throws AbortExecutionException {
            }

            @Override
            public void onExceptionWithServer(ExecutionContext context, Throwable exception,
                    ExecutionInfo info) {
                LOGGER.error("onExceptionWithServer msg {}; server {}",
                        exception.getMessage(),
                        context.getRequest().getEndpoint());
            }

            @Override
            public void onExecutionSuccess(ExecutionContext context, Response response,
                    ExecutionInfo info) {
                if (orginExecutor != null) {
                    orginExecutor.execute(() -> {
                        asyncResp.complete(response);
                    });
                } else {
                    asyncResp.complete(response);
                }
            }

            @Override
            public void onExecutionFailed(ExecutionContext context, Throwable finalException,
                    ExecutionInfo info) {
                if (orginExecutor != null) {
                    orginExecutor.execute(() -> {
                        asyncResp.consumerFail(finalException);
                    });
                } else {
                    asyncResp.consumerFail(finalException);
                }
            }
        };
        List> listeners = new ArrayList<>(0);
        listeners.add(listener);
        ExecutionContext context = new ExecutionContext<>(invocation, null, null, null);

        LoadBalancerCommand command = LoadBalancerCommand.builder()
                .withLoadBalancer(choosenLB)
                .withServerLocator(invocation)
                .withRetryHandler(new DefaultLoadBalancerRetryHandler(
                        Configuration.INSTANCE.getRetryOnSame(invocation.getMicroserviceName()),
                        Configuration.INSTANCE.getRetryOnNext(invocation.getMicroserviceName()), true))
                .withListeners(listeners)
                .withExecutionContext(context)
                .build();

        Observable observable = command.submit(new ServerOperation() {
            public Observable call(Server s) {
                return Observable.create(f -> {
                    try {
                        ((CseServer) s).setLastVisitTime(time);
                        choosenLB.getLoadBalancerStats().incrementNumRequests(s);
                        invocation.setHandlerIndex(currentHandler); // for retry
                        invocation.setEndpoint(((CseServer) s).getEndpoint());
                        invocation.next(resp -> {
                            if (resp.isFailed()) {
                                LOGGER.error("service call error, msg is {}, server is {} ",
                                        ((Throwable) resp.getResult()).getMessage(),
                                        s);
                                choosenLB.getLoadBalancerStats().incrementSuccessiveConnectionFailureCount(s);
                                f.onError(resp.getResult());
                            } else {
                                choosenLB.getLoadBalancerStats().incrementActiveRequestsCount(s);
                                choosenLB.getLoadBalancerStats().noteResponseTime(s,
                                        (System.currentTimeMillis() - time));
                                f.onNext(resp);
                                f.onCompleted();
                            }
                        });
                    } catch (Exception e) {
                        LOGGER.error("execution error, msg is " + e.getMessage());
                        f.onError(e);
                    }
                });
            }
        });

        observable.subscribe(response -> {
        }, error -> {
        }, () -> {
        });
    }

    private LoadBalancer createLoadBalancer(String appId, String microserviceName, String microserviceVersionRule,
            String transportName) {
        IRule rule;
        try {
            rule = (IRule) Class.forName(policy, true, Thread.currentThread().getContextClassLoader()).newInstance();
            LOGGER.info("Using loadbalance rule [{}] for service [{},{}].", policy, microserviceName, transportName);
        } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
            LOGGER.warn("Loadbalance rule [{}] is incorrect, using default RoundRobinRule.", policy);
            rule = new RoundRobinRule();
        }

        CseServerList serverList = new CseServerList(appId, microserviceName, microserviceVersionRule, transportName);
        LoadBalancer lb = new LoadBalancer(serverList, rule);
        return lb;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy