dubbo(默认): 单一长连接和 NIO 异步通讯,适合大并发小数据量的服务调用,以及消费者远大于提供者。传输协议 TCP,异步,Hessian 序列化;rmi: 采用 JDK 标准的 rmi 协议实现,传输参数和返回参数对象需要实现 Serializable 接口,使用 java 标准序列化机制,使用阻塞式短连接,传输数据包大小混合,消费者和提供者个数差不多,可传文件,传输协议 TCP。 多个短连接,TCP 协议传输,同步传输,适用常规的远程服务调用和 rmi 互操作。在依赖低版本的 Common-Collections包,java 序列化存在安全漏洞;webservice: 基于 WebService 的远程调用协议,集成 CXF 实现,提供和原生 WebService 的互操作。多个短连接,基于 HTTP 传输,同步传输,适用系统集成和跨语言调用;?http: 基于 Http 表单提交的远程调用协议,使用 Spring 的?HttpInvoke 实现。多个短连接,传输协议 HTTP,传入参数大小混合,提供者个数多于消费者,需要给应用程序和浏览器 JS 调用;?hessian: 集成 Hessian 服务,基于 HTTP 通讯,采用 Servlet 暴露服务,Dubbo 内嵌 Jetty 作为服务器时默认实现,提供与 Hession 服务互操作。多个短连接,同步 HTTP 传输,Hessian 序列化,传入参数较大,提供者大于消费者,提供者压力较大,可传文件;?memcache: 基于 memcached 实现的 RPC 协议?redis: 基于 redis 实现的 RPC 协议
传入传出参数数据包较小(建议小于100K),消费者比提供者个数多,单一消费者无法压满提供者,尽量不要用dubbo协议传输大文件或超大字符串。
通过timeout属性配置超时时间, 服务的提供者和消费者都可以配置, 尽量在服务提供者中配置,因为服务的提供者会对自己提供的服务情况更清楚超时时间不要设置太大(1~5S),会影响并发性能问题
dubbo在调用服务不成功时,默认会重试2次。Dubbo的路由机制,会把超时的请求路由到其他机器上,而不是本机尝试,所以 dubbo的重试机制也能一定程度的保证服务的质量
Multicast 注册中心Multicast 注册中心不需要任何中心节点,只要广播地址,就能进行服务注册和发现。基于网络中组播传输实现?Zookeeper 注册中心基于分布式协调系统 Zookeeper 实现,采用Zookeeper 的 watch 机制实现数据变更?redis 注册中心基于 redis 实现,采用 key/Map 存储,住 key 存储服务名和类型,Map 中 key 存储服务 URL,value 服务过期时间。基于 redis 的发布/订阅模式通知数据变更;
随机按权重设置随机概率。在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。(权重可以在dubbo管控台配置)?轮循按公约后的权重设置轮循比率。存在慢的提供者累积请求问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。?最少活跃调用数相同活跃数的随机,活跃数指调用前后计数差。使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。?一致性Hash相同参数的请求总是发到同一提供者。当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。
dubbo序列化:阿里尚未开发成熟的高效java序列化实现,阿里不建议在生产环境使用它?hessian2序列化(默认推荐):hessian是一种跨语言的高效二进制序列化方式。但这里实际不是原生的hessian2序列化,而是阿里修改过的hessian lite,它是dubbo RPC默认启用的序列化方式?json序列化:目前有两种实现,一种是采用的阿里的fastjson库,另一种是采用dubbo中自己实现的简单json库,但其实现都不是特别成熟,而且json这种文本序列化性能一般不如上面两种二进制序列化。?java序列化:主要是采用JDK自带的Java序列化实现,性能很不理想。
可以通信的,启动dubbo时,消费者会从zk拉取注册的生产者的地址接口等数据,缓存在本地。每次调用时,按照本地存储的地址进行调用;?但前提是你没有增加新的服务,如果你要调用新的服务,则是不能办到的。?另外如果服务的提供者全部宕机,服务消费者会无法使用,并无限次重连等待服务者恢复;
Dubbo 采用全 Spring 配置方式,透明化接入应用,对应用没有任何API 侵入,只需用 Spring 加载 Dubbo 的配置即可
NIO Netty框架
Failover Cluster(默认):失败自动切换,当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长延迟。Failfast Cluster快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。Failsafe Cluster失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。Failback Cluster失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。Forking Cluster并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks="2" 来设置最大并行数。Broadcast Cluster广播调用所有提供者,逐个调用,任意一台报错则报错 。通常用于通知所有提供者更新缓存或日志等本地资源信息。
Dubbo 是 SOA 时代的产物,它的关注点主要在于服务的调用,流量分发、流量监控和熔断。而 Spring Cloud 诞生于微服务架构时代,考虑的是微服务治理的方方面面,另外由于依托了 Spirng、Spirng Boot 的优势之上,两个框架在开始目标就不一致,Dubbo定位服务治理、Spirng Cloud 是一个生态。最大的区别:Dubbo 底层是使用 Netty 这样的 NIO 框架,是基于TCP 协议传输的,配合以 Hession 序列化完成 RPC 通信。而 SpringCloud 是基于 Http 协议+Rest 接口调用远程过程的通信,相对来说,Http 请求会有更大的报文,占的带宽也会更多。但是REST 相比 RPC 更为灵活,服务提供方和调用方的依赖只依靠一纸契约,不存在代码级别的强依赖。
ZooKeeper是一个 分布式 的,开放源码的分布式 应用程序协调服务 ,是Google的Chubby一个开源的实现, 它是集群的管理者,监视着集群中各个节点的状态根据节点提交的反馈进行下一步合理操作。最终,将简单易 用的接口和性能高效、功能稳定的系统提供给用户。 客户端的 读请求 可以被集群中的 任意一台机器处理 ,如果读请求在节点上注册了监听器,这个监听器也是由所 连接的zookeeper机器来处理。对于 写请求 ,这些请求会同 时发给其他 zookeeper 机器并且达成一致后,请 求才会返回成功 。因此,随着 zookeeper 的集群机器增多,读请求的吞吐会提高但是写请求的吞吐会下降 。 有序性是zookeeper中非常重要的一个特性,所有的 更新都是全局有序的 ,每个更新都有一个 唯一的时间戳 , 这个时间戳称为 zxid ( Zookeeper Transaction Id ) 。而 读请求只会相对于更新有序 ,也就是读请求的返回 结果中会带有这个 zookeeper 最新的 zxid 。
1、 文件系统 2、 通知机制
Zookeeper提供一个多层级的节点命名空间(节点称为znode)。与文件系统不同的是,这些节点 都可以设置 关联的数据 ,而文件系统中只有文件节点可以存放数据而目录节点不行。Zookeeper为了保证高吞吐和低延 迟,在内存中维护了这个树状的目录结构,这种特性使得Zookeeper 不能用于存放大量的数据 ,每个节点的存 放数据上限为 1M 。
1、 PERSISTENT- 持久化目录节点 二手交易平台客户端与zookeeper断开连接后,该节点依旧存在2、 PERSISTENT_SEQUENTIAL- 持久化顺序编号目录节点客户端与zookeeper断开连接后,该节点依旧存在,只是Zookeeper给该节点名称进行顺序编号3、 EPHEMERAL- 临时目录节点客户端与zookeeper断开连接后,该节点被删除4、 EPHEMERAL_SEQUENTIAL- 临时顺序编号目录节点客户端与zookeeper断开连接后,该节点被删除,只是Zookeeper给该节点名称进行顺序编号。
client端会对某个znode建立一个watcher 事件 ,当该znode发生变化时,这些client会收到zk的通知, 然后client可以根据znode变化来做出业务上的改变等。
1、命名服务 2、配置管理 3、集群管理 4、分布式锁 5、队列管理
命名服务是指通过指定的名字来 获取资源 或者 服务的地址 ,利用zk创建一个全局的路径,即是 唯一 的路径,这 个路径就可以作为一个名字,指向集群中的集群,提供的服务的地址,或者一个远程的对象等等。
程序分布式的部署在不同的机器上,将程序的配置信息放在zk的znode 下,当有配置发生改变时,也就是 znode发生变化时,可以通过改变zk中某个目录节点的内容,利用 watcher 通知给各个客户端,从而更改配置。
所谓集群管理无在乎两点: 是否有机器退出和加入、选举 master 。 对于第一点,所有机器约定在父目录下 创建临时目录节点 ,然后监听父目录节点的子节点变化消息。一旦有机 器挂掉,该机器与 zookeeper的连接断开,其所创建的临时目录节点被删除, 所有其他机器都收到通知:某个 兄弟目录被删除 ,于是,所有人都知道:它上船了。 新机器加入也是类似, 所有机器收到通知:新兄弟目录加入 ,highcount又有了,对于第二点,我们稍微改变 一下, 所有机器创建临时顺序编号目录节点,每次选取编号最小的机器作为 master 就好。
有了zookeeper的一致性文件系统,锁的问题变得容易。锁服务可以分为两类,一个是保持独占 ,另一个是控制时序 。 对于第一类,我们将zookeeper上的一个 znode 看作是一把锁 ,通过createznode的方式来实现。所有客户 端都去创建 /distribute_lock 节点,最终成功创建的那个客户端也即拥有了这把锁。用完删除掉自己创建的 distribute_lock 节点就释放出锁。(惊群)对于第二类, /distribute_lock 已经预先存在,所有客户端在它下面创建临时顺序编号目录节点,和选 master一样, 编号最小的获得锁 ,用完删除,依次方便。
1、Zookeeper当master挂了,会在30-120s进行leader选举,这点类似于redis的哨兵机制,在选举期间Zookeeper是不可用的,这么长时间不能进行服务注册,是无法忍受的,别说30s,5s都不能忍受。这时Zookeeper集群会瘫痪,这也是Zookeeper的CP,保持节点的一致性,牺牲了A/高可用。2、而Eureka不会,即使Eureka有部分挂掉,还有其他节点可以使用的,他们保持平级的关系,只不过信息有可能不一致,这就是AP,牺牲了C/一致性。并且Eureka还提供了自我保护机制(15分钟内超过85%的服务节点没有心跳/down),这点我觉得确实要比Zookeeper好,即使服务不可用,也会保留当前失效的微服务,默认90秒,在这90秒Eureka不会注销微服务,在这90秒内仍然可以接受新的服务注册,只是不会同步到其他节点上。当坏掉的服务恢复的时候,会自动加入到节点上,也是高可用的一种。然后退出自我保护机制,这也是应对网络异常的一种机制
Zookeeper 的核心是 原子广播 ,这个机制保证了 各个 Server 之间的同步 。实现这个机制的协议叫做Zab 协议 。Zab协议有两种模式,它们分别是 恢复模式(选主) 和 广播模式(同步) 。当服务启动或者在领导者崩溃 后,Zab就进入了恢复模式,当领导者被选举出来,且大多数Server完成了和 leader的状态同步以后,恢复 模式就结束了。状态同步保证了leader和Server具有相同的系统状态。
zookeeper采用了 递增的事务 Id 来标识,所有的proposal(提议)都在被提出的时候加上了zxid,zxid实际 上是一个64位的数字,高32位是epoch(时期; 纪元; 世; 新时代)用来标识leader是否发生改变,如果有 新的leader产生出来,epoch会自增, 低 32 位用来递增计数 。当新产生proposal的时候,会依据数据库的 两阶段过程,首先会向其他的server发出事务执行请求,如果超过半数的机器都能执行并且能够成功,那么就 会开始执行。
每个Server在工作过程中有三种状态: LOOKING:当前Server 不知道 leader 是谁 ,正在搜寻 LEADING:当前Server即为选举出来的leader FOLLOWING:leader已经选举出来,当前Server与之同步。
当leader崩溃或者leader失去大多数的follower,这时zk进入恢复模式,恢复模式需要重新选举出一个新的 leader,让所有的Server都恢复到一个正确的状态。Zk的选举算法有两种:一种是基于basic paxos实现 的,另外一种是基于fast paxos算法实现的。系统默认的选举算法为 fast paxos 。1、Zookeeper选主流程(basic paxos)(1)选举线程由当前Server发起选举的线程担任,其主要功能是对投票结果进行统计,并选出推荐的 Server;(2)选举线程首先向所有Server发起一次询问(包括自己);(3)选举线程收到回复后,验证是否是自己发起的询问(验证zxid是否一致),然后获取对方的id(myid),并存 储到当前询问对象列表中,最后获取对方提议的leader相关信息(id,zxid),并将这些信息存储到当次选举的投 票记录表中;(4)收到所有Server回复以后,就计算出zxid最大的那个Server,并将这个Server相关信息设置成下一次 要投票的Server;(5)线程将当前zxid最大的Server设置为当前Server要推荐的Leader,如果此时获胜的Server获得n/2 + 1的Server票数,设置当前推荐的leader为获胜的Server,将根据获胜的Server相关信息设置自己的状 态,否则,继续这个过程,直到leader被选举出来。 通过流程分析我们可以得出:要使Leader获得多数 Server的支持,则Server总数必须是奇数2n+1,且存活的Server的数目不得少于n+1. 每个Server启动后 都会重复以上流程。在恢复模式下,如果是刚从崩溃状态恢复的或者刚启动的server还会从磁盘快照中恢复数据和会话信息,zk会记录事务日志并定期进行快照,方便在恢复时进行状态恢复。2、Zookeeper选主流程(fast paxos)fast paxos流程是在选举过程中,某Server首先向所有Server提议自己要成为leader,当其它Server收到提 议以后,解决epoch和 zxid的冲突,并接受对方的提议,然后向对方发送接受提议完成的消息,重复这个流程,最后一定能选举出Leader。
选完Leader以后,zk就进入状态同步过程。1、Leader等待server连接;2、Follower连接leader,将最大的zxid发送给leader;3、Leader根据follower的zxid确定同步点;4、完成同步后通知follower 已经成为uptodate状态;5、Follower收到uptodate消息后,又可以重新接受client的请求进行服务了。
对于系统调度来说:操作人员发送通知实际是通过控制台 改变某个节点的状态 , 然后 zk 将这些变化发送给注册了这个节点的 watcher 的所有客户端 。 对于执行情况汇报:每个工作进程都在某个目录下 创建一个临时节点 。 并携带工作的进度数据 ,这样 汇总的进程可以监控目录子节点的变化获得工作进度的实时的全局情况 。
在分布式环境中,有些业务逻辑只需要集群中的某一台机器进行执行, 其他的机器可以共享这个结果 ,这样可 以大大 减少重复计算 , 提高性能 ,于是就需要进行leader选举。
Zookeeper本身也是集群,推荐配置不少于3个服务器。Zookeeper自身也要保证当一个节点宕机时,其他 节点会继续提供服务。 如果是一个Follower宕机,还有2台服务器提供访问,因为Zookeeper上的数据是有多个副本的,数据并不 会丢失; 如果是一个Leader宕机,Zookeeper会选举出新的Leader。 ZK集群的机制是只要超过半数的节点正常,集群就能正常提供服务。只有在ZK节点挂得太多,只剩一半或不 到一半节点能工作,集群才失效。 所以 3个节点的cluster可以挂掉1个节点(leader可以得到2票>1.5) 2个节点的cluster就不能挂掉任何1个节点了(leader可以得到1票<=1) 。
zk的负载均衡是可以调控,nginx只是能调权重,其他需要可控的都需要自己写插件;但是nginx的吞吐量比 zk大很多,应该说按业务选择用哪种方式。
Watch机制官方声明:一个Watch事件是一个一次性的触发器,当被设置了Watch的数据发生了改变的时 候,则服务器将这个改变发送给设置了Watch的客户端,以便通知它们。 Zookeeper机制的特点:1、一次性触发数据发生改变时,一个watcher event会被发送到client,但是client只会收到一次这样的信息 。2、watcher event异步发送watcher的通知事件从server发送到client是 异步 的,这就存在一个问题,不同 的客户端和服务器之间通过socket进行通信,由于 网络延迟或其他因素导致客户端在不通的时刻监听到事件 , 由于Zookeeper本身提供了 ordering guarantee ,即客户端监听事件后,才会感知它所监视 znode 发生了变化 。所以我们使用Zookeeper不能期望能够监控到节点每次的变化。Zookeeper 只能保证最终的一致性, 而无法保证强一致性 。3、数据监视Zookeeper有数据监视和子数据监视getdata() and exists()设置数据监视,getchildren()设置了 子节点监视。4、注册watcher getData 、 exists 、 getChildren。5、触发watcher create 、 delete 、 setData。6、 setData() 会触发znode上设置的data watch(如果set成功的话)。一个成功的 create() 操作会触发被 创建的znode上的数据watch,以及其父节点上的child watch。而一个成功的 delete() 操作将会同时触发一 个znode的data watch和child watch(因为这样就没有子节点了),同时也会触发其父节点的child watch。7、当一个客户端 连接到一个新的服务器上时,watch将会被以任意会话事件触发。当 与一个服务器失去连接的时候,是无法接收到watch的。而当client 重新连接时,如果需要的话,所有先前注册过的watch,都会被重 新注册。通常这是完全透明的。只有在一个特殊情况下, watch 可能会丢失 :对于一个未创建的znode的 exist watch,如果在客户端断开连接期间被创建了,并且随后在客户端连接上之前又删除了,这种情况下,这 个watch事件可能会被丢失。8、Watch是轻量级的,其实就是本地JVM的 Callback ,服务器端只是存了是否有设置了Watcher的布尔类型。