4, SOFA RPC parsing source code - Load balancing articles

We speak in front of a service call which will find a piece of code like this:

   // 创建代理类
   proxyIns = (T) ProxyFactory.buildProxy(consumerConfig.getProxy(), consumerConfig.getProxyClass(),proxyInvoker);

This code is what we call load according to SPI agent class, this class can be specified in the proxy service call time, jdk or javassist two types of difference between the two I am here simply to talk about: jdk agent is based on the reflection generated proxy class, so must request the proxy class is an abstract class or an interface, and are based javassist ASM bytecode art, does not require the type defined in the class, which is generated using the techniques asm subclass proxy class, this distinction is generally, then we look when the next call in the end is how we called, it is how to use load balancing it? We have to JDK proxy was it!

    // 创建代理类
    proxyIns = (T) ProxyFactory.buildProxy(consumerConfig.getProxy(), consumerConfig.getProxyClass(),proxyInvoker);

    /**
     * 构建代理类实例
     *
     * @param proxyType    代理类型
     * @param clazz        原始类
     * @param proxyInvoker 代码执行的Invoker
     * @param <T>          类型
     * @return 代理类实例
     * @throws Exception
     */
    public static <T> T buildProxy(String proxyType, Class<T> clazz, Invoker proxyInvoker) throws Exception {
        try {
            // 使用spi加载代理类,没设置则使用的javassist
            ExtensionClass<Proxy> ext = ExtensionLoaderFactory.getExtensionLoader(Proxy.class)
                .getExtensionClass(proxyType);
            if (ext == null) {
                throw ExceptionUtils.buildRuntime("consumer.proxy", proxyType,
                    "Unsupported proxy of client!");
            }
            Proxy proxy = ext.getExtInstance();
            return proxy.getProxy(clazz, proxyInvoker);
        } catch (SofaRpcRuntimeException e) {
            throw e;
        } catch (Throwable e) {
            throw new SofaRpcRuntimeException(e.getMessage(), e);
        }
    }
/**
 * Proxy implement base on jdk
 *
 * @author <a href=mailto:[email protected]>GengZhang</a>
 */
@Extension("jdk")
public class JDKProxy implements Proxy {

    @Override
    public <T> T getProxy(Class<T> interfaceClass, Invoker proxyInvoker) {
        InvocationHandler handler = new JDKInvocationHandler(interfaceClass, proxyInvoker);
        ClassLoader classLoader = ClassLoaderUtils.getCurrentClassLoader();
        // 通过反射获取数据
        T result = (T) java.lang.reflect.Proxy.newProxyInstance(classLoader,
            new Class[] { interfaceClass }, handler);
        return result;
    }

    @Override
    public Invoker getInvoker(Object proxyObject) {
        return parseInvoker(proxyObject);
    }

    /**
     * Parse proxy invoker from proxy object
     *
     * @param proxyObject Proxy object
     * @return proxy invoker
     */
    public static Invoker parseInvoker(Object proxyObject) {
        InvocationHandler handler = java.lang.reflect.Proxy.getInvocationHandler(proxyObject);
        if (handler instanceof JDKInvocationHandler) {
            return ((JDKInvocationHandler) handler).getProxyInvoker();
        }
        return null;
    }
}

Now get a proxy object, then we look at how he is handling requests it! JDKInvocationHandler # invoke method

    @Override
    public Object invoke(Object proxy, Method method, Object[] paramValues)
        throws Throwable {
        // 获取方法名称
        String methodName = method.getName();
        // 获取方法参数
        Class[] paramTypes = method.getParameterTypes();
        // 如果是调用toString、hashCode、equals 则直接调用本类的方法不进行远程调用
        if ("toString".equals(methodName) && paramTypes.length == 0) {
            return proxyInvoker.toString();
        } else if ("hashCode".equals(methodName) && paramTypes.length == 0) {
            return proxyInvoker.hashCode();
        } else if ("equals".equals(methodName) && paramTypes.length == 1) {
            Object another = paramValues[0];
            return proxy == another ||
                (proxy.getClass().isInstance(another) && proxyInvoker.equals(JDKProxy.parseInvoker(another)));
        }
        // 构建请求
        SofaRequest sofaRequest = MessageBuilder.buildSofaRequest(method.getDeclaringClass(),
            method, paramTypes, paramValues);
        // invoke调用开始
        SofaResponse response = proxyInvoker.invoke(sofaRequest);
        if (response.isError()) {
            throw new SofaRpcException(RpcErrorType.SERVER_UNDECLARED_ERROR, response.getErrorMsg());
        }
        Object ret = response.getAppResponse();
        if (ret instanceof Throwable) {
            throw (Throwable) ret;
        } else {
            if (ret == null) {
                return ClassUtils.getDefaultPrimitiveValue(method.getReturnType());
            }
            return ret;
        }
    }

