分布式

一  分布式微服务的区别

             1. 最大的区别是微服务是个组件化的,有自己独立的资源,可以独立运行,可插拔。

             2. 合理资源分配

                 比如订单系统压力大了我们可以单独给订单系统加机器,而分布式需要挪动很多逻辑

            3. 人尽其才

                因为服务拆分粒度细,可以使用各种语言开发服务,而分布式就不能这样完美

            4. 加快部署

                挂了之后可以尽快启动一个docker镜像

一:CAP定理和Base理论

     (1)CAP定理

              C:一致性,同一数据的多个副本是否实时相同。

              A:可用性,在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请

                    求(对数据更新具备高可用性)。

              P:分区容忍性,分区容错性约束了一个分布式系统具有如下特性:分布式系统在遇

                    到任何网络分区故障的时候,仍然需要能够保证对外提供满足一致性和可用性的

                    服务,除非是整个网络环境都发生了故障。

              定理:任何分布式系统只可同时满足二点,没法三者兼顾。

              忠告:架构师不要将精力浪费在如何设计能满足三者的完美分布式系统,而是应该进

                         行取舍。

             实际选择:对于一个分布式系统,我们始终要假设网络是不可靠的,所以分区容错性

                               是对一个分布式系统最基本的要求,所以我们更多的是尝试在可用性和一

                               致性之间寻找一个平衡点。让分布式集群始终对外提供可用的一致性服

                               务。例如数据库分库就是采用的最终一致性。

              一致性和可用性的说明:

                     对于一致性和可用性的完整解释请参考文章:
                     http://blog.csdn.net/jewes/article/details/43495639

                     假设我们用一台服务器A对外提供存储服务,为了避免这台服务器宕机导致服务

              不可用,我们又在另外一台服务器B上运行了同样的存储服务。每次用户在往服务器

              A写入数据的时候,A都往服务器B上写一份,然后再返回客户端。一切都运行得很

              好,用户的每份数据都存了两份,分别在A和B上,用户访问任意一台机器都能读取

              到最新的数据。这时不幸的事情发生,A和B之间的网络断了导致A和B无法通信,也

              就是说网络出现了分区,那么用户在往服务器A写入数据的时候,服务器A无法将该

              数据写入到服务器B。这时,服务器A就必须要做出一个艰难的选择:

           (a)选择一致性而牺牲可用性:为了保证服务器A和B上的数据是一致的,服务器A决

                    定暂停对外提供数据写入服务,从而保证了服务器A和B上的数据是一致,但是牺

                    牲了可用性。

                    注意:这里的可用性不是我们通常所说的高可用性(比如,服务器宕机导致服务不

                               可用),而是指服务器虽然活着,但是却不能对外提供写入服务。

           (b)选择可用性而牺牲一致性:为了保证服务不中断,服务器A先把数据写入到了本

                    地,然后返回客户端,从而让客户端感觉数据已经写入了。这导致了服务器A和

                    B上的数据就不一致了。

             分区容错性的说明:

                    一个分布式系统里面,节点组成的网络本来应该是连通的。然而可能因为一些故

             障,使得有些节点之间不连通了,整个网络就分成了几块区域。数据就散布在了这些

             不连通的区域中。这就叫分区。

                    当一个数据项只在一个节点中保存,那么分区出现后,和这个节点不连通的部分

             就访问不到这个数据了。这时分区就是无法容忍的。提高分区容忍性的办法就是一个

             数据项复制到多个节点上,那么出现分区之后,这一数据项就可能分布到各个区里。

             容忍性就提高了。然而,要把数据复制到多个节点,就会带来一致性的问题,就是多

             个节点上面的数据可能是不一致的。要保证一致,每次写操作就都要等待全部节点写

             成功,而这等待又会带来可用性的问题。总的来说就是,数据存在的节点越多,分区

             容忍性越高,但要复制更新的数据就越多,一致性就越难保证。为了保证一致性,更

             新所有节点数据所需要的时间就越长,可用性就会降低。

       (2)BASE理论:

                全称:Basically Available(基本可用),Soft state(软状态),和

                Eventually consistent(最终一致性)三个短语的缩写。

                Base 理论是对 CAP 中一致性和可用性权衡的结果,其来源于对大型互联网分布式

                实践的总结,是基于 CAP 定理逐步演化而来的。其核心思想是:满足 CAP 理论,

                通过 牺牲强一致性来保证系统可用性。由于牺牲了强一致性,系统在处理请求的过

                程中,数据可以存在短时的不一致。

              (a)Basically Available(基本可用):假设系统,出现了不可预知的故障,但还是能

                       用,相比较正常的系统而言:

                      Ⅰ:响应时间上的损失:正常情况下的搜索引擎0.5秒即返回给用户结果,而基

                             本可用的搜索引擎可以在 1 秒作用返回结果。

                      Ⅱ:功能上的损失:在一个电商网站上,正常情况下,用户可以顺利完成每一

                             笔订单,但是到了大促期间,为了保护购物系统的稳定性,部分消费者可

                             能会被引导到一个降级页面。

              (b)Soft state(软状态)

                       要求多个节点的数据副本都是一致的,这是一种 “硬状态”。

                       软状态指的是:允许系统中的数据存在中间状态,并认为该状态不影响系统的

                       整体可用性,即允许系统在多个不同节点的数据副本存在数据延时。

              (c)Eventually consistent(最终一致性)

                       最终数据是一致的就可以了,而不是时时保持强一致。

                              例如一个电商系统,当用户下单并支付后,系统需要修改订单的状态并且

                      增加用户积分。支付成功,订单也成功,但增加积分失败,此时,不应回滚支

                      付和订单,而应通过一些补偿方法来让积分得以正确地增加。

二:两阶段提交协议和三阶段提交协议

       1.两阶段提交协议

        (1)两阶段提交协议介绍

       ​  

               (a)请求阶段(commit-request phase,或称表决阶段,voting phase)

                        事务协调者通知每个参与者准备提交或取消事务,然后进入表决过程,参与者

                        要么在本地执行事务,写本地的redo和undo日志,但不提交,到达一种"万事俱

                        备,只欠东风"的状态。请求阶段,参与者将告知协调者自己的决策: 同意(事务

                        参与者本地作业执行成功)或取消(本地作业执行故障)。

               (b)提交阶段(commit phase)

                        在该阶段,协调者将基于第一个阶段的投票结果进行决策:提交或取消。当且

                        仅当所有的参与者同意提交事务协调者才通知所有的参与者提交事务,否则协

                        调者将通知所有的参与者取消事务。参与者在接收到协调者发来的消息后将执

                        行响应的操作。

        (2)两阶段提交协议的缺点

               (a)同步阻塞问题。执行过程中,所有参与节点都是事务阻塞型的。当参与者占有

                        公共资源时,其他第三方节点访问公共资源不得不处于阻塞状态。

               (b)单点故障。由于协调者的重要性,一旦协调者发生故障。参与者会一直阻塞下

                        去。尤其在第二阶段,协调者发生故障,那么所有的参与者还都处于锁定事务

                        资源的状态中,而无法继续完成事务操作。(如果是协调者挂掉,可以重新选

                        举一个协调者,但是无法解决因为协调者宕机导致的参与者处于阻塞状态的问

                        题)

               (c)数据不一致。在二阶段提交的阶段二中,当协调者向参与者发送commit请求之

                        后,发生了局部网络异常或者在发送commit请求过程中协调者发生了故障,这

                        回导致只有一部分参与者接受到了commit请求。

       2.三阶段提交协议

        (1)三阶段提交协议简介

                 三阶段提交(Three-phase commit),也叫三阶段提交协议(Three-phase

                 commit protocol),是二阶段提交(2PC)的改进版本。

                与两阶段提交不同的是,三阶段提交有两个改动点:

              (a)引入超时机制。同时在协调者和参与者中都引入超时机制。

              (b)两阶段提交协议的第一个阶段拆分成了两步:询问,然后再锁资源,最

                       后真正提交。说白了这样减少了阻塞。

        (2)三阶段提交协议详细介绍

                ​

               (a)CanCommit阶段

                        3PC的CanCommit阶段其实和2PC的准备阶段很像。协调者向参与者发送

                        commit请求,参与者如果可以提交就返回Yes响应,否则返回No响应。

                      (i)事务询问 协调者向参与者发送CanCommit请求。询问是否可以执行事

                              务提交操作。然后开始等待参与者的响应。

                      (ii)响应反馈 参与者接到CanCommit请求之后,正常情况下,如果其自身

                               认为可以顺利执行事务,则返回Yes响应,并进入预备状态。否则反馈

                               No。

               (b)PreCommit阶段

                        协调者根据参与者的反应情况来决定是否可以记性事务的PreCommit操作。

                        根据响应情况,有以下两种可能。

                               假如协调者从所有的参与者获得的反馈都是Yes响应,那么就会执行事

                        务的预执行。

                      (i)发送预提交请求协调者向参与者发送PreCommit请求,并进入Prepared

                              阶段。

                      (ii)事务预提交参与者接收到PreCommit请求后,会执行事务操作,并将

                               undo和redo信息记录到事务日志中(未提交事务)。

                      (iii)响应反馈 如果参与者成功的执行了事务操作,则返回ACK响应,同时

                               开始等待最终指令。

                               假如有任何一个参与者向协调者发送了No响应,或者等待超时之后,协

                        调者都没有接到参与者的响应,那么就执行事务的中断:

                      (i)发送中断请求 协调者向所有参与者发送abort请求。

                      (ii)中断事务 参与者收到来自协调者的abort请求之后(或超时之后,仍未收

                               到协调者的请求),执行事务的中断。

               (c)doCommit阶段

                        该阶段进行真正的事务提交,也可以分为以下两种情况:

                        执行提交

                      (i)发送提交请求 协调接收到参与者发送的ACK响应,那么他将从预提交状态进

                              入到提交状态。并向所有参与者发送doCommit请求。

                      (ii)事务提交 参与者接收到doCommit请求之后,执行正式的事务提交。并在完

                               成事务提交之后释放所有事务资源。

                      (iii)响应反馈 事务提交完之后,向协调者发送Ack响应。

                        中断事务

                        协调者没有接收到参与者发送的ACK响应(可能是接受者发送的不是ACK响应,

                        也可能响应超时),那么就会执行中断事务。

                      (i)发送中断请求 协调者向所有参与者发送abort请求

                      (ii)事务回滚 参与者接收到abort请求之后,利用其在阶段二记录的undo信息来

                               执行事务的回滚操作,并在完成回滚之后释放所有的事务资源。

                      (iii)反馈结果 参与者完成事务回滚之后,向协调者发送ACK消息

                      (iv)中断事务 协调者接收到参与者反馈的ACK消息之后,执行事务的中断。

                      (v)完成事务 协调者接收到所有参与者的ack响应之后,完成事务。

                        在doCommit阶段,如果参与者无法及时接收到来自协调者的doCommit或者

                        rebort请求时,会在等待超时之后,会继续进行事务的提交。(其实这个应该是

                        基于概率来决定的,当进入第三阶段时,说明参与者在第二阶段已经收到了

                        PreCommit请求,那么协调者产生PreCommit请求的前提条件是他在第二阶段开

                        始之前,收到所有参与者的CanCommit响应都是Yes。(一旦参与者收到了

                        PreCommit,意味他知道大家其实都同意修改了)所以,一句话概括就是,当进

                        入第三阶段时,由于网络超时等原因,虽然参与者没有收到commit或者abort响

                        应,但是他有理由相信:成功提交的几率很大。 )

        (3)三阶段提交协议和两阶段提交协议的区别

                 相对于2PC,3PC主要解决的单点故障问题,并减少阻塞,因为一旦参与者无法及

                 时收到来自协调者的信息之后,他会默认执行commit。而不会一直持有事务资源

                 并处于阻塞状态。但是这种机制也会导致数据一致性问题,因为,由于网络原因,

                 协调者发送的abort响应没有及时被参与者接收到,那么参与者在等待超时之后执行

                 了commit操作。这样就和其他接到abort命令并执行回滚的参与者之间存在数据不一

                 致的情况。

