业务逻辑的实现:数据如何处理
重点是数据如何处理:Riak Core提供了一个统一的接口以控制分布在ring上的数据的计算(操作)。
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. 数据处理:一个完整的例子
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,并作为服务启动