proxyInvoker.invoke (sofaRequest); this sentence when it comes to initialize the proxy invoker we return we are ClientProxyInvoker, so we click-ClientProxyInvoker # invoke

   /**
     * proxy拦截的调用
     *
     * @param request 请求消息
     * @return 调用结果
     */
    @Override
    public SofaResponse invoke(SofaRequest request) throws SofaRpcException {
        SofaResponse response = null;
        Throwable throwable = null;
        try {
            // 将请求的上下文放入Deque
            RpcInternalContext.pushContext();   
            RpcInternalContext context = RpcInternalContext.getContext();
            context.setProviderSide(false);
            // 包装请求
            decorateRequest(request);
            try {
                // 产生开始调用事件
                if (EventBus.isEnable(ClientStartInvokeEvent.class)) {
                    EventBus.post(new ClientStartInvokeEvent(request));
                }
                // 得到结果
                response = cluster.invoke(request);
            } catch (SofaRpcException e) {
                throwable = e;
                throw e;
            } finally {
                // 产生调用结束事件
                if (!request.isAsync()) {
                    if (EventBus.isEnable(ClientEndInvokeEvent.class)) {
                        EventBus.post(new ClientEndInvokeEvent(request, response, throwable));
                    }
                }
            }
            // 包装响应
            decorateResponse(response);
            return response;
        } finally {
            RpcInternalContext.removeContext();
            RpcInternalContext.popContext();
        }
    }

We first look at the packaging request decorateRequest (request), as previously set invoker is DefaultClientProxyInvoker so this method is invoked
DefaultClientProxyInvoker # decorateRequest, rather than ClientProxyInvoker # decorateRequest, this method is empty

   @Override
    protected void decorateRequest(SofaRequest request) {
        // 公共的设置,空的
        super.decorateRequest(request);
        // 缓存是为了加快速度
        request.setTargetServiceUniqueName(serviceName);
        // 设置序列化类型
        request.setSerializeType(serializeType == null ? 0 : serializeType);
        // 是否是泛化调用,限于篇幅泛化调用的概念可以先自行百度一下
        if (!consumerConfig.isGeneric()) {
            // 找到调用类型, generic的时候类型在filter里进行判断
            request.setInvokeType(consumerConfig.getMethodInvokeType(request.getMethodName()));
        }
        // 取出第一个context,如果我们需要使用回调和透传参数的话这个就会拿到值,否则如果不提前设置的话都是null的
        // 比如 RpcInvokeContext.getContext.setsetResponseCallback或putRequestBaggage这种等
        RpcInvokeContext invokeCtx = RpcInvokeContext.peekContext();
        // 取出我们上面存储的上下文context
        RpcInternalContext internalContext = RpcInternalContext.getContext();
        if (invokeCtx != null) {
            // 如果用户设置了调用级别回调函数
            SofaResponseCallback responseCallback = invokeCtx.getResponseCallback();
            if (responseCallback != null) {
                request.setSofaResponseCallback(responseCallback);
                invokeCtx.setResponseCallback(null); // 一次性用完
                invokeCtx.put(RemotingConstants.INVOKE_CTX_IS_ASYNC_CHAIN,
                    isSendableResponseCallback(responseCallback));
            }
            // 如果用户设置了调用级别超时时间
            Integer timeout = invokeCtx.getTimeout();
            if (timeout != null) {
                request.setTimeout(timeout);
                invokeCtx.setTimeout(null);// 一次性用完
            }
            // 如果用户指定了调用的URL
            String targetURL = invokeCtx.getTargetURL();
            if (targetURL != null) {
                internalContext.setAttachment(HIDDEN_KEY_PINPOINT, targetURL);
                invokeCtx.setTargetURL(null);// 一次性用完
            }
            // 如果用户指定了透传数据
            if (RpcInvokeContext.isBaggageEnable()) {
                // 需要透传
                BaggageResolver.carryWithRequest(invokeCtx, request);
                internalContext.setAttachment(HIDDEN_KEY_INVOKE_CONTEXT, invokeCtx);
            }
        }
        // 是否开启附件传递功能
        if (RpcInternalContext.isAttachmentEnable()) {
            internalContext.setAttachment(INTERNAL_KEY_APP_NAME, consumerConfig.getAppName());
            internalContext.setAttachment(INTERNAL_KEY_PROTOCOL_NAME, consumerConfig.getProtocol());
        }

        // 额外属性通过HEAD传递给服务端
        request.addRequestProp(RemotingConstants.HEAD_APP_NAME, consumerConfig.getAppName());
        request.addRequestProp(RemotingConstants.HEAD_PROTOCOL, consumerConfig.getProtocol());
    }