三:如何保证微服务架构的数据一致性?

       现在有三种方案:可靠消息最终一致性、TCC补偿性事务解决方案、最大努力通知。

       1.可靠消息最终一致性

          基于MQ的解决方案,需要业务系统结合MQ消息中间件实现,在实现过程中需要保证

          消息的成功发送及成功消费。即需要通过业务系统控制MQ的消息状态。

          此方案涉及3个模块:

               (1)上游应用:执行业务并发送 MQ 消息。

               (2) 可靠消息服务和MQ消息组件:协调上下游消息的传递,并确保上下游数据

                         的一致性。

               (3)下游应用:监听 MQ 的消息并执行自身业务。 

                     ​

                分两个阶段:

                第一阶段:上游应用执行业务并发送 MQ 消息。

                                  上游应用将本地业务执行和消息发送绑定在同一个本地事务中,保证要

                                  么本地操作成功并发送MQ消息,要么两步操作都失败并回滚。

                                  上游应用和可靠消息之间的业务交互图如下:

​             

 

 

          

                                (1)上游应用发送待确认消息到可靠消息系统。

                                (2)可靠消息系统保存待确认消息并返回。

                                (3)上游应用执行本地业务。

                                (4)上游应用通知可靠消息系统确认业务已执行并发送消息。

                                (5)可靠消息系统修改消息状态为发送状态并将消息投递到 MQ 中间

                                         件。

                                 假设第(4)、(5)步因为网络不好没发出去,或者网络不好没接收

                                 到等情况,会出现业务执行成功,但消息依然未确认的情况。这个时

                                 候我们通过“可靠性消息服务”来解决该问题。

               第二阶段:下游应用监听 MQ 消息并执行业务(第二阶段)

                                 下游应用监听 MQ 消息并执行业务,并且将消息的消费结果通知可靠

                                 消息服务。可靠消息的状态需要和下游应用的业务执行保持一致,可

                                 靠消息状态不是已完成时,确保下游应用未执行,可靠消息状态是已

                                 完成时,确保下游应用已执行。

                          下游应用和可靠消息服务之间的交互图如下:

​                        

                              (1)下游应用监听 MQ 消息组件并获取消息。

                              (2)下游应用根据 MQ 消息体信息处理本地业务。

                              (3)下游应用向 MQ 组件自动发送 ACK 确认消息被消费。

                              (4)下游应用通知可靠消息系统消息被成功消费,可靠消息将该消息

                                       状态更改为已完成。  

                               假设第(4)步因为网络不好,可靠消息没有接到消费成功的消息,导致

                               消息消费成功但是可靠消息系统的状态为“未消费”或是干脆就是MQ把消

                               息丢了,我们同样可以通过消息重发解决。

                通过分析以上两个阶段可能失败的情况,为了确保上下游数据的最终一致性,在可

                靠消息系统中,需要开发“消息状态确认”和“消息重发”两个功能以实现BASE理论的

                Eventually Consistent(最终一致性)特性。

             (1)消息状态确认

                       可靠消息服务定时监听消息的状态,如果存在状态为待确认并且超时的消息,

                       则表示上游应用和可靠消息交互中的步骤(4)或者 (5) 出现异常。可靠消

                       息则携带消息体内的信息向上游应用发起请求查询该业务是否已执行。上游应

                       用提供一个可查询接口供可靠消息追溯业务执行状态,如果业务执行成功则更

                       改消息状态为已发送,否则删除此消息确保数据一致。

              (2)消息重发

                       可靠消息服务发现可靠消息服务中存在消息状态为已发送并且超时的消息,则

                       表示可靠消息服务和下游应用中存在异常的步骤,无论哪个步骤出现异常,可

                       靠消息服务都将此消息重新投递到 MQ 组件中供下游应用监听。下 游应用监

                       听到此消息后,在保证幂等性的情况下重新执行业务并通知可靠消息服务此消

                       息已经成功消费,最终确保上游应用、下游应用的数据最终 一致性。

                              当然在实际接入过程中,需要引入 人工干预 功能。比如引入重发次数限

                       制,超过重发次数限制的将消息修改为死亡消息,等待人工干预。

       2.TCC(Try-Confirm-Cancel)

                  TCC 方案是二阶段提交的另一种实现方式,它涉及3个模块,主业务、从业务和

          活动管理器(协作者)。

         下面这张图是互联网上关于 TCC 比较经典的图示:

​   

        第一阶段:主业务服务分别调用所有从业务服务的try操作,并在活动管理器中记录所

                          有从业务服务。当所有从业务服务try成功或者某个从业务服务try失败时,

                          入第二阶段。

        第二阶段:活动管理器根据第一阶段从业务服务的try结果来执行confirm或cancel操

                          作。如果第一阶段所有从业务服务都try成功,则协作者调用所有从业务

                          服务的confirm操作,否则,调用所有从业务服务的cancel操作。

        在第二阶段中,confirm 和 cancel 同样存在失败情况,所以需要对这两种情况做异

        常处理 以保证数据一致性。

              Confirm 失败:则回滚所有 confirm 操作并执行 cancel 操作。

              Cancel 失败:从业务服务需要提供自动 cancel 机制,以保证 cancel 成功。

       目前有很多基于 RPC 的 TCC 框架,但是不适用于微服务架构下基于 HTTP 协议的

       交互模式。我们这次只讨论基于 HTTP 协议的 TCC 实现。具体的实现流程如下:

​       

           (1)主业务服务调用从业务服务的try操作,并获取 confirm/cancel接口和超时时

                    间。

           (2)如果从业务都try成功,主业务服务执行本地业务,并将获取的confirm/cancel

                    接口发送给活动管理器,活动管理器会顺序调用从业务1和从业务2的confirm

                    接口并记录请求状态,如果请求成功,则通知主业务服务提交本地事务。如果

                    confirm部分失败,则活动管理器会顺序调用从业务1和从业务2的cancel接口

                    来取消try的操作。

           (3)如果从业务部分或全部try失败,则主业务直接回滚并结束,而try成功的从业

                    务服务则通过定时任务来处理处于try完成但超时的数据,将这些数据做回滚

                    处理保证主业务服务和从业务服务的数据一致。

                代入开篇提到的案例,通过 TCC 方案,订单服务在订单状态修改之前执行预增

         积分操作(try),并从积分服务获取confirm/cancel预增积分的请求地址。如果预增

         积分 (try)成功,则订单服务更改订单状态并通知活动管理器,活动管理器请求积

         分模块的 confirm 接口来增加积分。如果预增积分(try)失败,则订单服务业务回

         滚。积分服务通过定时任务删除预增积分(try)超时的数据。另外如果活动管理器

         调用积分服务的confirm接口失败,则活动管理器调用积分服务cancel接口来取消预

         增积分,从而,保证订单和积分数据的最终一致性。

                通过上面的对可靠消息服务和TCC方案的描述,我们 解决了技术栈一致和不一

         致的两种情况下的数据一致性问题。但是,通常在这些核心业务上有 很多附加业务,

         比如当用户支付完成后,需要通过短信通知用户支付成功。这一类业务的成功或者失

         败不会影响核心业务,甚至很多大型互联网平台在并高并发的情况下会主动关闭这一

         类业务以保证核心业务的顺利执行。那么怎么处理这类情况呢,我们来看看最大努力

         通知方案。

       3.最大努力通知

          最大努力通知方案涉及三个模块

        (1)上游应用,发消息到MQ队列。

        (2)下游应用(例如短信服务、邮件服务),接受请求,并返回通知结果。

        (3)最大努力通知服务,监听消息队列,将消息存储到数据库中,并按照通知规则

                 调用下游应用的发送通知接口。

                 具体流程如下:

​                

        (1)上游应用发送MQ消息到MQ组件内,消息内包含通知规则和通知地址。

        (2)最大努力通知服务监听到MQ内的消息,解析通知规则并放入延时队列等待触

                 发通知。

        (3)最大努力通知服务调用下游的通知地址,如果调用成功,则该消息标记为通知

                 成功,如果失败则在满足通知规则(例如5分钟发一次,共发送10次)的情况下

                 重新放入延时队列等待下次触发。

 

                 最大努力通知服务表示在不影响主业务的情况下,尽可能地确保数据的一致性。

         它需要开发人员根据业务来指定通知规则,在满足通知规则的前提下,尽可能的确保

         数据的一致,以尽到最大努力的目的。

               根据不同的业务可以定制不同的通知规则,比如通知支付结果等相对严谨的业务,

        可以将通知频率设置高一些,通知时间长一些,比如隔5分钟通知一次,持续时间1小时。

        如果不重要的业务,比如通知用户积分增加,则可以将通知频率设置低一些,时间短一

        些,比如10分钟通知一次,持续 30 分钟。

               代入上面提到的支付成功短信通知用户的案例,通过最大努力通知方案,当支付成

        功后,将消息发送到MQ中间件,在消息中,定义发送规则为5分钟一次,最大发送数为

        10次。最大努力通知服务监听MQ消息并根据规则调用消息通知服务(短信服务)的消

         息发送接口,并记录每次调用的日志信息。在通知成功或者已通知10次时,停止通知。

       4.总结

         

         

四.分布式锁

     1.Zookeeper实现分布式锁

      (1)原理:使用zookeeper中的临时有序节点,详细见本篇“五 Zookeeper”。

     2.Redis实现分布式锁

        set的多参数命令,指定NX操作和过期时间(高版本);低版本的redis不支持set的多

        参数命令。

