嗨,我还在,但是比较忙。

啊,好久不更新blog,可不是我的作风阿。呵呵,因为最近比较忙。

最近主要是在忙一个erlang相关的slide,一边准备资料,一边写代码,竭尽全力的希望作好一点,尽管时间和能力有限。

我已经该掉了一个臭毛病,那就是以前中午看新闻,现在我中午已经不去任何网站,看任何新闻了,那些空无飘渺,歌功颂德,离奇荒诞,一不留神就要搞的我心情不好,呵呵,我的精力可不想贡献给那些窃喜的编辑。中午有时间,跟朋友聊几句,看看mail,学点流行的技术,更有意义。

前几天看到十七世大宝法王的介绍,引用一句专业点的词汇,法相庄严!呵呵,肃然起敬阿,精神偶像,顶礼膜拜。相关的音乐也非常好听哦,比如修心,坚心等。

呵呵。很高兴,每天能知道新的知识。

继续写代码了。回头说朋友们。

转贴-memcached完全剖析

最近在准备一个slide,其中和memcached有些相关性。

在网上找到这个memcached剖析,原文是日文,被国内的同志翻译了,感谢!

整理了一下做成pdf,留个备份。各位喜欢的朋友可以下载查看。感谢译者

包括四部分:

  • memcached完全剖析1- memcached的基础
  • Memcached完全剖析2-Slab Allocation
  • memcached全面剖析3-memcached的删除机制和发展方向
  • memcached全面剖析4. memcached的分布式算法

地址:

slideshare

本地

local pid and remote pid

pid 既可以代表本地的process,也可以代表其他节点即远程 process,因此pid ! Msg,既可以向本地节点又可以向其他节点发送消息。

如何来区分local pid和remote pid呢,让我们先看看pid具体包含哪些信息。
打印pid变量的时候,其为X.Y.Z格式的数据,其中X代表的便是本地节点或者其他node(其可以为一个索引,指向某个已知节点),Y为4字节unsigned integer代表的是process id(序列号),Z为1个字节,用来标识节点的新旧(如果节点发生了crash,那么这个数值就会发生变化,通常为0)。
具体的pid数据格式请参考:http://www.erlang.org/doc/apps/erts/erl_ext_dist.html#8.8

在分布式开发中,如果节点之间交互,涉及到pid,那么erlang会自动为我们进行local pid到remote pid的转化。具体请参看下例:
为了方便我们的观察,研究,我对kernel中的rpc模块作了一点小改动,添加了一些调试信息:
rpc.erl:
handle_call/3函数 Line 99添加:

