最近咱们在客户端的驱动程序中引入了一些变动,这些变动会影响传入的请求在Apache Cassandra集群内的分发方式。html
新的默认负载均衡算法即将随驱动程序推出,这些算法将有助于缩短长尾延迟,并提供更好的整体响应时间。nginx
01 Cassandra中数据分区和数据复制的方式算法
Cassandra根据分区键(partition key)的值将数据分配至节点。每一个分区键对应的分区有多个副本,从而确保可靠性和容错能力。数据库
复制策略决定了要把这些副本放置在哪些节点。整个集群中的副本总数被称为“复制因子(replication factor)”。例如,“复制因子为3”就表示在一个给定的数据中心或集群中,每条数据有三个副本。服务器
对于一个给定的查询请求,驱动程序会选择一个副本节点做为本次查询的协调节点(coordinator),并负责知足一致性级别(consistency level)的要求。网络
写入路径示例:客户端驱动程序选择了节点A,该节点A是某个给定的分区键的副本节点,它会做为本次请求的协调节点将数据发送到其余副本节点(E和C)负载均衡
02 客户端是如何路由的dom
DataStax Drivers(DataStax的驱动程序)控制传入的查询请求将如何在集群中分发。借助集群的元数据,DataStax Drivers能够知道哪些节点是某个给定分区键的副本节点。而后驱动程序会将查询请求直接发送到其中的一个副本节点,以免额外的网络跃点(network hop)。分布式
当复制因子大于1时,驱动程序会把收到的请求在不一样的副本间保持平衡。以往,驱动程序经常以随机的方式,从副本节点集(replica set)中为某个查询选择协调节点。微服务
03 随机选择
鉴于微服务应用程序一般包含多个服务实例,咱们必须将客户端的驱动程序视为一种分布式的负载均衡器(n个客户端将流量路由到m个节点)。随机选择是一种很好的分布式负载均衡器的算法,由于它是无状态的,不须要客户端实例互相通讯以后生成统一的负载。
多个客户端将多个请求分发到同一些节点
随机选择让请求负载被均匀地分配(从数量的角度),但这些负载的分配却不是公平的——由于随机选择没有考虑请求的大小或复杂性,也没考虑在服务器节点的后台可能正在处理的其余任务。
03 随机选择的二次方(The Power of Two Random Choices)
在分布式系统中,根据某个标识信号就肯定性地选择某一个候选节点,这多是很危险的——由于具备相同逻辑的多个客户端,可能会将同一服务器节点标识为最佳候选节点,并将全部流量导向到该节点。这会致使负载峰值,并有级联失效(cascading failure)的风险。所以,具备必定程度的随机性的算法仍然是咱们所须要的。
经过泰勒·麦克穆伦(Tyler McMullen)的演讲,咱们首此接触到“随机选择的二次方(The Power of Two Random Choice)”这个算法。在那以后,这个算法已经被诸多负载均衡软件(诸如Netflix的Zuul,Nginx和Haproxy之类的)实现,用于众多的服务网格部署(service mesh deployment)中(好比Twitter)。
这个算法的逻辑很简单:相比试着根据某个标识信号来选出那个绝对最佳的候选节点,“随机选择的二次方”这个算法会让你随机选择两个候选节点,而后再在这二者中选择更好的那个,同时也避免了更糟的选择。
咱们有几种不一样的方法来识别哪一个节点是最佳候选节点。过去,咱们公开了一种使用时延信息(LatencyAwarePolicy)做为标识信号的方法。因为咱们在路由数据库请求时没有考虑查询的复杂性,所以聚合的节点时延信息并非服务器实例的当前健康状态的最佳指标。
例如,某个查询可能因为须要整理合并大量数据而须要更长的时间,可是该服务器可能仍有大量的空闲资源来处理其余查询。另外,请求的响应时间是在收到响应以后才肯定的,但是这已经不能反映服务器的最新状态了。
客户端驱动实例会向节点发送请求,咱们决定用正在处理中的请求数来选择最佳候选节点。简而言之,咱们会随机选择两个副本,而后选择其中等待处理的请求较少的副本节点做为协调节点。
04 监测陈旧队列
借助咱们对节点的行为模式的了解,咱们发现了更多的改进空间。当节点正在进行压实或垃圾回收(GC)时,它可能须要更长的时间来处理请求队列中的项目。若是仅仅基于最短队列的算法,在负载增长的状况下,即便节点的请求队列过期,客户端驱动程序也可能会继续向其路由流量。
例如,请考虑如下情形。
系统中有等待处理的请求,用队列来表示
请求源源不断而来,应用程序中的客户端驱动程序实例在不断地平衡发送至三个副本节点请求。在某个时间点,节点C变得很忙(即一段时间不能处理请求)。其余正常的副本节点将继续处理请求,如图所示。
与此同时,系统传入的负载正在增长,此时健康的副本节点们就会收到更多的待处理请求。
因为负载增长,健康的节点收到的待处理请求的数量增长了
在这种状况下,咱们之前会将请求路由到那个没法以正常速度处理请求的节点(即上图中的节点C)。
为了克服上述状况,咱们引入了第二个指标:节点是否有待处理的请求(10+),而且在最近的几百毫秒内还没发回任何响应。这个指标并不做为”随机选择的二次方“的标识信号,而是做为对副本节点的健康情况进行的初步探测。
若是大多数副本节点是健康的,那么不健康的副本节点将不会做为初始的协调节点(它们仍会被归入重试和投机性执行speculative execution的考虑范围)。
为何把探测副本节点的健康情况做为第一步?由于若是有大量节点是不健康的,则可能意味着该指标不适用于当前的工做负载、查询类别或预计的延迟时间。这样,咱们确保不会根据一个在特定状况下并不适用的标识信号作出错误的决定, 从而避免了让状况变得更糟 。
05 总结
咱们针对具备多个客户端的一系列场景同时测试了这个新算法,经过CPU burns(stress-ng)和人工网络延迟(tc)模拟了处于压力下的节点,结果是该新算法的性能优于全部其余方法。
做为示例,下图按百分位分布展示了延迟数据,在一些节点被CPU burns的状况下,对新算法和随机选择算法进行了比较。
具备较低尾部延迟的新算法(蓝色)
新的负载平衡算法基于“随机选择的二次方”,再加上可以躲开忙碌节点的逻辑判断,即将成为DataStax Drivers新的默认设置。借助简单的逻辑判断,这个新的算法绕开了最差的候选节点,同时保证了流量分配有必定程度的随机性。于是它可以避免你们不但愿看到的羊群效应,大大下降了尾部延迟时间。
咱们将在全部的客户端驱动程序中采用这种新算法。Java和Node.js驱动程序从4.4版开始已经默认使用此算法,其他驱动程序也将很快跟上。