Ripple - 代码分析

一、RPC远程调用

1、介绍

RPC程序作为JobQueue上不间断的运行任务,对于快速执行的命令而言这是很好的,但对于数据量很大的任务请求如全总账可能运行的性能不是很好。为此,Ripple对RPC程序的处理通过挂起暂停RPC响应并在稍后再继续执行(非阻塞模式)。

 

2、代码组织结构

在impl目录下的Handler.cpp文件中入口handlerArray根据请求命令调起相对应的业务功能。处理请求业务实现在handlers目录下,根据相应的业务去调用对应模块的接口函数。

 

3、RPC控制流程

Ripple RPC提供四种功能模式:

using Callback = std::function <void ()>;

using Continuation = std::function <void (Callback const&)>; //挂起

using Suspend = std::function <void (Continuation const&)>; //暂停

using Coroutine = std::function <void (Suspend const&)>;   //协同执行   

 

RPC请求产生ServerHandler的实例对象;

实例对象通过Coroutine并提交至协程管理器;

协程管理器通过Coroutine提交后,运行Suspend调用Coroutine ;

RPC请求开始执行;

当RPC处理程序需要挂起时,它用Continuation调用Suspend函数,

协程执行被暂停;

Continuation被协程管理器创建的Callback调用,Continuation可以选择立即执行,或延迟执行消息队列,或者等待某些资源空闲再执行;

当Continuation完成时,它会调用协程管理器给出的Callback,Callback在暂停的Coroutine上继续执行。

 

二、Peerfinder发现节点

1、介绍

Ripple交易网络多数节点连接集合组成,每个节点都在维护多个传出连接和特定的节点连接,这些连接是通过公网或私网进行,该网络定义的连接是向量性的。节点可向其连接的节点发送和接收消息。

当一个节点上线时,需要一组IP地址进行点对点的连接,以便在bootstrapping的过程中获得初始进入网络的权限。一旦建立起初始的点对点连接,则需要获得其他节点的地址来建立更多的点对点连接,直到达到连接数上限。此外,需要一种机制来将其IP地址通告到网络节点,以便可以接收入站连接,达到一定的限制。

PeerFinder是一个独立模块,是用来获取网络中新节点的IP并建立起连接。

2、PeerFinder的特点

保持一组适用于引导到对等网络的端点地址,并对效用地址进行排序。

发送和接收协议消息以发现端点地址。

为需要的新结点提供节点地址。

保持连接到一组配置固定的节点。

限制节点连接消耗的通道。

启动到节点地址的传出连接尝试,以维护网络连接。

对入站连接进行验证。

防止重复的连接和自我连接。

 

3、Config配置文件对PeerFinder的设置参数

`autoConnect`:指示是否启用自动连接功能。

`wantIncoming`:指示节点是否需要入站连接。当此标志关闭时,节点不会在

Endpoint消息中广播自己地址。

`listeningPort`:为其他节点连接创建侦听套接字使用的端口号。

`maxPeers`:允许的最大数量的节点连接。这包括入站和出站连接,但不包括固定 和集群节点。

`outPeers`:启用自动连接功能时PeerFinder将维护的自动出站连接的数量。

 

4、PeerFinder模块代码中主要的对象功能

Livecache对象:保存最近连接的节点IP地址信息。

 

Bootcache对象:

存储用于获得初始连接的IP地址的次数。m_valence存储连接数 量,表示成功连续连接尝试次数为正数的次数,以及连续尝试失败次数为负时的次数。

 

Slot对象:

点对点网络建立连接,并提供建立连接的属性和状态:

`accept`:接受是通过在侦听套接字上接受连接请求而产生的状态。

`connect`:是节点主动连接其他节点的状态。

`connected`:向其他节点连接成功时的状态。

`active`:当处于已接受或已连接状态的连接完成握手过程时,状态变为Active, 并且根据属性提供一个通道。如果握手完成时没有通道可用,则套接字将关闭。

`closing`:关闭状态代表正在关闭连接中的套接字。

三、Overlay网络

1、介绍

Ripple交易网络是由一系列的节点构成的点对点的网络,每个节点都维护多个传出连接和可选的固定节点的连接。建立连接后可向网络的其他节点发送和接受信息。

 

2、Overlay网络由以下主要的几个对象组成

2.1 Message

Message对象主要是对网路数据进行打包和解包以及数据格式、数据缓存的处理。kHeaderBytes函数消息头信息、getBuffer函数打包信息数据、getLength函数计算信息长度、getType信息类型等。

 

2.2 overlay

overlay对象管理节点的连接。函数connect执行异步连接,建立到指定端点的对等连接;onHandoff接收节点的一些特定信息HTTP请求;

广播提案 send (protocol::TMProposeSet& m)

广播验证send (protocol::TMValidation& m)

