(3)riak_core系统的工作方式

来源:互联网 发布:淘宝免费流量来源 编辑:程序博客网 时间:2024/05/17 03:46
三、riak_core系统的工作方式

从原理上讲,riak_core通过一致性hash算法将数据随机均匀的分布在一个环上,对这些数据的操作可以并发的进行,因此riak_core本质上还是一个数据并行的分布式系统。

因此,基于riak_core构建分布式系统涉及到两个问题:
  • 对数据如何分布:riak_core通过某种hash算法将数据随机均匀的分布在一个环上。选取那种hash算法,如何hash,都由开发人员进行选择。如果数据有唯一key,那么可以对key进行hash,具体来讲,riak这个kv 存储系统默认是对bucket+key做hash。(当然riak也可以对不同的bucket配置不同的hash方式,包括hash算法和要hash的数据)
  • 对数据如何处理:对数据的处理涉及到系统的业务逻辑,这得由开发人员自己实现。例如对riak来说,主要的业务逻辑就是存储和查找了。


2. 关于数据的分布

实际上数据hash的工作一般是通过riak_core提供助手模块riak_core_util中的函数chash_key进行,在交给vnode_master之前就预先计算好了数据在ring上的位置了(Index)。

%% @spec chash_key(BKey :: riak_object:bkey()) -> chash:index()chash_key({Bucket,Key}) ->    BucketProps = riak_core_bucket:get_bucket(Bucket),    {chash_keyfun, {M, F}} = proplists:lookup(chash_keyfun, BucketProps),    M:F({Bucket,Key}).

(可以看到chash_key不做具体的hash计算,它只是调用了配置文件中设定的hash函数,换句话,我们可以自定义自己的hash函数)

以riak这个NoSQL数据库为例,它是通过对bucket和数据的key做hash计算得到数据的分布节点的:
BKey={Bucket,_Key}
DocIdx = riak_core_util:chash_key(BKey), 

类似的,我们使用时主要根据数据计算出它分布的节点,然后
DocIdx = riak_core_util:chash_key({<<"ping">>, term_to_binary(now())}),PrefList = riak_core_apl:get_primary_apl(DocIdx, 1, rts),IdxNode = hd(PrefList), % 数据分布的第一个节点riak_core_vnode_master:command(IdxNode, ...  % 在数据分布节点上进行数据处理



重点是数据如何处理:riak_core提供了一个统一的接口以控制分布在ring上的数据的计算(操作)。

2. riak_core的数据控制接口

如前所述,每类vnode提供了一套服务,每个服务由在各个partition上的vnode进程组成,这些进程实际分布在各个物理节点上。对于每一个物理节点,每类服务会有一个riak_core_vnode_master进程提供统一的接口。实际上riak_core_vnode_master进程的主要作用是将请求转发给对应的vnode进程进行处理,无论这个vnode进程在哪个物理节点上。

riak_core_vnode_master模块是一个gen_server的实现,它对外提供了一套API。

引用
用OO打个比方,如果将riak_core_vnode_master比作OO中的一个类,这些API相当于这个类的静态公共方法,这些静态公共方法提供控制这个类的对象的初始化、以及这些对象的控制等,也就是说所有与这些对象打交道的操作统一通过这些静态公共方法进行。


我们主要通过riak_core_vnode_master模块提供的API控制集群内的所有vnode进程,从而对外提供数据处理服务,这些API有:
  • start_vnode(VNodeModule)函数:启动vnode_master进程(一个gen_server进程)并注入用户逻辑,用户逻辑在VNodeModule参数指定的模块中实现。通过这种方式在启动时将用户的业务逻辑注入进来(类似IoC的构造器注入);
  • get_vnode_pid(PartitionId, VNodeModule)函数:获取某个partition上某类服务的vnode进程,如果不存在则调用riak_core_vnode_sup创建之(也就是说vnode进程是懒加载,不过全部挂在riak_core_vnode_sup这个supervisor树下);
  • 用command/3,sync_command/3,sync_spawn_command/3:会将请求命令(command)发给对应的vnode进程,最后调用用户实现的vnode模块中的handle_command回调函数处理;
  • converage/5函数:稍候研究 to be continue...

可以看到riak_core_vnode_master的主要任务是转发用户请求,可以异步转发,也可以同步转发。如何转发:根据请求的数据所在的partition,可以算出partition所在的物理节点和对应的vnode进程Pid,知道了物理节点和vnode进程的pid后就可以向此物理节点上的vnode进程直接发送消息。


不过,vnode进程的生成不由vnode_master直接负责,所有vnode进程的创建实际上由一个专门的riak_core_vnode_sup模块负责(这可模块实际上是一个simple_one_for_one策略的supervisor,这种启动策略使它只能管理同一类erlang进程,不过它的特点是能动态的创建无数的相同类型的子进程,见supervision principles )。vnode进程本质上是一个gen_fsm状态机(可以看到有一个active状态)



3. riak_core的数据处理:一个完整的例子
try-try-try的例子代码要简单许多,这个例子是随机ping一个分区vnode,返回结果是该分区的ID和物理节点,整个过程如下:
ping() ->    DocIdx = riak_core_util:chash_key({<<"ping">>, term_to_binary(now())}),    PrefList = riak_core_apl:get_primary_apl(DocIdx, 1, rts),    [{IndexNode, _Type}] = PrefList,    riak_core_vnode_master:sync_spawn_command(IndexNode, ping, rts_vnode_master).


riak_kv中的例子要复杂很多,不过基本过程还是类似的。

to be continue...

4. 其它

在每个物理节点上会为每一类vnode启动一个riak_core_vnode_master进程,该进程控制这个物理节点上的所有同类vnode进程(通过上面提到的API)。所有这些riak_core_vnode_master进程的注册遵循一套约定的命名规则:master进程的注册名就是该类vnode的模块名字加上尾缀 "_master"。例如应用系统实现了一类vnode,其模块叫rts_stat_vnode,对应的riak_core_vnode_master进程名就在erlang中注册为“rts_stat_vnode_master”。

可以通过riak_core:vnode_modules()察看当前有多少vnode模块。(它实际上反映了通过riak_core:register_mod/3注册的模块,该函数将vnode模块信息放置在application环境中。许多函数调用了它,例如riak_core:register_vnode_module/1)

vnode进程是懒加载的,由riak_core_vnode_sup这个supervisor负责动态创建vnode进程(可以通过supervisor:which_children(riak_core_vnode_sup).察看当前的所有vnode_worker进程)。


浏览rts实时日志统计的代码有助于了解基于riak_core的应用系统的工作方式。
应用系统的功能由rts这个application实现,在rts_app这个application behaviour中可以看到它启动了rts_sup,成功后注册了3类vnode,并作为服务启动
0 0
原创粉丝点击