依然采用一致性hash算法去进行扩容,但是对该算法进行了改进,收益在于:
沿用现有的S3存储模型以及标准协议,将多个底层bucket(带权重)聚合成一个大的bigbucket,用户所有的操作都基于同一个bigbucket进行,不再需要进行bucket切换。
新增一个Bugbucket Gateway进行路由和请求处理,将来自client端的请求根据hash路由规则转发到后端zone上面的bucket,之后再将后端的返回的请求内容返回给最终的客户端。
数据访问需要在客户端的object名称之前新增一个{ringtoken}字段,同时将支持path和Virtual host两种方式。
将多个底层的bucket聚合成一个ring,形成一个资源分组,其中底层bucket名称仍然需要保持全局唯一。单个ring里面的bucket可以在同一个zone(如 Ring1和ring2),也可以跨越多个zone(如 Ring3)。同时每个Ring里面的bucket都按weight分配权重。这样灵活的组合能够最大程度的实现底层资源的灵活调度。
由一组ring形成一个周期环状的结构,每次扩容以ring为单位,比如第一轮新建一个集群,由bucket1~4 组成一个ring0,当经过一个时间周期(比如一个月),如果之前的ring0对应的bucket仍然有比较多的空间,可以新建一个ring1 指向同样的bucket1~4,实现底层资源的复用。之后如果之前分配的空间不足,可以依次新建ring2、ring3 指向新的bucket5~8,实现底层资源的扩容。
下图为,在Ring0分配的空间bucket2和bucket4还有剩余的情况下(比如剩余还有30%空间,分配权重为30),通过在另外一个集群新建bucket5和bucket6(分配权重100),组成一个新的ring1,实现跨集群资源的组合,到集群资源利用的最大化。
整个算法在工程实践上需要解决的一个问题是如何确保客户端能够按照预期去更新对应的ringtoken,将最新的写入请求落到正确的后端所在bucket。目前有两种解决方案
客户端每次写入之前从网关处查询最新的ringtoken。(获取到ringtoken以后缓存到本地,并设置过期时间,发现过期以后再更新)
和客户端商定ringtoken的轮换规则,比如按一个月一次,12个月为一轮,如此往复。
#/usr/bin/python
import time
ringtoken={"01":"ring0",
"02":"ring1",
"03":"ring2",
"04": "ring3",
"05": "ring4",
"06": "ring5",
"07": "ring6",
"08": "ring7",
"09": "ring8",
"10": "ring9",
"11": "ring10",
"12": "ring11"}
current_month = time.strftime("%m", time.localtime())
current_ringtoken = ringtoken[str(current_month)]
print time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
print current_ringtoken
output:
2019-05-22 16:37:55
ring4
整个Bigbucket Gateway主要功能如下: