Dubbo 延迟与粘滞链接

前言

你们好,今天开始给你们分享 — Dubbo 专题之 Dubbo 延迟和粘滞链接。在前一个章节中咱们介绍了 Dubbo 并发控制,Dubbo 为咱们提供两大类的配置:消费端的配置和服务提供端配置,咱们分别能够对服务提供端和服务消费端进行并发数量的控制。同时咱们也例举了常见的使用场景而且进行了源码解析来分析其实现原理。有的小伙伴学习了并发控制可能会想到:若是咱们的服务消费端有大量的服务须要引用,那咱们的 Dubbo 应用程序可能启动至关的缓慢其缘由是:当咱们消费端应用启动的时候须要获取远程服务的代理对象的引用,若是咱们每个获取的远程代理对象都在启动的时候建立链接,这样一定会影响咱们的应用程序启动,幸亏咱们的 Dubbo 提供一种配置的方式解决这个问题。那同时也延伸出另一个问题,就是咱们对某个服务的调用能不能一直分配到上次调用的服务提供者呢?带着这些疑问咱们开始本章节学习,咱们会经过介绍什么是延迟和粘滞链接?怎样经过参数配置改变默认行为?来解决这些问题。下面就让咱们快速开始吧!java

1. 延迟和粘滞链接简介

经过前面对 Dubbo 相关章节的介绍我相信你们应该有个基本的概念就是咱们 Dubbo 中服务消费方持有服务提供端的服务引用,这个引用又经过层层的代理最终经过咱们的 TCP/IP (底层使用Netty进行网络通信)与远程服务端进行通信。在 Dubbo 中为了优化消费端获取服务提供端的引用对象时候建立底层的物理链接,好比在与 Spring 集成中咱们的引用对象须要经过 Spring 容器进行对外发布 Bean 实例,然而此时咱们并无真正的使用代理对象,只是将代理对象交给 Spring 管理。为了使代理对象在真正使用的时候才去建立底层的物理从而减小底层链接的建立和释放这就叫作延迟链接。同理粘滞链接的意思就是尽量让客户端老是向同一提供者发起调用,除非该提供者挂了,再连另外一台。下图简单的描述了粘滞链接在第二次、第三次调用服务的时候调用原服务提供者:git

粘滞链接

2. 配置方式

下面咱们主要经过 XML 和 注解的方式进行配置介绍:spring

2.1 延迟链接:

  1. XML 方式
<dubbo:reference id="bookFacade"
                     interface="com.muke.dubbocourse.common.api.BookFacade" lazy="true" ></dubbo:reference>
  1. 注解方式
@Reference(lazy = true)

2.2 粘滞链接

  1. XML 方式
<dubbo:reference id="bookFacade" interface="com.muke.dubbocourse.common.api.BookFacade" sticky="true" />

方法级别控制:apache

<dubbo:reference id="xxxService" interface="com.muke.dubbocourse.common.api.BookFacade">
    <dubbo:mothod name="queryAll" sticky="true" />
</dubbo:reference>
  1. 注解方式
@Reference(sticky = true)

从上面的配置中咱们能够简单的总结:延迟链接经过lazy进行配置,粘滞链接使用sticky进行配置。编程

3. 使用场景

根据前面的介绍咱们大概理解了什么是延迟和粘滞链接。其中延迟链接就是为了减小无用的链接而在真正使用对象时候才建立链接,而粘滞链接是为了屡次调用都尽量地使用同一个服务提供者也有减小服务链接建立的做用。下面咱们简单的介绍几种常见使用场景:api

  1. 当咱们的服务消费端须要大量的引用服务提供者或者建立远程链接成本很是高(通常指耗时时间)时咱们能够考虑开启延迟链接。
  2. 假设咱们的应用有大量的静态数据须要加载到应用本地缓存( JVM 缓存)时当第一次调用Service A进行缓存加载,那么在第二次调用的时候咱们指望也调用刚才已经存在缓存的服务 Service A 这样提升了服务的访问速度。这种场景可使用粘滞链接。
  3. 若是咱们的应用调用过程存在某种状态,例如:调用服务 Service A 进行用户登陆返回 token,那第二次调用查询用户信息的时候须要根据携带的token来查询用户的登陆状态,此时若是访问 Service A 那么用户登陆的 token 信息是存的,若是访问到 Service B 这时就不存在 token (假设这里没有使用分布式缓存)。这种场景可使用粘滞链接。

4. 示例演示

下面我以获取图书列表为例进行演示。项目结构以下:缓存

idea

咱们的延迟链接主要配置在服务消费端dubbo-consumer-xml.xml配置文件以下:服务器

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
       http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">

    <dubbo:application name="demo-consumer" logger="log4j"/>

    <dubbo:registry address="zookeeper://127.0.0.1:2181"/>

    <!--lazy="true"延迟链接-->
    <dubbo:reference id="bookFacade"
                     interface="com.muke.dubbocourse.common.api.BookFacade" lazy="true" ></dubbo:reference>

</beans>

经过lazy="true"配置消费端引用服务提供者服务时开启延迟链接。下面咱们继续看看粘滞链接配置:微信

<!--sticky="true"开启粘滞链接 -->
    <dubbo:reference id="bookFacade"
                     interface="com.muke.dubbocourse.common.api.BookFacade" sticky="true" ></dubbo:reference>

经过sticky="true"配置消费端引用服务提供者服务时开启粘滞链接。网络

Tips:这里在演示粘滞链接的时候小伙伴们在部署应用的时候至少须要部署两个或以上的实例才能看出效果。若是咱们开启粘滞链接那么咱们能够看到老是访问同一个服务提供者。