五.Zookeeper

    学习zookeeper推荐阅读的一篇博客

     https://www.cnblogs.com/crazylqy/p/7132133.html

    1.Zookeeper介绍

       Zookeeper是Hadoop下的一个子项目,它是一个针对大型分布式系统的可靠的协调系

       统,提供的功能包括配置维护、名字服务、分布式同步、组服务等。Zookeeper是可以

       集群复制的。集群间通过Zab协议来保持数据的一致性。该协议包括两个阶段:leader

       election阶段和Atomic broadcas阶段。leader election阶段,集群中将选举一个出一个

       leader,其它机器称为follower,所有写操作都被传送给leader,并通过broadcas将所

       有更新告诉follower,当leader崩溃或是leader失去大多数follower时,需要重新选取一

       个新的leader,让所有服务器都恢复到一个正确的状态。当leader被选举出来且大多数

       服务器完成了和leader的状态同步(状态同步是指数据同步)后,leader election的过

       程就结束了,将进入Atomic broadcas的过程。

       有空看看不同的介绍,有的地方叫两种服务:

       https://www.jianshu.com/p/5dce97c9ba85

    2.Zookeeper节点类型

       持久节点、持久有序、临时节点、临时有序节点(重点,Zookeeper的分布式锁使用的

       该节点类型)

       持久和临时的概念一目了然,就不介绍了。

       有序节点:假如当前有一个父节点为/lock,我们可以在这个父节点下面创建子节点;

                         zookeeper提供了一个可选的有序特性,例如我们可以创建子节点

                         “/lock/node-”并且指明有序,那么zookeeper在生成子节点时会根据当前的子

                         节点数量自动添加整数序号,也就是说如果是第一个创建的子节点,那么生

                         成的子节点为/lock/node-0000000000,下一个节点则为

                         /lock/node-0000000001,依次类推。

       临时节点:客户端可以建立一个临时节点,在会话结束或者会话超时后,zookeeper会

                         自动删除该节点。

    3.Zookeeper的事件通知机制

       在读取数据时,我们可以同时对节点设置事件监听(也叫订阅),当节点数据或结构变

       化时,zookeeper会通知订阅它的客户端。当前zookeeper有如下四种事件:

       * 节点创建;

      * 节点删除;

      * 节点数据修改;

      * 子节点变更。

    4.Zookeeper实现分布式锁

       简单的说是(心跳+通知)

     (1)公平方式

              下面描述使用zookeeper实现分布式锁的算法流程,假设锁空间的根节点为/lock:

             (a)客户端连接zookeeper,并在/lock下创建临时的且有序的子节点,第一个客户端对

                      应的子节点为/lock/lock-0000000000,第二个为/lock/lock-0000000001,以此类推。

             (b)客户端获取/lock下的子节点列表,判断自己创建的子节点是否为当前子节点列表中

                      序号最小的子节点,如果是则认为获得锁,否则监听/lock的子节点变更消息,获得

                      子节点变更通知后重复此步骤直至获得锁。

             (c)执行业务代码;

             (d)完成业务流程后,删除对应的子节点释放锁

     (2)非公平方式

              每个进程p尝试创建临时节点znode,名为/lock。如果进程p成功创建了znode,就表示它

              获得了锁并可以继续执行其临界区域的代码。他进程因znode存在而创建/lock失败。因

              此,进程监听/lock的变化,并在检测到/lock删除时再次尝试创建节点来获得锁。当收到

              /lock删除的通知时,如果进程p还需要继续获取锁,它就继续尝试创建/lock的步骤,如果

              其他进程已经创建了,就继续监听节点。

       问题:为什么要使用临时节点而不是持久节点

                  步骤1中创建的临时节点能够保证在故障的情况下锁也能被释放,考虑这么个场

                  景:假如客户端a当前创建的子节点为序号最小的节点,获得锁之后客户端所在

                  机器宕机了,客户端没有主动删除子节点;如果创建的是永久的节点,那么这个

                  锁永远不会释放,导致死锁;由于创建的是临时节点,客户端宕机后,过了一定

                  时间zookeeper没有收到客户端的心跳包判断会话失效,将临时节点删除从而释

                  放锁;

       问题:关于 API中设置监听器的操作与读操作是原子性。

                  考虑这么个场景:客户端a对应子节点为/lock/lock-0000000000,客户端b对应子

                  节点为/lock/lock-0000000001,客户端b获取子节点列表时发现自己不是序号最

                  小的,但是在设置监听器前客户端a完成业务流程删除了子节点

                  /lock/lock-0000000000,客户端b设置的监听器岂不是丢失了这个事件从而导致

                  永远等待了?这个问题不存在的。因为zookeeper提供的API中设置监听器的操

                  作与读操作是原子执行的,也就是说在读子节点列表时同时设置监听器,保证不

                  会丢失事件。

    5. Zookeeper是如何实现数据一致性的?

        原文:https://blog.csdn.net/qqqq0199181/article/details/80865828

        答:是通过ZAB协议

               ZAB协议介绍:

             (1)学习协议前需要了解的几个概念

                      --  事务

                          zookeeper作为一个分布式协调服务,需要leader节点去接受外部请求,转化为内部操作(比如创

                          建,修改,删除节点),需要原子性执行的几个操作就组成了事务,这里用T代表。zookeeper要

                          求有序的处理事务,因此给每个事务一个编号,每进来一个事务编号加1,假设leader节点中进来

                          n个事务,可以表示为T1,T2,T3…Tn。为了防止单点问题导致事务数据丢失,leader节点会把事务

                          数据同步到follower节点中。

                      --  事务队列

                          leader和follower都会保存一个事务队列,用L表示,L=T1,T2,T3…Tn,leader的事务队列是接受

                          请求保存下来的,follower的事务队列是leader同步过来的,因此leader的事务队列是最新,最全

                          的。

                     --  任期

                         在zookeeper的工作过程中,leader节点奔溃,重新选举出新的leader是很正常的事情,所以

                         zookeeper的运行历史中会产生多个leader,就好比一个国家的历史中会相继出现多为领导人。为

                         了区分各个leader,ZAB协议用一个整数来表示任期,我们假设用E表示任务。zookeeper刚运行时

                         选举出的第一个leader的任期为E=1;第一个leader奔溃后,下面选举出来的leader,任期会加1,

                         E=2;一次类推。加入任期概念的事务应该表示为T(E,n)

             (2)协议过程:

                    (a)选举leader

                             --  每个follower广播自己事务队列中最大事务编号maxId

                             --  获取集群中其他follower发出来的maxId,选取出最大的maxId所属的follower,

                                 投票给改follower,选它为leader。

                             --  统计所有投票,获取投票数超过一半的follower被推选为leader

                    (b)同步数据

                             --  各个follower向leader发送自己保存的任期E

                             --  leader,比较所有的任期,选取最大的E,加1后作为当前的任期E=E+1

                             --  将任期E广播给所有follower

                             --  follower将任期改为leader发过来的值,并且返回给leader事务队列L

                             --  leader从队列集合中选取任期最大的队列,如果有多个队列任期都是最大,则选

                                 取事务编号n最大的队列Lmax。将Lmax最为leader队列,并且广播给各个

                                 follower。

                            --  follower接收队列替换自己的事务队列,并且执行提交队列中的事务。

                                至此各个节点的数据达成一致,zookeeper恢复正常服务。

                     (c)广播

                              --  leader节点接收到请求,将事务加入事务队列,并且将事务广播给各个follower。

                              --  follower接收事务并加入都事务队列,然后给leader发送准备提交请求。

                              --  leader 接收到半数以上的准备提交请求后,提交事务同时向follower 发送提交事

                                  务请求follower提交事务。

                    (2)这里有一个我不了解的知识:Zookeeper中的角色

                             zookeeper 存在leader,follower,observer三种角色,这三种角色在实际服务集群中都是

                             服务节点。

                             leader:处理所有请求,为客户的提供读和写服务

                             follower:只提供读服务,有机会通过选举成为leader

                             observer:只提供读服务

                             由以上三种角色的介绍可知,zookeeper中所有请求都是交给leader处理的,因此,

                             如果leader挂了,zookeeper就无法再提供服务,这就是单点问题。所幸在leader挂了

                             之后,follower能够通过选举成为新的leader。

                

                

    5.Zookeeper作为dubbo注册中心的原理

       简单的说是(心跳+临时节点+通知)

              在zookeeper中,进行服务注册,实际上就是在zookeeper中创建了一个

       znode节点,该节点存储了该服务的IP、端口、调用方式(协议、序列化方式)

       等。该节点承担着最重要的职责,它由服务提供者(发布服务时)创建,以供服

       务消费者获取节点中的信息,从而定位到服务提供者真正网络拓扑位置以及

       得知如何调用。RPC服务注册、发现过程简述

       如下:

       * 服务提供者启动时,会将其服务名称,ip地址注册到配置中心。

      *  服务消费者在第一次调用服务时,会通过注册中心找到相应的服务的IP地

         址列表,并缓存到本地,以供后续使用。当消费者调用服务时,不会再去

         请求注册中心,而是直接通过负载均衡算法从IP列表中取一个服务提供者

         的服务器调用服务。

         注意:这里的客户端缓存根据Zookeeper的节点的更新而自动更新是

                   应该是dubbo实现的。

      * 当服务提供者的某台服务器宕机或下线时,相应的ip会从服务提供者IP列

        表中移除。同时,注册中心会将新的服务IP地址列表发送给服务消费者机

        器,缓存在消费者本机。

      * 当某个服务的所有服务器都下线了,那么这个服务也就下线了。同样,当

        服务提供者的某台服务器上线时,注册中心会将新的服务IP地址列表发送

        给服务消费者机器,缓存在消费者本机。

      * 服务提供方可以根据服务消费者的数量来作为服务下线的依据感知服务的

        下线&上线

                zookeeper提供了“心跳检测”功能,它会定时向各个服务提供者发送

        一个请求(实际上建立的是一个 socket 长连接),如果长期没有响应,服

        务中心就认为该服务提供者已经“挂了”,并将其剔除,比如100.19.20.02这

        台机器如果宕机了,那么zookeeper上的路径就会只剩

        /HelloWorldService/1.0.0/100.19.20.01:16888。服务消费者会去监听相应

        路径(/HelloWorldService/1.0.0),一旦路径上的数据有任何变化(增加

        或减少),zookeeper都会通知服务消费方服务提供者地址列表已经发生改

        变,从而进行更新。更为重要的是zookeeper 与生俱来的容错容灾能力(比

        如leader选举),可以确保服务注册表的高可用性。使用 zookeeper 作为注

        册中心时,客户端订阅服务时会向 zookeeper 注册

        自身;主要是方便对调用方进行统计、管理。但订阅时是否注册 client 不是

        必要行为,和不同的注册中心实现有关,例如使用 consul 时便没有注册。

    6.Zookeeper和Eureka比较

     (1)Zookeeper实现的是CP,Eureka实现的是AP

                     当向注册中心查询服务列表时,我们可以容忍注册中心返回的是几分

              钟以前的注册信息,但不能接受服务直接down掉不可用。也就是说,服

              务注册功能对可用性的要求要高于一致性。但是zk会出现这样一种情况,

              当master节点因为网络故障与其他节点失去联系时,剩余节点会重新进行

              leader选举。问题在于,选举leader的时间太长,30 ~ 120s, 且选举期间

              整个zk集群都是不可用的,这就导致在选举期间注册服务瘫痪。在云部署

              的环境下,因网络问题使得zk集群失去master节点是较大概率会发生的事,

              虽然服务能够最终恢复,但是漫长的选举时间导致的注册长期不可用是不

              能容忍的由于它不能很好的处理网络分区的问题,所以会导致一些客户

              端状态会丢失(如果在同一个网络分区(partition)的节点数(nodes)

              数达不到ZooKeeper选取Leader节点的“法定人数”时,它们就会从

              ZooKeeper中断开,当然同时也就不能提供Service发现服务了,导致连

              接到这些节点的客户端状态丢失)。

                     Eureka看明白了这一点,因此在设计时就优先保证可用性。Eureka

              各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节

              点依然可以提供注册和查询服务。而Eureka的客户端在向某个Eureka注

              册或如果发现连接失败,则会自动切换至其它节点,只要有一台Eureka还

              在,就能保证注册服务可用(保证可用性),只不过查到的信息可能不是最

              新的(不保证强一致性)。除此之外,Eureka还有一种自我保护机制,如果

              在15分钟内超过85%的节点都没有正常的心跳,那么Eureka就认为客户

              端与注册中心出现了网络故障,此时会出现以下几种情况:

              * Eureka不再从注册列表中移除因为长时间没收到心跳而应该过期的服务 

              * Eureka仍然能够接受新服务的注册和查询请求,但是不会被同步到其它

                节点上(即保证当前节点依然可用) 

              * 当网络稳定时,当前实例新的注册信息会被同步到其它节点中

              扩展1:Zookeeper做服务发现就是个错误

                           * 因为相对于CP,更能接受的是AP

                           * ZooKeeper本身并没有正确的处理网络分割的问题 

                             在ZooKeeper中,如果在同一个网络分区(partition)的节点数

                           (nodes)数达不到ZooKeeper选取Leader节点的“法定人数”时,

                             它们就会从ZooKeeper中断开,当然同时也就不能提供Service

                             发现服务了,导致连接到这些节点的客户端状态丢失。Pinterest

                             与Airbnb公司通过增加客户端缓存的方案来解决该问题,但是这

                             种方式只是从表面上解决这个问题,因为当部分或者所有

                             节点跟ZooKeeper断开的情况下,每个节点还可以从本地缓存中

                              获取到数据并不一定是所有的服务注册信息,所引这导致我们并

                              不能得到一个CP的系统也不能得到一个AP的系统。

            扩展2:Zookeeper为什么实现CP

                         ZooKeeper是Hadoop的一个子项目,旨在解决大规模分布式应用

                         场景下,服务协调同步(Coordinate Service)的问题。ZooKeeper

                         是分布式协调服务,它的职责是保证数据(注:配置数据,状态数

                         据)在其管辖下的所有服务之间保持同步、一致,所以就不难理解

                         为什么ZooKeeper被设计成CP而不是AP特性的了,如果是AP的,

                         那么将会带来恐怖的后果。

       https://my.oschina.net/thinwonton/blog/1622905 

       zookeeper使用场景:https://blog.csdn.net/he90227/article/details/70157046

     (2)Zookeeper是服务端的负载均衡而Eureka是服务端的负载均衡

              客户端模式:

                         调用端调用微服务时,首先到服务注册中心获取服务列表,然后在根据调用

                         端本地的负载均衡策略进行服务调用。(服务列表保存在客户端)

              服务端模式:

                         调用端直接像服务注册中心发起请求,服务注册中心在通过自身负载均衡策

                         略,对微服务进行调用。调用端不需要维护服务的发现逻辑。(服务列表保存

                         在服务端)     

    7.ACL

       Zkeeper本身提供了ACL机制,表示为scheme:id:permissions,第一个字段表示采用哪

       一种身份认证机制,第二个id表示用户,permissions表示相关权限(如只读,读写,

       管理等)。

       scheme有如下几种:

       world:默认方式,相当于全世界都能访问。

       auth:代表已经认证通过的用户(cli中可以通过addauth digest user:pwd 来添加当前上

                  下文中的授权用户)。

       digest:即用户名:密码这种方式认证,这也是业务系统中最常用的。

       ip:使用Ip地址认证

       permissions:

       代表ZK中的操作权限,有5种:

       CREATE、READ、WRITE、DELETE、ADMIN 也就是 增、删、改、查、管理权限,

       这5种权限简写为crwda(即:每个单词的首字符缩写)

       注:这5种权限中,delete是指对子节点的删除权限,其它4种权限指对自身节点的操作

              权限。

       设置访问控制:

       方式一:(推荐)

       * 增加一个认证用户

         addauth digest 用户名:密码明文

         eg. addauth digest user1:password1

       * 设置权限

          setAcl /path auth:用户名:密码明文:权限

          eg. setAcl /test auth:user1:password1:cdrwa

       * 查看Acl设置

          getAcl /path

       方式二:

       * setAcl /path digest:用户名:密码密文:权限

         注:这里的加密规则是SHA1加密,然后base64编码。

