基于Memcached协议的路由器Mcrouter介绍

概述

Mcrouter的开源地址GitHub,一个基于Memcached协议的路由器,适用于大规模集群,峰值每秒可以处理接近50亿个请求。

主要特性:

  • 支持标准的Memcached ASCII协议,使得支持Memcached协议的所有客户端无需做任何修改即可支持Mcrouter。
  • 多个客户端可以通过连接Mcrouter共享后端Memcached的连接池。
  • 多种一致性Hash算法可供选择,允许给多个Memcached实例分配哈希值。
  • 灵活的路由,支持前缀路由。
  • 复制模式连接池,写操作复制到连接池中所有实例,读操作从其中一个实例读取。
  • 支持流量复制,从生产环境复制流量,对新上的集群进行测试。
  • 在线更新配置。
  • 支持后端的Memcached健康检测和失败后的自动保护。
  • 对cold集群热身。
  • 在后端连接池/集群广播接收到的操作。
  • 可靠删除。在后端某个实例删除失败时,Mcrouter将操作记录在redolog中,后台线程间隔性地重新执行删除操作。
  • 多集群支持,拥有丰富的监控和调试命令。
  • QoS支持。Mcrouter支持根据主机、连接池、集群进行流量控制,可以对任何操作进行流控,如get/set/delete。还可以对超限流的请求进行拒绝,也可以对流量整形。
  • 支持超大对象,Mcrouter可以对不能放入Memcached Slab中的超大对象自动切分/重组。
  • 支持本地缓存,实现多级高速缓存。
  • IPv6和SSL的支持。
  • 提供丰富的stats和debug命令,以及安全可靠的删除操作。
  • 通过一个内核一个线程的方式充分利用多核系统的优势,在异步处理网络事件时,使用内部的轻量级线程,即纤程。

架构

任何要接入Memcached服务的客户端都会使用标准ASCII编码的Memcached协议。对于客户端来说,Mcrouter就像一个Memcached服务器;而对于服务器端来说,Memcached却又像一个普通的Memcached客户端。采用Memcached的通用API作为通信方式如下图所示:
在这里插入图片描述

路由算法

Mcrouter提供的路由算法:

  • AllAsyncRoute:向children属性指定的所有Memcached集群发送相同的请求,不等待集群响应,返回由NullRoute定义的返回值。
  • AllFastestRoute:向children属性指定的所有Memcached集群发送相同的请求,把第一个响应的集群返回内容返回给客户端。其他的Memcached集群响应被忽略。
  • AllInitialRoute:向children属性指定的所有Memcached集群发送相同的请求,等待children属性指定的第一个Memcached集群返回,并将返回内容返回给客户端。其他的Memcached集群响应被忽略。
  • AllMajorityRoute:向children属性指定的所有Memcached集群发送相同的请求,并等待半数以上Memcached集群成功返回,如果没有半数以上成功,则返回最后一个错误响应。
  • AllSyncRoute:向children属性指定的所有Memcached集群发送相同的请求,并等所有Memcached集群返回,给客户端返回最坏的一个响应。
  • DevNullRoute:和NullRoute类似,会同时返回stat报告。
  • ErrorRoute:立即返回一个指定的错误信息。
  • FailoverRoute:将请求转发给children属性指定的Memcached集群列表中的第一个集群,然后等待返回,如果成功,则将响应发送给客户端。如果失败,则轮询第二个集群,直到有一个成功。如果所有集群都失败,则返回给客户端最后接收到的失败消息,key miss不被当作错误,如果希望key miss做为一种错误处理,可以使用MissFailoverRoute。
  • FailoverWithExptimeRoute:带失效时间的FailoverRoute,在某个集群被标记为失败后,隔一段时间重新路由到该集群,如果发生错误,重新进行failover。
  • HashRoute:基于key的Hash路由。
  • HostIdRoute:根据客户端主机ID计算hash,进行路由。
  • LatestRoute:随机连接,如果返回错误则再次随机连接,最大随机次数为failover_count。
  • MigrateRoute:迁移模式路由。
  • MissFailoverRoute:路由同Failover模式,key miss认为后端集群出错。
  • ModifyExptimeRoute:经过这个路由的请求,TTL将被重新赋值。
  • NullRoute:为每个请求都返回一个空的响应。
  • PrefixSelectorRoute:通过key的前缀进行路由。
  • PoolRoute:路由到一个Memcached集群,类似HashRoute,但通常还有限流等功能。
  • RandomRoute:随机路由到children属性指定的Memcached集群列表中的某一个集群。
  • WarmUpRoute:set和delete命令发送到cold集群。get命令首先尝试从cold集群获取,如果key miss,则从warm集群获取,如果从warm集群取到,则返回客户端,并异步地更新cold集群。