We look at their invoke method, the first call is AbstractCluster # invoke method:

    @Override
    public SofaResponse invoke(SofaRequest request) throws SofaRpcException {
        SofaResponse response = null;
        try {
            // 做一些初始化检查,例如未连接可以连接
            checkClusterState();
            // 开始调用
            countOfInvoke.incrementAndGet(); // 计数+1   
            // 调用实现类的doInvoke
            response = doInvoke(request);
            return response;
        } catch (SofaRpcException e) {
            // 客户端收到异常(客户端自己的异常)
            throw e;
        } finally {
            countOfInvoke.decrementAndGet(); // 计数-1
        }
    }

We can see here he is made to load the default cluster configuration file FailoverCluster # doInvoke from the previous DefaultConsumerBootstrap

    public SofaResponse doInvoke(SofaRequest request) throws SofaRpcException {
        String methodName = request.getMethodName();
        // 获取配置的失败重试次数
        int retries = consumerConfig.getMethodRetries(methodName);
        int time = 0;
        SofaRpcException throwable = null;// 异常日志
        List<ProviderInfo> invokedProviderInfos = new ArrayList<ProviderInfo>(retries + 1);
        do {
            // 选择调用的provider,这里也就是我们讲的负载均衡的选择啦
            ProviderInfo providerInfo = select(request, invokedProviderInfos);
            try {
                // 执行过滤器链,得到返回结果
                SofaResponse response = filterChain(providerInfo, request);
                if (response != null) {
                    if (throwable != null) {
                        if (LOGGER.isWarnEnabled(consumerConfig.getAppName())) {
                            LOGGER.warnWithApp(consumerConfig.getAppName(),
                                LogCodes.getLog(LogCodes.WARN_SUCCESS_BY_RETRY,
                                    throwable.getClass() + ":" + throwable.getMessage(),
                                    invokedProviderInfos));
                        }
                    }
                    return response;
                } else {
                    throwable = new SofaRpcException(RpcErrorType.CLIENT_UNDECLARED_ERROR,
                        "Failed to call " + request.getInterfaceName() + "." + methodName
                            + " on remote server " + providerInfo + ", return null");
                    time++;
                }
            } catch (SofaRpcException e) { // 服务端异常+ 超时异常 才发起rpc异常重试
                if (e.getErrorType() == RpcErrorType.SERVER_BUSY
                    || e.getErrorType() == RpcErrorType.CLIENT_TIMEOUT) {
                    throwable = e;
                    time++;
                } else {
                    throw e;
                }
            } catch (Exception e) { // 其它异常不重试
                throw new SofaRpcException(RpcErrorType.CLIENT_UNDECLARED_ERROR,
                    "Failed to call " + request.getInterfaceName() + "." + request.getMethodName()
                        + " on remote server: " + providerInfo + ", cause by unknown exception: "
                        + e.getClass().getName() + ", message is: " + e.getMessage(), e);
            } finally {
                if (RpcInternalContext.isAttachmentEnable()) {
                    RpcInternalContext.getContext().setAttachment(RpcConstants.INTERNAL_KEY_INVOKE_TIMES,
                        time + 1); // 重试次数
                }
            }
            invokedProviderInfos.add(providerInfo);
        } while (time <= retries);

        throw throwable;
    }