五.dubbo(相关文档已上传到博客的附件中)

     1.Zookeeper

     2.Rpc

        下面我只记录了一些重要内容,关于Rpc比较详细的内容在:

        https://blog.csdn.net/mindfloating/article/details/39474123/

        https://blog.csdn.net/kingcat666/article/details/78577079

        用着的时候在看吧。

      (1)概念

               远程过程调用协议,通过网络从远程计算机上请求调用某种服务。RPC 的主要功

               能目标是让构建分布式计算(应用)更容易,在提供强大的远程调用能力时不损

               失本地调用的语义简洁性。

               Rpc和Rmi的区别

               * 语言范围不同

                 Rpc不区分语言和操作系统而Rmi(全称是Java Rmi)只属于Java的。

               * 方法调用方式不同(略,闲的蛋疼在看)

                 RMI中是通过在客户端的Stub对象作为远程接口进行远程方法的调用。每个远程

                 方法都具有方法签名。如果一个方法在服务器上执行,但是没有相匹配的签名被

                 添加到这个远程接口(stub)上,那么这个新方法就不能被RMI客户方所调用。

                 RPC中是通过网络服务协议向远程主机发送请求,请求包含了一个参数集和一个

                 文本值,通常形成“classname.methodname(参数集)”的形式。RPC远程主机就去

                 搜索与之相匹配的类和方法,找到后就执行方法并把结果编码,通过网络协议发

                 回。

               * 调用结果的返回形式不同(略,闲的蛋疼在看)

                 Java是面向对象的,所以RMI的调用结果可以是对象类型或者基本数据类型;

                 RMI的结果统一由外部数据表示 (External Data Representation, XDR) 语言表

                 示,这种语言抽象了字节序类和数据类型结构之间的差异。

               Rpc和Http

               * RPC主要是基于TCP/IP协议的,而HTTP服务主要是基于HTTP协议的,我们都

                 知道HTTP协议是在传输层协议TCP之上的,所以效率来看的话,RPC要好一些。

               * RPC服务主要是针对大型企业的,而HTTP服务主要是针对小企业的,因为

                 RPC效率更高,而HTTP服务开发迭代会更快。

               * 其实在很久以前,我对于企业开发的模式一直定性为HTTP接口开发,也就是我

                 们常说的RESTful风格的服务接口。的确,对于在接口不多、系统与系统交互较

                 少的情况下,解决信息孤岛初期常使用的一种通信手段;优点就是简单、直接、

                 开发方便。利用现成的http协议进行传输。我们记得之前本科实习在公司做后台

                 开发的时候,主要就是进行接口的开发,还要写一大份接口文档,严格地标明输

                 入输出是什么?说清楚每一个接口的请求方法,以及请求参数需要注意的事项等。

                 比如下面这个例子:

                 POST http://www.httpexample.com/restful/buyer/info/share

                 接口可能返回一个JSON字符串或者是XML文档。然后客户端再去处理这个返回

                 的信息,从而可以比较快速地进行开发。但是对于大型企业来说,内部子系统较

                 多、接口非常多的情况下,RPC框架的好处就显示出来了,首先就是长链接,不

                 必每次通信都要像http一样去3次握手什么的,减少了网络开销;其次就是RPC框

                 架一般都有注册中心,有丰富的监控管理;发布、下线接口、动态扩展等,对调

                 用方来说是无感知、统一化的操作。

      (2)Rpc介绍

               一个完整的RPC架构里面包含了四个核心的组件,分别是Client、Server、

               Client Stub以及Server Stub。

               客户端(Client):服务的调用方。

               服务端(Server):真正的服务提供者。

               客户端存根(Client Stub):存放服务端的地址消息,再将客户端的请求参数打包

                                                              成网络消息,然后通过网络远程发送给服务方。

               服务端存根(Server Stub):接收客户端发送过来的消息,将消息解包,并调用

                                                               本地的方法。

               ​

               ​

​        

               一次Rpc调用大约分10次

               * 服务消费方(client)调用以本地调用方式调用服务;

               * client stub接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体;

               * client stub找到服务地址,并将消息发送到服务端;

               * server stub收到消息后进行解码;

               * server stub根据解码结果调用本地的服务;

               * 本地服务执行并将结果返回给server stub;

               * server stub将返回结果打包成消息并发送至消费方;

               * client stub接收到消息,并进行解码;

               * 服务消费方得到最终结果。

               扩展知识

               Rmi调用过程(平时不用看,用得着时候或是闲的蛋疼在看)

           ​

               * 客户调用客户端辅助对象stub上的方法

               * 客户端辅助对象stub打包调用信息(变量,方法名),通过网络发送给服务端辅

                 助对象skeleton

               * 服务端辅助对象skeleton将客户端辅助对象发送来的信息解包,找出真正被调用

                 的方法以及该方法所在对象

               * 调用真正服务对象上的真正方法,并将结果返回给服务端辅助对象skeleton

               * 服务端辅助对象将结果打包,发送给客户端辅助对象stub

               * 客户端辅助对象将返回值解包,返回给调用者

               * 客户获得返回值

     3.socket

               注册中心与生产者、消费者之间的心跳检测是基于socket长连接的,所以有必要了

               解socket。了解socket查看文章:有空在把这的知识补全

               https://www.cnblogs.com/embedded-linux/p/6261894.html

               http://m.elecfans.com/article/630956.html

               Socket的特点:

               Socket 传输方式适合于对传输速度,安全性,实时交互,费用等要求高的应用中,

               如网络游戏,手机应用,银行内部交互等。

               基于http协议传输的特点:基于http协议传输方式适合于对传输速度,安全性要求

               不是很高,且需要快速开发的应用。如公司OA系统,互联网服务等。

               创建Socket连接时,可以指定使用的传输层协议,Socket可以支持不同的传输层协

               议(TCP或UDP),当使用TCP协议进行连接时,该Socket连接就是一个TCP连

               接。

               Socket 传输的特点:
               优点

               1) 传输数据为字节级,传输数据可自定义,数据量小(对于手机应用讲:费用低)

               2) 传输数据时间短,性能高

               3) 适合于客户端和服务器端之间信息实时交互

               4) 可以加密,数据安全性强

              缺点:

              1) 需对传输的数据进行解析,转化成应用级的数据

              2) 对开发人员的开发水平要求高

              3) 相对于Http协议传输,增加了开发量

              基于Http协议传输的定义和其特点

              目前基于http协议传输的主要有http协议 和基于http协议的Soap协议(web service),

              常见的方式是 http 的post 和get 请求,web 服务。

              优点:

              1) 基于应用级的接口使用方便

              2) 程序员开发水平要求不高,容错性强

                    缺点:

                    1) 传输速度慢,数据包大(Http协议中包含辅助应用信息)

                    2) 如实时交互,服务器性能压力大。

                    3) 数据传输安全性差

               建立socket连接: 

               建立Socket连接至少需要一对套接字,其中一个运行于客户端,称为ClientSocket,

               另一个运行于服务器端,称为ServerSocket 。 套接字之间的连接过程分为三个步

               骤:服务器监听,客户端请求,连接确认。 

               服务器监听:服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的

                                     状态,实时监控网络状态,等待客户端的连接请求。 

               客户端请求:指客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。

                                    为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指

                                    出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连

                                    接请求。 

               连接确认:当服务器端套接字监听到或者说接收到客户端套接字的连接请求时,就

                                 响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描

                                 述发给客户 端,一旦客户端确认了此描述,双方就正式建立连接。而服

                                 务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请

                                 求。 

     4.  dubbo图

       

       

 

 

     4.dubbo原理

      (1)注册中心(本节介绍了分布式系统中的注册中心是个什么鬼,但是放在后面了)

               注册中心和每个Server/Client之间会作一个实时的心跳检测(因为它们都是建立

               的Socket长连接,该功能由Zookeeper实现),比如几秒钟检测一次。收集每个

               Server提供的服务的信息,每个Client的信息,整理出一个服务列表,如:  