io:format("rpc handle_call:[M:~p F:~p A:~p] From:~p~n”, [Mod, Fun, Args, To]),

handle_info/2函数 Line 148添加:

io:format("rpc reply To: ~p Msg:~p~n", [To, Reply]),

do_call/3函数 Line 232

io:format("rpc docall: Node:~p self:~p~n", [Node, self()]),

添加完这三条打印信息,随后编译rpc,将rpc.beam放置在kernerl/ebin目录下。(修改前,相信你会把rpc.erl和rpc.beam都备份一下的)。

随后我们启动两个erlang shell:

Node A: erl -sname a
Node B: erl -sname b

在Node b中,我们register一个名叫proc_on_b的process:

(b@desktop)4> register(proc_on_b, spawn(fun() -> receive Msg -> Msg end end)).

在Node a中:

(a@desktop)7> rpc:call('b@desktop', erlang, whereis, [proc_on_b]).

查看屏幕的输出:
Node a:

(a@desktop)7> rpc docall: Node:'b@desktop' self:<0.35.0>
<5094.53.0>

Node b:

(b@desktop)5> rpc handle_call:[M:erlang F:whereis A:[proc_on_b]] From:{<6253.35.0>, #Ref<6253.0.0.79>}
(b@desktop)5> rpc reply To: {<6253.35.0>,#Ref<6253.0.0.79>} Msg:<0.53.0>

仔细的查看屏幕输出,在a中,显示的rpc调用process的pid为<0.35.0>,而在b中rpc module收到的From的pid为<6253.35.0>,这里erlang已经把a中的local pid转化成为b所使用的remote pid。这样,b节点可以方便的发送信息。同样a中收到的pid信息为<5094.53.0>,也是对b所返回的信息Msg:<0.53.0>进行了转化。

是不是所有Node之间交互的pid都会进行转化呢?
我们再做下一个试验:
我们写一个模块,让其通过c:pid/3生成一个pid,然后返回给某个节点,看这个节点是否将这个pid进行了转化。
在b的某个模块(pid_test)中添加下面的函数:

get_pid() ->
c:pid(0, 2222, 1).

随后a中调用:

(a@desktop)10> rpc:call('b@desktop', pid_test, get_pid, []).
rpc docall: Node:’b@desktop’ self:<0.35.0>
<5094.2222.1>

同样,b中的pid:<0.2222.1> 贝进行了转化。

对于global module,如果我们中间传递了local pid,在另一方显示的是remote pid。

Erlang不能错过的盛宴

昨天写了一个简单的文档,为想学习Erlang的朋友,提供一点点的资料。

书写仓促,纰漏难免。望各位朋友不吝指正。

下载地址:

下载地址1

下载地址2

Erlang China 大会 - CN Erlounge III - 发起

详细信息参阅: http://ecug.org/

欢迎大家四处传播此消息。 :)

————————-

:)正在传播!

erlang项目开发之-dialyzer和eunit

这个东西如果写,可能会写很长很长的篇幅,但是有悖erlang的精神,
我就挑选最精要的说,也是自己这几天学习到的一些东西。

一个项目,首先设计完成后,日夜赶工,写成了代码。这个时候,我们就要检验代码是否真实的表达了我们的意图。

dialyzer和eunit就派上用场了!
dialyzer偏向于static“静态”的代码分析,其主要完成以下工作:
1,可能出现的拼写错误
2,没有使用的代码和函数
3,无用的测试代码
如果想让dialyzer更高效出色的工作,就需要代码中的type(类型)和spec(函数)进行配合。
我们知道erlang是动态的语言,本身没有很多静态的信息,我们就是通过这些-type和-spec来规范代码的静态性。
具体的type和spec的说明,请参考:http://www.erlang.org/eeps/eep-0008.html
说明几点:type通常我们定义在一个hrl文件中,需要这个type信息的erl文件,只要include此头文件既可。每个module的所有的export函数都应该有spec信息.

验证了静态信息,我们还需要通过eunit测试我们的逻辑是否正确.当然这里只是单元测试.每次我们修改完代码以后,我们都可以重新进行单元测试,验证我们的模块,大大节省时间.
eunit文档:
http://svn.process-one.net/contribs/trunk/eunit/doc/overview-summary.html

dialyzer和eunit进行完以后,那么就集成测试了.最后期待能制作一个坚固的产品!

erlkad-erlang kademlia dht library

http://code.google.com/p/erlkad/

闲暇无事,就用erlang写了一个kad libarary。

放到google code上,最近刚做完单元测试,还需要进一步的测试。

还有一点就是没有做好release,回头准备用erlware的系列工具来生成安装包。

回头会写一些开发过程中的心得。 :)

Amazon Kindle?我也好期待!

socklabs收到他的生日礼物非常兴奋!哇,什么东西呢?

Amazon Kindle!手持的电子阅读器。只有400页书的重量,电池可以持续一周,阳光下也能看清字体,通过手机网络连接,内置SD卡。。可惜就是国内网络不支持。

喔。。我什么时候也能有这么一个东西呢。。我也曾经好期待一个便携式的电子图书阅读器阿。

erlang lru cache module

1个月前写ecrawler的时候,需要一个lru cache算法。
erlang中好像没有特别合适的选择,就自己实现了一个。
思想:
采用gb_trees保存key-value pair,可以实现快速的key lookup,但是当发生替换时,
需要根据访问时间清理item,我们可能需要gb_trees中的数据导入到list中,随后进行排序,然后调换掉比较旧的内容。为了改善性能,我将数据在list中做一个拷贝,但是注意不包含value,只是key和一些状态信息。
当发生替换时,我们可以直接对list进行排序(也可以在每次操作的时候,将item插入到list中的合适位置,保持list有序),随后删除最旧的元素。