适用场景

适用场景包括:

  • 分片池:Sharded pools,就是常说的数据水平切分。Mcrouter可以将请求按照缓存key的哈希值发送到不同的Memcached服务器上。这样缓存数据将被均匀地分布在不同的Memcached服务器上,同时对同一个缓存的操作也将按照缓存key的哈希值发送到同一台Memcached服务器上。Mcrouter对一个服务器资源池默认使用分片池的方式管理。配置示例:
{
	"pools": {
		"A": {
			"servers": [
				"127.0.0.1:12345",
				"127.0.0.1:12346"
			]
		}
	},
	"route": "PoolRoute|A"
}

配置解读:定义一个含有两个后端服务的连接池,路由规则是,将Key按照一致性Hash算法,分布到两个后端服务上。

  • 复制池:Replicated pools,Mcrouter支持随机发送一个get请求到复制池中。如果请求在复制池中的第一个服务上失败,将从其他服务上获取数据。同时发送send和delete到复制池将被池内的所有服务主机接受。基于这种能力,可以设计出多个Memcached实例同步写入,多个Memcached实例同时提供读能力。配置示例:
{
	"pools": {
		"A": {
			"servers": [
				"127.0.0.1:12345",
				"127.0.0.1:12346"
			]
		}
	},
	"route": {
		"type": "OperationSelectorRoute",
		"operation_policies": {
			"add": "AllSyncRoute|Pool|A",
			"delete": "AllSyncRoute|Pool|A",
			"get": "LatestRoute|Pool|A",
			"set": "AllSyncRoute|Pool|A"
		}
	}
}

配置解读:add、delete和set被发送到池A中的所有实例执行,get被随机发送到池A中的一个实例执行。如果get请求失败,自动尝试另一个实例,默认尝试5次。

  • 前缀路由:Prefix Routing,Mcrouter可根据key前缀把客户端分配到不同的Memcahed池。配置举例:
{
	"pools": {
		"workload1": {"servers": [/* list of cache hosts for workload1 */]},
		"workload2": {"servers": [/* list of cache hosts for workload2 */]},
		"common_cache": {"servers": [/* list of cache hosts for common use */]}
	},
	"route": {
		"type": "PrefixSelectorRoute",
		"policies": {
			"a": "PoolRoute|workload1",
			"b": "PoolRoute|workload2"
		},
		"wildcard": "PoolRoute|common_cache"
	}
}

配置解读:把以a为前缀的所有key分配到workload1池,把以b为前缀的所有key分配到workload2池,其他的key都分配到wildcard池。JSON文件不支持注释,上面配置文件里的注释仅作示范用。

  • 缓存预热:Cold cache warm up,每当有新的Memcached服务器加入到缓存服务集群中时,它是没有任何数据的,称这类缓存实例为cold cache。为了不对性能产生影响,Mcrouter提供方法来为cold cache预热,用已有的warm cache填充cold cache。配置实例:
{
	"pools": {
		"cold": {"servers": [/* cold hosts */]},
		"warm": {"servers": [/* warm hosts */]}
	},
	"route": {
		"type": "WarmUpRoute",
		"cold": "PoolRoute|cold",
		"warm": "PoolRoute|warm"
	}
}

配置解读:所有的set和delete的请求发送到cold cache路由处理时。数据都将先从warm cache路由处理(其中请求可能导致缓存命中)获取。如果warm cache返回命中,将响应转发给客户端,同时异步请求更新cold cache的路由处理。

  • 两级缓存:Two level caching:当一个单一Memcached服务池出现超载情况时,可考虑使用二级高速缓存架构。例如,除了一个大容量的共享缓存,可以给客户端额外增加一个附加Memcached实例。数据获取逻辑为:
    • 先从本地的Memcached实例读取;
    • 如果未命中,再从共享池中读取;
    • 如果数据存在于共享池,将其同步到本地的Memcached实例中。

配置如下:

{
	"pools": {
		"shared": {"servers": [/* shared memcached hosts */ ]},
		"local": {"servers": [/* local memcached instance, e.g."localhost:<port>" */]}
	},
	"route": {
		"type": "OperationSelectorRoute",
		"operation_policies": {
			"get": {
				"type": "WarmUpRoute",
				"cold": "PoolRoute|local",
				"warm": "PoolRoute|shared",
				"exptime": 10
			}
		},
		"default_policy": {
			"type": "AllSyncRoute",
			"children": [
				"PoolRoute|shared", {
					"type": "ModifyExptimeRoute",
					"target": "PoolRoute|local",
					"exptime": 10
				}
			]
		}
	}
}

