leaf-distributer
leaf-distributer 是一个遵循PSR-3规范的,分布式算法组件,适用于基于客户端的轻量级集群分布式场景:如Redis、Memcached、MySQL。
特点及出发点:
使用composer包,遵循psr-3标准规范,便于集成到基于composer的项目中。
提供autoload注册方式,即便你的项目没有使用composer,也能方便的集成。
本组件的原型,已经在我司内部使用并得到验证。因Memcached拓展本身已经提供了一致性哈希算法的解决方案,而MySQL也有相对成熟的Cluster方案可做分库分表,所以,该组件在我的实际运用中,目前主要还是用在做redis分布式集群环境中,究其原因,主要还是Redis(版本<=2.8)没有一套官方的分布式解决方案。当然你可以选择使用Codis,只不过运维成本稍高。因此,如果你的公司规模不大,运维能力不是足够强大,或者项目相对较小的话,使用本组件做分布式解决方案,是相对较好的选择。
适用说明:
集成一致性哈希算法与取模算法。
适用于基于客户端的轻量级的分布式解决方案。
特别适用场景:Redis的集群分布式算法(Redis官方在3.0版本才出了自己的分布式解决方法)。
基于MySQL做分布式方案解决方案有很多且比较成熟,但对于小团队而言,使用一致性哈希替代取模算法同样不失为一种较好的解决方案。
支持集群中的配置是组模式(多个组,组成一个集群,每个组都是主从配置,一主多从,或多主多从,或主从配置一样均可)。
支持取配置类型:举例来说,根据某个维度(比如根据Redis的一个Key),通常来说,你根据这个Key获取的server配置,是这个配置组中的Read(从)库之一。但是某些情况下,你可能需要读主库的数据,而不是从库的数据,这种情况的话,你需要获取的就不是从库的配置,而是这个配置组下主库(Write)的配置了。
1. leaf-distributer 使用——引入
引入有2种方式:
1、基于composer的项目的引入方式:
composer require leaf-distributer
2、非composer项目引入方式:
\Leaf\Distributer\Autoloader::register();
2. leaf-distributer 的使用——配置
本组件的使用场景:
你有多台server服务器,想做集群。
你需要根据一个Key(维度,在Redis中,你可以称之为Key,如果是MySQL,你也可以认为是你要做分库分表的维度),根据这个Key,返回集中中某一台server的配置。
最后,你根据这个配置,做实例连接,操作数据的读取和写入等。
这其中,有几点你可能需要考虑:
主从配置问题:多台server,可能是分组的,每个集群都是多个组组成,每个组都是主从方式。本组件要求配置这样来定义:
return
[
'groupA' => [ //cluster name, required
//server group for writing data
'write' => [
['host' => 'x.x.1.11', 'port' => 6379, 'auth' => 'passwd', 'db' => 0, 'weight' => 3], //weight: server weight
['host' => 'x.x.1.12', 'port' => 6379, 'auth' => 'passwd', 'db' => 0, 'weight' => 2],
],
//server group for reading data
'read' => [
['host' => 'x.x.1.21', 'port' => 6379, 'auth' => 'passwd', 'db' => 0, 'weight' => 1],
['host' => 'x.x.1.31', 'port' => 6379, 'auth' => 'passwd', 'db' => 0, 'weight' => 1],
],
//cluster weight
'weight' => 1, //cluster的权重配置,用于做hash虚拟节点数量分配
],
'GroupB' => [
'write' => [
['host' => 'x.x.2.11', 'port' => 6379, 'auth' => 'passwd', 'db' => 0, 'weight' => 1],
],
'read' => [
['host' => 'x.x.2.21', 'port' => 6379, 'db' => 0, 'weight' => 1],
['host' => 'x.x.2.31', 'port' => 6379, 'db' => 0, 'weight' => 1],
['host' => 'x.x.2.41', 'port' => 6379, 'db' => 0, 'weight' => 1],
['host' => 'x.x.2.51', 'port' => 6379, 'db' => 0, 'weight' => 1],
],
'weight' => 1,
],
];
上面的配置,是==一个==一群的配置,这个集群有2组配置,每组都是主从的(实际线上业务,推荐你用哨兵做高可用)。关于每个组的配置,说明如下:
weight:标识这组配置在cluster集群中的权重。权重越高,数据在落在这组server的概率就越高。默认为1。
write:主库(具有write权限的)配置,可以为多个。
read:从库(具有read权限的)配置,可以为多个。如果你的项目中,没有主从,则write项和read项配置为相同的即可。需要说明的是,read项和write项配置都是可以为多个的。每一个也可以配置权重。
Tips:每一组配置,可以为主从,推荐一主多从+哨兵模式对这组配置做高可用,当然,从太多并不推荐,因为主从同步带宽占用你也得考虑,否则你的项目从从太多,同步数据网络带宽太高高必是隐患。
3. leaf-distributer 的使用——基本使用
使用示例在src/Example/single-consistent-hashing.php中,具体示例代码如下:
//1、引入:如果你的项目没有使用composer,需要先引入Autoloader,并注册自动加载
$autoloader = dirname(__FILE__) . '/../Autoloader.php';
require $autoloader;
\Leaf\Distributer\Autoloader::register();
//2、先实例化分布式算法的管理器
use \Leaf\Distributer\DistriManager;
$distriManager = new DistriManager();
//3、定义配置
$configGroup = require( 'standard-config.php' );
//4、获取分布式算法分发对象
$distributer = $distriManager->getDistributer()->init($configGroup);
$config = $distributer->lookUp('aa', 'write');
首先,你需要做包引入,如果没有使用composer,则需要注册一下自动加载机制。如果你的项目使用了composer,则下面注册自动加载机制的代码可忽略。
$autoloader = dirname(__FILE__) . '/../Autoloader.php';
require $autoloader;
\Leaf\Distributer\Autoloader::register();
然后,实例化一个分布式管理器
$distriManager = new DistriManager();
接下来,获取一个分布式分发器,设置分发器的名字为A,并制定分布式算法为一致性哈希。
$distributer = $distriManager->getDistributer('A',DistriMode::DIS_CONSISTENT_HASHING);
需要说明的是,获取分布式分发器,是可以指定分布式分发器的名称的。默认获取的分布式分发器的名称是default。如果你的项目中,需要使用多个集群,那么多个分布式集群都可以用 $distriManager 来管理。但是分布式分发器的名称不能相同。
举例来说:
获取一个redis集群A的分布式分发器,并设置这个分发器使用分布式算法为一致性哈希分布式算法:$distriManager->getDistributer('A',DistriMode::DIS_CONSISTENT_HASHING);
然后我们获取一个redis集群B的分布式分发器,并制定这个分布式分发器使用的分布式算法为取模算法:$distriManager->getDistributer('B',DistriMode::DIS_MODULO);
接下来,我们需要告诉这个分发器我们的其中配置,并做初始化。
$distributer->init($configGroup);
然后,我们可以通过一个key,获取查找出某个server的配置了
$config = $distributer->lookUp('aa');
需要说明的是,有些情况下,你可能需要获取写库的配置,比如某些场景下,你存入数据之后,需要立即读出来,这个时候,从库有可能并未同步数据,如果直接由从库中取数据,是可能出不出来的。所以,你可以这样指定查找主库(具备write权限的库)
的配置:
$config = $distributer->lookUp('aa', 'write');
4. leaf-distributer 的基本使用——高级使用
建议:你可以将分布式算法管理器 $distriManager 注册到全局树种。可以使用依赖注入的方式,也可以使用注册树模式,这取决于你的项目。
高级使用分为几个部分:
使用一致性哈希分布式算法的话,如何最大限度的避免哈希碰撞
关于权重的设置
如何写自己的分布式算法,并注册到 leaf-distributer 组件中
####如何最大限度的避免哈希碰撞
在本组件内,一致性哈希的分布式算法,实际上是维护了一个哈希环,环上有虚拟节点,虚拟节点的值,是实际配置组的哈希值(具体机理,可阅读源码)。默认情况下,我们并没有使用md5算法作为hash值,原因是hash值位数相对较低,产生哈希碰撞的概率也相对高一些(虽然你极难遇到)。默认的hash算法是 ==sha384== 算法。当然,如果你觉得效率或者其他原因,你想用默认的这个哈希算法,可以指定其他算法。
所有支持的哈希算法,都定义HashMode中,举例来说,你想使用MD5算法生成哈希环上的节点的值:
$distributer = $distriManager->getDistributer('A',DistriMode::DIS_CONSISTENT_HASHING);
$distributer->getDistriHandler()->setHashFunc(HashMode::STR_MD5);
$distributer->init($configGroups);
####关于权重的设置
权重的设置,取决于你的server的配置,如果全部server配置相同,则权重无需配置,如果不同。你有2个地方可能需要配置权重
1、集群配置中,配置server组的配置,它决定了这组server在集群中的权重,默认情况下,会根据权重虚拟成160个节点,如果weight设置了1个倍数,则这组server虚拟节点数量同等翻倍。
2、每个组中,write项下的server和read项下的server都是可以单独配置权重的。它只会影响单个server在同组中同类型server中的权重。
####如何自定义分布式算法并注册到本组件
1、你的分布式算法需要继承基类:
Leaf\Distributer\DistriHandler\DistriAbstract
并实现相关方法。
2、注册分布式算法的标识和相关的处理类:
Leaf\Distributer\DistriMode::registerDistriModeClass($intMode,$className);
需要注意:$intMode参数必须是整型,且不能为1和2,因为1和2分别是系统的一致性哈希和取模算法的标识和对应的处理类。
3、然后你就可以使用你自己的分布式算法来处理了:
//假定3是你设置的分布式算法的标识,并假定你已经实现了处理类
$intMode = 3;
//获取分布式处理器并制定其分布式算法为自定义分布式算法
$distributer = $distriManager->getDistributer('A',$intMode);
//根据配置初始化
$distributer->init($configGroups);
//根据某个维度获取一个配置
$config = $distributer->lookUp('someKey');
todo.计划内容:
暂无