Learn Riak Core Step By Step 2

Riak
Core, The Coordinator

What
is a Coordinator?

顾名思义。 Coordinator即使一个协调者,主要工作就是用来协调进来的请求。它强行运行N,
R, and W
的一致性语义,而且运行想read repairanti-entropy 服务。足药用在分布式集群中,当出现冲突时,用来同步数据。

从技术上说, 协调器是一个gen_fsm,每个请求都会被他自己的erlang进程处理,一个协调器会和vnode保持通信,直请求结束。

一个协调器总的来说:

  • 协调请求
  • 强一致性
  • 运行anti-entropy
  • 一个实现了gen-fsm行为的erlang进程
  • 与运行请求的vnode实例保持通信

Implementing
a Coordinator

和vnode不一样,riak core未定义协调器的行为。样例中现实了get和put的协调器,能够參照,但不是一成不变的。

样例中使用supervisour和gen_fsm worker的方式实现了协调器。

init(Args)
-> {ok, InitialState, SD, Timeout}

Args                :: term()
InitialState :: atom()
SD :: term()
Timeout :: integer()

这实际上是gen_fsm行为的一部分,必须在回调中制定初始状态名和数据(SD)。

有些情况下。你也能够制定超时的值为0以至于立即进入到初始状态-prepare

一个get rts的协调器须要4个參数:

  • RequestId:本次请求的唯一Id

  • From: 应答者

  • Client: The name of the client entity -- the entity that is writing log events to RTS.

  • StatName: 统计项的名字.

init([ReqId, From, Client, StatName]) ->
SD = #state{req_id=ReqId,
from=From,
client=Client,
stat_name=StatName},
{ok, prepare, SD, 0}.

rts的写协调者也是一样。可是有两个额外的參数。

  • Op: 运行的操作能够是setappendincrincrby或者sadd中的一种.

  • Val: 被操作的值,incr操作没有这项定义。

init([ReqID, From, Client, StatName, Op, Val]) ->
SD = #state{req_id=ReqID,
from=From,
client=Client,
stat_name=StatName,
op=Op,
val=Val},
{ok, prepare, SD, 0}.

prepare(timeout,
SD0) -> {next_state, NextState, SD, Timeout}

SD0 = SD            :: term()
NextState :: atom()
Timeout :: integer()

prepare的工作是建立一个优先列表。这个列表是应该參与本次请求的优先的vnode集合的列表.大部分的工作都被riak_core_util:chash_key/1riak_core_apl:get_apl/3做完了.getwrite协调器这时后做的工作都一样。

计算请求落在环的索引,从索引中确定N个优先处理这个请求的分区。

以下是代码:

prepare(timeout, SD0=#state{client=Client,
stat_name=StatName}) ->
DocIdx = riak_core_util:chash_key({list_to_binary(Client),
list_to_binary(StatName)}),
Prelist = riak_core_apl:get_apl(DocIdx, ?N, rts_stat),
SD = SD0#state{preflist=Prelist},
{next_state, execute, SD, 0}.

execute(timeout,
SD0) -> {next_state, NextState, SD}

SD0 = SD            :: term()
NextState :: atom()

prepare之后就会调用excute,excute会依据优先列表来运行对应的stat请求。

execute(timeout, SD0=#state{req_id=ReqId,
stat_name=StatName,
preflist=Prelist}) ->
rts_stat_vnode:get(Prelist, ReqId, StatName),
{next_state, waiting, SD0}.

写协调器和get协调器一样,就是多了op.

execute(timeout, SD0=#state{req_id=ReqID,
stat_name=StatName,
op=Op,
val=undefined,
preflist=Preflist}) ->
rts_stat_vnode:Op(Preflist, ReqID, StatName),
{next_state, waiting, SD0}.

waiting(Reply,
SD0) -> Result

Reply               :: {ok, ReqID}
Result :: {next_state, NextState, SD}
| {stop, normal, SD}
NextState :: atom()
SD0 = SD :: term()

以下是get的代码