serviceName serverAddressList clientAddressList
 UserService 192.168.0.1,192.168.0.2,192.168.0.3,192.168.0.4  172.16.0.1,172.16.0.2
 ProductService 192.168.0.3,192.168.0.4,192.168.0.5,192.168.0.6 172.16.0.2,172.16.0.3
 OrderService 192.168.0.10,192.168.0.12,192.168.0.5,192.168.0.6  172.16.0.3,172.16.0.4

                当某个Server不可用,那么就更新受影响的服务对应的serverAddressList,即把这

                个Server从serverAddressList中剔除出去(从地址列表中删除),同时将推送

                serverAddressList给这些受影响的服务的clientAddressList里面的所有Client。如:

                192.168.0.3挂了,那么UserService和ProductService的serverAddressList都要把

                192.168.0.3删除掉,同时把新的列表告诉对应的Client 172.16.0.1,172.16.0.2,

                172.16.0.3;当某个Client挂了,那么更新受影响的服务对应的clientAddressList

                ConfigServer根据服务列表,就能提供一个web管理界面,来查看管理服务的提供

                者和使用者。新加一个Server时,由于它会主动与ConfigServer取得联系,而

                ConfigServer又会将这个信息主动发送给Client,所以新加一个Server时,只需要

                启动Server,然后几秒钟内,Client就会使用上它提供的服务。

                dubbo与zookeeper的交互过程:https://blog.csdn.net/qq_27529917/article/details/80632078

      (2)client

               调用服务的机器,每个Client启动时,主动与ConfigServer建立Socket长连接,并

               将自己的IP等相应信息发送给ConfigServer。Client在使用服务的时候根据服务名称

               去ConfigServer中获取服务提供者信息(这样ConfigServer就知道某个服务是当前哪

               几个Client在使用),Client拿到这些服务提供者信息后,与它们都建立连接,后面

               就可以直接调用服务了,当有多个服务提供者的时候,Client根据一定的规则来进行

               负载均衡,如轮询,随机,按权重等。一旦Client使用的服务它对应的服务提供者有

               变化(服务提供者有新增,删除的情况),ConfigServer就会把最新的服务提供者列

               表推送给Client,Client就会依据最新的服务提供者列表重新建立连接,新增的提供者

               建立连接,删除的提供者丢弃连接。

      (3)Server

               真正提供服务的机器,每个Server启动时,主动与ConfigServer建立Scoket长连接,

               并将自己的IP,提供的服务名称,端口等信息直接发送给ConfigServer,

               ConfigServer就会收集到每个Server提供的服务的信息。

      (4)这样做的好处

             (i)只要在Client和Server启动的时候,ConfigServer是好的,服务就可调用了,如果后

                     面ConfigServer挂了,那只影响ConfigServer挂了以后服务提供者有变化,而Client

                     还无法感知这一变化。

             (ii)Client每次调用服务是不经过ConfigServer的,Client只是与它建立联系,从它那里

                      获取提供服务者列表而已。

             (iii)调用服务-负载均衡:Client调用服务时,可以根据规则在多个服务提供者之间轮流

                       调用服务。

             (iv)服务提供者-容灾:某一个Server挂了,Client依然是可以正确的调用服务的,当前

                       提是这个服务有至少2个服务提供者,Client能很快的感知到服务提供者的变化,

                       并作出相应反应。

             (v)服务提供者-扩展:添加一个服务提供者很容易,而且Client会很快的感知到它的存

                      在并使用它。

      (5)现在开始介绍一下分布式系统的注册中心

                这里我摘录了一些

              (1)为什么要使用服务的注册中心

                       分布式服务框架部署在多台不同的机器上,例如服务提供者在集群A,服务调用者

                       在集群B,那么B在调用A的服务的过程中,集群A的机器需要和集群B的机器进行

                       通信。那么会出现如下问题: 

                     (i)集群A的服务调用者如何发现集群B的服务提供者

                     (ii)集群A的服务调用者如何选择集群B中的某一台服务提供者机器发起调用

                     (iii)集群B的服务提供者机器上/下线之后,集群A中调用者如何感知到这台

                               机器的上/下线,不在对已下线的机器发起调用。

                     (iv)集群B提供的某个服务如何获知集群A中那些机器正在消费该服务。

                       以上问题将通过服务注册中心来解决,采用服务注册中心来实时存储更新服务提供

                       者信息及该服务的实时调用者信息。

                       其工作过程如下:

                     (i)在服务启动的时候,将服务提供者信息主动上报到服务注册中心进行服务注册

                     (ii)服务调用者启动的时候,将服务提供者信息从注册中心下拉到服务调用者机器

                             本地缓存,服务调用者从本地缓存的服务提供者列表中,基于某种负载均衡策

                             略选择一台服务提供者发起远程调用。

                    (iii)服务注册中心能够感知服务提供者集群中某一台机器下线,将该机器服务提供

                             者信息从注册中心删除,并且通知服务调用者集群中的每一台机器,是的服务

                             调用者不在调用该机器。

                      服务注册中心有下面几个优点:

                    (i)软负载及透明化路由:服务提供者和服务调用者之间相互解耦,服务调用者不

                                                                需要硬编码服务提供者地址。

                    (ii)服务动态发现及可伸缩能力:服务提供者机器增减能被服务调用者通过注册中

                                                                           心动态感知,而且通过增减机器可以实现服务的

                                                                           弹性伸缩

                    (iii)通过注册中心可以动态的监控服务运行质量及服务依赖,为服务提供服务治理

                              能力。

                      注册中心其实充当是服务的协调者,一般包含如下几个功能:

                    (i)服务发现:

                            服务注册/反注册:保存服务提供者和服务调用者的信息

                            服务订阅/取消订阅:服务调用者订阅服务提供者的信息,最好有实时推送的功

                                                             能

                            服务路由(可选):具有筛选整合服务提供者的能力。
                    (ii)服务配置(不包括其它无关配置):

                             配置订阅:服务提供者和服务调用者订阅微服务相关的配置

                             配置下发(可选):主动将配置推送给服务提供者和服务调用者

                    (iii)服务健康检测

                             检测服务提供者的健康情况

     5.dubbo和webservice

      (1)WebService

             (a)效率不高基于soap协议,其主要的特点是跨语言、跨平台的。项目中不推荐使用,

                      可用于不同公司间接口的调用。

             (b)若使用restful形式的服务:http+json。很多项目中应用。如果服务太多,服务之间

                      调用关系混乱,需要治疗服务。

      (2)使用dubbo。使用rpc协议进行远程调用,直接使用socket通信。传输效率高,并且可以

               统计出系统之间的调用关系、调用次数。使用Java语言开发,只能用于Java语言开发的

               项目间的通信,不具备跨语言,跨平台的特点。

      (3)WebService是什么鬼

               https://blog.csdn.net/u013168253/article/details/79695492

    5.dubbo功能详细

      (1)常规用法,Hello World例子

               生产者

                      IDemoService.java

package com.baizhi.service;

public interface IDemoService {
    /**
     * 求和
     * @param x
     * @param y
     * @return
     */
    public int sum(Integer x ,Integer y);
    /**
     * 计算连个数的乘积
     * @param x
     * @param y
     * @return
     */
    public int multi(Integer x ,Integer y);
    
}

                      DemoService_v1.java        

 
package com.baizhi.service.impl;

import com.baizhi.service.IDemoService;

public class DemoService_v1 implements IDemoService {

    public int sum(Integer x, Integer y) {
        // TODO Auto-generated method stub
        return x+y;
    }

    public int multi(Integer x, Integer y) {
        // TODO Auto-generated method stub
        return x*y;
    }

}

               applicationContext.xml

 
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
    xsi:schemaLocation="http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans.xsd        http://code.alibabatech.com/schema/dubbo        http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
 
    <!-- 配置Bean -->
    <bean id="demoService" class="com.baizhi.service.impl.DemoService_v1"/>
    <!-- 引入配置文件 -->
    <import resource="classpath:dubbo.xml"/>
 </beans>

               dubbo.xml

 
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
    xsi:schemaLocation="http://www.springframework.org/schema/beans        
    http://www.springframework.org/schema/beans/spring-beans.xsd        
    http://code.alibabatech.com/schema/dubbo        
    http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
 
     <!-- 指定web服务名字。建议该名称和你的应用名保持一致,该名称可以用来搞路由-->
    <dubbo:application name="Dubbo_HelloWorld"/>
    <!-- 声明服务注册中心 -->
    <dubbo:registry protocol="zookeeper" address="192.168.1.122:2181"/>
    <!-- 指定传输层通信协议 -->
    <dubbo:protocol name="dubbo" port="20880"/>
    <!-- 暴露服务。把自己的接口注册到zookeeper; 通过ref自定服务的实现-->
    <dubbo:service 
        interface="com.baizhi.service.IDemoService" 
        protocol="dubbo"
        ref="demoService"
     />
 </beans>

               消费者

                      applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
    xsi:schemaLocation="http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans.xsd        http://code.alibabatech.com/schema/dubbo        http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
 
    <!-- 引入配置文件 -->
    <import resource="classpath:dubbo.xml"/>
 </beans>

                      dubbo.xml

 
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
    xsi:schemaLocation="http://www.springframework.org/schema/beans        
    http://www.springframework.org/schema/beans/spring-beans.xsd        
    http://code.alibabatech.com/schema/dubbo        
    http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
 
     <!-- 指定web服务名字 -->
    <dubbo:application name="Dubbo_HelloWorld_ref"/>
    <!-- 声明服务注册中心 -->
    <dubbo:registry protocol="zookeeper" address="127.0.0.1:2181"/>
    <!-- 指定传输层通信协议  因为在同一台机器测试,所以端口不一样-->
    <dubbo:protocol name="dubbo" port="20881"/>
    <!-- 引用服务,interface指定具体引用的服务,因为具体在zookeeper里暴露的服务就是com.baizhi.service.IDemoService-->
    <dubbo:reference
        id="demoService" 
        interface="com.baizhi.service.IDemoService"
        protocol="dubbo"
    />
 </beans>

                      DubboUseage.java

 
import java.io.IOException;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.expression.spel.ast.Identifier;

import com.baizhi.service.IDemoService;


public class DubboUseage {
    public static void main(String[] args) throws IOException {
        ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
        
        IDemoService demoService=(IDemoService) ctx.getBean("demoService");
        //此处可以发现打印的是代理类型 底层dubbo使用NIO的RCP做远程调度
        System.out.println("打印引用类型:"+demoService.getClass());
        
        //测试远程方法调度
        
        int sum=demoService.sum(1, 3);
        System.out.println("和:"+sum);
    }
}

              到此为止,老师说dubbo已经讲完了。

      (2)启动时检查

               check="true/false"

               check=“true”:启动时检查它所依赖的服务是否可用,如果不可用会报错。

                                       报的是个spring的错,具体是啥没看到。

               check="false":启动时不检查它所依赖的服务是否可用,只管自己启动

      (3)集群容错

             (a)集群容错概念介绍

                      集群容错是指我们在一个服务下暴露多个provider,任何一个provider宕机式,

                      服务。依然正常工作。不用配,默认就支持,dubbo天然就是一个集群模式。

                      需要我们配置的是集群容错模式,集群容错模式可以配在客户端也可以配在

                      服务端(个人感觉配置在哪端都没有区别)。

                      注:服务可以自动注册和自动发现(该功能依赖zookeeper节点的watch功能)。

                             例如,A、B两个节点在提供服务,但是访问量太大,A、B支撑不过来,于

                             是又加入C节点,这个时候C节点可以自动加入服务集群。

             (b)集群容错模式

                    (i)Failover Cluster

                            失败自动切换,当出现失败,重试其它服务器(默认)。

                            * 通常用来读操作,但重试会带来更长延迟。

                            * 用于写操作的时候应该注意因为网络延时造成重复写的问题。

                              如下图,当消费端访问服务A后,服务A开始执行,但是因为网络故障消费

                              端无法接到A的响应,消费端就认为是失败,所以消费端又尝试请求服务

                              B,这就造成重复写的问题。

​                       

                              设置服务端的幂等性并设置reetries="0"。

                              reetries参数用来设置重试次数(不含第一次)。

                              示例:

<dubbo:service retries="2" /><dubbo:referenceretries="2" /><dubbo:reference><dubbo:methodname="findFoo" retries="2" /></dubbo:reference>

                    (ii)Failfast Cluster

                             * 快速失败(只发起一次调用),抛出异常 

                             * 通常用于非幂等性的写操作,比如新增记录。

                             示例:

<dubbo:service cluster="failfast" /><dubbo:referencecluster="failfast" />

                    (iii)Failsafe Cluster

                             * 快速失败(只发起一次调用),不抛出异常 。

                             * 通常用于写入审计日志等操作

<dubbo:service cluster="failsafe" /><dubbo:referencecluster="failsafe" />

                    (iv)Failback Cluster

                              * 失败后定时重试 

                              * 通常用于新消息通知操作。

<dubbo:service cluster="failback" /><dubbo:referencecluster="failback" />

                    (v)Forking Cluster

                             并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的

                             读操作,但需要浪费更多服务器资源,可通过forks="2"来设置最大并行数。

<dubbo:service cluster=“forking" /><dubbo:reference cluster=“forking" />

                    (vi)Broadcast Cluster (2.10开始支持)

                              广播调用所有提供者,逐个调用,任意一台报错则报错。通常用于通知所

                              有提供者更新缓存或日志等本地资源信息。    

      (4)负载均衡

             (a)负载均衡简介

                      dubbo的负载均衡机制可以写在代码里(服务端和客户端)也可以通过管控台

                      配置,但是推荐通过管控台配置,这样更改里规则不需要重启服务。

             (b)负载均衡机制

                    (i)Random LoadBalance:随机,按权重设置随机概率,在一个截面上碰撞

                            概率高,但调用量越大分布越均匀,二期按概率使用,权重也比较均匀,

                            有利于动态调整提供者权重。

                    (ii)RandomRobin LoadBalance:轮询,按公约后的权重设置轮询比率,存

                             在小量提供者累计请求的问题,比如第二台集器很慢但没挂,当请求调到

                             第二台机器就卡在那,久而久之所有请求都卡在调用第二台机器上。

                    (iii)LeastActive LoadBalance:最少活跃调用数,相同活跃随机,活跃数指

                             调用前后计数差,使慢的提供者收到更少的请求,因为越慢的提供者前后

                             计数差越大。

                    (iv)ConsistentHash LoadBalancee:一致性Hash,相同参数的请求总是发

                              到同一提供者。当某一台机器挂时,原本发往该提供者的请求,基于下

                              虚拟节点,平摊到其它提供者,不会引起剧烈变动。剩下自己百度吧,

                              很少用,因为管控台里没有。

             (c)配置示例,只写一个其它的差不多照抄

                      服务端服务级别

   <dubbo:service interface="..." loadbalance="roundrobin" />

                      客户端服务级别

 
   <dubbo:reference interface="..." loadbalance="roundrobin" />

                      服务端方法级别

 
  <dubbo:service interface="...">
      <dubbo:method name="..." loadbalance="roundrobin"/>
  </dubbo:service>

                      客户端方法级别

 
  <dubbo:reference interface="...">
      <dubbo:method name="..." loadbalance="roundrobin"/>
  </dubbo:reference>

      (4)服务降级

               在<dubbo:reference/>里使用Mock(及上游服务中配置),该参数有四个值,

               false,default,true,或者Mock类的类名。分别代表如下含义:

               false:不调用mock服务。

               true:当服务调用失败时,使用mock服务。

               default:当服务调用失败时,使用mock服务。

               force:强制使用Mock服务(不管服务能否调用成功)。(使用xml配置不生效,

                           使用ReferenceConfigAPI可以生效)

               https://www.jianshu.com/p/ce8de35986cf

               上面网址中的例子mock的值非Mock类名,下面的例子是mock的值为Mock类名。

               服务端:

                      IDemoService.java

