• Dubbo 知识点整理


    一、基础知识

    1.Dubbo是什么他的核心功能是什么?

    dubbo 阿里开源的一个SOA服务治理框架,从目前来看把它称作是一个RCP远程调用框架更为贴切。单从RPC框架来说,功能较完善,支持多种传输和序列化方案。所以想必大家已经知道他的核心功能了:就是远程调用。

    2.Dubbo常规配置说明

    配置分布图:

    定位属性:

    定位属性是指消费者匹配提供者的全部标识,总共有如下属性

    interface: 接口名称
    group: 接口分组
    version: 接口版本

    标签

    属性

    描述

    必填

    application

    name

    应用名称

    registry

    protocol

    注册中心协议

    address

    注册中心地址

    provider

    threadpool

    线程模型 fixed/cache

    threads

    service

    interface

    ref

    group

    version

    timeout

    重试次数

    retries

    重试次数

    consumer

    timeout

    retries

    reference

    id

    interface

    version

    check

    retries

    重试次数

    属性覆盖:

    二、RPC 底层协议原理与实现

    RPC 协议名词解释

    在一个典型RPC的使用场景中,包含了服务发现、负载、容错、网络传输、序列化等组件,其中RPC协议就指明了程序如何进行网络传输和序列化 。

    也就是说一个RPC协议的实现就等于一个非透明的RPC调用,如何做到的的呢?

    协议基本组成:

    1.地址:服务提供者地址
    2.端口:协议指定开放的端口
    3.运行服务:
    1.netty
    2.mina
    3.RMI 服务
    4.servlet 容器(jetty、Tomcat、Jboss)
    4.报文编码:协议报文编码 。注①:http 报文编码 。注②:Dubbo 报文编码
    5.序列化方式:
    1.Hessian2Serialization、
    2.DubboSerialization、
    3.JavaSerialization
    4.JsonSerialization

    RPC协议报文编码与实现详解

    注②Dubbo 协议报文编码:

    (注:相关源码参见 com.alibaba.dubbo.rpc.protocol.dubbo.DubboCodec)

    协议的编解码过程:

    Dubbo 协议编解码实现过程 (源码来源于dubbo2.5.8 )
    1、DubboCodec.encodeRequestData() 116L // 编码request
    2、DecodeableRpcInvocation.decode() 89L // 解码request
    3、DubboCodec.encodeResponseData() 184L // 编码response
    4、DecodeableRpcResult.decode() 73L // 解码response

    dubbo 支持的RPC协议列表

    名称

    实现描述

    连接描述

    适用场景

    dubbo

    传输服务: mina, netty(默认), grizzy
    序列化: dubbo, hessian2(默认), java, fastjson 自定义报文

    单个长连接
    NIO异步传输

    1、常规RPC调用
    2、传输数据量小 3、提供者少于消费者

    rmi

    传输:java rmi 服务
    序列化:java原生二进制序列化

    多个短连接
    BIO同步传输

    1、常规RPC调用
    2、与原RMI客户端集成 3、可传少量文件 4、不支持防火墙穿透

    hessian

    传输服务:servlet容器
    序列化:hessian二进制序列化

    基于Http 协议传输,
    依懒servlet容器配置

    1、提供者多于消费者
    2、可传大字段和文件 3、跨语言调用

    http

    传输服务:servlet容器
    序列化:http表单

    依懒servlet容器配置

    1、数据包大小混合

    thrift

    与thrift RPC 实现集成,并在其基础上修改了报文头

    长连接、NIO异步传输

    关于RMI不支持防火墙穿透的补充说明:
    原因在于RMI 底层实现中会有两个端口,一个是固定的用于服务发现的注册端口,另外会生成一个随机端口用于网络传输。

    这个随机端口就不能在防火墙中提前设置开放开。所以存在防火墙穿透问题

    dubbo 协议采用 json 进行序列化 (源码参见:com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol)
    采用RMI协议 (源码参见:com.alibaba.dubbo.rpc.protocol.rmi.RmiProtocol)
    采用Http协议 (源码参见:com.alibaba.dubbo.rpc.protocol.http.HttpProtocol.InternalHandler)
    采用Heason协议 (源码参见:com.alibaba.dubbo.rpc.protocol.hessian.HessianProtocol.HessianHandler)

    三、调用模块详解

    dubbo调用模块核心功能是发起一个远程方法的调用并顺利拿到返回结果,其体系组成如下:
    1.透明代理:通过动态代理技术,屏蔽远程调用细节以提高编程友好性。
    2.负载均衡:当有多个提供者是,如何选择哪个进行调用的负载算法。
    3.容错机制:当服务调用失败时采取的策略
    4.调用方式:支持同步调用、异步调用
    5.结果获取:指同步等待结果返回,还是异步通过回调通知获取结果。

    负载均衡
    Dubbo 目前官方支持以下负载均衡策略:
    1.随机(random):按权重设置随机概率。此为默认算法.
    2.轮循 (roundrobin):按公约后的权重设置轮循比率。
    3.最少活跃调用数(leastactive):相同活跃数的随机,活跃数指调用前后计数差。
    4.一致性Hash(consistenthash ):相同的参数总是发到同一台机器

    容错
    Dubbo 官方目前支持以下容错策略:
    1.失败自动切换:调用失败后基于retries=“2” 属性重试其它服务器
    2.快速失败:快速失败,只发起一次调用,失败立即报错。
    3.勿略失败:失败后勿略,不抛出异常给客户端。
    4.失败重试:失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作
    5.并行调用: 只要一个成功即返回,并行调用指定数量机器,可通过 forks=“2” 来设置最大并行数。
    6.广播调用:广播调用所有提供者,逐个调用,任意一台报错则报错

    异步调用
    异步调用是指发起远程调用之后获取结果的方式。
    1.同步等待结果返回
    2.异步等待结果返回
    3.不需要返回结果
    Dubbo 中关于异步等待结果返回的实现流程如下图:

    异步调用配置:
    interface=“com.tuling.teach.service.async.AsyncDemoService”>



    异步获取结果演示:
    编写异步调用代码
    编写同步调用代码
    分别演示同步调用与异步调用耗时
    异步调用结果获取Demo

    demoService.sayHello1("han");
    Future future1 = RpcContext.getContext().getFuture();
    demoService.sayHello2("han2");
    Future future2 = RpcContext.getContext().getFuture();
    Object r1 = null, r2 = null;
    // wait 直到拿到结果 获超时
    r1 = future1.get();
    // wait 直到拿到结果 获超时
    r2 = future2.get();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    过滤器
    类似于 WEB 中的Filter ,Dubbo本身提供了Filter 功能用于拦截远程方法的调用。其支持自定义过滤器与官方的过滤器使用:
    #TODO 演示添加日志访问过滤:

    以上配置 就是 为 服务提供者 添加 日志记录过滤器, 所有访问日志将会集中打印至 accesslog 当中

    泛化提供&引用

    泛化提供
    是指不通过接口的方式直接将服务暴露出去。通常用于Mock框架或服务降级框架实现。

    #TODO 示例演示
    public static void doExportGenericService() {
    ApplicationConfig applicationConfig = new ApplicationConfig();
    applicationConfig.setName("demo-provider");
    // 注册中心
    RegistryConfig registryConfig = new RegistryConfig();
    registryConfig.setProtocol("zookeeper");
    registryConfig.setAddress("192.168.0.147:2181");
    ProtocolConfig protocol=new ProtocolConfig();
    protocol.setPort(-1);
    protocol.setName("dubbo");
    GenericService demoService = new MyGenericService();
    ServiceConfig service = new ServiceConfig();
    // 弱类型接口名
    service.setInterface("com.tuling.teach.service.DemoService");
    // 指向一个通用服务实现
    service.setRef(demoService);
    service.setApplication(applicationConfig);
    service.setRegistry(registryConfig);
    service.setProtocol(protocol);
    // 暴露及注册服务
    service.export();
    }
    泛化引用
    是指不通过常规接口的方式去引用服务,通常用于测试框架。
    ApplicationConfig applicationConfig = new ApplicationConfig();
    applicationConfig.setName("demo-provider");
    // 注册中心
    RegistryConfig registryConfig = new RegistryConfig();
    registryConfig.setProtocol("zookeeper");
    registryConfig.setAddress("192.168.0.147:2181");
    // 引用远程服务
    ReferenceConfig reference = new ReferenceConfig();
    // 弱类型接口名
    reference.setInterface("com.tuling.teach.service.DemoService");
    // 声明为泛化接口
    reference.setGeneric(true);
    reference.setApplication(applicationConfig);
    reference.setRegistry(registryConfig);
    // 用com.alibaba.dubbo.rpc.service.GenericService可以替代所有接口引用
    GenericService genericService = reference.get();
    Object result = genericService.$invoke("sayHello", new String[]{"java.lang.String"}, new Object[]{"world"});
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    隐示传参
    是指通过非常方法参数传递参数,类似于http 调用当中添加cookie值。通常用于分布式追踪框架的实现。使用方式如下 :

    //客户端隐示设置值
    RpcContext.getContext().setAttachment("index", "1"); // 隐式传参,后面的远程调用都会隐
    //服务端隐示获取值
    String index = RpcContext.getContext().getAttachment("index"); 
    
    • 1
    • 2
    • 3
    • 4

    令牌验证
    通过令牌验证在注册中心控制权限,以决定要不要下发令牌给消费者,可以防止消费者绕过注册中心访问提供者,另外通过注册中心可灵活改变授权方式,而不需修改或升级提供者

    使用:

    
    
    • 1

    四、日常开发

    开发联调:

    在项目开发过程当中,一个开发或测试环境的注册中心很有可能会同时承载着多个服务,如果两组服务正在联调,如何保证调用的是目标服务呢?
    1、基于临时分组联调
    group 分组
    在reference 和server 当中采用相同的临时组 ,通过group 进行设置
    2、直连提供者:
    在reference 中指定提供者的url即可做到直连

    
    
    • 1
    • 2
    • 3

    3、只注册:
    一个项目有可能同是为即是服务提供者又消费者,在测试时需要调用某一服务同时又不希望正在开发的服务影响到其它订阅者如何实现?
    通过修改 register=false 即可实现

    五、dubbo 并发链接底层探究

    网络传输的实现组成

    1.IO模型:
    1.BIO 同步阻塞
    2.NIO 同步非阻塞
    3.AIO 异步非阻塞
    2.连接模型:
    1.长连接
    2.短连接
    3.线程分类:
    1.IO线程
    2.服务端业务线程
    3.客户端调度线程
    4.客户端结果exchange线程。
    5.保活心跳线程
    6.重连线程
    4.线程池模型:
    1.固定数量线程池
    2.缓存线程池
    3.有限线程池
    二、长连接的创建与维护
    Dubbo 长连接实现与配置
    初始连接:
    引用服务|增加提供者==>获取连接===》是否获取共享连接==>创建连接客户端==》开启心跳检测状态检查定时任务===》开启连接状态检测
    源码见:com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol#getClients
    心跳发送:
    在创建一个连接客户端同时也会创建一个心跳客户端,客户端默认基于60秒发送一次心跳来保持连接的存活,可通过 heartbeat 设置。
    源码见:com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeClient#startHeatbeatTimer
    断线重连:
    每创建一个客户端连接都会启动一个定时任务每两秒中检测一次当前连接状态,如果断线则自动重连。
    源码见:com.alibaba.dubbo.remoting.transport.AbstractClient#initConnectStatusCheckCommand
    连接销毁:
    基于注册中心通知,服务端断开后销毁
    源码见:com.alibaba.dubbo.remoting.transport.AbstractClient#close()

    dubbo传输uml类图:

    Dubbo 传输协作线程
    1.客户端调度线程:用于发起远程方法调用的线程。
    2.客户端结果Exchange线程:当远程方法返回response后由该线程填充至指定ResponseFuture,并叫醒等待的调度线程。
    3.客户端IO线程:由传输框架实现,用于request 消息流发送、response 消息流读取与解码等操作。
    4.服务端IO线程:由传输框架实现,用于request消息流读取与解码 和response编码与发送。
    5.业务执行线程:服务端具体执行业务方法的线程
    客户端线程协作流程:

    1.调度线程
    1.调用远程方法
    2.对request 进行协议编码
    3.发送request 消息至IO线程
    4.等待结果的获取
    2.IO线程
    1.读取response流
    2.response 解码
    3.提交Exchange 任务
    3.Exchange线程
    1.填写response值 至 ResponseFuture
    2.唤醒调度线程,通知其获取结果
    服务端线程协作:

    1.IO线程:
    1.request 流读取
    2.request 解码
    3.提交业务处理任务
    2.业务线程:
    1.业务方法执行
    2.response 解码
    3.回写结果至channel
    线程池
    1.fixed:固定线程池,此线程池启动时即创建固定大小的线程数,不做任何伸缩,
    2.cached:缓存线程池,此线程池可伸缩,线程空闲一分钟后回收,新请求重新创建线程
    3.Limited:有限线程池,此线程池一直增长,直到上限,增长后不收缩。

  • 相关阅读:
    python 获取下载文件的后缀
    旋转图片和坐标变换
    Python学习笔记--类的访问控制
    目标追踪算法DeepSORT简介
    大数据课程L7——网站流量项目的操作步骤
    OpenAI重大更新!为ChatGPT推出语音和图像交互功能
    P7961 [NOIP2021] 数列
    【uni-app从入门到实战】环境搭建和配置学习
    open3d中的kd树详解
    Android shape记录
  • 原文地址:https://blog.csdn.net/m0_67402774/article/details/126327795