waiting({ok, ReqID, Val}, SD0=#state{from=From, num_r=NumR0, replies=Replies0}) ->
NumR = NumR0 + 1,
Replies = [Val|Replies0],
SD = SD0#state{num_r=NumR,replies=Replies},
if
NumR =:= ?R ->
Reply =
case lists:any(different(Val), Replies) of
true ->
Replies;
false ->
Val
end,
From ! {ReqID, ok, Reply},
{stop, normal, SD};
true -> {next_state, waiting, SD}
end.

从代码中能够看出所谓强一致性就是等待所有应答,然后把应答结果组织后。一起返回去,没有达到应答数量会一直等待。

写协调更加easy:

waiting({ok, ReqID}, SD0=#state{from=From, num_w=NumW0}) ->
NumW = NumW0 + 1,
SD = SD0#state{num_w=NumW},
if
NumW =:= ?W ->
From ! {ReqID, ok},
{stop, normal, SD};
true -> {next_state, waiting, SD}
end.

What
About the Entry Coordinator?

Entry仅仅是解析每个日志,不是必需使用协调器。协调器一般用在存储。

Changes
to rts.erl and rts_stat_vnode

rts的模块也须要更新,主要添加fsm的代码,rts不会直接和vnode通信,交给fsm间接通信。

rts:get ----> rts_stat_vnode:get (local)

                                                          /--> stat_vnode@rts1
rts:get ----> rts_get_fsm:get ----> rts_stat_vnode:get --|---> stat_vnode@rts2
\--> stat_vnode@rts3

rts:get/2函数如今也是调用get协调器然后等待结果。

get(Client, StatName) ->
{ok, ReqID} = rts_get_fsm:get(Client, StatName),
wait_for_reqid(ReqID, ? TIMEOUT).

写请求也经过了像是的重构。

do_write(Client, StatName, Op) ->
{ok, ReqID} = rts_write_fsm:write(Client, StatName, Op),
wait_for_reqid(ReqID, ?TIMEOUT). do_write(Client, StatName, Op, Val) ->
{ok, ReqID} = rts_write_fsm:write(Client, StatName, Op, Val),
wait_for_reqid(ReqID, ? TIMEOUT).

rts_stat_vnode也进行了重构。使用riak_core_vnode_master:command/4而且携带了从參数PreflistMsgSender 和VMaster.

Preflist: 被发送命令的vnode列表

Msg: 被发送的命令.

Sender: 发送者,这里表示协调者. 主要用于vnode正确返回信息。

VMaster: VNode master的名字.
get(Preflist, ReqID, StatName) ->
riak_core_vnode_master:command(Preflist, {get, ReqID, StatName}, {fsm, undefined, self()}, ?MASTER).

Coordinators
in Action

  • Build the devrel
make
make devrel
  • Start the Cluster
for d in dev/dev*; do $d/bin/rts start; done
for d in dev/dev{2,3}; do $d/bin/rts-admin join rts1@127.0.0.1; done
  • Feed in Some Data
gunzip -c progski.access.log.gz | head -100 | ./replay --devrel progski
  • Get Some Stats
./dev/dev1/bin/rts attach
(rts1@127.0.0.1)1> rts:get("progski", "total_reqs").
{ok,97}
(rts1@127.0.0.1)2> rts:get("progski", "GET").
{ok,91}
(rts1@127.0.0.1)3> rts:get("progski", "total_sent").
{ok,445972}
(rts1@127.0.0.1)4> rts:get("progski", "HEAD").
{ok,6}
(rts1@127.0.0.1)5> rts:get("progski", "PUT").
{ok,not_found}
(rts1@127.0.0.1)6> rts:get_dbg_preflist("progski", "total_reqs").
[{730750818665451459101842416358141509827966271488,
'rts3@127.0.0.1'},
{753586781748746817198774991869333432010090217472,
'rts1@127.0.0.1'},
{776422744832042175295707567380525354192214163456,
'rts2@127.0.0.1'}]
(rts1@127.0.0.1)7> rts:get_dbg_preflist("progski", "GET").
[{274031556999544297163190906134303066185487351808,
'rts1@127.0.0.1'},
{296867520082839655260123481645494988367611297792,
'rts2@127.0.0.1'},
{319703483166135013357056057156686910549735243776,
'rts3@127.0.0.1'}]
  • Kill a Node
(rts1@127.0.0.1)8> os:getpid().
"91461"
Ctrl^D
kill -9 91461
  • Verify it's Down
$ ./dev/dev1/bin/rts ping
Node 'rts1@127.0.0.1' not responding to pings.
  • Get Stats on rts2
./dev/dev2/bin/rts attach
(rts2@127.0.0.1)1> rts:get("progski", "total_reqs").
{ok,97}
(rts2@127.0.0.1)2> rts:get("progski", "GET").
{ok,[not_found,91]}
(rts2@127.0.0.1)3> rts:get("progski", "total_sent").
{ok,445972}
(rts2@127.0.0.1)4> rts:get("progski", "HEAD").
{ok,[not_found,6]}
(rts2@127.0.0.1)5> rts:get("progski", "PUT").
{ok,not_found}
  • Let's Compare the Before and After Preflist

注意:落在rts2的gets有些返回单一的值,而有些还是和曾经一样返回列表值。主要原因是优先列表的计算包括了fallback
vnode
fallback vnode 是一个没有落在适当的物理节点的虚拟节点。由于rts1被杀死掉了,所以落在他的节点的请求必须路由到其它节点去.由于请求-应答的模型在协调器和vnode之间是异步的,因此,我们的应答值将会依赖与第一个vnode的应答事例,假设假设是第一次,你将会的到单一值,当kill
掉一个节点之后。得到的将是列表值。详细原因请看waiting函数.

(rts2@127.0.0.1)6> rts:get_dbg_preflist("progski", "total_reqs").
[{730750818665451459101842416358141509827966271488,
'rts3@127.0.0.1'},
{776422744832042175295707567380525354192214163456,
'rts2@127.0.0.1'},
{753586781748746817198774991869333432010090217472,
'rts3@127.0.0.1'}]
(rts2@127.0.0.1)7> rts:get_dbg_preflist("progski", "GET").
[{296867520082839655260123481645494988367611297792,
'rts2@127.0.0.1'},
{319703483166135013357056057156686910549735243776,
'rts3@127.0.0.1'},
{274031556999544297163190906134303066185487351808,
'rts2@127.0.0.1'}]

由于一个节点已经fallback了,所以要获取第3个的话是获取不到的,由于[rts1,
rts2, rts3] 已经变成[ rts3, rts2, rts3], 就是说rts1已经被rts2或者rts3替代了。替代后会产生心的进程。这个新的进程没有存储有数据。所以,请求的结果是not-found

(rts2@127.0.0.1)8> rts:get_dbg_preflist("progski", "total_reqs", 1).
[{730750818665451459101842416358141509827966271488,
'rts3@127.0.0.1'},
97]
(rts2@127.0.0.1)9> rts:get_dbg_preflist("progski", "total_reqs", 2).
[{776422744832042175295707567380525354192214163456,
'rts2@127.0.0.1'},
97]
(rts2@127.0.0.1)10> rts:get_dbg_preflist("progski", "total_reqs", 3).
[{753586781748746817198774991869333432010090217472,
'rts3@127.0.0.1'},
not_found]
(rts2@127.0.0.1)11> rts:get_dbg_preflist("progski", "GET", 1).
[{296867520082839655260123481645494988367611297792,
'rts2@127.0.0.1'},
91]
(rts2@127.0.0.1)12> rts:get_dbg_preflist("progski", "GET", 2).
[{319703483166135013357056057156686910549735243776,
'rts3@127.0.0.1'},
91]
(rts2@127.0.0.1)13> rts:get_dbg_preflist("progski", "GET", 3).
[{274031556999544297163190906134303066185487351808,
'rts2@127.0.0.1'},
not_found]

** 注意 ** :fallbacks是在列表的最后一个。

Riak Core Guide 2的更多相关文章

  1. Linux/centos下安装riak

    必备的组件: gccgcc-c++glibc-develmakepam-devel 使用yum安装相关组件 sudo yum install gcc gcc-c++ glibc-devel make ...

  2. 学习笔记之.NET Core

    source code https://github.com/haotang923/dotnet/tree/master/src .NET Documentation | Microsoft Docs ...

  3. What's the difference between SDK and Runtime in .NET Core?

    What's the difference between SDK and Runtime in .NET Core? Answer1 According to the .Net Core Guide ...

  4. .NET Core CSharp初级篇 1-8泛型、逆变与协变

    .NET Core CSharp初级篇 1-8 本节内容为泛型 为什么需要泛型 泛型是一个非常有趣的东西,他的出现对于减少代码复用率有了很大的帮助.比如说遇到两个模块的功能非常相似,只是一个是处理in ...

  5. .NET Core CSharp 中级篇 2-2 List,ArrayList和Dictionary

    .NET Core CSharp 中级篇 2-2 本节内容为List,ArrayList,和Dictionary 简介 在此前的文章中我们学习了数组的使用,但是数组有一个很大的问题就是存储空间不足,我 ...

  6. .NET Core Ecosystem

    .NET .NET Blog Application Models Web Mobile Desktop Microservices Gaming Machine Learning Cloud Int ...

  7. .NET & C# & ASP.NET

    .NET && C# && ASP.NET https://docs.microsoft.com/zh-cn/dotnet/ .NET Documentation We ...

  8. .NET C#教程初级篇 1-1 基本数据类型及其存储方式

    .NET C# 教程初级篇 1-1 基本数据类型及其存储方式 全文目录 (博客园).NET Core Guide (Github).NET Core Guide 本节内容是对于C#基础类型的存储方式以 ...

  9. .NET Core installation guide

      .NET Core installation guide 1.Download Visual Studio 2015 Make sure you have Visual Studio 2015 U ...

随机推荐

  1. Python入门--18--异常与try,except语句

    Python标准异常总结 AssertionError 断言语句(assert)失败 AttributeError 尝试访问未知的对象属性 EOFError 用户输入文件末尾标志EOF(Ctrl+d) ...

  2. AC日记——[ZJOI2009]假期的宿舍 cogs 1333

    1333. [ZJOI2009] 假期的宿舍 ★★☆   输入文件:zjoi09holiday.in   输出文件:zjoi09holiday.out   简单对比时间限制:1 s   内存限制:25 ...

  3. 做IT这几年,我整理了这些干货想要送给你!

    没有一条路是容易的,特别是转行计算机这条路. 松哥接触过很多转行做开发的小伙伴,我了解到很多转行人的不容易,记得松哥大二时刚刚决定转行计算机,完全不知道这些东西到底应该怎么学,每天就是抱着书啃,书倒是 ...

  4. liteos事件(六)

    1. 概述 1.1 基本概念 事件是一种实现任务间通信的机制,可用于实现任务间的同步,但事件通信只能是事件类型的通信,无数据传输.一个任务可以等待多个事件的发生:可以是任意一个事件发生时唤醒任务进行事 ...

  5. k8s之nginx-ingress、 Daemonset实现生产案例

    上一篇中用node ip + 非80端口,访问k8s集群内部的服务.实际生产中更希望用node ip + 80端口的方式,访问k8s集群内的服务. # 修改mandatory.yaml中创建控制器部分 ...

  6. Spring Cloud Eureka 自我保护机制实战分析

    前些天栈长在Java技术栈微信公众号分享过 Spring Cloud Eureka 的系列文章: Spring Cloud Eureka 自我保护机制 Spring Cloud Eureka 常用配置 ...

  7. 分享tiny4412,emmc烧录u-boot, 支持fastboot模式烧写emmc

    转载 : http://www.arm9home.net/read.php?tid-83474.html 本人是第一次在此发帖,希望大家多多支持,发帖目的是为了分享,分享的目的是传递开源的精神.Tin ...

  8. Free Pascal 的安装

    Free Pascal 的安装 https://www.cnblogs.com/cnssc/p/6110492.html https://wenku.baidu.com/view/ee80cc8eed ...

  9. Exchange 2013 Database Move to New Partition

    建议不要删除默认数据库,可以通过修改默认数据库名称.路径等实现您的需求. 客戶:The HK Anti-Cancer Society. 要求:遷移數據庫(01)到新分區,實際是遷移成為數據庫(05)  ...

  10. DIV浮动IE文本产生3象素的bug

    描写叙述:DIV浮动IE文本产生3象素的bug    左边对象浮动.右边採用外补丁的左边距来定位,右边对象(div)会离左边有3px的间距 复现:在开发者工具里把文本模式设置了杂项后会出现3像素的bu ...