dubbo

一 什么是dubbo

下面从Dubbo官网直接拿来,看一下基于RPC层,服务提供方和服务消费方之间的调用关系,如图所示:

上述节点简单介绍以及他们之间的关系:

  • Container: 服务运行容器,负责加载、运行服务提供者。必须。
  • Provider: 暴露服务的服务提供方,会向注册中心注册自己提供的服务。必须。
  • Consumer: 调用远程服务的服务消费方,会向注册中心订阅自己所需的服务。必须。
  • Registry: 服务注册与发现的注册中心。注册中心会返回服务提供者地址列表给消费者。非必须。
  • Monitor: 统计服务的调用次数和调用时间的监控中心。服务消费者和提供者会定时发送统计数据到监控中心。 非必须。

 

上图中,蓝色的表示与业务有交互,绿色的表示只对Dubbo内部交互。上述图所描述的调用流程如下:

    1. 服务提供方发布服务到服务注册中心;
    2. 服务消费方从服务注册中心订阅服务;
    3. 服务消费方调用已经注册的可用服务

 

二  dubbo通信协议

1. dubbo协议

缺省协议,使用基于mina1.1.7+hessian3.2.1的tbremoting交互。
连接个数:单连接
连接方式:长连接
传输协议:TCP
传输方式:NIO异步传输
序列化:Hessian二进制序列化
适用范围:传入传出参数数据包较小(建议小于100K),消费者比提供者个数多,单一消费者无法压满提供者,尽量不要用dubbo协议传输大文件或超大字符串。
适用场景:常规远程服务方法调用

1、dubbo默认采用dubbo协议,dubbo协议采用单一长连接和NIO异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况
2、他不适合传送大数据量的服务,比如传文件,传视频等,除非请求量很低。
配置如下:

<dubbo:protocol name="dubbo" port="20880" />
<dubbo:protocol name=“dubbo” port=“9090” server=“netty” client=“netty” codec=“dubbo”
serialization=“hessian2” charset=“UTF-8” threadpool=“fixed” threads=“100” queues=“0” iothreads=“9”
buffer=“8192” accepts=“1000” payload=“8388608” />
3、Dubbo协议缺省每服务每提供者每消费者使用单一长连接,如果数据量较大,可以使用多个连接。

<dubbo:protocol name="dubbo" connections="2" />
4、为防止被大量连接撑挂,可在服务提供方限制大接收连接数,以实现服务提供方自我保护

<dubbo:protocol name="dubbo" accepts="1000" />

2. rmi协议
Java标准的远程调用协议。
连接个数:多连接
连接方式:短连接
传输协议:TCP
传输方式:同步传输
序列化:Java标准二进制序列化
适用范围:传入传出参数数据包大小混合,消费者与提供者个数差不多,可传文件。
适用场景:常规远程服务方法调用,与原生RMI服务互操作

RMI协议采用JDK标准的java.rmi.*实现,采用阻塞式短连接和JDK标准序列化方式 。

3. hessian协议
基于Hessian的远程调用协议。
连接个数:多连接
连接方式:短连接
传输协议:HTTP
传输方式:同步传输
序列化:表单序列化
适用范围:传入传出参数数据包大小混合,提供者比消费者个数多,可用浏览器查看,可用表单或URL传入参数,暂不支持传文件。
适用场景:需同时给应用程序和浏览器JS使用的服务。

1、Hessian协议用于集成Hessian的服务,Hessian底层采用Http通讯,采用Servlet暴露服务,Dubbo缺省内嵌Jetty作为服务器实现。
2、Hessian是Caucho开源的一个RPC框架:http://hessian.caucho.com,其通讯效率高于WebService和Java自带的序列化。

4. http协议
基于http表单的远程调用协议。参见:[HTTP协议使用说明]
连接个数:多连接
连接方式:短连接
传输协议:HTTP
传输方式:同步传输
序列化:表单序列化
适用范围:传入传出参数数据包大小混合,提供者比消费者个数多,可用浏览器查看,可用表单或URL传入参数,暂不支持传文件。
适用场景:需同时给应用程序和浏览器JS使用的服务。


5. webservice协议

