高级JAVA开发 Dubbo 部分
- Dubbo
- Dubbo架构&框架设计
- Dubbo的网络通信协议&序列化
- 负载均衡策略
- 集群容错
- Dubbo SPI(Service Provider Interface)& Dubbo的Filter
Dubbo
参考和摘自:
中华石杉 《Java工程师面试突击第1季》
Dubbo 官网文档
Dubbo架构&框架设计
Dubbo 架构具有以下几个特点,分别是连通性、健壮性、伸缩性、以及向未来架构的升级性。参考:架构
重点摘抄:
- 注册中心和监控中心全部宕机,不影响已运行的提供者和消费者,消费者在本地缓存了提供者列表
- 注册中心和监控中心都是可选的,服务消费者可以直连服务提供者
- 服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复
加上用户实现的业务service,一共10层:
- service层(用户实现):用户实现接口提供服务
- 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
Dubbo的网络通信协议&序列化
网络通信协议不同,在各场景下性能不同。dubbo协议采用单链接长链接,适合频繁小数据包传输,不适合大数据包传输(会一直占用IO通道)。单链接的好处就是减少了频繁创建链接时不得不做出的额外消耗(握手验证 等)。并使用异步 IO,复用线程池,防止 C10K 问题。以下介绍不同协议适合的场景。
协议 | 连接个数 | 连接方式 | 传输协议 | 传输方式 | 序列化 | 适用范围 | 适用场景 |
单连接 | 长连接 | TCP | NIO 异步传输 | Hessian 二进制序列化 | - 传入传出参数数据包较小(建议小于100K) - 消费者比提供者个数多,单一消费者无法压满提供者 - 尽量不要用 dubbo 协议传输大文件或超大字符串 | 常规远程服务方法调用 | |
多连接 | 短连接 | TCP | 同步传输 | Java 标准二进制序列化 | - 传入传出参数数据包大小混合 - 消费者与提供者个数差不多 - 可传文件 | 常规远程服务方法调用,与原生RMI服务互操作 | |
多连接 | 短连接 | HTTP | 同步传输 | Hessian二进制序列化 | - 传入传出参数数据包较大 - 提供者比消费者个数多,提供者压力较大 - 可传文件 | 页面传输,文件传输,或与原生hessian服务互操作 | |
多连接 | 短连接 | HTTP | 同步传输 | 表单序列化 | - 传入传出参数数据包大小混合 - 提供者比消费者个数多 - 可用浏览器查看,可用表单或URL传入参数 - 暂不支持传文件 | 需同时给应用程序和浏览器 JS 使用的服务。 | |
多连接 | 短连接 | HTTP | 同步传输 | SOAP 文本序列化 | 系统集成,跨语言调用 | ||
序列化框架优缺点:
优点 | 缺点 | |
Kryo | 速度快,序列化后体积小 | 跨语言支持较复杂 |
Hessian | 默认支持跨语言 | 较慢 |
Protostuff | 速度快,基于protobuf | 需静态编译 |
Protostuff-Runtime | 无需静态编译,但序列化前需预先传入schema | 不支持无默认构造函数的类,反序列化时需用户自己初始化序列化后的对象,其只负责将该对象进行赋值 |
Java | 使用方便,可序列化所有类 | 速度慢,占空间 |
引自:序列化框架性能对比(kryo、hessian、java、protostuff)
负载均衡策略
缺省为 random 随机调用。
- Random LoadBalance
随机,按权重设置随机概率。
在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。 - RoundRobin LoadBalance
轮询,按公约后的权重设置轮询比率。
存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。 - LeastActive LoadBalance
最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。
使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。 - ConsistentHash LoadBalance
一致性 Hash,相同参数的请求总是发到同一提供者。
当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。
算法参见:http://en.wikipedia.org/wiki/Consistent_hashing 缺省只对第一个参数 Hash,如果要修改,请配置 <dubbo:parameter key=“hash.arguments” value=“0,1” />
缺省用 160 份虚拟节点,如果要修改,请配置 <dubbo:parameter key=“hash.nodes” value=“320” />
参见官方 负载均衡
集群容错
缺省为 failover 重试
- Failover Cluster
失败自动切换,当出现失败,重试其它服务器 。通常用于读操作,但重试会带来更长延迟。可通过 retries=“2” 来设置重试次数(不含第一次)。 - Failfast Cluster
快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。 - Failsafe Cluster
失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。 - Failback Cluster
失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。 - Forking Cluster
并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks=“2” 来设置最大并行数。 - Broadcast Cluster
广播调用所有提供者,逐个调用,任意一台报错则报错 [2]。通常用于通知所有提供者更新缓存或日志等本地资源信息。
参见官方 集群容错
Dubbo SPI(Service Provider Interface)& Dubbo的Filter
官方:DubboSPI 总结:Dubbo 并未使用 Java SPI,而是重新实现了一套功能更强的 SPI 机制。
两者在使用上最大的区别是Java SPI配置多实现出来的是个列表,Dubbo SPI配置出来的是个字典(键值对)。在框架中使用:在接口上注解@SPI(“keyName”),keyName即为实现类key,dubbo自己的jar包的/META-INF/dubbo/internal/下寻找实现类配置后加载实现类。
替换动态代理示例:
# 添加如下JavassistProxyFactory 实现类:
public class MyselfJavassistProxyFactory extends JavassistProxyFactory {
public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
return (T) Proxy.getProxy(interfaces).newInstance(new SevenMinInvokerInvocationHandler(invoker));
}
public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
return super.getInvoker(proxy, type, url);
}
}
# /META-INF/dubbo/com.alibaba.dubbo.rpc.ProxyFactory 配置如下:
myselfJavassistProxyFactory=com.example.dubbo.rpc.proxy.javassist.MyselfJavassistProxyFactory
# dubbo.xml 配置如下:
<dubbo:consumer timeout="8000" proxy="myselfJavassistProxyFactory"/>
利用Dubbo SPI取得实现类实例:
另外,(服务提供方和服务消费方)Protocol层设置了调用过程拦截(Filter),Dubbo 本身的大多功能均基于此扩展点实现,每次远程方法执行,该拦截都会被执行,扩展时请注意对性能的影响。
可用SPI机制配置com.alibaba.dubbo.rpc.Filter实现类,并配置到框架Filter中去。
参见:
这里有一个利用Filter在Dubbo集成hystrix的例子可以参考:
dubbo-hystrix-support