`
ahua186186
  • 浏览: 561930 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

dubbo服务治理之路由规则研究

 
阅读更多
1.今天没太多事情,挤出点时间,研究了下dubbo的路由规则实现。

首先,看看dubbo消费端的主要调用流程(这里就不画图了),以后补充:

第一步:创建消费端代理:ReferenceConfig.createProxy,这里没什么特别,典型的C/S调用设计,都是通过JAVA动态代理或是Javassist的代理实现,比如mybatis mapper。


思考:假如我想实现API网关的dubbo协议路由怎么实现呢:毫无疑问必须使用ReferenceConfig的get.set以及init方法,下一篇将展开讨论实现细节..

第二步:直接跳到动态代理代码里的InvokerInvocationHandler:
这里忽略invoker的初始化(基本就是提供者注册+消费端订阅后初始化)

思考:假如让你实现一个RPC的客户端同步和异步调用你会怎么实现呢?

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();
        }
    }
}

第三步:AbstractClusterInvoker.invoke;
这里主要是加载一个列表:invoker list

  public Result invoke(Invocation invocation) throws RpcException {
        this.checkWheatherDestoried();
//选择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;
    }


第四步:真正加载invoker的实现:AbstractDirectory

注意这里有个路由选择的逻辑,也就是我们今天关注的重点:
router.route:具体的路由实现逻辑:比如ConditionRouter

思考:假如你的程序中需要实现动态的插件式功能,你如何实现呢:基本思路:配置中心配置+ 配置通知更新+插件根据优先级生效。---典型的分布式协调场景,ZK标配

相比静态的插件式功能实现思路:SPI思想,要灵活很多


 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;
        }
    }


第五步:FailoverClusterInvoker.doInvoke:

这里通过负责均衡策略选择一个invoker,然后通过各种过滤器过滤。

最后:通过DubboInvoker发送数据给服务提供端。

了解完调用的主流程,我们基本就梳理清楚路由逻辑的实现在那个环节,以及需要注意设置那些属性,比如runtime.


但是经过我本人测试dubbo的路由功能并不稳定,所有用它做灰度发布不靠谱(但是通过权重设置实现rolling update还是比较靠谱的),不稳定因素有:
1.消费端没有及时订阅新增的服务提供者信息,而我们的需求是需要路由到增的服务提供者,导致ConditionRouter.matchCondition无法匹配成功,然后就不会使用路由规则。
2.路由规则没有及时通知更新到消费端,导致AbstractDirectory.setRouters一直没有添加ConditionRouter。(经过测试这个经常出现,这个问题比较严重,也有可能是我环境的问题)

学习感悟:总的来说通过今天的研究还是学到了一些常用的设计思路,而不仅仅是研究代码和原理。
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics