Dubbo的服务治理

来源:互联网 发布:上海mao livehouse知乎 编辑:程序博客网 时间:2024/05/18 00:49

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

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

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

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

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

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

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

这里写图片描述

这张图是站在服务消费方的视角来看的(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 <T>     * @param directory     * @return cluster invoker     * @throws RpcException     */    @Adaptive    <T> Invoker<T> join(Directory<T> directory) throws RpcException;}/** * 快速失败,只发起一次调用,失败立即报错,通常用于非幂等性的写操作。 *   * <a href="http://en.wikipedia.org/wiki/Fail-fast">Fail-fast</a> *  * @author william.liangf */public class FailfastCluster implements Cluster {    public final static String NAME = "failfast";    public <T> Invoker<T> join(Directory<T> directory) throws RpcException {        return new FailfastClusterInvoker<T>(directory);    }}

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

路由和配置

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

这里写图片描述
这里写图片描述

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

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

这里写图片描述

没错,就是这个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<Invoker<T>> 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")    <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException;}

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

0 0