5. 实现原理

下面咱们经过源码的方式简单的分析它们的实现原理。

首先是延迟链接其核心方法`org.apache.dubbo.rpc.protocol.dubbo.
DubboProtocol#protocolBindingRefer`代码以下:

/***
     *
     * 协议绑定并建立链接
     *
     * @author liyong
     * @date 16:11 2020-03-08
     * @param serviceType
     * @param url
     * @exception
     * @return org.apache.dubbo.rpc.Invoker<T>
     **/
    @Override
    public <T> Invoker<T> protocolBindingRefer(Class<T> serviceType, URL url) throws RpcException {
        optimizeSerialization(url);

        // 建立DubboInvoker而且建立网络链接
        DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
        invokers.add(invoker);

        return invoker;
    }

这里的 DubboInvoker 是咱们远程 RPC 调用的封装,这里`org.apache.dubbo.rpc.protocol.dubbo.
DubboProtocol#getClients`方法建立客户端链接而且绑定请求处理器核心代码以下:

/***
     *
     * 获取客户端链接,而且绑定了请求处理器
     *
     * @author liyong
     * @date 16:55 2020-03-08
     * @param url
     * @exception
     * @return org.apache.dubbo.remoting.exchange.ExchangeClient[]
     **/
    private ExchangeClient[] getClients(URL url) {
        //...
        ExchangeClient[] clients = new ExchangeClient[connections];
        for (int i = 0; i < clients.length; i++) {
            if (useShareConnect) {
                clients[i] = shareClients.get(i);//共享链接

            } else {
                clients[i] = initClient(url);//不共享 新建链接
            }
        }

        return clients;
    }

其中initClient方法建立链接核心代码以下:

private ExchangeClient initClient(URL url) {

        //...

        ExchangeClient client;
        try {
            //是否延迟链接 lazy="true"
            if (url.getParameter(LAZY_CONNECT_KEY, false)) {
                //这里并无建立链接对象
                client = new LazyConnectExchangeClient(url, requestHandler);

            } else {
                //HeaderExchangeClient链接到服务器并绑定请求处理器
                client = Exchangers.connect(url, requestHandler);
            }

        } catch (RemotingException e) {
           //...
        }

        return client;
    }

根据咱们的配置获取LAZY_CONNECT_KEY参数(lazy)的值,当咱们配置为true时建立LazyConnectExchangeClient对象,为false是建立物理链接。

下面继续讨论粘滞链接核心方法`org.apache.dubbo.rpc.cluster.support.
AbstractClusterInvoker#select`以下:

protected Invoker<T> select(LoadBalance loadbalance, Invocation invocation,
                                List<Invoker<T>> invokers, List<Invoker<T>> selected) throws RpcException {

        if (CollectionUtils.isEmpty(invokers)) {
            return null;
        }
        String methodName = invocation == null ? StringUtils.EMPTY_STRING : invocation.getMethodName();

        //获取粘滞链接配置
        boolean sticky = invokers.get(0).getUrl()
                .getMethodParameter(methodName, CLUSTER_STICKY_KEY, DEFAULT_CLUSTER_STICKY);

        //ignore overloaded method
        if (stickyInvoker != null && !invokers.contains(stickyInvoker)) {
            stickyInvoker = null;
        }
        //开启粘滞链接配置 且存在已经建立的stickyInvoker粘滞链接
        if (sticky && stickyInvoker != null && (selected == null || !selected.contains(stickyInvoker))) {
            //有效性检测
            if (availablecheck && stickyInvoker.isAvailable()) {
                return stickyInvoker;
            }
        }

        //根据负载均衡策略进行服务选择
        Invoker<T> invoker = doSelect(loadbalance, invocation, invokers, selected);

        if (sticky) {
            //若是配置粘滞链接为true 则当前选择的Invoker保存在stickyInvoker粘滞链接变量
            stickyInvoker = invoker;
        }
        return invoker;
    }

从上面的代码咱们能够看出从 URL 中获取 sticky 配置判断是否开启粘滞链接,若是开启那么在第一次获取 InvokerstickyInvokernull 建立一个 Invoker 代理对象,当第二次获取 Invoker 时会判断是否存在 stickyInvoker 若是存在且没有被排除则继续使用前面保存下来的 Invoker 代理对象也就是stickyInvoker

Tips:这里 URL 包括咱们配置的 XML 或注解配置中的参数、通信协议、序列化方式等等参数,能够参考前面章节详细描述。

6. 小结

在本小节中咱们主要学习了 Dubbo 延迟和粘滞链接,同时咱们也分析了延迟和粘滞链接的实现原理。延迟链接本质上是延迟 Dubbo 底层物理链接的建立,而粘滞链接本质上是重复利用已建立的链接。

本节课程的重点以下:

  1. 理解 Dubbo 延迟和粘滞链接
  2. 了解了延迟和粘滞链接使用方式
  3. 了解延延迟和粘滞链接使用场景
  4. 了解延迟和粘滞链接实现原理

做者

我的从事金融行业,就任过易极付、思建科技、某网约车平台等重庆一流技术团队,目前就任于某银行负责统一支付系统建设。自身对金融行业有强烈的爱好。同时也实践大数据、数据存储、自动化集成和部署、分布式微服务、响应式编程、人工智能等领域。同时也热衷于技术分享创立公众号和博客站点对知识体系进行分享。关注公众号: 青年IT男 获取最新技术文章推送!

博客地址: http://youngitman.tech

微信公众号:

相关文章
相关标签/搜索