转发提案relay (protocol::TMProposeSet& m,uint256 const& uid)

转发验证relay (protocol::TMValidation& m,uint256 const& uid)

然后通过make_Overlay对象对overlay进行实例化。

 

2.3 peer

peer对象主要功能是对节点负载平衡的处理,对网络节点的上限和掉线做标识,id_t作为节点的唯一标识。

 

2.4 在predicates文件中,send_always向其他节点发送消息、send_if_pred发送消 息去匹配节点等。

四、Consensus投票

  1. 介绍

该模块包含通用DPOS共识算法的实现。

2、Consensus共识机制由以下主要的几个对象组成

2.1 consensus

共识算法的通用实现。以下两点需要达成共识:

1、 总账中包含的一组交易。

2、 总类帐的关闭时间。

共识基本流程:

  1. 调用`startRound`将节点置于`Open`阶段。在这阶段,节点正在等待交易包含在其开放中的总帐。
  2. 连续调用`timerEntry`检查节点是否可以关闭总账。一旦节点“close”开放的总帐,它就转换到`建立'阶段。在此阶段,节点共享/接收其他节点应在关闭总账中接受交易的建议。
  3. 在随后调用`timerEntry`时,节点确定它有与其他节点达成共识,包括哪些交易。它过渡到“接受”阶段。在此阶段,节点将继续工作将交易应用于先前的总帐以生成新的结算总帐。完成新总帐后,该节点将共享经过验证的总帐与网络总账,做一些记账,然后通知给 `startRound`再次开始循环。

 

2.2 ValStatus

维护当前和最近总账的验证。管理与网络上收到的验证相关的存储和查询。

 

2.3 ConsensusParms

共识算法参数设置。原文提示这些参数不能随意修改。

proposeFRESHNESS = 20s 投票时间。

proposeINTERVAL = 12s 多久会强制产生新的提案。

minCONSENSUS_PCT = 80 宣布达成共识的百分比。

avINIT_CONSENSUS_PCT = 50  UNL上必须投赞成票的节点百分比。

avSTUCK_CONSENSUS_PCT = 95  卡住后必须投赞成票的节点的百分比。

avCT_CONSENSUS_PCT = 75  在总帐关闭时间达成一致所需的节点百分比。

 

2.4 ConsensusProposal

共识提案。代表在一轮共识协商中提出的投票立场。在共识协商中,其他节点可能会改变它在交易中的立场,或选择放弃。

isInitial():是否在本轮协商一致意见中的立场。

proposeSeq():获取投票的序列号。

 

2.5 consensusTypes

节点当前如何参与共识。一个节点参与不同模式的共识,具体取决于节点是如何由运营商配置的,以及它在共识期间与网络保持同步的程度。

      proposing 提案        observing 观察

         \                                     /

          \---> wrongLedger <---/  错误的总帐

                     ^

                     |

                     v

                switchedLedger     交换总账

节点进入轮询提议或观察。如果发现正在处理错误的总帐,会选择错误的总帐去尝试获取正确的总帐。 一旦获得了正确的总,就进入了切换平台模式。有可能节点稍后并再发现有一个新的总账,会试图在wrongLedger和switchLedger之间来回移动。

单个总账轮询的共识阶段:

          "close"             "accept"

     open -------------- > establish --------------> accepted

       ^                             |                                        |

       |-------------------------|                                        |

       ^                      "startRound"                            |

       |-----------------------------------------------------------|

通常的转变从开放转变为接受,然后startRound的过程重新开始。如果在建立或接受阶段检测并恢复错误的总账,共识将在内部重新打开。

Open:还没有关闭总账,但其他人可能有关闭。

Establish:通过与同行交换建议达成共识。

Accepted:已经接受了一个关闭的总账,并且等待startRound的通知开始下一轮的共识协商。

 

2.6 DisputedTx

在达成共识时发现有争议的交易,会创建DisputedTx,该对象只有在争议发生后才会持续存在。无争议的交易不会响应DisputedTx对象。

五、Ledger读取

1、介绍

Ledger用于总账交易数据的读写操作。在ReadView中提供了一些接口:

添加数据 v.insert(sle);

查找所修改的值 sle = v.peek(k);

更新数据 v.update(sle);

删除数据 v.erase(sle);

 

六、Proto 

1、介绍

主要是定义一些全局数据的配置、消息类型等,在ripple.proto定义。

 

七、Core线程池

1、介绍

Ripple项目通过多线程的协程机制来处理并发的问题。当接收到RPC命令、ServerHandler(HTTP)或Handler(websocket)的连接,处理程序然后调用JobQueue::postCoro()方法来创建协程并在稍后运行它。

postCoro()创建一个Coro对象。当Coroctor被调用,并且它的coro成员被初始化时(boost::coroutines::pull_type),执行会自动传递到协程。

postCoro()后调用Coro::post(),它在工作队列上调度一个事件,以便在以后的JobQueue工作线程中继续执行协程。当事件运行时,锁定Coro::mutex并调用coro。

 

八、beast库

存放Bool.beast库的源码。

 

九、Basics数据结构

1、介绍

Ripple basic是比较独立的模块,不对其他模块有所依赖。Basic是对容器类和功能进行封装,对订单数据的尾部插入和删除较多的特性封装std :: vector容器、对头和尾插入删除较多的订单封装了std :: deque容器等。

 

十、APP/Consensus共识

1、介绍

这模块主要是对Ripple共识所需要的类型进行了封装。RCLCxTx是SHAMapItem上进行一层包装,用来做事物功能;而RCLCxTxSet是对SHAMap来作为事物的封装;RCLCxLedger是作为RCLConsensus中的总帐, RCLCxLedger是用std :: shared_ptr <Ledger const>进行封装。

 

十一、APP/Ledger总账

  1. 介绍

每个节点总是有一个公开的总账。所有收到新的交易都放到公开的总账,在对上一个公开的总账达成共识之前,公开的总账是不能关闭。当公开的总账关闭时,公开的总账中的交易成为初始提案。验证者将发送提案(非验证者不会发送提案)。公开的总账包含节点认为应该进入下一个总账的一组交易。

当达成共识时,服务器从上一个已关闭的总账开始并应用一致的交易集,从而构建新的已结算总账。在正常情况下,服务器将同意最后一次关闭的总账和一致的交易集。

公开总账的目的在协商一致期间形成初步提案的基础和用于决定是否可以拒绝交易而不转交交易。

验证器和常规服务器之间唯一区别是验证者将其提议和验证发送到网络。

 

  1. 总账流

两个总账对于瑞波服务器来说是最重要,共识总账和最后验证的总帐。LedgerMaster是获取历史总账数据的中心,它跟踪最后打包的总帐、最后验证的总账和历史总账。

具体来说,LedgerMaster :: doAdvance()方法触发代码获取历史数据并控制状态机以获取总帐。

服务器试图向其他节点发布连续总账的连续流。服务器启动并连网后,当总账编号500正在结算时,那么服务器会发布已验证的总账500,然后再验证的总账501,然后是502。这种过程一直持续到服务器关闭。但是加载或网络连接有时可能会干扰该账本流。因此,假定服务器发布已验证的总账600,然后接收已验证的总账603。然后,服务器再去拉起总帐601和602回填总账历史。

服务器优先考虑正在共识的总账。但如果它赶上了正在共识的总帐,并且在服务器上没有更高的优先级请求,那么它会试图回填其的历史的总帐。首先通过尝试从本地数据库检索历史总帐数据来填充历史总帐数据。如果本地数据库没有全部数据,则服务器向网络节点请求剩余的信息。

假设服务器缺少多个历史总帐。以上面的例子为例,我们有总帐603和600,但我们错过了601和602。在这种情况下,在回填帐簿601之前,服务器首先请求总帐602的信息。希望扩展服务器本地最新的总帐的连续范围。历史总帐数据的用量也有限制。所以,如果我们在总帐603上,但我们缺少总账4,我们可能不要求拉起总账4回填。

 

十二、Resource模块

这模块这要是对费率做了定义和设置,有关特定的费用在impl/Fees中做了定义:

Charge const feeInvalidRequest        (  100, "malformed request"   );

Charge const feeRequestNoReply       (  10, "unsatisfiable request"  );

Charge const feeInvalidSignature       ( 1000, "invalid signature"      );

Charge const feeUnwantedData        (  150, "useless data"         );

Charge const feeBadData             (  200, "invalid data"          );

Charge const feeInvalidRPC           (  100, "malformed RPC"       );

Charge const feeReferenceRPC        (   20, "reference RPC"        );

Charge const feeExceptionRPC        (  100, "exceptioned RPC"      );

Charge const feeMediumBurdenRPC   (  400, "medium RPC"         );

Charge const feeHighBurdenRPC      ( 3000, "heavy RPC"            );

Charge const feeLightPeer           (    1, "trivial peer request"     );

Charge const feeMediumBurdenPeer  (  250, "moderate peer request"  );

Charge const feeHighBurdenPeer     ( 2000, "heavy peer request"      );

Charge const feeWarning           ( 2000, "received warning"        );

Charge const feeDrop              ( 3000, "dropped"               );

如果客户端在服务器上持续的高负载,则该客户端最初会收到警告消息(feeWarning)。如果高负载继续,Manager可能会告诉负载很重,服务器完全断开连接(feeDrop),并且不允许重新连接一段时间。

监控每个节点的负载由DecayingSample类实现。

猜你喜欢

转载自blog.csdn.net/qun_y/article/details/81261203