基于WebService的远程调用协议。 
连接个数:多连接 
连接方式:短连接 
传输协议:HTTP 
传输方式:同步传输 
序列化:SOAP文本序列化 
适用场景:系统集成,跨语言调用

 

 

三 dubbo序列化

dubbo支持hession、rmi(Java二进制序列化)、json、SOAP文本序列化多种序列化协议。但是 hessian 是其默认的序列化协议。

序列化是将一个对象变成一个二进制流就是序列化, 反序列化是将二进制流转换成对象。

为什么要序列化?

1. 减小内存空间和网络传输的带宽

2. 分布式的可扩展性

3. 通用性,接口可共用。

dubbo序列化:阿里尚未开发成熟的高效java序列化实现,阿里不建议在生产环境使用它

hessian2序列化:hessian是一种跨语言的高效二进制序列化方式。但这里实际不是原生的hessian2序列化,而是阿里修改过的hessian lite,它是dubbo RPC默认启用的序列化方式
json序列化:目前有两种实现,一种是采用的阿里的fastjson库,另一种是采用dubbo中自己实现的简单json库,但其实现都不是特别成熟,而且json这种文本序列化性能一般不如上面两种二进制序列化。
java序列化:主要是采用JDK自带的Java序列化实现,性能很不理想。
这四种主要序列化方式的性能从上到下依次递减。对于dubbo RPC这种追求高性能的远程调用方式来说,实际上只有1、2两种高效序列化方式比较般配,而第1个 dubbo 序列化由于还不成熟,所以实际只剩下2可用,所以dubbo RPC默认采用 hessian2 序列化。

但 hessian 是一个比较老的序列化实现了,而且它是跨语言的,所以不是单独针对java进行优化的。而dubbo RPC实际上完全是一种Java to Java的远程调用,其实没有必要采用跨语言的序列化方式(当然肯定也不排斥跨语言的序列化)。

最近几年,各种新的高效序列化方式层出不穷,不断刷新序列化性能的上限,最典型的包括:

专门针对Java语言的:Kryo,FST等等
跨语言的:ProtoBuf,Thrift等
这些序列化方式的性能多数都显著优于 hessian2。所以我们可以为dubbo引入Kryo和FST这两种高效 Java 来优化 dubbo 的序列化。
使用Kryo和FST非常简单,只需要在dubbo RPC的XML配置中添加一个属性即可:

<dubbo:protocol name="dubbo" serialization="kryo"/>

 

四 pb了解么,为什么性能高

可能大家比较习惯于 JSON or XML 数据存储格式,对于 Protocol Buffer 还比较陌生。Protocol Buffer 其实是 Google 出品的一种轻量并且高效的结构化数据存储格式,性能比 JSON、XML要高很多。

其实 PB 之所以性能如此好,主要得益于两个:第一,它使用 proto 编译器,自动进行序列化和反序列化,速度非常快,应该比 XML 和 JSON 快上了 20~100 倍;第二,它的数据压缩效果好,就是说它序列化后的数据量体积小。因为体积小,传输起来带宽和速度上会有优化。

 

 

五 路由机制

https://blog.csdn.net/prestigeding/article/details/80848594

 

 

六 谈谈dubbo的超时重试

dubbo 启动时默认有重试机制和超时机制。如果在一定的时间内,provider没有返回,则认为本次调用失败。重试机制出现在调用失败时,会再次调用,如果在配置的调用次数内都失败,则认为此次请求异常,消费端出现RpcException提示retry了多少次还是失败。

如果出现超时,通常是业务处理太慢,可在服务提供方执行 jstack PID > jstack.log 分析线程都卡在哪个方法调用上。如果不能调优性能,请将timeout设大。

 

dubbo消费端设置的超时时间需要根据业务实际情况来设定,如果设置的过短,一些复杂业务需要很长时间完成,导致在设定的超时时间内无法完成正常的业务处理。这样消费端达到超时时间,那么dubbo会进行重试,不合理的重试在一些特殊的业务场景下可能会引发很多问题。比如发送邮件,可能会发出多份重复邮件等。

 

dubbo调用服务不成功时,默认会重试两次。dubbo的路由机制,会把超时的请求路由到其他机器上,而不是本机尝试,所以dubbo的重试机制也能得到一定程度的保证。但是不合理地配置重试次数,当失败时会进行重试多次,这样在某个时间点出现性能问题,调用方继续重试请求为正常retries倍,容易引起服务雪崩。

最佳实践:

1.对于核心的服务中心,去除dubbo超时重试机制,并重新评估设置超时时间。
2.业务处理代码必须放在服务端,客户端只做参数验证和服务调用,不涉及业务流程处理

 

 

七 关于dubbo的配置上,你有什么经验

(1) 在Provider上尽量多地配置Consumer的属性

原因: 

a. 作为服务的提供者,比服务使用方更清楚服务性能参数,如调用的超时时间,合理的重试次数等

b. 在provider配置后,consumer不配置则自动使用provider的配置,即作为了consumer端的缺省值。否则,consumer会使用consumer端的全局配置,这对于provider端往往是不可控的。

ps: 配置的优先级:

1. 方法级配置别优于接口级别,即小Scope优先 
2. Consumer端配置优于Provider配置,优于全局配置 
3. Dubbo Hard Code的配置值(默认)

根据规则2, 纵使消费端配置优于服务器配置,但消费端配置超时时间不能随心所欲,需要根据业务实际情况来设定。如果设置的太短,复杂业务本来就需要很长时间完成,服务端无法在设定的超时时间内完成业务处理; 如果设置太长,会由于服务端或者网络问题导致客户端大量线程挂起。

配置示例

<dubbo:service interface="com.alibaba.hello.api.HelloService" version="1.0.0" ref="helloService"
    timeout="300" retry="2" loadbalance="random" actives="0"/>
 
<dubbo:service interface="com.alibaba.hello.api.WorldService" version="1.0.0" ref="helloService"
    timeout="300" retry="2" loadbalance="random" actives="0" >
    <dubbo:method name="findAllPerson" timeout="10000" retries="9" loadbalance="leastactive" actives="5" />
<dubbo:service/>

在provider端可以配置的consumer属性有:

1 timeout 方法调用超时时间

2 retry 失败重试次数,缺省是2(加上第一次调用,共调用三次)

3 loadbalance 负责均衡算法(即多个provider如何挑选provider调用),缺省是随机(random),还可以有轮训(roundrobin),最不活跃优先(leastactive, 指从consumer端并发调用效果最好的provider,这样可以相对减少并发的堆积)

4 actives 消费端最大并发调用限制,即当consumer对一个服务的并发调用达到上限后,新调用会wait直到超时。粒度上,在方法上配置(dubbo: method)则并发针对方法,在接口上配置(dubbo:service)则并发限制针对服务

 

(2) 在Provider上配置合理的provider属性

<dubbo:protocol threads="200" />
<dubbo:service interface="com.alibaba.hello.api.HelloService" version="1.0.0" ref="helloService" executes="200" >
    <dubbo:method name="findAllPerson" executes="50" />
</dubbo:service>

Provider上可以配置的Provider端属性有:

  1. threads,服务线程池大小
  2. executes,一个服务提供者并行执行请求上限,即当Provider对一个服务的并发调用到上限后,新调用会wait(Consumer可能到超时)。在方法上配置(dubbo:method )则并发限制针对方法,在接口上配置(dubbo:service),则并发限制针对服务。

 

八 Dubbo 序列化协议

Dubbo 支持多种序列化方式:JDK 自带的序列化、hessian2、JSON、Kryo、FST、Protostuff,ProtoBuf 等等。

Dubbo 默认使用的序列化方式是 hessian2。

一般我们不会直接使用 JDK 自带的序列化方式。主要原因有两个:

  1. 不支持跨语言调用 : 如果调用的是其他语言开发的服务的时候就不支持了。
  2. 性能差:相比于其他序列化框架性能更低,主要原因是序列化之后的字节数组体积较大,导致传输成本加大。

JSON 序列化由于性能问题,我们一般也不会考虑使用。

像 Protostuff,ProtoBuf、hessian2 这些都是跨语言的序列化方式,如果有跨语言需求的话可以考虑使用。

Kryo 和 FST 这两种序列化方式是 Dubbo 后来才引入的,性能非常好。不过,这两者都是专门针对 Java 语言的。Dubbo 官网的一篇文章中提到说推荐使用 Kryo 作为生产环境的序列化方式。

 

九 Dubbo 中的 Invoker 概念了解么?

Invoker 是 Dubbo 领域模型中非常重要的一个概念,你如果阅读过 Dubbo 源码的话,你会无数次看到这玩意。就比如下面我要说的负载均衡这块的源码中就有大量 Invoker 的身影。

简单来说,Invoker 就是 Dubbo 对远程调用的抽象。

按照 Dubbo 官方的话来说,Invoker 分为

  • 服务提供 Invoker
  • 服务消费 Invoker

假如我们需要调用一个远程方法,我们需要动态代理来屏蔽远程调用的细节吧!我们屏蔽掉的这些细节就依赖对应的 Invoker 实现, Invoker 实现了真正的远程服务调用。

 

  

十 Dubbo 的工作原理了解么?

下图是 Dubbo 的整体设计,从下至上分为十层,各层均为单向依赖。

左边淡蓝背景的为服务消费方使用的接口,右边淡绿色背景的为服务提供方使用的接口,位于中轴线上的为双方都用到的接口。

  • config 配置层:Dubbo 相关的配置。支持代码配置,同时也支持基于 Spring 来做配置,以 ServiceConfig, ReferenceConfig 为中心
  • proxy 服务代理层:调用远程方法像调用本地的方法一样简单的一个关键,真实调用过程依赖代理类,以 ServiceProxy 为中心。
  • registry 注册中心层:封装服务地址的注册与发现。
  • cluster 路由层:封装多个提供者的路由及负载均衡,并桥接注册中心,以 Invoker 为中心。
  • monitor 监控层:RPC 调用次数和调用时间监控,以 Statistics 为中心。
  • protocol 远程调用层:封装 RPC 调用,以 Invocation, Result 为中心。
  • exchange 信息交换层:封装请求响应模式,同步转异步,以 Request, Response 为中心。
  • transport 网络传输层:抽象 mina 和 netty 为统一接口,以 Message 为中心。
  • serialize 数据序列化层:对需要在网络传输的数据进行序列化。

 

十一 Dubbo 的 SPI 机制了解么? 如何扩展 Dubbo 中的默认实现?

SPI(Service Provider Interface) 机制被大量用在开源项目中,它可以帮助我们动态寻找服务/功能(比如负载均衡策略)的实现。

SPI 的具体原理是这样的:我们将接口的实现类放在配置文件中,我们在程序运行过程中读取配置文件,通过反射加载实现类。这样,我们可以在运行的时候,动态替换接口的实现类。和 IoC 的解耦思想是类似的。

Java 本身就提供了 SPI 机制的实现。不过,Dubbo 没有直接用,而是对 Java 原生的 SPI 机制进行了增强,以便更好满足自己的需求。

 

十二 注册中心的几个小问题

注册中心的作用了解么?

注册中心负责服务地址的注册与查找,相当于目录服务,服务提供者和消费者只在启动时与注册中心交互。

服务提供者宕机后,注册中心会做什么?

注册中心会立即推送事件通知消费者。

监控中心的作用呢?

监控中心负责统计各服务调用次数,调用时间等。

注册中心和监控中心都宕机的话,服务都会挂掉吗?

不会。两者都宕机也不影响已运行的提供者和消费者,消费者在本地缓存了提供者列表。注册中心和监控中心都是可选的,服务消费者可以直连服务提供者。

 

十三 Dubbo 提供的负载均衡策略有哪些?

在集群负载均衡时,Dubbo 提供了多种均衡策略,默认为 random 随机调用。我们还可以自行扩展负载均衡策略(参考 Dubbo SPI 机制)。

在 Dubbo 中,所有负载均衡实现类均继承自 AbstractLoadBalance,该类实现了 LoadBalance 接口,并封装了一些公共的逻辑。

posted @ 2018-03-28 12:11  balfish  阅读(556)  评论(0编辑  收藏  举报