package com.baizhi.service;

import com.baizhi.entity.Computer;
import com.baizhi.entity.User;

public interface IDemoService {

    public User methodInvoke(Integer id,String name);
}

                      DemoService_v1.java

 
package com.baizhi.service.impl;

import java.util.Date;
import java.util.Map;

import javax.management.RuntimeErrorException;

import com.alibaba.dubbo.rpc.RpcContext;
import com.alibaba.dubbo.rpc.RpcException;
import com.baizhi.entity.Computer;
import com.baizhi.entity.User;
import com.baizhi.service.IDemoService;

public class DemoService_v1 implements IDemoService {

    public User methodInvoke(Integer id, String name) {
        // TODO Auto-generated method stub
        System.out.println("id:"+id+","+name);
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
        
    }
   
    

}

                      DemoServicemock.java(该方法只是一个空实现,最终调用的是客户端的

                                                                methodInvoke方法)

package com.baizhi.service;

import java.util.Date;
import java.util.Map;

import com.alibaba.dubbo.rpc.RpcContext;
import com.baizhi.entity.Computer;
import com.baizhi.entity.User;

public class DemoServicemock implements IDemoService {
    
    public User methodInvoke(Integer id, String name) {
        // TODO Auto-generated method stub
        return null;
    }
   

}

                      dubbo.xml

 
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
    xsi:schemaLocation="http://www.springframework.org/schema/beans        
    http://www.springframework.org/schema/beans/spring-beans.xsd        
    http://code.alibabatech.com/schema/dubbo        
    http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
 
     <!-- 指定web服务名字 -->
    <dubbo:application name="Dubbo_HelloWorld"/>
    <!-- 声明服务注册中心 -->
    <dubbo:registry protocol="zookeeper" address="192.168.1.122:2181"/>
    <!-- 指定传输层通信协议 -->
    <dubbo:protocol name="dubbo" port="20880"/>
    <!-- 暴露你的服务地址 -->
    <dubbo:service 
        interface="com.baizhi.service.IDemoService" 
        protocol="dubbo"
        ref="demoService"
        mock="com.baizhi.service.DemoServicemock"
     />
 </beans>

               客户端:

                      dubbo.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
    xsi:schemaLocation="http://www.springframework.org/schema/beans        
    http://www.springframework.org/schema/beans/spring-beans.xsd        
    http://code.alibabatech.com/schema/dubbo        
    http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
 
     <!-- 指定web服务名字 -->
    <dubbo:application name="Dubbo_HelloWorld"/>
    <!-- 声明服务注册中心 -->
    <dubbo:registry protocol="zookeeper" address="192.168.1.122:2181"/>
    <!-- 指定传输层通信协议 -->
    <dubbo:protocol name="dubbo" port="20880"/>
    <!-- 暴露你的服务地址 -->
    <dubbo:reference 
        id="demoService" 
        interface="com.baizhi.service.IDemoService" 
        protocol="dubbo" timeout="3000" />
     
 </beans>

                      IDemoService.java

 
package com.baizhi.service;

import com.baizhi.entity.Computer;
import com.baizhi.entity.User;

public interface IDemoService {

    public User methodInvoke(Integer id,String name);
}

                      DemoServicemock.java(真正执行的服务降级方法)

 
package com.baizhi.service;

import java.util.Date;
import java.util.Map;

import com.alibaba.dubbo.rpc.RpcContext;
import com.baizhi.entity.Computer;
import com.baizhi.entity.User;

public class DemoServicemock implements IDemoService {
    
    public User methodInvoke(Integer id, String name) {
        // TODO Auto-generated method stub
         User user=new User();
         user.setName("不可用");
         user.setId(-1);
        return user;
    }
   

}

                      DubboUseage.java

 
import java.io.IOException;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.baizhi.entity.User;
import com.baizhi.service.IDemoService;


public class DubboUseage {
    public static void main(String[] args) throws IOException {
        ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
        
        IDemoService bean = (IDemoService) ctx.getBean("demoService");
        
        
        User user = bean.methodInvoke(1, "jiangzz");
        
        System.out.println(user.getName()+"  "+user.getId());
    }
}

      (5)路由规则

               讲课时的版本老师说路由规则有bug,目前只能支持简单路由,后续版本什么样

               不知道。(比如读写分离不能用)

               路由规则是干什么的:

                      Dubbo的路由机制主要解决的目的就是服务调用时,从已知的所有服务提供

                      者中根据路由规则刷选服务提供者。

              路由规则详细介绍:

​                    

​                     

 

​                

​                 

      (5)直接连提供者

               绕过注册中心直接连提供者,没啥用。

      (5)多协议

              (a)dubbo协议简介

                       dubbo支持多种协议,dubbo协议为其默认协议,我们可以通过配置来指定服

                       务间通信的协议。

                       dubbo支持的协议如下:

                       dubbo协议、hessian 协议、rmi、http 协议、webservice 协议、thrift 协议、

                       memcached协议、redis 协议(不知道还有没了,知道这些就行了)。

                     (i)dubbo 协议 (默认)

                            NIO通信及BIO的缺陷查看:

                            https://www.jianshu.com/p/13bef2795c44

                             协议简介:

                                    连接个数:单连接

                                    连接方式:长连接

                                    传输协议:TCP

                                    传输方式:NIO异步传输

                                    序列化:Hessian 二进制序列化

                                    适用范围:传入传出参数数据包较小(建议小于100K),消费者比

                                                      提供者个数多,单一消费者无法压满提供者,尽量不要

                                                      用dubbo协议传输大文件或超大字符串。

                                    适用场景:常规远程服务方法调用

                             使用注意:

                                    * dubbo默认采用dubbo协议,dubbo协议采用单一长连接和NIO异

                                      步通讯,适合于小数据量大并发的服务调用,以及服务消费者机

                                      器数远大于服务提供者机器数的情况。

                                    * 他不适合传送大数据量的服务,比如传文件,传视频等,除非请

                                      求量很低。

                                    * Dubbo协议缺省每服务每提供者每消费者使用单一长连接,如果

                                      数据量较大,可以使用多个连接。

                                      <dubbo:protocol name="dubbo" connections="2" />

                                      <dubbo:service connections=”0”>或

                                      <dubbo:reference connections=”0”>表示该服务使用JVM共享长连

                                      接。(缺省) 

                                      <dubbo:service connections=”1”>或

                                      <dubbo:reference connections=”1”>表示该服务使用独立长连接。

                                      <dubbo:service connections=”2”>或

                                      <dubbo:reference connections=”2”>表示该服务使用独立两条长连

                                      接。

                                      * 为防止被大量连接撑挂,可在服务提供方限制大接收连接数,以

                                        实现服务提供方自我保护。

                                       <dubbo:protocol name="dubbo" accepts="1000" />
                             一些问题的解释:

                                    为什么不能传大包? 

                                           因dubbo协议采用单一长连接,如果每次请求的数据包大小为

                                           500KByte,假设网络为千兆网卡(1024Mbit=128MByte),每条

                                           连接最大7MByte(不同的环境可能不一样,供参考),单个服务

                                           提供者的TPS(每秒处理事务数)最大为:

                                           128MByte / 500KByte = 262。单个消费者调用单个服务提供者

                                           的TPS(每秒处理事务数)最大为:7MByte / 500KByte = 14。如

                                           果能接受,可以考虑使用,否则网络将成为瓶颈。

                                    什么采用异步单一长连接?

                                           因为服务的现状大都是服务提供者少,通常只有几台机器,而

                                           服务的消费者多,可能整个网站都在访问该服务,比如Morgan

                                           的提供者只有6台提供者,却有上百台消费者,每天有1.5亿次

                                           调用,如果采用常规的hessian服务,服务提供者很容易就被压

                                           跨,通过单一连接,保证单一消费者不会压死提供者,长连接,

                                           减少连接握手验证等,并使用异步IO,复用线程池,防止C10K

                                           问题。                                                     

                     (ii)rmi 协议

                              Java标准的远程调用协议。 

                                  连接个数:多连接

                                  连接方式:短连接

                                  传输协议:TCP

                                  传输方式:同步传输 

                                  序列化:Java标准二进制序列化 

                                  适用范围:传入传出参数数据包大小混合,消费者与提供者个数差不

                                                    多,可传文件。 

                                  适用场景:常规远程服务方法调用,与原生RMI服务互操作

                       剩下的协议请看:

                       https://blog.csdn.net/fuyuwei2015/article/details/72848310/           

              (b)dubbo多协议例子

                       服务端,dubbo.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
    xsi:schemaLocation="http://www.springframework.org/schema/beans        
    http://www.springframework.org/schema/beans/spring-beans.xsd        
    http://code.alibabatech.com/schema/dubbo        
    http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
 
     <!-- 指定web服务名字 -->
    <dubbo:application name="Dubbo_HelloWorld"/>
    <!-- 声明服务注册中心 -->
    <dubbo:registry protocol="zookeeper" address="192.168.1.122:2181"/>
    <!-- 指定传输层通信协议 -->
    <dubbo:protocol name="dubbo" port="20880"/>
    <dubbo:protocol name="rmi" port="1099"/>
    
    <!-- 暴露你的服务地址 -->
    <dubbo:service 
        ref="demoService" 
        interface="com.baizhi.service.IDemoService"
        protocol="dubbo,rmi"
    />
 </beans>

              客户端,dubbo.xml

 
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
    xsi:schemaLocation="http://www.springframework.org/schema/beans        
    http://www.springframework.org/schema/beans/spring-beans.xsd        
    http://code.alibabatech.com/schema/dubbo        
    http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
 
     <!-- 指定web服务名字 -->
    <dubbo:application name="Dubbo_HelloWorld"/>
    <!-- 声明服务注册中心 -->
    <dubbo:registry protocol="zookeeper" address="192.168.1.122:2181"/>
    <!-- 指定传输层通信协议 -->
    <dubbo:protocol name="dubbo" port="20881"/>
    <dubbo:protocol name="rmi" port="1010"/>
    
    <!-- 暴露你的服务地址 -->
    <dubbo:reference 
        id="demoService" 
        interface="com.baizhi.service.IDemoService"
        protocol="dubbo"
    />
 </beans>

      (6)多注册中心

               比如:中文站有些服务来不及在中国,只在美国部署,而美国的其他应用需

               要引用此服务,就可以将服务同时注册到两个注册中心。

               服务端,dubbo.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
    xsi:schemaLocation="http://www.springframework.org/schema/beans        
    http://www.springframework.org/schema/beans/spring-beans.xsd        
    http://code.alibabatech.com/schema/dubbo        
    http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
 
     <!-- 指定web服务名字 -->
    <dubbo:application name="Dubbo_HelloWorld"/>
    <!-- 声明服务注册中心 -->
    <dubbo:registry id="china" protocol="zookeeper" address="192.168.1.122:2181"/>
    <dubbo:registry id="us" protocol="zookeeper" address="192.168.1.122:2182"/>
    <!-- 指定传输层通信协议 -->
    <dubbo:protocol name="dubbo" port="20880"/>
    <dubbo:protocol name="rmi" port="1099"/>
    
    <!-- 暴露你的服务地址 -->
    <dubbo:service 
        ref="demoService1" 
        interface="com.baizhi.service.IDemoService"
        protocol="dubbo,rmi"
        registry="china"
    />
     <dubbo:service 
        ref="demoService2" 
        interface="com.baizhi.service.IDemoService"
        protocol="dubbo,rmi"
        registry="us"
    />
 </beans>

               客户端,dubbo.xml

 
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
    xsi:schemaLocation="http://www.springframework.org/schema/beans        
    http://www.springframework.org/schema/beans/spring-beans.xsd        
    http://code.alibabatech.com/schema/dubbo        
    http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
 
     <!-- 指定web服务名字 -->
    <dubbo:application name="Dubbo_HelloWorld"/>
    <!-- 声明服务注册中心 -->
    <dubbo:registry id="china" protocol="zookeeper" address="192.168.1.122:2181"/>
    <dubbo:registry id="us" protocol="zookeeper" address="192.168.1.122:2182"  />
    
    <!-- 指定传输层通信协议 -->
    <dubbo:protocol name="dubbo" port="20881"/>
    <dubbo:protocol name="rmi" port="1010"/>
    
    <!-- 暴露你的服务地址 -->
    <dubbo:reference 
        id="demoService1" 
        interface="com.baizhi.service.IDemoService"
        protocol="dubbo"
        registry="china"
    />
     <dubbo:reference 
        id="demoService2" 
        interface="com.baizhi.service.IDemoService"
        protocol="dubbo"
        registry="us"
    />
 </beans>

      (7)多版本

               服务端

                      applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
    xsi:schemaLocation="http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans.xsd        http://code.alibabatech.com/schema/dubbo        http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
 
    <!-- 配置Bean -->
    <bean id="demoService1" class="com.baizhi.service.impl.DemoService_v1"/>
    <bean id="demoService2" class="com.baizhi.service.impl.DemoService_v2"/>
    <!-- 引入配置文件 -->
    <import resource="classpath:dubbo.xml"/>
 </beans>

                      dubbo.xml

 
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
    xsi:schemaLocation="http://www.springframework.org/schema/beans        
    http://www.springframework.org/schema/beans/spring-beans.xsd        
    http://code.alibabatech.com/schema/dubbo        
    http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
 
     <!-- 指定web服务名字 -->
    <dubbo:application name="Dubbo_HelloWorld"/>
    <!-- 声明服务注册中心 -->
    <dubbo:registry  protocol="zookeeper" address="192.168.1.122:2181"/>
    <!-- 指定传输层通信协议 -->
    <dubbo:protocol name="dubbo" port="20880"/>
    <dubbo:protocol name="rmi" port="1099"/>
    
    <!-- 暴露你的服务地址 -->
    <dubbo:service 
        ref="demoService1" 
        interface="com.baizhi.service.IDemoService"
        protocol="dubbo,rmi"
        version="1.1.0"
    />
     <dubbo:service 
        ref="demoService2" 
        interface="com.baizhi.service.IDemoService"
        protocol="dubbo,rmi"
        version="1.1.1"
    />
 </beans>

               客户端

                      dubbo.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
    xsi:schemaLocation="http://www.springframework.org/schema/beans        
    http://www.springframework.org/schema/beans/spring-beans.xsd        
    http://code.alibabatech.com/schema/dubbo        
    http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
 
     <!-- 指定web服务名字 -->
    <dubbo:application name="Dubbo_HelloWorld"/>
    <!-- 声明服务注册中心 -->
    <dubbo:registry protocol="zookeeper" address="192.168.1.122:2181"/>
    
    <!-- 指定传输层通信协议 -->
    <dubbo:protocol name="dubbo" port="20881"/>
    <dubbo:protocol name="rmi" port="1010"/>
    
    <!-- 暴露你的服务地址 -->
    <dubbo:reference 
        id="demoService1" 
        interface="com.baizhi.service.IDemoService"
        protocol="dubbo"
        version="1.1.0"
    />
     <dubbo:reference 
        id="demoService2" 
        interface="com.baizhi.service.IDemoService"
        protocol="dubbo"
        version="1.1.1"
    />
    <dubbo:reference 
        id="demoService3" 
        interface="com.baizhi.service.IDemoService"
        protocol="dubbo"
        version="*"
    />
 </beans>

      (8)服务分组

               服务端

                      applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
    xsi:schemaLocation="http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans.xsd        http://code.alibabatech.com/schema/dubbo        http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
 
    <!-- 配置Bean -->
    <bean id="demoService1" class="com.baizhi.service.impl.DemoService_v1"/>
    <bean id="demoService2" class="com.baizhi.service.impl.DemoService_v2"/>
    <!-- 引入配置文件 -->
    <import resource="classpath:dubbo.xml"/>
 </beans>

                      dubbo.xml

 
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
    xsi:schemaLocation="http://www.springframework.org/schema/beans        
    http://www.springframework.org/schema/beans/spring-beans.xsd        
    http://code.alibabatech.com/schema/dubbo        
    http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
 
     <!-- 指定web服务名字 -->
    <dubbo:application name="Dubbo_HelloWorld"/>
    <!-- 声明服务注册中心 -->
    <dubbo:registry  protocol="zookeeper" address="192.168.1.122:2181"/>
    <!-- 指定传输层通信协议 -->
    <dubbo:protocol name="dubbo" port="20880"/>
    <dubbo:protocol name="rmi" port="1099"/>
    
    <!-- 暴露你的服务地址 -->
    <dubbo:service 
        ref="demoService1" 
        interface="com.baizhi.service.IDemoService"
        protocol="dubbo,rmi"
        group="new"
    />
     <dubbo:service 
        ref="demoService2" 
        interface="com.baizhi.service.IDemoService"
        protocol="dubbo,rmi"
        group="old"
    />
 </beans>

               客户端

 
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
    xsi:schemaLocation="http://www.springframework.org/schema/beans        
    http://www.springframework.org/schema/beans/spring-beans.xsd        
    http://code.alibabatech.com/schema/dubbo        
    http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
 
     <!-- 指定web服务名字 -->
    <dubbo:application name="Dubbo_HelloWorld"/>
    <!-- 声明服务注册中心 -->
    <dubbo:registry protocol="zookeeper" address="192.168.1.122:2181"/>
    
    <!-- 指定传输层通信协议 -->
    <dubbo:protocol name="dubbo" port="20881"/>
    <dubbo:protocol name="rmi" port="1010"/>
    
    <!-- 暴露你的服务地址 -->
    <dubbo:reference 
        id="demoService1" 
        interface="com.baizhi.service.IDemoService"
        protocol="dubbo"
        group="new"
    />
     <dubbo:reference 
        id="demoService2" 
        interface="com.baizhi.service.IDemoService"
        protocol="dubbo"
        group="old"
    />
    <!--为*的时候会随机选取一个-->
    <dubbo:reference 
        id="demoService3" 
        interface="com.baizhi.service.IDemoService"
        protocol="dubbo"
        group="*"
    />
 </beans>

      (9)分组聚合

               常见的集合都可以做分组。下面的例子是把old组的和new组的进行聚合。

               服务端

                      IDemoService.java

