Research on Routing Rules of Dubbo Service Governance

1. There is not much to do today, so take some time to study the implementation of the routing rules of dubbo.

First, look at the main call process of the dubbo consumer (no drawing here), and add later:

Step 1: Create a consumer proxy: ReferenceConfig.createProxy, nothing special here, the typical C/S call design is all through JAVA Dynamic proxy or proxy implementation of Javassist, such as mybatis mapper.


Thinking: Suppose I want to implement the dubbo protocol routing of the API gateway: there is no doubt that I must use the get.set and init methods of ReferenceConfig. The next article will discuss the implementation details.

Step 2: Jump directly to the dynamic proxy code InvokerInvocationHandler in
here: Ignore the initialization of the invoker here (basically, the provider registration + the initialization after the consumer subscribes).

Thinking: If you were to implement an RPC client-side synchronous and asynchronous call, how would you implement it?

public class InvokerInvocationHandler implements InvocationHandler {
    private final Invoker<?> invoker;

    public InvokerInvocationHandler(Invoker<?> handler) {
        this.invoker = handler;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();
        Class[] parameterTypes = method.getParameterTypes();
        if (method.getDeclaringClass() == Object.class) {
            return method.invoke(this.invoker, args);
        } else if ("toString".equals(methodName) && parameterTypes.length == 0) {
            return this.invoker.toString();
        } else if ("hashCode".equals(methodName) && parameterTypes.length == 0) {
            return this.invoker.hashCode();
        } else {
            return "equals".equals(methodName) && parameterTypes.length == 1 ? this.invoker.equals(args[0]) : this.invoker.invoke(new RpcInvocation(method, args)).recreate();
        }
    }
}

The third step: AbstractClusterInvoker.invoke;
here is mainly to load a list: invoker list

  public Result invoke(Invocation invocation) throws RpcException {
        this.checkWheatherDestoried();
//select invoker
        List<Invoker<T>> invokers = this.list(invocation);
        LoadBalance loadbalance;
        if (invokers != null && invokers.size() > 0) {
            loadbalance = (LoadBalance)ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(((Invoker)invokers.get(0)).getUrl().getMethodParameter(invocation.getMethodName(), "loadbalance", "random"));
        } else {
            loadbalance = (LoadBalance)ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension("random");
        }

        RpcUtils.attachInvocationIdIfAsync(this.getUrl(), invocation);
        return this.doInvoke(invocation, invokers, loadbalance);
    }

 protected List<Invoker<T>> list(Invocation invocation) throws RpcException {
        List<Invoker<T>> invokers = this.directory.list(invocation);
        return invokers;
    }


Step 4: Really load the implementation of the invoker: AbstractDirectory

Note that there is a routing logic here, which is the focus of our attention today:
router.route: specific routing implementation logic: such as ConditionRouter

thinking: If you need to implement plug-ins in your program How do you implement this function: Basic idea: configuration center configuration + configuration notification update + plugin takes effect according to priority.


public List<Invoker<T>> list(Invocation invocation) throws RpcException {
        if (this.destroyed) {
            throw new RpcException("Directory already destroyed .url: " + this.getUrl());
        } else {
            List<Invoker<T>> invokers = this.doList(invocation);
            List<Router> localRouters = this.routers;
            if (localRouters != null && localRouters.size() > 0) {
                Iterator var5 = localRouters.iterator();

                while(var5.hasNext()) {
                    Router router = (Router)var5.next();

                    try {
                        if (router.getUrl() == null || router.getUrl().getParameter("runtime", true)) {
                            invokers = router.route(invokers, this.getConsumerUrl(), invocation);
                        }
                    } catch (Throwable var7) {
                        logger.error("Failed to execute router: " + this.getUrl() + ", cause: " + var7.getMessage(), var7);
                    }
                }
            }

            return invokers;
        }
    }


Step 5: FailoverClusterInvoker.doInvoke:

Here, an invoker is selected by the balancing strategy, and then filtered through various filters.

Finally: send data to the service provider through DubboInvoker.

After understanding the main process of the call, we basically sort out the implementation of the routing logic in which link, and need to pay attention to setting those attributes, such as runtime.


However, after I tested the routing function of dubbo, it is not stable, so I use it for grayscale publishing Unreliable (but it is more reliable to implement rolling update through weight setting), unstable factors are:
1. The consumer does not subscribe to the new service provider information in time, and our demand is to route to the increased service provider Otherwise, ConditionRouter.matchCondition cannot match successfully, and then the routing rule will not be used.
2. The routing rules are not updated to the consumer in time, resulting in the ConditionRouter not being added to AbstractDirectory.setRouters. (After testing this often occurs, this problem is more serious, and it may also be a problem with my environment)

Learning perception: In general, through today's research, I have learned some common design ideas, not just code and principles.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326264794&siteId=291194637