Dubbo的服务治理

首先我们要清楚为什么要服务治理,服务治理是什么
服务治理演进过程
还有限流、链路分析等

如果说dubbo只完成远程调用的话,还算不上是一个合格的SOA服务架构,它之所以那么碉堡,是因为它还提供了服务治理的功能,我们来研究一下关于服务治理,dubbo都做了什么

听起来服务治理挺高大上的,了解了dubbo的做法后,你会发现一切并没有想的那么复杂。远程调用要解决的最本质问题是通信,通信就好像人和人之间的互动,有效的沟通建立在双方彼此了解的基础上,这基础就是dubbo的URL。

前几篇中大量提到dubbo分层之间依靠什么纽带工作的:Invoker,而客户端和服务端之间的纽带就是URL

依靠URL,dubbo不仅打通了通信两端,而且还依靠URL完成了服务治理的任务,我们先看一些以下内容:

  • 路由规则
  • 配置规则
  • 服务降级
  • 负载均衡

其实dubbo的集群、目录和路由是在服务引用(客户端)中透明完成的,dubbo官方提供了一张清晰的关于dubbo服务治理的图:

Dubbo的服务治理_第1张图片

这张图是站在服务消费方的视角来看的(dubbo的服务治理都是针对服务消费方),当业务逻辑中需要调用一个服务时,你真正调用的是dubbo创建的一个proxy,该proxy会把调用转化成指定的Invoker(Cluster封装过的),而在这一系列的委托调用的过程里就完成了服务治理的逻辑。

Cluster

当相同服务由多个提供方同时提供时,消费方就需要有个选择的步骤,就好比你去电商平台买本书,你会先选择去哪个电商平台。同样,消费方根据需要会选择到底使用哪个提供方的服务,而Cluser的主要作用就是从容错的维度来帮我们选择合适的服务提供方。

在服务引用这篇文章中有这样一个调用过程RegistryProtocol.refer()->doRefer(),其中有行代码:

cluster.join(directory);
---------------------------------------------------
Cluster接口如下:
@SPI(FailfastCluster.NAME)
public interface Cluster {

    /**
     * Merge the directory invokers to a virtual invoker.
     *
     * @param 
     * @param directory
     * @return cluster invoker
     * @throws RpcException
     */
    @Adaptive
     Invoker join(Directory directory) throws RpcException;

}


/**
 * 快速失败,只发起一次调用,失败立即报错,通常用于非幂等性的写操作。
 *  
 * Fail-fast
 * 
 * @author william.liangf
 */
public class FailfastCluster implements Cluster {

    public final static String NAME = "failfast";

    public  Invoker join(Directory directory) throws RpcException {
        return new FailfastClusterInvoker(directory);
    }

}

可以看到Cluster封装了存有多个Invoker的Directory对象,默认仅仅是创建了一个FailfastCluster,具体的调用在AbstractClusterInvoker.invoke(Invocation)方法中处理,各种集群容错算法就交给大家自己阅读源码来消化了

路由和配置

如果说Cluster帮我们以容错的维度来完成选择,那么路由和配置是在更细颗粒度的层面做的选择,如下多图:

Dubbo的服务治理_第2张图片
Dubbo的服务治理_第3张图片

总之很细吧,这么多配置参数最终交给谁来管理呢?

我们需要从Directory接口出发,你应该想到了该接口的一个实现类:

Dubbo的服务治理_第4张图片

没错,就是这个RegistryDirectory,它在服务引用时被创建,用于充当url与多个Invoker的代理(或者叫目录类),从源码可以看出,当服务引用时,对应该服务的目录类实例会负责向注册中心(zookeeper)订阅该服务,第一次订阅会拿到所有服务提供方的信息(注册中心会调用NotifyListener的notify函数返回服务列表),包括:地址、配置、路由等,然后该目录实例会根据这些信息来为后续的服务调用提供支撑。

根据以上描述,我们看RegistryDirectory.notify():

......
// configurators 更新缓存的服务提供方动态配置规则
if (configuratorUrls != null && configuratorUrls.size() >0 ){
    this.configurators = toConfigurators(configuratorUrls);
}
// routers  更新缓存的路由配置规则
if (routerUrls != null && routerUrls.size() >0 ){
    List<Router> routers = toRouters(routerUrls);
    if(routers != null){ // null - do nothing
        setRouters(routers);
    }
}
......

这些配置在什么时候发挥作用呢?我们来看FailoverClusterInvoker.invoke():

public Result invoke(final Invocation invocation) throws RpcException {

    checkWheatherDestoried();

    LoadBalance loadbalance;

    //这里就是路由,配置等发挥作用地方,返回所有合法的invoker供集群做下一步的筛选        
    List> invokers = list(invocation);

    if (invokers != null && invokers.size() > 0) {
        loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(invokers.get(0).getUrl()
                .getMethodParameter(invocation.getMethodName(),Constants.LOADBALANCE_KEY, Constants.DEFAULT_LOADBALANCE));
    } else {
        loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(Constants.DEFAULT_LOADBALANCE);
    }
    RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
    return doInvoke(invocation, invokers, loadbalance);
}

这段代码中,还有一个很重要的概念:负载均衡Loadbalance

负载均衡

到了负载均衡坏境,维度就成了性能,接口如下:

@SPI(RandomLoadBalance.NAME)
public interface LoadBalance {

    /**
     * select one invoker in list.
     * 
     * @param invokers invokers.
     * @param url refer url
     * @param invocation invocation.
     * @return selected invoker.
     */
    @Adaptive("loadbalance")
     Invoker select(List> invokers, URL url, Invocation invocation) throws RpcException;

}

具体细节感兴趣的读者可以去研究一下

你可能感兴趣的:(RPC)