配置解读:设置一个时间间隔定时同步数据,假定系统可以在一定时间内容忍旧数据的读取。所有的set和delete都将发送到shared共享池和local本地池中。第一次从local池读取数据;​shared池会监听是否命中。如果shared池命中,则返回数据给客户端并且异步请求更新local池中的值。客户端不会阻塞更新,所有数据在local池中只存活10秒。

可扩展性

可扩展性与Twemproxy类似,Mcrouter集群内部也可以是多个相同配置的对等节点,可通过在LVS上增加节点的方式完成扩容。

缓存服务或缓存集群服务在容量上产生瓶颈时,都可以通过更改Mcrouter的配置,增加缓存服务器的数量。Mcrouter在扩容方面相对于Twemproxy有更完善的支持,可通过warm up方式对新加入的节点进行预热。
在这里插入图片描述
Mcrouter层中的Mcrouter各个实例是对等实例,通过LVS做高可用,节点宕掉时,LVS将流量摘除。Mcrouter通过以下能力确保在后端Memcached发生故障时的可靠性:

  • 心跳检测和自动故障转移:Mcrouter能够通过心跳握手来检测每个Memcached实例的状态。一旦Mcrouter将一个Memcached实例标记为无响应,它会直接将所有的请求转移到另一个可用的Memcached实例上。同时,后台将向无响应Memcached发送心跳请求,只要Memcached的心跳恢复正常,Mcrouter将会重新启用这个Memcached实例。​“软错误”​(比如:数据超时)允许连续发生多次,但是一旦发生“硬错误”​(比如:拒绝连接),Mcrouter将立即该Memcached实例标记为无响应。
  • 多集群互备:通过在请求key里面增加自定义的前缀,可以把请求备份到多个Memcached池或者集群。
  • 可靠的删除操作:Mcrouter尽量保证所有的删除操作都被执行。Mcrouter将所有的删除操作都记录到硬盘上,防止由于网络中断或者其他原因导致的Memcached不可用。当连接恢复之后,Mcrouter将启动一个单独的进程异步地重新执行这些删除操作。
  • 保障服务质量:Mcrouter允许以主机、池或者集群为单位设置任何请求的速率阈值,当请求个数超过阈值时,剩下的请求将会被拒绝服务。

监控

Mcrouter提供丰富的stats和debug命令。通过stats命令可导出很多内部计数器。Mcrouter还提供自我调试命令,能够反应在运行时一个特定的请求被分配到哪一个主机。默认在/var/Mcrouter/stats路径下记录运行状态,每10秒更新一次。

stats命令返回的数据样式:

{
	"libMcrouter.Mcrouter.5000.config_last_success": 1410744409,
	"libMcrouter.Mcrouter.5000.uptime": 10,
	"libMcrouter.Mcrouter.5000.result_busy_shadow": 0,
	"libMcrouter.Mcrouter.5000.result_busy_failover": 0,
	"libMcrouter.Mcrouter.5000.cmd_get_out_failover": 0,
	"libMcrouter.Mcrouter.5000.cmd_meta": 0,
	"libMcrouter.Mcrouter.5000.result_busy_failover_count": 0,
	"libMcrouter.Mcrouter.5000.cmd_delete_out": 0,
	"libMcrouter.Mcrouter.5000.result_error": 0
}

源码解析

Mcrouter大量采用C++11的语法,并引入fiber进行流程异步化组装。依赖于Facebook两个开源组件:Follyfbthrift。在启动时,会启动多个相互独立的线程,使用libevent的线程来处理请求,源码参考EventBase::runInEventBaseThread​。处理请求路由到后端进行编排时,Mcrouter将任务包装成context,使用轻量级线程进行异步化处理,线程为folly::fibers::fiber

Mcrouter的管控能力和支持的集群模式都非常丰富,对生产环境中的各种场景、多机房复制等需求有着全面的支持,被Facebook、AWS、Reddit等大型互联网公司实践过,可以稳定可靠地运行。但Mcrouter在国内使用较少,主要原因是匮乏中文文档,Mcrouter本身实现较为复杂,掌握起来需要一定的时间。

参考

  • 深入分布式缓存:从原理到实践
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

johnny233

晚饭能不能加鸡腿就靠你了

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值