采用此方法比gb_trees:to_list方法效率稍高。当然没有做太多的性能测试,比如gb_trees用dict, ets替换等,我们的原则还是,先让代码正确,随后再让代码高效。

接口:
new/1, get/2, insert/3, insert/4, size/1, max/1, hitrate/1, to_list/1

废话不说了,看代码吧,写的不足之处请见谅。

下载代码

erlang gen_server牛角尖问答

我们以Module代表gen_server的callback模块
1, 实现gen_server behaviour的模块会产生一个新的process么?
毫无疑问,太会了!通过调用proc_lib:start_link/5创建process

2, gen_server:start_link/3,4中的Options参数有什么用处?
Options中只有这几个选项:{debug,Dbgs} | {timeout,Time} | {spawn_opt,SOpts},
debug是用来和sys模块相关联的;咱们通过proc_lib:start_link/5创建process的时候,如果初始化等待的时间超过了Time,那么我们的gen_server:start_link会返回{error, timeout}的错误;spawn_opt是给spawn_opt传递的参数。

3, Module:init/1的那些返回值都什么意思?
{ok, State},就不用说了,一切正常,State将为gen_server的State;
{ok, State, Timeout},也很正常,只是如果process在Timeout(>=0,ms)时间内没有收到任何消息,那么将产生一个timeout消息,这个消息要在handle_info中处理哦。
{ok, State, hibernate}, 还是正常,只是我们在process启动后,就让它先”睡眠“,因为我们知道最近一段时间内,我们还不用这个process,此process睡眠的好处就是可以最大限度的减少其内存占用,当有消息到达时,process就会”惊醒“,重新工作。
{stop, Reason},oops,出错了,process将会调用exit(Reason)退出。如果init没有预期,我们就退出吧。
ingore,既然要求忽略了,什么都不做,退出吧。

4, gen_server:multi_call/2,3,4这几个哥们是干嘛用的?
multi就是多的意思嘛,mulit_call肯定就是进行多个调用请求哦。
先说他们的关系:
gen_server:multi_call(Name, Request)
= gen_server:multi_call([node() | nodes()], Name, Request)
= gen_server:multi_call([node() | nodes()], Name, Request, infinity)
称他们为兄弟,我看称他们为三胞胎还不错。
想多个node请求本地名为Name的gen_server behaviour, 然后收集结果,返回。
其返回值格式为:{Replies,BadNodes},其中Replies为[{Node, Reply}],BadNodes,不用说就是那些没有正常返回应答的节点了。
mulit_call/4最后一个Time参数指定的是每个Reply等待的超时时间。

5, gen_server:reply/2有嘛用处?
如果在Module:handle_call/3中,我们不能返回结果,我们可以保存handle_call中的From参数,在结果生成的时候,返回给Caller。

6, Module:handle_call/3 返回{noreply,NewState}会怎样?
怎样?明明是handle_call处理的是同步的请求,处理完了就应该告诉Caller,是死是活,是成功是失败,可是你noreply,
Caller只能傻傻的等待了。等多久?默认的是5秒,gen_server:call/3就调用exit({timeout, Description}),退出了。。

7, 我用gen_server竟然死锁了?
天呀,在erlang中怎么遇到锁了?肯定是你使用不当!


name() ->
	gen_server:call(?SERVER, name).
address() ->
	gen_server:call(?SERVER, address).

all_info() ->
	gen_server:call(?SERVER, all_info).

.....
handle_call(all_info, _From, State) ->
	Name = name(),
	Address = address(),
	{reply, {Name, Address}, State};

这么写,活该你死锁。
handle_call是同步调用,你在内部又调用name(), address(),能不锁么?
要求就是:不要在handle_call/3内部调用另一个gen_server:call相关的函数。

时间关系,gen_server先钻到这里。