package com.baizhi.service;

import java.util.List;
import java.util.Map;

public interface IDemoService {
    /**
     * 求和
     * @param x
     * @param y
     * @return
     */
    public int sum(Integer x ,Integer y);
    /**
     * 计算连个数的乘积
     * @param x
     * @param y
     * @return
     */
    public int multi(Integer x ,Integer y);
    
    /**
     * 普通的方法遍历
     * @return
     */
    public String methodInvoke();
    /**
     * 测试分组聚合
     * @return
     */
    public String[] groupArray();
    
    /**
     * 测试分组聚合
     * @return
     */
    public List<String> groupList();
    
    /**
     * 测试分组聚合
     * @return
     */
    public Map<String,Object> groupMap();
}

                      DemoService_v1.java

 
package com.baizhi.service.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.baizhi.service.IDemoService;

public class DemoService_v1 implements IDemoService {

    public int sum(Integer x, Integer y) {
        // TODO Auto-generated method stub
        return x+y;
    }

    public int multi(Integer x, Integer y) {
        // TODO Auto-generated method stub
        return x*y;
    }

    public String methodInvoke() {
        // TODO Auto-generated method stub
        return "new";
    }

    public String[] groupArray() {
        // TODO Auto-generated method stub
        return new String[]{"A","B"};
    }

    public List<String> groupList() {
        // TODO Auto-generated method stub
        ArrayList<String> arrayList = new ArrayList<String>();
        arrayList.add("张三");
        arrayList.add("李四");
        return arrayList;
    }

    public Map<String, Object> groupMap() {
        // TODO Auto-generated method stub
        Map<String,Object> map=new HashMap<String, Object>();
        map.put("user01", "张三");
        map.put("user02", "李四");
        return map;
    }
 
}

                      applicationContext.xml

 
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
    xsi:schemaLocation="http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans.xsd        http://code.alibabatech.com/schema/dubbo        http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
 
    <!-- 配置Bean -->
    <bean id="demoService1" class="com.baizhi.service.impl.DemoService_v1"/>
    <bean id="demoService2" class="com.baizhi.service.impl.DemoService_v2"/>
    <!-- 引入配置文件 -->
    <import resource="classpath:dubbo.xml"/>
 </beans>

                      dubbo.xml

 
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
    xsi:schemaLocation="http://www.springframework.org/schema/beans        
    http://www.springframework.org/schema/beans/spring-beans.xsd        
    http://code.alibabatech.com/schema/dubbo        
    http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
 
     <!-- 指定web服务名字 -->
    <dubbo:application name="Dubbo_HelloWorld"/>
    <!-- 声明服务注册中心 -->
    <dubbo:registry  protocol="zookeeper" address="192.168.1.122:2181"/>
    <!-- 指定传输层通信协议 -->
    <dubbo:protocol name="dubbo" port="20880"/>
    <dubbo:protocol name="rmi" port="1099"/>
    
    <!-- 暴露你的服务地址 -->
    <dubbo:service 
        ref="demoService1" 
        interface="com.baizhi.service.IDemoService"
        protocol="dubbo,rmi"
        group="new"
    />
     <dubbo:service 
        ref="demoService2" 
        interface="com.baizhi.service.IDemoService"
        protocol="dubbo,rmi"
        group="old"
    />
 </beans>

               客户端

                      dubbo.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
    xsi:schemaLocation="http://www.springframework.org/schema/beans        
    http://www.springframework.org/schema/beans/spring-beans.xsd        
    http://code.alibabatech.com/schema/dubbo        
    http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
 
     <!-- 指定web服务名字 -->
    <dubbo:application name="Dubbo_HelloWorld_ref"/>
    <!-- 声明服务注册中心 -->
    <dubbo:registry protocol="zookeeper" address="192.168.1.122:2181"/>
    <!-- 指定传输层通信协议  因为在同一台机器测试,所以端口不一样-->
    <dubbo:protocol name="dubbo" port="20881"/>
    <!-- 暴露你的服务地址 -->
    <dubbo:reference
        id="demoService" 
        interface="com.baizhi.service.IDemoService"
        protocol="dubbo"
        cache="lru"
        generic="true"
        />
 </beans>

                      DubboUseage.java(打印聚合后的集合)

 
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.expression.spel.ast.Identifier;

import com.alibaba.dubbo.rpc.cluster.support.FailoverCluster;
import com.alibaba.dubbo.rpc.service.GenericService;


public class DubboUseage {
    public static void main(String[] args) throws IOException {
        ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
        
        GenericService genericService=(GenericService) ctx.getBean("demoService");
        
        Object result = genericService.$invoke("sum", 
                    new String[] { "java.lang.Integer","java.lang.Integer" }, 
                    new Object[] { 1,5 });
        
        System.out.println(result);

        Object resultUser = genericService.$invoke("queryUser", 
                new String[] { "java.lang.Integer"}, 
                new Object[] { 1 });
        HashMap<String, Object> userMap=(HashMap<String, Object>) resultUser;
        for(Map.Entry<String, Object> entry1: userMap.entrySet()){
            if(entry1.getValue() instanceof HashMap){
                HashMap<String, Object> computerMap=(HashMap<String, Object>) entry1.getValue();
                System.out.println(entry1.getKey());
                for(Map.Entry<String, Object> entry: computerMap.entrySet()){
                    System.out.println("\t"+entry.getValue());
                }
            }else{
                System.out.println(entry1.getKey()+" "+entry1.getValue());
            }
            
        }
        
        //演示用户数据的封装
        Map<String, Object> user = new HashMap<String, Object>(); 
        user.put("name", "张三"); 
        HashMap<String, String> value = new HashMap<String, String>();
        value.put("name", "张三的电脑");
        user.put("computer", value);
        
        genericService.$invoke("saveUser", new String[]{"com.baizhi.entity.User"}, 
                new Object[]{user});
    }
}

      (10)结果缓存

                 略

      (11)隐式传参

                 客户端在调用服务器端的时候可以传参数给服务器端(java代码中写并不是在配置

                 文件中),服务器端通过特定的api获取到客户端的该参数

                 客户端

                        DubboUseage.java 

import java.io.IOException;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.expression.spel.ast.Identifier;

import com.alibaba.dubbo.rpc.RpcContext;
import com.alibaba.dubbo.rpc.cluster.support.FailoverCluster;
import com.baizhi.service.IDemoService;


public class DubboUseage {
    public static void main(String[] args) throws IOException {
        ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
        
        
        RpcContext.getContext().setAttachment("attch", "逗你玩");
        IDemoService demoService=(IDemoService) ctx.getBean("demoService");
        // setAttachment设置的KV,在完成下面一次远程调用会被清空。即多次远程调用要多次设置。
        
        for(int i=0;i<5;i++){
            //RpcContext.getContext().setAttachment("attch", "逗你玩"+i);
            demoService.methodInvoke();
        }
        
    }
}

                 服务器端

                        DemoService_v1.java

package com.baizhi.service.impl;

import java.util.Date;
import java.util.Map;

import com.alibaba.dubbo.rpc.RpcContext;
import com.baizhi.service.IDemoService;

public class DemoService_v1 implements IDemoService {

    public String methodInvoke() {
        // TODO Auto-generated method stub
        Map<String, String> attachments = RpcContext.getContext().getAttachments();
        String attch= attachments.get("attch");
        System.out.println("附件信息:"+attch);
        return "node-A";
    }

}

      (12)异步调用

                 客户端调用服务器端的方法A和方法B,其中方法A消耗8秒,方法B消耗3秒,

                 非异步调用要消耗11秒(8秒+3秒),异步调用只需要消耗8秒。通过配置

                 async参数来实现。demo中配置在客户端,是否能配置在服务器端不知道,

                 用到的时候在去确定吧。

                        dubbo.xml

                              接口的中的所有方法都是异步的示例:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
    xsi:schemaLocation="http://www.springframework.org/schema/beans        
    http://www.springframework.org/schema/beans/spring-beans.xsd        
    http://code.alibabatech.com/schema/dubbo        
    http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
 
     <!-- 指定web服务名字 -->
    <dubbo:application name="Dubbo_HelloWorld_ref"/>
    <!-- 声明服务注册中心 -->
    <dubbo:registry protocol="zookeeper" address="192.168.1.122:2181"/>
    <!-- 指定传输层通信协议  因为在同一台机器测试,所以端口不一样-->
    <dubbo:protocol name="dubbo" port="20881"/>
    <!-- 这种配置方式,会让该接口的中的所有方法都是异步的 -->
    <dubbo:reference
        id="demoService" 
        interface="com.baizhi.service.IDemoService"
        protocol="dubbo"
        timeout="10000"
        async="true"
        />
 </beans>

                              指定方法为异步的示例:

 
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
    xsi:schemaLocation="http://www.springframework.org/schema/beans        
    http://www.springframework.org/schema/beans/spring-beans.xsd        
    http://code.alibabatech.com/schema/dubbo        
    http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
 
     <!-- 指定web服务名字 -->
    <dubbo:application name="Dubbo_HelloWorld_ref"/>
    <!-- 声明服务注册中心 -->
    <dubbo:registry protocol="zookeeper" address="192.168.1.122:2181"/>
    <!-- 指定传输层通信协议  因为在同一台机器测试,所以端口不一样-->
    <dubbo:protocol name="dubbo" port="20881"/>
    <!-- 暴露你的服务地址 -->
    <dubbo:reference
        id="demoService" 
        interface="com.baizhi.service.IDemoService"
        protocol="dubbo">
        <dubbo:method name="method1" async="true" timeout="10000"/>
    </dubbo:reference>
 </beans>

    (13)参数回调

                 就是客户端在请求服务端方法后,服务端在执行被调用方法时,可以回调客户

                 端的方法。

                 这个例子看得我有点困惑,有时间在去把它扣明白吧,困惑在于服务端

                 com.baizhi.service.CallbackListener这个类是需要手动写一个还是导一个jar包

                 服务端

                        dubbo.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
    xsi:schemaLocation="http://www.springframework.org/schema/beans        
    http://www.springframework.org/schema/beans/spring-beans.xsd        
    http://code.alibabatech.com/schema/dubbo        
    http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
 
     <!-- 指定web服务名字 -->
    <dubbo:application name="Dubbo_HelloWorld"/>
    <!-- 声明服务注册中心 -->
    <dubbo:registry protocol="zookeeper" address="192.168.1.122:2181"/>
    <!-- 指定传输层通信协议 -->
    <dubbo:protocol name="dubbo" port="20880"/>
    <!-- 暴露你的服务地址 -->
    <dubbo:service 
        ref="demoService" 
        interface="com.baizhi.service.IDemoService"
        protocol="dubbo"
    >
     <dubbo:method name="methodInvoke" >
               <dubbo:argument index="1" callback="true" type="com.baizhi.service.CallbackListener"/>
       </dubbo:method>       
    </dubbo:service>
 </beans>

                        IDemoService.java

 
package com.baizhi.service;

import com.baizhi.entity.Computer;
import com.baizhi.entity.User;

public interface IDemoService {

    public Computer methodInvoke(String value,CallbackListener callbackListener);
}

                        DemoService_v1.java(该方法中回到客户端的方法)

 
package com.baizhi.service.impl;

import java.util.Date;
import java.util.Map;

import com.alibaba.dubbo.rpc.RpcContext;
import com.baizhi.entity.Computer;
import com.baizhi.entity.User;
import com.baizhi.service.CallbackListener;
import com.baizhi.service.IDemoService;

public class DemoService_v1 implements IDemoService {

    public Computer methodInvoke(String value, CallbackListener callbackListener) {
        // TODO Auto-generated method stub
        User user=new User();
        user.setName(value);
        System.out.println("-----------");
        //这一步回调客户端的方法
        User callUser = callbackListener.callBack(user);
        callUser.getComputer().setId(1);
        System.out.println(callUser.getComputer().getName());
        
        System.out.println(callUser.getComputer());
        
        return callUser.getComputer();
    }

}

                        CallbackListener.java

 
package com.baizhi.service;

import java.io.Serializable;

import com.baizhi.entity.User;

public interface CallbackListener extends Serializable{
    public User callBack(User v);
}

                 客户端

                        dubbo.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
    xsi:schemaLocation="http://www.springframework.org/schema/beans        
    http://www.springframework.org/schema/beans/spring-beans.xsd        
    http://code.alibabatech.com/schema/dubbo        
    http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
 
     <!-- 指定web服务名字 -->
    <dubbo:application name="Dubbo_HelloWorld_ref"/>
    <!-- 声明服务注册中心 -->
    <dubbo:registry protocol="zookeeper" address="192.168.1.122:2181"/>
    <!-- 指定传输层通信协议  因为在同一台机器测试,所以端口不一样-->
    <dubbo:protocol name="dubbo" port="20881"/>
    <!-- 暴露你的服务地址 -->
    <dubbo:reference
        id="demoService" 
        interface="com.baizhi.service.IDemoService"
        protocol="dubbo"
        />
      
 </beans>

                        CallbackListener.java

 
package com.baizhi.service;

import java.io.Serializable;

import com.baizhi.entity.User;

public interface CallbackListener extends Serializable{
    public User callBack(User v);
}

                        IDemoService.java

 
package com.baizhi.service;

import com.baizhi.entity.Computer;
import com.baizhi.entity.User;

public interface IDemoService {
    /**
     * 
     * @param value
     * @param callbackListener
     * @return
     */
    public Computer methodInvoke(String value,
                  CallbackListener callbackListener);
}

                        DubboUseage.java

 
import java.io.IOException;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.expression.spel.ast.Identifier;

import com.alibaba.dubbo.rpc.RpcContext;
import com.alibaba.dubbo.rpc.cluster.support.FailoverCluster;
import com.baizhi.entity.Computer;
import com.baizhi.entity.User;
import com.baizhi.service.CallbackListener;
import com.baizhi.service.IDemoService;


public class DubboUseage {
    public static void main(String[] args) throws IOException {
        ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
        IDemoService demoService=(IDemoService) ctx.getBean("demoService");
        //new CallbackListener()这种方式实现或是写一个类实现CallbackListener接口也可以
        Computer res=demoService.methodInvoke("zhangsan", new CallbackListener() {
                public User callBack(User v) {
                    // TODO Auto-generated method stub
                    Computer computer=new Computer();
                    computer.setName("客户端设置的Computer");
                    v.setComputer(computer);
                    return v;
                }
            
        });
        System.out.println(res.getName()+" "+res.getId());
        
    }
}

    (14)事件通知

                 略

      (15)本地存根

                 比较复杂(略)

     4.Spring Cloud

     5.Dubbo和Spring Cloud对比

        除了我下面记录的,还有一版详细的在这里https://www.jianshu.com/p/02f9854a1717

      (1)背景

               dubbo是阿里的,它的主要业务并不在开源框架上;Spring Cloud是Spring家

               族的,它的主要业务是开源框架,所以背景上spring cloud更胜一筹。

      (2)Dubbo的服务调用是通过RPC实现的,SpringCloud是基于HTTP协议的

               REST API。

               RPC vs REST

                  先不得不觉得可恶的说明一下什么是REST

                  所谓REST就是用URL定位资源,用HTTP描述操作。

                  就是一傻逼觉得HTTP使用得无法忍受弄了一个这鸡巴玩意,祝他全家死光

                  光

                  有空去看:

                  https://blog.csdn.net/dxcn2015/article/details/65443676

                  https://blog.csdn.net/qq_21383435/article/details/80032375

               * 服务提供方与调用方接口依赖方式太强(下面这段话简单的讲就是dubbo的方式

                 接口的提供方和调用放依赖方式太强,而springcloud的的REST方式使得提供方

                 接口更轻量级化)

                 我们为每个微服务定义了各自的service抽象接口,并通过持续集成发布到私

                 有仓库中,调用方应用对微服务提供的抽象接口存在强依赖关系,因此不论

                 开发、测试、集成环境都需要严格的管理版本依赖,才不会出现服务方与调

                 用方的不一致导致应用无法编译成功等一系列问题,以及这也会直接影响本

                 地开发的环境要求,往往一个依赖很多服务的上层应用,每天都要更新很多

                 代码并install之后才能进行后续的开发。若没有严格的版本管理制度或开发

                 一些自动化工具,这样的依赖关系会成为开发团队的一大噩梦。而REST接

                 口相比RPC更为轻量化,服务提供方和调用方的依赖只是依靠一纸契约,

                 不存在代码级别的强依赖,当然REST接口也有痛点,因为接口定义过轻,

                 很容易导致定义文档与实际实现不一致导致服务集成时的问题,但是该问题

                 很好解决,只需要通过每个服务整合swagger,让每个服务的代码与文档一

                 体化,就能解决。所以在分布式环境下,REST方式的服务依赖要比RPC方

                 式的依赖更为灵活。

               * 服务对平台敏感,难以简单复用

                 通常我们在提供对外服务时,都会以REST的方式提供出去,这样可以实现

                 跨平台的特点,任何一个语言的调用方都可以根据接口定义来实现。那么在

                 Dubbo中我们要提供REST接口时,不得不实现一层代理,用来将RPC接口

                 转换成REST接口进行对外发布。若我们每个服务本身就以REST接口方式

                 存在,当要对外提供服务时,主要在API网关中配置映射关系和权限控制就

                 可实现服务的复用了。

      (3)社区活跃度上Spring Cloud优于Dubbo

               社区越活跃,解决问题的速度越快,框架也会越来越完善,不然当我们碰到问

               题,就不得不自己解决。而对于团队来说,也就意味着我们不得不自己去维护

               框架的源码,这对于团队来说也将会是一个很大的负担。

      (4)框架完整度上Spring Cloud优于Dubbo

               或许很多人会说Spring Cloud和Dubbo的对比有点不公平,Dubbo只是实现了

               服务治理,而Spring Cloud下面有17个子项目(可能还会新增)分别覆盖了微

               服务架构下的方方面面,服务治理只是其中的一个方面,一定程度来说,

               Dubbo只是Spring Cloud Netflix中的一个子集。但是在选择框架上,方案完整

               恰恰是一个需要重点关注的内容。

               

      (5)速度上Dubbo优于Spring Cloud

​          

 

posted @ 2019-05-12 17:49  jialanshun  阅读(409)  评论(0编辑  收藏  举报