Skip to content

Latest commit

 

History

History
133 lines (101 loc) · 12.3 KB

Dubbo.md

File metadata and controls

133 lines (101 loc) · 12.3 KB

[toc]

Dubbo是什么?

Dubbo是一款高性能轻量级的开源JavaRPC框架,它提供了三大核心能力:面向接口的远程方法调用智能容错和负载均衡,以及服务自动注册和发现。简单来说Dubbo是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。

  • 负载均衡——同一个服务部署在不同的机器时该调用那一台机器上的服务。
  • 服务调用链路生成——随着系统的发展,服务越来越多,服务间依赖关系变得错踪复杂,甚至分不清哪个应用要在哪个应用之前启动,架构师都不能完整的描述应用的架构关系。Dubbo可以为我们解决服务之间互相是如何调用的。
  • 服务访问压力以及时长统计、资源调度和治理——基于访问压力实时管理集群容量,提高集群利用率。
  • 服务降级——某个服务挂掉之后调用备用服务。

Dubbo的图解?

dubbo架构-qvfIHl

  • **Provider:**暴露服务的服务提供方
  • **Consumer:**调用远程服务的服务消费方
  • **Registry:**服务注册与发现的注册中心
  • **Monitor:**统计服务的调用次数和调用时间的监控中心
  • **Container:**服务运行容器

调用关系说明

  • 服务容器负责启动,加载,运行服务提供者。
  • 服务提供者在启动时,向注册中心注册自己提供的服务。
  • 服务消费者在启动时,向注册中心订阅自己所需的服务。
  • 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
  • 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
  • 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。

各个组件总结

  • 注册中心负责服务地址的注册与查找,相当于目录服务,服务提供者和消费者只在启动时与注册中心交互,注册中心不转发请求,压力较小
  • 监控中心负责统计各服务调用次数,调用时间等,统计先在内存汇总后每分钟一次发送到监控中心服务器,并以报表展示
  • 注册中心,服务提供者,服务消费者三者之间均为长连接,监控中心除外
  • 注册中心通过长连接感知服务提供者的存在,服务提供者宕机,注册中心将立即推送事件通知消费者
  • 注册中心和监控中心全部宕机,不影响已运行的提供者和消费者,消费者在本地缓存了提供者列表
  • 注册中心和监控中心都是可选的,服务消费者可以直连服务提供者
  • 服务提供者无状态,任意一台宕掉后,不影响使用
  • 服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复

Dubbo和SpringCloud的区别?

  • 底层Dubbo底层是使用Netty的NIO框架,基于TCP协议传输,使用Hession序列化完成RPC通信;SpringCloud是基于HTTP协议+REST接口调用远程过程的通信,HTTP请求会有更大的报文,占的带宽也会更多。但是REST相比RPC更为灵活,不存在代码级别的强依赖。
  • 集成:springcloud相关组件多,有自己的注册中心网关等,集成方便,Dubbo需要自己额外去集成。Dubbo是SOA时代的产物,它的关注点主要在于服务的调用,流量分发、流量监控和熔断。而SpringCloud诞生于微服务架构时代,考虑的是微服务治理的方方面面,另外由于依托了Spring、SpringBoot的优势之上,两个框架在开始目标就不一致,Dubbo定位服务治理、SpirngCloud是一个生态。

Dubbo的容错机制

  1. Failover Cluster失败自动切换,当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长延迟。可通过 retries="2" 来设置重试次数
  2. Failfast Cluster快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。
  3. Failsafe Cluster失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。
  4. Failback Cluster失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。
  5. Forking Cluster并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks="2" 来设置最大并行数。
  6. 广播调用所有提供者,逐个调用,任意一台报错则报错。通常用于通知所有提供者更新缓存或日志等本地资源信息

Dubbo的限流是怎么实现的?

Dubbo默认是令牌桶算法实现限流。在某段时间内,以某种速度向桶里面只能放n个令牌,然后来一个请求就减少一个令牌,如果桶内的令牌没有了,则不能继续执行请求。

限流主要是通过TPSLimitFilter实现。

常见的限流算法有哪些?

  • 计数算法
  • 滑动窗口,解决计数算法同一时刻进入很多请求
  • 令牌桶算法
  • 漏桶算法

什么是dubbo服务降级?

  • dubbo在服务调用时,可能由于服务器宕机、网络超时、并发数太高等,导致调用失败。服务降级就是指在非业务异常导致的服务不可用时,可以返回默认值,避免影响主业务的处理。
  • dubbo可以通过mock配置实现服务降级。

Dubbo的注册中心挂了,还可以继续通信吗?

可以。因为在开始初试化的时候,消费者会将提供者的地址等信息拉取到本地缓存中。

负载均衡

个人理解:

比如我们的系统中的某个服务的访问量特别大,我们将这个服务部署在了多台服务器上,当客户端发起请求的时候,多台服务器都可以处理这个请求。那么,如何正确选择处理该请求的服务器就很关键。假如,你就要一台服务器来处理该服务的请求,那该服务部署在多台服务器的意义就不复存在了。负载均衡就是为了避免单个服务器响应同一请求,容易造成服务器宕机、崩溃等问题,我们从负载均衡的这四个字就能明显感受到它的意义。

  1. RandomLoadBalance:随机负载均衡。随机的选择一个。是Dubbo的默认负载均衡策略。
  2. RoundRobinLoadBalance:轮询负载均衡。轮询选择一个。
  3. LeastActiveLoadBalance:最少活跃调用数,相同活跃数的随机。活跃数指调用前后计数差。使慢的 Provider 收到更少请求,因为越慢的 Provider 的调用前后计数差会越大。
  4. ConsistentHashLoadBalance:一致性哈希负载均衡。相同参数的请求总是落在同一台机器上。