We have a step by step analysis, look at our side the most important part of the selection provider, that is, we talk about load balancing!

    /**
     * 根据规则进行负载均衡
     *
     * @param message              调用对象
     * @param invokedProviderInfos 已调用列表
     * @return 一个可用的provider
     * @throws SofaRpcException rpc异常
     */
    protected ProviderInfo select(SofaRequest message, List<ProviderInfo> invokedProviderInfos)
        throws SofaRpcException {
        // 粘滞连接,当前连接可用
        if (consumerConfig.isSticky()) {
            if (lastProviderInfo != null) {
                ProviderInfo providerInfo = lastProviderInfo;
                ClientTransport lastTransport = connectionHolder.getAvailableClientTransport(providerInfo);
                if (lastTransport != null && lastTransport.isAvailable()) {
                    checkAlias(providerInfo, message);
                    return providerInfo;
                }
            }
        }
        // 原始服务列表数据 --> 路由结果
        List<ProviderInfo> providerInfos = routerChain.route(message, null);

        //保存一下原始地址,为了打印
        List<ProviderInfo> orginalProviderInfos = new ArrayList<ProviderInfo>(providerInfos);

        if (CommonUtils.isEmpty(providerInfos)) {
            throw noAvailableProviderException(message.getTargetServiceUniqueName());
        }
        // 这个主要是重试的时候将异常的调用剔除掉,后面那个总数大于已调用数的条件是为了防止剔除后没有provider了
        if (CommonUtils.isNotEmpty(invokedProviderInfos) && providerInfos.size() > invokedProviderInfos.size()) { 
            providerInfos.removeAll(invokedProviderInfos);// 已经调用异常的本次不再重试
        }

        String targetIP = null;
        ProviderInfo providerInfo;
        RpcInternalContext context = RpcInternalContext.peekContext();
        if (context != null) {
            targetIP = (String) RpcInternalContext.getContext().getAttachment(RpcConstants.HIDDEN_KEY_PINPOINT);
        }
        if (StringUtils.isNotBlank(targetIP)) {
            // 如果指定了调用地址
            providerInfo = selectPinpointProvider(targetIP, providerInfos);
            if (providerInfo == null) {
                // 指定的不存在
                throw unavailableProviderException(message.getTargetServiceUniqueName(), targetIP);
            }
            ClientTransport clientTransport = selectByProvider(message, providerInfo);
            if (clientTransport == null) {
                // 指定的不存在或已死,抛出异常
                throw unavailableProviderException(message.getTargetServiceUniqueName(), targetIP);
            }
            return providerInfo;
        } else {
            do {
                // 再进行负载均衡筛选,前面在发布consumerConfig的时候有初始化loadBalancer,默认使用的random
                providerInfo = loadBalancer.select(message, providerInfos);
                ClientTransport transport = selectByProvider(message, providerInfo);
                if (transport != null) {
                    return providerInfo;
                }
                providerInfos.remove(providerInfo);
            } while (!providerInfos.isEmpty());
        }
        throw unavailableProviderException(message.getTargetServiceUniqueName(),
            convertProviders2Urls(orginalProviderInfos));
    }

For loadBalancer.select (message, providerInfos); we went in point is AbstractLoadBalancer # select

    @Override
    public ProviderInfo select(SofaRequest request, List<ProviderInfo> providerInfos) throws SofaRpcException {
        if (providerInfos.size() == 0) {
            throw noAvailableProviderException(request.getTargetServiceUniqueName());
        }
        // 如果只有一个provider就会直接使用这个
        if (providerInfos.size() == 1) {
            return providerInfos.get(0);
        } else {
            // 否则调用子类的筛选方法,我们以random为例,也就是RandomLoadBalancer#doSelect
            return doSelect(request, providerInfos);
        }
    }
    
    @Override
    public ProviderInfo doSelect(SofaRequest invocation, List<ProviderInfo> providerInfos) {
        ProviderInfo providerInfo = null;
        int size = providerInfos.size(); // 总个数
        int totalWeight = 0; // 总权重
        boolean isWeightSame = true; // 权重是否都一样
        for (int i = 0; i < size; i++) {
            // 这里的权重数据来自于provider发布的时候的设置,有一个weight属性可以设置
            int weight = getWeight(providerInfos.get(i));
            totalWeight += weight; // 累计总权重
            if (isWeightSame && i > 0 && weight != getWeight(providerInfos.get(i - 1))) {
                isWeightSame = false; // 计算所有权重是否一样
            }
        }
        if (totalWeight > 0 && !isWeightSame) {
            // 如果权重不相同且权重大于0则按总权重数随机
            int offset = random.nextInt(totalWeight);
            // 并确定随机值落在哪个片断上
            for (int i = 0; i < size; i++) {
                // 使用总权重随机取值,然后落区间,那个减了之后小于0则落在哪个区间就直接取它既可
                offset -= getWeight(providerInfos.get(i));
                if (offset < 0) {
                    providerInfo = providerInfos.get(i);
                    break;
                }
            }
        } else {
            // 如果权重相同或权重为0则均等随机
            providerInfo = providerInfos.get(random.nextInt(size));
        }
        return providerInfo;
    }

