发布网友 发布时间:2022-04-22 07:09
共2个回答
懂视网 时间:2022-04-10 19:52
zk是比较典型场景,所以注册中心都是以zk作为例子的
1 对于registry,提供者没有这个,消费者才有。为什么?
因为只有消费者才需要去注册中心拿到provide的信息,而provider是不需要关注的,provider只需要去注册就好。在RegistryProtocol的export方法中,可以看到在registry方法里面直接在注册中心写信息就够了。
2 RegistryDirectory是啥意思?如果有三个zk,有几个directory?几个registry?
一个目录其实就是很容易想到就是一个dubbo提供者的interfacename在zk上面的/duboo+interfacename的目录,但不仅仅是这样,如果有多个zk,那么有三个目录,因为这三个目录在不同的zk上面。
一个消费者在初始化得到引用的时候,在loadRegistries里面,如果url里面有多个以分号隔开的注册中心的ip+port,那么就得到多个注册中心的url,那么每个url都需要经过registryProcol处理,具体来说createProxy里面就是要对每个注册url进行refer操作
所以directory的构造函数里面有两个东西来限定唯一性:注册中心url和provider的interfacename。对于一个消费者,假设只有一个provider的interfacename需要引用的话,在三个zk的前提下,那么要维护三个registry,三个registry都各自有自己的directory。消费者初始化引用的时候每个directory和对应的registry都需要一个方面跟提供者一样,去不同的zk上面的consumer写入自己的信息,另外要分别订阅这个interfaceface路径下的provider、configure、route信息。第一次订阅的时候,顺便把provider的具体信息都存在directory的methodInvokerMap中,以后要调用的时候就从这里取。
3 dubbo里面经常说的FailoverClusterinvoer、BroadcastClusterInvoker等等这些Cluster是啥意思?
首先cluster接口只有一个方法,就是通过join得到一个invoker,不要被名字误导了,虽然叫cluster,其实没有保存多个invoker,并不是保存了一个集合。虽然叫join,但是其实是利用spi根据配置得到不同的cluster,可以理解成:cluster的join就是根据配置得到不同ClusterInvoker实现。当然还有一个MockClusterWrapper,所以所有的Cluster其实都被这个Wrapper都包了一层,这个是dubbo的spi注入做的,看名字就知道加这一层是为了提前拦截,方便mock测试用的。
4 Cluster和loadbalance的关系
不同的Cluster的实现里面都有一个doInvoke方法,dubbo提供者被调用的时候都会走这个方法,因为cluster本质也是一个invoker,不同的Cluster具有不同的doInvoke实现,在遇到多个invoker调用不顺利情况下 做不同处理。
而loadbalance不同,他是用来挑选优先使用哪个invoker的,一般有随机、一致性哈希,这个也是spi来挑选的。在cluster的共同的父类实现中,也就是在doinvoke之前,都会调用一次loadbalance的select来选出优先的invoker
5 dubbo与zk的关系咋样的,dubbo怎么被zk通知的?
我们说过provider是不需要监听的,消费者才要。我感觉dubbo的zklistener太绕了,dubbox稍微好点。消费者在两个地方进行监听:与zk连接的statechanged回调以及自己引用的interface目录的provider、configure、route目录发生变化的时候,也会有childchanged的回调。
statechanged回调的注册应该比较好找,就是zkclient去连接zk的时候,建立的回调注册。这个里的回调处理是,一旦发现重连,那么把已经注册的、已经订阅的全部划分到failed的list里面,zkregistry有一个心跳,定期检查这些failed的list里面是不是有数据,如果有那么重新注册、订阅。
childchanged是在订阅的时候注册的,也就是doSubscribe里面。当provider、configure、route发生变化的时候,最终调用到RegistryDirectory的notify方法,回调的传过来的参数是当前这个目录比如provider目录下面的实际子节点的全部信息。以provider目录为例子,拿到这些重要信息以后,RegistryDirectory就会根据新的provider的url-list,做一次refreshInvoker,做之前还把缓存的文件也根据这个更新的。
所以说consumer就是通过这个回调感知provider的变化的,看得出来如果zk回调出问题,dubbo就找不到提供者,并且缓存文件也坏了。并且dubbo没有主动去轮训检查zk的当前信息,这块还是比较脆弱的。
我看dubbo官网说的是,阿里内部没用zk,而是用自己的数据库作为注册中心。
6 refreshInvoker 里面做了啥?
前面只是讲到得到了一个provider的url、list,并没有得到一个provider的实体,其实有了provider,需要使用dubboProcol的refer去真正引用一个service,与service建立长链接关系。底层建立transport层的通信关系,我是使用netty4看的有时间可以写写。
至于序列化那块,默认用的hessian,比较繁琐。dubbo没有用protobuf,如果用的话,性能更好,并且代码应该也不需要写这么多。所以没有细研究了
7 消费者refer得到的到底是啥?
根据前面提到的,我们可以简单总结下,假如有三个zk,那么dorefer干下面几个事情:
创建三个registry、三个directory。
与三个registry建立长链接,创建consumer目录上自己的信息,这个叫注册。
然后订阅provider的信息,并且把provider的url信息拿到后refreshInvoker
与这些dubbo-invoker建立连接关系
8 那么问题来了,refer方法结果是dubbo-invoker吗?如果是的话 就用不到cluster、loadbalance这些了。
在doRefer方法的最后,还是调用了cluster.join(directory)得到invoke返回回去。也就是最终返回了三个MockClusterWrapper,里面是FailoverClusterInvoker(默认spi)
对于directory或者说registry做了一次cluster.join,这是因为一个directory可能相同的版本的provider都不止一个,所以不同的provider是一个cluster,这里面存在一个选择。
刚刚外面说了返回了三个MockClusterWrapper,但是consumer或者说我们的业务使用者来说,不应该感知这些,所以对于三个注册中心的三个MockClusterWrapper还要做一次cluster.join:
URL u = registryURL.addParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME);
invoker = cluster.join(new StaticDirectory(u, invokers));
这次是指定用了AvailableCluster,于是又返回了一个MockClusterInvoker,里面包裹了真正的AvailableCluster,这个AvailableCluster的doInvoke方法就是遍历自己的invoke-list,只要可用就用这个,也就是说三个zk,只要有一个没问题,就直接用这个zk的url代表的provider
当然这个ref还要做一次proxy动态代理才能真正返回给用户使用
9 Route是干啥用的,在哪里生效的?
在执行真正的invoke时候,为了使用cluster、loadbalance,必须经过clusterinvoke,在虚基类AbstractClusterInvoker的doInvoke方法中,首先是利用directory.list针对这个invocation做一次筛选,
List<Invoker<T>> invokers = doList(invocation);可以不用考虑,就是根据调用方法拿到invokers(这里的invokers都是通过refreshinvokers拿到的),对于StaticDirectory直接返回所有invokers。
然后会用默认的MockInvokersSelector做一次route筛选处理,通过名字知道还是为了mock调试用的。其他的route还有ConditionRouter和ScriptRouter,路由规则决定一次 dubbo 服务调用的目标服务器。dubbo官网目前是试用阶段。
10 一次调用进行了两次cluster的选择
一次调用,首先选择任一一个可用registry或者说directory的invoker,也就是确定用这个注册中心。后面还要选择到底用这个注册中心的哪个provider,默认spi是FailoverClusterinvoer,然后走loadbalance、cluster逻辑选出真正的invoker,走dubbo调用流程
dubbo中registry、route、directory、cluster、loadbalance、route的关系以及一个引用操作和调用操作到底干了啥
标签:url cat 官网 res 路由规则 构造 router 不用 fail
热心网友 时间:2022-04-10 17:00
Dubbo分层
config(配置层 )
proxy(服务代理层)
registry( 注册中心层)
cluster( 路由层)
monitor( 监控层)
protocol( 远程调用层)
exchange( 信息交换层)
transport( 网络传输层)
serialize( 数据序列化层)
对外配置接口
以ServiceConfig, ReferenceConfig为中心,可以直接new配置类,也可以通过spring解析配置生成配置类
Javassist ProxyFactory
Jdk ProxyFactory
服务接口透明代理,生成服务的客户端Stub和服务器端Skeleton
以ServiceProxy为中心,扩展接口为ProxyFactory
选择
Zookeeper
Redis
Multicast
Simple
支持基于网络的集群方式,有广泛周边开源产品,建议使用bbo-2.3.3以上版本(推荐使用)
依赖于Zookeeper的稳定性
支持基于客户端双写的集群方式,性能高
要求服务器时间同步,用于检查心跳过期脏数据
去中心化,不需要安装注册中心
依赖于网络拓普和路由,跨机房有风险
Dogfooding,注册中心本身也是一个标准的RPC服务
没有集群支持,可能单点故障
封装服务地址的注册与发现
以服务URL为中心,扩展接口为RegistryFactory, Registry, RegistryService
选择
Spring
Jetty
Log4j
自动加载META-INF/spring目录下的所有Spring配置
启动一个内嵌Jetty,用于汇报状态
大量访问页面时,会影响服务器的线程和内存
自动配置log4j的配置,在多进程启动时,自动给日志文件按进程分目录
用户不能控制log4j的配置,不灵活
条件路由
脚本路由
基于条件表达式的路由规则,功能简单易用
有些复杂多分支条件情况,规则很难描述
基于脚本引擎的路由规则,功能强大
没有运行沙箱,脚本能力过于强大,可能成为后门
Random
RoundRobin
LeastActive
ConsistentHash
随机,按权重设置随机概率(推荐使用)
在一个截面上碰撞的概率高,重试时,可能出现瞬间压力不均
轮循,按公约后的权重设置轮循比率
存在慢的机器累积请求问题,极端情况可能产生雪崩
最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差,使慢的机器收到更少请求
不支持权重,在容量规划时,不能通过权重把压力导向一台机器压测容量
一致性Hash,相同参数的请求总是发到同一提供者,当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动
压力分摊不均
Failover
Failfast
Failsafe
Failback
Forking
Broadcast
失败自动切换,当出现失败,重试其它服务器,通常用于读操作(推荐使用)
重试会带来更长延迟
快速失败,只发起一次调用,失败立即报错,通常用于非幂等性的写操作
如果有机器正在重启,可能会出现调用失败
失败安全,出现异常时,直接忽略,通常用于写入审计日志等操作
调用信息丢失
失败自动恢复,后台记录失败请求,定时重发,通常用于消息通知操作
不可靠,重启丢失
并行调用多个服务器,只要一个成功即返回,通常用于实时性要求较高的读操作
需要浪费更多服务资源
广播调用所有提供者,逐个调用,任意一台报错则报错,通常用于更新提供方本地状态
速度慢,任意一台报错则报错
封装多个提供者的路由及负载均衡,并桥接注册中心
以Invoker为中心,扩展接口为Cluster, Directory, Router, LoadBalance
Cluster选择
Router选择
路由规则
容器
RPC调用次数和调用时间监控
以Statistics为中心,扩展接口为MonitorFactory, Monitor, MonitorService
Dubbo协议
Rmi协议
Hessian协议
连接个数:单连接
连接方式:长连接
传输协议:TCP
传输方式:NIO异步传输
序列化:Hessian二进制序列化
适用范围:传入传出参数数据包较小(建议小于100K),消费者比提供者个数多,单一消费者无法压满提供者,尽量不要用bbo协议传输大文件或超大字符串。
适用场景:常规远程服务方法调用
采用NIO复用单一长连接,并使用线程池并发处理请求,减少握手和加大并发效率,性能较好(推荐使用)
适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况
Dubbo缺省协议不适合传送大数据量的服务,比如传文件,传视频等,除非请求量很低
Dubbo协议缺省每服务每提供者每消费者使用单一长连接,如果数据量较大,可以使用多个连接
为防止被大量连接撑挂,可在服务提供方*大接收连接数,以实现服务提供方自我保护
在大文件传输时,单一连接会成为瓶颈
总结
可与原生RMI互操作,基于TCP协议
偶尔会连接失败,需重建Stub
参数及返回值需实现Serializable接口
参数及返回值不能自定义实现List, Map, Number, Date, Calendar等接口,只能用JDK自带的实现,因为hessian会做特殊处理,自定义实现类中的属性值都会丢失
连接个数:多连接
连接方式:短连接
传输协议:HTTP
传输方式:同步传输
序列化:Hessian二进制序列化
适用范围:传入传出参数数据包较大,提供者比消费者个数多,提供者压力较大,可传文件
适用场景:页面传输,文件传输,或与原生hessian服务互操作
提供者用Dubbo的Hessian协议暴露服务,消费者直接用标准Hessian接口调用
或者提供方用标准Hessian暴露服务,消费方用Dubbo的Hessian协议调用
基于Hessian的远程调用协议
可与原生Hessian互操作,基于HTTP协议
需hessian.jar支持,http短连接的开销大
Hessian协议用于集成Hessian的服务,Hessian底层采用Http通讯,采用Servlet暴露服务,Dubbo缺省内嵌Jetty作为服务器实现
可以和原生Hessian服务互操作
总结
约束
封装RPC调用
以Invocation, Result为中心,扩展接口为Protocol, Invoker, Exporter
选择
封装请求响应模式,同步转异步
以Request, Response为中心,扩展接口为Exchanger, ExchangeChannel,ExchangeClient, ExchangeServer
Netty
Mina
Grizzly
性能较好(推荐使用)
一次请求派发两种事件,需屏蔽无用事件
老牌NIO框架,稳定
待发送消息队列派发不及时,大压力下,会出现FullGC
Sun的NIO框架,应用于GlassFish服务器中
线程池不可扩展,Filter不能拦截下一Filter
抽象mina和netty为统一接口
以Message为中心,扩展接口为Channel, Transporter, Client, Server, Codec
选择
Hessian
Dubbo
Json
Java
性能较好,多语言支持(推荐使用)
Hessian的各版本兼容性不好,可能和应用使用的Hessian冲突,Dubbo内嵌了hessian3.2.1的源码
通过不传送POJO的类元信息,在大量POJO传输时,性能较好
当参数对象增加字段时,需外部文件声明
纯文本,可跨语言解析,缺省采用FastJson解析
性能较差
Java原生支持
性能较差
可复用的一些工具
扩展接口为Serialization, ObjectInput, ObjectOutput, ThreadPool
选择
Business
RPC
Remoting
Service
Config
Proxy
Registry
Cluster
Monitor
Protocol
Exchange
Transport
Serialize
层次结构
层说明