http://dubbo.apache.org/zh-cn/blog/dubbo-loadbalance.html

为什么使用zk当dubbo的注册中心

Zookeeper的数据模型很简单,有一系列被称为ZNode的数据节点组成,与传统的磁盘文件系统不同的是,zk将全量数据存储在内存中,可谓是高性能,而且支持集群,可谓高可用,另外支持事件监听。这些特点决定了zk特别适合作为注册中心(数据发布/订阅)。不过要注意网络闪断引发的节点摘除问题。

几个服务注册与发现的对比

https://zhuanlan.zhihu.com/p/145296163

SPI源码(过程)

先说一下Java的SPI机制 Java的SPI机制利用ServiceLoader的load方法传递个接口,就会得到该接口的所有的实现类 要在指定的META-INF的services下 但是有一说一,只能通过iterator来遍历判断想要的实现类 而Dubbo和Spring的SPI比Java的灵活一些,可以通过key来获取对应的实例

直接说Dubbo的SPI源码过程 先说一下Dubbo的SPI机制,不仅支持有着Java的SPI,还有着AOP的功能,同时有着DI功能

  1. 通过getExtensionLoader得到该接口的load,不过获取之间会对一些type检查,同时有缓存机制。
  2. 然后通过load调用getExtension,也是一系列检查和缓存,最关键的就是createExtension
  3. 其中getExtensionClasses,这个方法返回对应name的接口的实例对象,接着来到injectExtension注入属性
  4. 如果有wrapper包装,就是通过接口的实例类有木有构造器,如果有,最后injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));无限遍历AOP,也就是构造器注入,最后返回带包装的接口的实例对象。
  5. 以上是没有讲依赖注入的过程,官网上有。

服务导出与暴漏(源码流程)

源码省略,针对于面试就说源码流程即可 总结一波吧:

  1. 当我们看源码知道,导出和暴漏在IoC初始化的最后一步的finishRefresh中的ServiceBean中。
  2. 其中在onApplicationEvent执行export->doExport,在doExport中首先检查provider呀,register呀,monitor呀等,最后来到关键的一步doExportUrls();
  3. 在这一步当中,实际上,就是对注册的url和导出的url拼接,并且将导出的url远程注册到注册中心,最后暴漏一下自己的url,具体的话就第四步。
  4. doExportUrlsFor1Protocol 包括:1. exportLocal,默认本地导出,2. 远程导出:proxyFactory.getInvoker,然后得到wrapperInvoker,最后就是这个关键了protocol.export(wrapperInvoker),然后会有个子流程去构造buildInvokerChain,调用链。这个是服务调用链路
  5. 实际上找Protocol.class接口的实例代理类,默认是dubbo协议,因此调用的dubbo的实例代理类的export方法,继续使用dubbo协议的url,一步一步绑定nettyClient客户端,最后导出自己的调用链。

服务引入与目录(源码过程)

肯定是ReferenceBean

  1. 当我们看源码知道,首先进来的ReferenceBean的get方法->ReferenceConfig的init方法内部
  2. checkDefault检查消费端的全局配置,接着通过SPI获取消费服务的实现类,经过一些列检查又进入了HashMap的缓存当中
  3. init方法中的最后一步createProxy中,这个方法就是将要引入订阅注册中心的服务端的目录,首先是refprotocol.refer方法从注册中心引入和订阅,该方法是核心。
  4. 首先通过RegistryProtocol的refer中,如果是zk协议,那么就启动zk客户端去连接,接着进入doRefer方法中,先在注册中心,注册消费端服务,接着开始通过subscribe订阅注册中心的目录,category、providers、configurators和routers,然后进入notify,调用listener.notify(categoryList),通知categoryList
  5. 这时候来到了协议Dubbo的refer中,开始构造路由链,首先buildInvokerChain调用链,Dubbo启动的是netty客户端哦,debug时候看出来的,获取的是netty的client,最后构建成功就返回。
  6. 最后将所有的目录添加到cluster中,并返回invoker,其实该invoker是MockClusterInvoker,ref是它的代理实现类最后初始化完毕。

总感觉处处invoker(执行)类似于发送请求一样。

服务调用、服务降级和集群容错

先说一下invorker,在服务引入那里最终返回的是MockClusterInvoker的代理实现类,意思就是说,首先进入Java的动态代理,InvokerInvocationHandler,然后调用invork,进入MockClusterInvoker,然后调用invoke进入默认的FailoverClusterInvoker的invoker。每个invoker就是InvokerDelegate委托实现类

  1. 根据我上面说的,其实从服务目录获取所有的提供者Invokers,在经过MockClusterInvoker的时候,如果配置了服务降级,服务降级就是通过mock机制而已,那么如果调用失败,先走Mock的服务降级策略,如果没有配置,然后开始初始化负载均衡策略,
  2. 就进入了容错策略的Invoker类,然后通过负载均衡选择一个invoker,开始调用过滤链,最后才会执行我们的Dubbo协议上的客户端,应该是netty吧,去执行invoker
  3. 服务那边开始被触发事件之后,也会执行自己的过滤链,然后最后执行自己的InvokerDelegate服务实现委托类,将结果先返回给自己,然后在通过负责处理请求的控制器传给消费端。
  4. 以上是一次调用过程粗略的经过。