Then the next line mentioned ClientTransport transport = selectByProvider (message, providerInfo);

    /**
     * 得到provider得到连接
     *
     * @param message      调用对象
     * @param providerInfo 指定Provider
     * @return 一个可用的transport或者null
     */
    protected ClientTransport selectByProvider(SofaRequest message, ProviderInfo providerInfo) {
        // 这里面就是前面维护的存活列表,亚健康列表和不可用列表或者第一次初始化下列表
        ClientTransport transport = connectionHolder.getAvailableClientTransport(providerInfo);
        if (transport != null) {
            // 获得服务提供者的客户端传输者
            if (transport.isAvailable()) {
                // 将这个provider设置为最后一个调用的provider
                lastProviderInfo = providerInfo;
                checkAlias(providerInfo, message); //检查分组
                return transport;
            } else {
                connectionHolder.setUnavailable(providerInfo, transport);
            }
        }
        return null;
    }

After we finished load balancing on finished here, posted at the front of the source code:

    public SofaResponse doInvoke(SofaRequest request) throws SofaRpcException {
        String methodName = request.getMethodName();
        int retries = consumerConfig.getMethodRetries(methodName);
        int time = 0;
        SofaRpcException throwable = null;// 异常日志
        List<ProviderInfo> invokedProviderInfos = new ArrayList<ProviderInfo>(retries + 1);
        do {
            ProviderInfo providerInfo = select(request, invokedProviderInfos);
            try {
                SofaResponse response = filterChain(providerInfo, request);
                if (response != null) {
                    if (throwable != null) {
                        if (LOGGER.isWarnEnabled(consumerConfig.getAppName())) {
                            LOGGER.warnWithApp(consumerConfig.getAppName(),
                                LogCodes.getLog(LogCodes.WARN_SUCCESS_BY_RETRY,
                                    throwable.getClass() + ":" + throwable.getMessage(),
                                    invokedProviderInfos));
                        }
                    }
                    return response;
                } else {
                    throwable = new SofaRpcException(RpcErrorType.CLIENT_UNDECLARED_ERROR,
                        "Failed to call " + request.getInterfaceName() + "." + methodName
                            + " on remote server " + providerInfo + ", return null");
                    time++;
                }
            } catch (SofaRpcException e) { // 服务端异常+ 超时异常 才发起rpc异常重试
                if (e.getErrorType() == RpcErrorType.SERVER_BUSY
                    || e.getErrorType() == RpcErrorType.CLIENT_TIMEOUT) {
                    throwable = e;
                    time++;
                } else {
                    throw e;
                }
            } catch (Exception e) { // 其它异常不重试
                throw new SofaRpcException(RpcErrorType.CLIENT_UNDECLARED_ERROR,
                    "Failed to call " + request.getInterfaceName() + "." + request.getMethodName()
                        + " on remote server: " + providerInfo + ", cause by unknown exception: "
                        + e.getClass().getName() + ", message is: " + e.getMessage(), e);
            } finally {
                if (RpcInternalContext.isAttachmentEnable()) {
                    RpcInternalContext.getContext().setAttachment(RpcConstants.INTERNAL_KEY_INVOKE_TIMES,
                        time + 1); // 重试次数
                }
            }
            invokedProviderInfos.add(providerInfo);
        } while (time <= retries);

        throw throwable;
    }

As can be seen from the above posted the source code, we ProviderInfo providerInfo = select (request, invokedProviderInfos ); analysis has been completed, leaving only the last step
SofaResponse response = filterChain (providerInfo, request ); this is a filter chain of calls, we pick a send requests to speak about, ConsumerInvoker # invoke, in fact, nothing to speak of

    public SofaResponse invoke(SofaRequest sofaRequest) throws SofaRpcException {
        // 设置下服务器应用
        ProviderInfo providerInfo = RpcInternalContext.getContext().getProviderInfo();
        String appName = providerInfo.getStaticAttr(ProviderInfoAttrs.ATTR_APP_NAME);
        if (StringUtils.isNotEmpty(appName)) {
            sofaRequest.setTargetAppName(appName);
        }
        // 目前只是通过client发送给服务端
        return consumerBootstrap.getCluster().sendMsg(providerInfo, sofaRequest);
    }

These are used in the context of load balancing all resolved, the next we summarize:
1, to find routing address all of Providers
2, Screening Provider
3, get clientTransport (sent using client)
4, call chain execution filterInovker

Guess you like

Origin www.cnblogs.com/paul-lb/p/11498793.html