# dubbo-lean **Repository Path**: interview_44/dubbo-lean ## Basic Information - **Project Name**: dubbo-lean - **Description**: dubbo分析学习 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-04-21 - **Last Updated**: 2023-02-23 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## dubbo-lean 主要用于快速学习dubbo。 dubbo工作原理:服务注册,注册中心,消费者,负载均衡。 网络通信:长连接,nio,异步通信,hessian序列化。 策略:dubbo跑起来的功能如何运作,负载均衡,集群容错,动态代理。 集群容错策略:provider1宕机,会发送到别的provider。 [1、介绍](#1介绍) [2、dubbo分层模型](#2dubbo分层模型) [3、如何调度](#3如何调度) [4、dubbo通讯协议](#4dubbo通讯协议) [5、负载均衡策略](#5负载均衡策略) [6、分布式系统中接口的幂等性](#6分布式系统中接口的幂等性) [7、分布式服务接口请求时的顺序性](#7分布式服务接口请求时的顺序性) [8、说一说底层运行逻辑](#8说一说底层运行逻辑) [9、dubbo灰度发布](#9dubbo灰度发布) [10、如何通过dubbo:service来把服务暴露出去。](#10如何通过dubbo:service来把服务暴露出去。) [11、dubbo线程池配置](#11dubbo线程池配置) #### 1、介绍 1、为什么要使用分布式系统? 不拆服务,单系统的代码太多,太大了。在一个异常庞大的系统里,每改一个功能,基本上全部功能都要测试。代码量大,滋生bug。开发难度高,上线难度高,可优化性低。所以要拆成微服务。 2、怎么拆服务? 按照业务划分边界拆分,每个服务不超过20万行代码,超过了继续拆,一定要轻量级,明确边界。一个人负责1到2个微服务。 3、注册中心挂了之后,可以程序可以继续使用吗? 可以,服务启动之后,会把provider缓存到本地,zookeeper挂了之后不会影响。 4、如何进行服务治理? zipkin,统计服务关系,统计每个服务的花费时间,被访问的次数,高峰期时每秒多少次。 5、如何进行服务降级? mock设置,mock设为true。服务B调用服务A,失败,调用几次都失败,那么。创建一个降级类,降级类会替代A服务。 6、如何进行服务重试? 可以指定连接超时时间为1000ms,失败次数一般为3次。 #### 2、dubbo分层模型 ![](./picture/1、dubbo内部分层.png) 第十层-接口层:provider层和comsumer层,留个开发人员实现。 第九层-config 配置层:对外配置接口,以 ServiceConfig, ReferenceConfig 为中心,可以直接初始化配置类,也可以通过 spring 解析配置生成配置类。 第八层-proxy 服务代理层:服务接口透明代理,生成服务的客户端 Stub 和服务器端 Skeleton, 以 ServiceProxy 为中心,扩展接口为 ProxyFactory。 第七层-registry 注册中心层:封装服务地址的注册与发现,以服务 URL 为中心,扩展接口为 RegistryFactory, Registry, RegistryService。 第六次-cluster 路由层:封装多个提供者的路由及负载均衡,并桥接注册中心,以 Invoker 为中心,扩展接口为 Cluster, Directory, Router, LoadBalance。 第五层-monitor 监控层:RPC 调用次数和调用时间监控,以 Statistics 为中心,扩展接口为 MonitorFactory, Monitor, MonitorService。 第四次-protocol 远程调用层:封装 RPC 调用,以 Invocation, Result 为中心,扩展接口为 Protocol, Invoker, Exporter。 第三层-exchange 信息交换层:封装请求响应模式,同步转异步,以 Request, Response 为中心,扩展接口为 Exchanger, ExchangeChannel, ExchangeClient , ExchangeServer。 第二层-transport 网络传输层:抽象 mina 和 netty 为统一接口,以 Message 为中心,扩展接口为 Channel, Transporter, Client, Server, Codec。 第一层-serialize 数据序列化层:可复用的一些工具,扩展接口为 Serialization, ObjectInput, ObjectOutput, ThreadPool。 ### 3、如何调度 ![](./picture/2、调度.png) 1、provider服务部署了3台机器,启动时往注册中心注册地址信息,注册中心就知道了3台机器,部署在z,x,c。 2、consumer启动时,从注册中心问有没有服务A,并且知道服务A在哪几个机器。 3、当consumer需要消费provider,会找到代理层,代理层通过负载均衡找到provider代理; provider也是通过代理知道到资源接口,执行完逻辑后返回provider代理,再返回consumer代理,通讯结束。 ### 4、dubbo通讯协议 通讯协议默认:dubbo协议 样例 dubbo://192.168.0.188 单一长连接,nio异步性能很高,hessian序列化协议,并发非常高,每次传输的数据量比较少。 ![](./picture/3、consumer调他provider.png) 打开一个长连接之后,一直不断的高频传输数据。 通讯的底层是用netty框架: ![](./picture/4、dubbo调用netty框架.png) ### 5、负载均衡策略 权重机制:可以根据服务器的资源配置来分配请求权重 ![](./picture/5、负载均衡-权重机制.png) 轮询机制:一人一次 ![](./picture/6、负载均衡-轮询机制.png) 最少活跃数调用:谁调用最少就调用谁的! 随机调用:随机算法,调用一个即可! 一致性 hash 负载均衡策略:根据id运算来做负载均衡 ### 6、分布式系统中接口的幂等性 ![](./picture/7、分布式接口不幂等.png) 如何保证幂等性? 1、一个请求一个唯一id。 2、请求处理完后标识该记录被处理过,每次处理都插入一条流水。 3、数据库的唯一键。 4、做一个分布式锁。 ### 7、分布式服务接口请求时的顺序性 一、使用内存队列: 1、首先你得用 dubbo 的一致性 hash 负载均衡策略,将比如某一个订单 id 对应的请求都给分发到某个机器上去,接着就是在那个机器上,因为可能还是多线程并发执行的,你可能得立即将某个订单 id 对应的请求扔一个内存队列里去,强制排队,这样来确保他们的顺序性。 2、中间加一个mq,把3步让如mq一个队列中 ![](./picture/8、分布式接口有序性-内存队列.png) 3、使用分布式锁,调用链非常长、非常重 ![](./picture/9、分布式接口有序性-分布式锁.png) 系统A,B,C可能同一个系统,也可能是多个不同的系统。 ### 8、说一说底层运行逻辑 ![](./picture/10、底层运行逻辑.png) 根据protocol配置地址,连接Zookeeper,生成连接客户端对象,Zookeeper Client,向注册中心注册自己作为一个Consumer,订阅Zookeeper下指定Path,获取此目录下的Providers的URL字符串,根据获取到的字符串,重解析成URL对象,然后根据Interface,Version,Group 择取匹配的URL,然后根据这些URL连接相应的Privoder,将连接Provider生成的Invokers生成Directory ,指定相应的集群容错机制,负载均衡器,之后返回给Spring容器。 在订阅Providers目录时,会注册一个监听器Listener,当Provider的元素发生改变时,Zookeeper会通知Client,触发此回调函数,更新Consumer持有的Provider列表,更新Invoker集群。 消费者感知服务机器列表,cluster动态代理生成代理对象,负载均衡选择一台服务器,加载protocol协议内容,exchange层将信息转为request信息,通过hessian序列化后,用netty把请求发送给服务提供者。 服务提供者接收到消息后,hessian反序列化,exchange将信息转换,cluster层产生动态代理对象执行代码,原路返回。 ### 9、dubbo灰度发布 ![](./picture/11、dubbo灰度发布.png) 新功能上线,如何使用dubbo进行灰度发布? 服务器A、B新版本代码,根据网关调度,只把服务少部分的业务提交到A服务,A服务会调用到B服务,这样有足够的测试样本跟用户反馈,知道功能运行如何。运行一段时间都是正常,可以全部一起发布了。 ### 10、如何通过dubboservice来把服务暴露出去。 Dubbo指明了DubboNamespaceHandler类作为标签解析。 ![](./picture/12、dubbo如何暴露服务-1.png) 进入ServiceBean。它实现了两个重要的机制,一个是InitializingBean,当组件创建完对象以后会调用InitializingBean的唯一的方法afterPropertiesSet,也就是在属性设置完以后来回调这个方法。 ![](./picture/13、dubbo暴露服务-2.png) ![](./picture/14、dubbo暴露服务-3.png) ![](./picture/15、dubbo暴露-4.png) ![](./picture/16、dubbo暴露服务-5.png) ServiceBean还实现了ApplicationListener接口,叫应用的监听器。它监听的事件是ContextRefreshedEvent,当我们ioc容器整个刷新完成,也就是ioc容器里面所有对象都创建完成以后来回调方法onApplicationEvent(ContextRefreshedEvent event)。 ![](./picture/17、dubbo暴露-6.png) export()方法暴露服务 1、执行暴露地址 2、加载注册中心的信息,相当于读取注册中心的地址 3、对protocols的分析,要把我们的服务在对应的端口号暴露了 ![](./picture/18、dubbo暴露-7.png) 4、通过ProxyFactory得到invoker,就是提供服务的service的一个包装类,加上url。 5、进入RegistryProtocol的export方法,openServer(url),最终open的是一个netty服务器,我们要暴露服务,创建服务器,其实就是启动netty服务器,监听20880端口。 ### 11、dubbo线程池配置 dubbo 默认线程池大小是200 调整线程池大小配置是 dubbo.protocol.threads = 5000 调整线程池类型配置是 dubbo.protocol.threadpool = cached 调整事件处理方式配置是 dubbo.protocol.dispatcher = message 注册中心地址: 用dubbo协议在20880端口暴露服务: