Dubbo协议
dubbo://
Dubbo缺省协议采用单一长连接和NIO异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器远大于服务提供者机器数的情况
反之,Dubbo 缺省协议不适合传送大数据量的服务,比如传文件,传视频等,除非请求量很低
特性
缺省协议,使用基于 mina 1.1.7 和 hessian 3.2.1 的 tbremoting 交互
- 连接个数:单连接
- 连接方式:长连接
- 传输协议:TCP
- 传输方式:NIO 异步传输
- 序列化:Hessian 二进制序列化
- 适用范围:传入传出参数数据包较小(建议小于100K),消费者比提供者个数多,单一消费者无法压满提供者,尽量不要用 dubbo 协议传输大文件或超大字符串
- 适用场景:常规远程服务方法调用
约束
- 参数及返回值需实现 Serializable 接口
- 参数及返回值不能自定义实现 List, Map, Number, Date, Calendar 等接口,只能用 JDK 自带的实现,因为 hessian 会做特殊处理,自定义实现类中的属性值都会丢失
- Hessian 序列化,只传成员属性值和值的类型,不传方法或静态变量,兼容情况
数据通讯 | 情况 |
A->B | 类A多一种属性(或者说类B少一种属性) |
A->B | 枚举A多一种枚举(或者说B少一种枚举),A使用多出来的枚举进行传输 |
A->B | 枚举A多一种枚举(或者说B少一种枚举),A不使用多出来的枚举进行传输 |
A->B | A和B的属性名相同,但类型不相同 |
A->B | serialId不相同 |
配置
配置协议
<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" />
多连接配置
Dubbo 协议缺省每服务每提供者每消费者使用单一长连接,如果数据量较大,可以使用多个连接
<dubbo:protocol name="dubbo" connections="2" />
为防止被大量连接撑挂,可在服务提供方限制大接收连接数,以实现服务提供方自我保护
<dubbo:protocol name="dubbo" accepts="1000" />
rmi://
RMI 协议采用 JDK 标准的 java.rmi.* 实现,采用阻塞式短连接和 JDK 标准序列化方式
特性
- 连接个数:多连接
- 连接方式:短连接
- 传输协议:TCP
- 传输方式:同步传输
- 序列化:Java 标准二进制序列化
- 适用范围:传入传出参数数据包大小混合,消费者与提供者个数差不多,可传文件
- 适用场景:常规远程服务方法调用,与原生RMI服务互操作
约束
参数及返回值需实现 Serializable 接口 dubbo 配置中的超时时间对 RMI 无效,需使用 java 启动参数设置:
Dsun.rmi.transport.tcp.responseTimeout=3000
配置
定义RMI协议
<dubbo:protocol name="rmi" port="1099" />
多端口
<dubbo:protocol id="rmi1" name="rmi" port="1099" />
<dubbo:protocol id="rmi2" name="rmi" port="2099" />
<dubbo:service protocol="rmi1" />
hessian://
Hessian 协议用于集成 Hessian 的服务,Hessian 底层采用 Http 通讯,采用 Servlet 暴露服务,Dubbo 缺省内嵌 Jetty 作为服务器实现
特性
- 连接个数:多连接
- 连接方式:短连接
- 传输协议:HTTP
- 传输方式:同步传输
- 序列化:Hessian二进制序列化
- 适用范围:传入传出参数数据包较大,提供者比消费者个数多,提供者压力较大,可传文件
- 适用场景:页面传输,文件传输,或与原生hessian服务互操作
依赖
<dependency>
<groupId>com.caucho</groupId>
<artifactId>hessian</artifactId>
<version>4.0.7</version>
</dependency>
约束
- 参数及返回值需实现 Serializable 接口参数及返回值不能自定义实现 List, Map, Number, Date, Calendar等接口,只能用 JDK 自带的实现,因为 hessian 会做特殊处理,自定义实现类中的属性值都会丢失
配置
定义 hessian 协议
<dubbo:protocol name="hessian" port="8080" server="jetty" />
多端口
<dubbo:protocol id="hessian1" name="hessian" port="8080" />
<dubbo:protocol id="hessian2" name="hessian" port="8081" />
直连
<dubbo:reference id="helloService" interface="HelloWorld" url="hessian://10.20.153.10:8080/helloWorld" />
zookeeper注册中心
Zookeeper 是 Apacahe Hadoop 的子项目,是一个树型的目录服务,支持变更推送,适合作为 Dubbo 服务的注册中心,工业强度较高,可用于生产环境,并推荐使用
流程说明
- 服务提供者启动时: 向 /dubbo/com.foo.BarService/providers 目录下写入自己的 URL 地址
- 服务消费者启动时: 订阅 /dubbo/com.foo.BarService/providers 目录下的提供者 URL 地址。并向 /dubbo/com.foo.BarService/consumers 目录下写入自己的 URL 地址
- 监控中心启动时: 订阅 /dubbo/com.foo.BarService 目录下的所有提供者和消费者 URL 地址
支持以下功能
- 当提供者出现断电等异常停机时,注册中心能自动删除提供者信息
- 当注册中心重启时,能自动恢复注册数据,以及订阅请求
- 当会话过期时,能自动恢复注册数据,以及订阅请求
- 当设置 <dubbo:registry check=”false” /> 时,记录失败注册和订阅请求,后台定时重试
- 可通过 <dubbo:registry username=”admin” password=”1234” /> 设置 zookeeper 登录信息
- 可通过 <dubbo:registry group=”dubbo” /> 设置 zookeeper 的根节点,不设置将使用无根树
- 支持 * 号通配符 <dubbo:reference group=”” version=”” />,可订阅服务的所有分组和所有版本的提供者
使用
在 provider 和 consumer 中增加 zookeeper 客户端 jar 包依赖
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.3.3</version>
</dependency>
使用zkclient客户端实现
缺省配置
<dubbo:registry ... client="zkclient" />
需依赖
<dependency>
<groupId>com.github.sgroschupf</groupId>
<artifactId>zkclient</artifactId>
<version>0.1</version>
</dependency>
Zookeeper 单机配置
<dubbo:registry protocol="zookeeper" address="10.20.153.10:2181" />
Zookeeper 集群配置
<dubbo:registry protocol="zookeeper" address="10.20.153.10:2181,10.20.153.11:2181,10.2 0.153.12:2181" />
同一 Zookeeper,分成多组注册中心
<dubbo:registry id="chinaRegistry" protocol="zookeeper" address="10.20.153.10:2181" group="china" />
<dubbo:registry id="intlRegistry" protocol="zookeeper" address="10.20.153.10:2181" group="intl" />
服务化最佳实践
分包
建议将服务接口,服务模型,服务异常等均放在 API 包中,因为服务模型及异常也是 API 的一部分,同时,这样做也符合分包原则:重用发布等价原则(REP),共同重用原则(CRP)
如果需要,也可以考虑在 API 包中放置一份 spring 的引用配置,这样使用方,只需在 spring 加载过程中引用此配置即可,配置建议放在模块的包目录下,以免冲突, 如: com/alibaba/china/xxx/dubbo-reference.xml
粒度
服务接口尽可能大粒度,每个服务方法应代表一个功能,而不是某功能的一个步骤,否则将面临分布式事务问题,Dubbo 暂未提供分布式事务支持
服务接口建议以业务场景为单位划分,并对相近业务做抽象,防止接口数量爆炸
不建议使用过于抽象的通用接口,如: Map query(Map) ,这样的接口没有明确语义,会给后 期维护带来不便
版本
每个接口都应定义版本号,为后续不兼容升级提供可能,如:<dubbo:service interface=”com.xxx.XxxService” version=”1.0” />
建议使用两位版本号,因为第三位版本号通常表示兼容升级,只有不兼容时才需要变更服务版本
当不兼容时,先升级一半提供者为新版本,再将消费者全部升为新版本,然后将剩下的一半提供者升为新版本
兼容性
服务接口增加方法,或服务模型增加字段,可向后兼容,删除方法或删除字段,将不兼容,枚举类型新增字段也不兼容,需通过变更版本号升级
枚举值
如果是完备集,可以用 Enum,比如: ENABLE, DISABLE
如果是业务种类,以后明显会有类型增加,不建议用 Enum,可以用 String 代替
如果是在返回值中用了 Enum,并新增了 Enum 值,建议先升级服务消费方,这样服务提供方不会返回新值
如果是在传入参数中用了 Enum,并新增了 Enum 值,建议先升级服务提供方,这样服务消费方不会传入新值
序列化
服务参数及返回值建议使用 POJO 对象,即通过 setter, getter 方法表示属性的对象
服务参数及返回值不建议使用接口,因为数据模型抽象的意义不大,并且序列化需要接口实现类的元信息,并不能起到隐藏实现的意图
服务参数及返回值都必需是 byValue 的,而不能是 byReference 的,消费方和提供方的参数或返回值引用并不是同一个,只是值相同,Dubbo 不支持引用远程对象
异常
建议使用异常汇报错误,而不是返回错误码,异常信息能携带更多信息,以及语义更友好
如果担心性能问题,在必要时,可以通过 override 掉异常类的 fillInStackTrace() 方法为空方法,使其不拷贝栈信息
查询方法不建议抛出 checked 异常,否则调用方在查询时将过多的 try…catch,并且不能进行有效处理
服务提供方不应将 DAO 或 SQL 等异常抛给消费方,应在服务实现中对消费方不关心的异常进行包装,否则可能出现消费方无法反序列化相应异常
调用
不要只是因为是 Dubbo 调用,而把调用 try…catch 起来。 try…catch 应该加上合适的 回滚边界上
对于输入参数的校验逻辑在 Provider 端要有。如有性能上的考虑,服务实现者可以考虑在 API 包上加上服务 Stub 类来完成检验
推荐用法
在Provider上尽量多配置Consumer端属性
原因
- 作服务的提供者,比服务使用方更清楚服务性能参数,如调用的超时时间,合理的重试次数,等等
- 在 Provider 配置后,Consumer 不配置则会使用 Provider 的配置值,即 Provider 配置可以作为 Consumer 的缺省值。否则,Consumer 会使用 Consumer 端的全局设置,这对于 Provider 不可控的,并且往往是不合理的
示例
<dubbo:service interface="com.alibaba.hello.api.HelloService" version="1.0.0" ref="hel loService" 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属性有
- timeout 方法调用超时
- retries 失败重试次数,缺省是 2
- loadbalance 负载均衡算法,缺省是随机 random。还可以有轮询 roundrobin、最不活跃优先 leastactive
- actives 消费者端,最大并发调用限制,即当 Consumer 对一个服务的并发调用到上限后,新调用会 Wait 直到超时,在方法上配置 dubbo:method 则并发限制针对方法,在接口上配置 dubbo:service ,则并发限制针对服务
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 端属性有
- threads 服务线程池大小
- executes 一个服务提供者并行执行请求上限,即当 Provider 对一个服务的并发调用到上限后,新调用会 Wait,这个时候Consumer可能会超时。在方法上配置 dubbo:method 则并发限制针对方法,在接口上配置 dubbo:service,则并发限制针对服务
配置Dubbo缓存文件
提供者列表缓存文件
<dubbo:registry file="${user.home}/output/dubbo.cache" />
这个文件会缓存注册中心的列表和服务提供者列表。有了这项配置后,当应用重启过程中,Dubbo 注册中心不可用时则应用会从这个缓存文件读取服务提供者列表的信息,进一步保证应用可靠性