畢業相~~

2009-04-30


昨天拍了畢業照,感覺....好复雜~~覺得自己已經唔再系學校既一份子,本應系一個喜慶的日子,我卻總覺得自己....似乎未准備好~~Anyway, tomorrow will be a better day, I guess.

Reload Erlang Module

2009-04-27
有时模块代码更新并编译后,需要重新载入才能生效,此时可以在erlang shell里输入如下:
> l(my_module).
{module, my_module)

输入help().还可以查看更多的erlang shell命令~~

Embed Prezi into your website

2009-04-26
Prezi系一個很有特色的在線動態簡報服務,詳細簡介見這里~~
本文講解如何將Prezi嵌入到你的個人blog或其他网站里,首先打開你的Prezi主頁,點擊要嵌入的簡報,選擇Share,然后將右下角的link填入如下代碼的src里,然后在你的网頁上插入下面的代碼就OK了~~
<iframe height="360" src="http://prezi.com/6272/view" width="480"></iframe>



嵌入效果如下:


PS:很喜歡Prezi,可惜啊惡心的网路經常出問題,很多有創意的WebApp就是卡死了在网速上~~

在Erlang啟動時執行一系列操作

可以將任意的Erlang代碼放入HOME目錄里的.erlang文件中,當Erlang啟動時,它就會自動執行該文件里的命令,例如:
io:format("Erlang is running ....\n").
code:add_patha("."),
code:add_pathz("/home/m/bin").

啟動erlang時,可以看到如下輸出:
$ erl
Erlang (BEAM) emulator version 5.6.5 [source] [smp:2] [async-threads:0] [hipe] [kernel-poll:false]

Erlang is running ....
Eshell V5.6.5 (abort with ^G)
1>


如果當前目錄下也有一個.erlang文件,則會優先于HOME目錄下的那個,這樣就可以在不同啟動位置定制不同的行為:

如果你不知道系統的HOME目錄的位置,可以看看Erlang認為的HOME目錄在哪里:
1> init:get_argument(home).
{ok,[["/home/m"]]}

設置Erlang裝載代碼的路徑

code:get_path() 查看當前的裝載路徑:
code:get_path().
["ebin",".","/usr/local/lib/erlang/lib/kernel-2.12.5/ebin",
"/usr/local/lib/erlang/lib/stdlib-1.15.5/ebin",
"/usr/local/lib/erlang/lib/xmerl-1.1.10/ebin",
"/usr/local/lib/erlang/lib/webtool-0.8.3.2/ebin",
"/usr/local/lib/erlang/lib/typer-0.1.5/ebin",
"/usr/local/lib/erlang/lib/tv-2.1.4.2/ebin",
"/usr/local/lib/erlang/lib/tools-2.6.2/ebin",
"/usr/local/lib/erlang/lib/toolbar-1.3.0.1/ebin",
"/usr/local/lib/erlang/lib/test_server-3.2.4/ebin",
"/usr/local/lib/erlang/lib/syntax_tools-1.5.6/ebin",
"/usr/local/lib/erlang/lib/ssl-3.10/ebin",
"/usr/local/lib/erlang/lib/ssh-1.0.2/ebin",
[...]|...]


code:add_patha(Dir) -> true | {error, bad_directory}
添加新目錄Dir到裝載路徑列表的開頭

code:add_pathz(Dir) -> true | {error, bad_directory}
添加新目錄Dir到裝載路徑列表的尾部

如果怀疑裝載了錯誤的模塊,可以調用函數code:all_loaded()查看所有己加載的模塊列表,或者調用code:clash()

也可以在啟動Erlang的時候指定代碼載入路徑:
> erl -pa Dir1 -pa Dir2 ... -pz DirK1 -pz DirK2

其中-pa將目錄添加到搜索路徑的開頭,而-pz則把目錄加到路徑的末尾~~

Erlang的Makefile~~

Erlang有一個類似makefile的Emakefile文件,用于編譯項目,當我們在Erlang虛擬机里執行make:all()或輸入shell命令erl -make時,Erlang會搜索當前目錄下的Emakefile文件,并按其設置編譯一個或多個模塊。Emakefile內容格式如下:
{Modules, Options}

Modules即需要編譯的模塊(atom),可以是模塊的文件名,如:mymod;
也可以是其他目錄中的模塊名,如:/somefolder/mymod;
也可以使用通配符來指定一組模塊名,如:my*;
還可以是上述種種的列表,如:['mymod', '/somefolder/mymod', 'my*'];
Options是編譯參數,詳細可參見compile模塊,下面几個比較常用:

debug_info
在輸出的beam文件里包含debug調試信息,諸如Debugger、Xref、Cover的工具需要使用這些信息,BUT可以從這些調試信息里重构出源代碼,用debuf_info_key選項可以加密debug信息以防止泄露源代碼;

{i, "/path/to/include"}
導入文件所在的目錄;

{outdir, "ebin"}
指定編譯輸出路徑。

[转]Erlang开发建议(杂记版)

2009-04-25

由 Oscar Tang 透過 Google 閱讀器傳送給您:

經由 Erlang Display - erlang:display(All_About_Erlang) litaocheng 著 (日期為 2009/4/24)
以下是在erlang项目开发中的一些记录,即包含很多通俗易懂的原则,也包含一些似是而非的建议,比较混乱,还没有积累到一个可以分门别类的地步,各位就将就看吧..
:)

* 确保没有任何编译警告

* Erlang中String采用list实现,32位系统中,其1个字符用8个字节的空间(4个保存value, 4个保存指针)。因此string速度较慢,空间占用较大

* 在Server中,总是尽力书写尾递归(tail-recursive)的函数

* 使用'++'时,left list会被拷贝,然后添加到right list的头部,因此最好把length较短的list放在左侧

* 避免使用regexp,如果需要正则表达式,请使用re

* timer模块的大部分函数实现,依赖于一个process,如果过多使用timer,会导致这个process负载过大,影响效率。推荐使用erlang:send_after/3及erlang:start_timer/3

* 避免使用list_to_atom/1,因为erlang中atom数量最大为1048576, 且不进行GC控制。因此如果持续性的调用list_to_atom/1,可能很容易达到系统上限,从而导致emulator terminate。请使用list_to_existing_atom/1。

* list内部实现为一个列表,因此length(List), 需要遍历整个list比较耗时

* 对于不同的数据类型,使用不同的size函数:tuple_size/1, byte_size/1, bit_size/1

* 使用binary match来进行binary的分割,而不使用split_binary/2

* 如果两个list都拥有很多数据,那么请不要使用'--',而是将数据转化到ordsets,然后调用ordsets:substract/2.

* 对于binary相关操作可以进行binary优化(bin_opt_info编译选项)代码框架:

* f(<<Pattern1,...,Rest/bits>>,...) ->
... % Rest is not used here
f(Rest,...);
f(<<Pattern2,...,Rest/bits>>,...) ->
... % Rest is not used here
f(Rest,...);
...
f(<<>>, ...) ->
ReturnValue.

* 调用lists:flatten/1可以将list扁平化,这个操作代价很大,比'++'还要昂贵。下面这些时候我们可以避免:
将数据发送给port时
调用list_bo_binary/1和iolist_to_binary前

* 小的函数可以让您方便的找出错误的函数和代码

* 不要在同一行出现相同的符号
20 some_fun() ->
21 L = [{key1, v1}, {key2, [some_record#v21, v22]}],
22 ...
编译时,会提示line 21 '[' 语法错误, 因为21行有多个 '[' ,所以这个bug不能准确定位,你需要花时间去排查代码。
好的做法是:
20 some_fun() ->
21 L = [{key1, v1},
22 {key2, [some_record#v21, v22]}
23 ],
...
这样,编译其会提示你 line 22 '[' 语法错误,你很开就知道是那个地方错了。

* 使用 CTRL + \ 或 init:stop(), 可以退出Erlang, 使用CTRL + G 及 CTRL + C 弹出菜单选项,可以选择是否退出Erlang。
其中CTRL + G可以用来连接其他的shell, CTRL + C可以查看其他一些系统信息
Ctrl + C abort 是野蛮的退出方式

* use "open_port({fd,0,2}, [out])" make erlang program write standard error to unix system

* If you don't run experiments before you start designing a new system, your entire system will be an experiment!

* standard data structure desc:

Module Description
sets sets, i.e. a collection of unique elements.
gb_sets sets, but based on a general balanced data structure
gb_tree a general balanced tree
dict maps, also called associative arrays
ets hash tables and ordered sets (trees)
dets on-disk hash tables

Suggestion:
elments count: 0 - 100 | 100 - 10000 | 10000 -
our select : list | ets | gb_tree

* 通过code:clash/0 检测代码中是否有module冲突现象(sticky)

* epmd -d -d 启动 epmd 可以查看erlang node之间的通讯

* 将正常的逻辑代码和错误处理代码分离,发生错误时,尽管错误。由另一个错误处理模块进行处理

* 类似于操作系统,我们的程序也可以分为kernel 和 user 两层, 对于kernel绝对不能出现错误, 对于user可以出现错误,进行恢复

* process顶层loop涉及的代码及函数,最好在一个module中实现

* process 的register name和module名称一致, 便于寻找代码

* 每个process具有一个单一的角色,比如:supervisor 用来进行错误恢复, work 工作者,可以出现错误, trusted worker 不会出现错误

* 通过函数调用可以实现的功能,就不要使用sever实现(如gen_server, 及类似的loop 实现)

* 给消息加一个tag,在发生错误的时候,可以定位到消息,同时也有利于程序的稳健

* 在消息循环中,对于unknown的消息,请调用lib:flush_receive/0 将其清除,减轻process msg queue的长度

* server中总是书写尾递归的循环

* 尽量使用record, 而不是原始的tuple来表现数据结构, 在使用record时,使用select match:
#person{name = Name, age = Age} = Person

* 对于返回值,最好也添加一个tag,用来说明返回值类型,或者执行成功与否

* 尽可能少的使用catch和try,在erlang程序中,不推荐主动捕获异常。只有当我们的逻辑特别复杂,我们可以使用throw来返回数据,使用catch来获取返回值。

* 当然程序与外界交互,外界数据不可靠时,需要使用catch和try

* 慎重使用process dictory, 当你使用get/1, put/1时,你的应用会具有很大的slide effect。可以通过加入一个新的参数来保存原本需要存储到process dictory中数据

* 如果不想使自己糊涂,请不要使用import

* 使用export时,将功能类似的接口组合在一起,并添加合理的注视,这样你的接口更清晰,别人使用起来更方便

* 不要书写嵌套太深的代码

* 不要书写太长的module

* 不要书写太长的函数

* 每行代码不能太长

* 避免使用 "_" 匿名变量,请为每个变量选择有意义的名称,如够某个变量暂时不使用,请以下划线 "_" 开始

* {error, enfile} enfile error in socket 是以为内linux系统中 ulimit 限制, 在root下修改:ulimit -n 25000

* {error, enotconn} 表示socket已经关闭

* 在erlang开发时,慎重使用macro,因为erlang的single assign的缘故,同时调用某个marco,而macro又定义了某个变量,可能导致badmatch错误。
比如:
-define(ADDLINEINFO1(F),
(
begin
Str1 = lists:concat(["[Mod:", ?MODULE, " Line:", ?LINE, "]"]),
Str1 ++ F
end
)).
-define(WARN(Log, F, D), log4erl:warn(Log, ?ADDLINEINFO(F), D)).
如果连续使用 WARN, 会出现此错误

* erlang中可以定义很多环境变量:
ERL_MAX_ETS_TABLES 设置最大的ets数目 默认1400
ERL_MAX_PORTS erlang最大的port数目 默认1024

* .app文件中的start_phases, 选项既可以用来作为include applications之间的同步启动,也可以用来对单个application进行分布启动。
顺序如下
包含included app:

application:start(prim_app)
=> prim_app_cb:start(normal, [])
=> prim_app_cb:start_phase(init, normal, [])
=> prim_app_cb:start_phase(go, normal, [])
=> incl_app_cb:start_phase(go, normal, [])
ok

无included app:
application:start(prim_app)
=> prim_app_cb:start(normal, [])
=> prim_app_cb:start_phase(init, normal, [])
=> prim_app_cb:start_phase(go, normal, [])
ok

* 任何时候,都要重视函数的返回值,通过match确保您的预期,如果发生错误,那么就大胆的表达出来。


您可以在這裡操作的事項:

Embedding Resource with AS 3.0

2009-04-23
哇,用了ActionScript那么久,现在才发现原来Embed那么有用~~可以直接嵌入外部图片,或是外部SWF内的元件,直接用嵌入的资源作为类来创建实例,例如:
[Embed(source="....jpg")]
private var MyImage:Class;
....
var img:Bitmap = new MyImage();

[Embed(source="....swf", symbol="SomeSymbolId")]
private var MySymbol:Class;
....
var sym:Sprite = new MySymbol()

What's my purpose ?

2009-04-22
越來越近畢業,覺得每一天都有那麼多想做的事情,那麼多想學的東東,卻只剩下這麼少的時間~~最近一直听某人說A公司薪水多高B部門福利多好C城市生活多悠閒....感覺像是讀完大學就能過無憂的生活,不同的家庭環境養出來的人想法果然不同,難道讀20年書,學一身技術,就只為了高薪厚祿?為什么我們要重复又重复他人走過的路?

考到間不錯的大學,找到份体面的工作,朋友都覺得我前程無憂,家人都對我寄予厚望,但我卻感受不到畢業生應有的興奮,反而系有點后悔,后悔大學這四年學得太少太少了....

What's my purpose ? Where should I go ?

Prezi 在线动态简报



Prezi系一個可縮放旋轉的在線動態簡報,比傳統的PowerPoint效果好好多,由于系基于FLASH的,所有文本和圖案內容都可以無限量伸縮,過渡效果十分有動感,可以將內容更好的關聯和展示~~可惜Prezi貌似比較多限制,雖然簡報可以下載,但免費用戶卻只能下載3次....分享協作功能也不如Google Docs~~不過總体來說還是創意十足的,有點衝動山寨一個~~

Firefox翻墙记之Youtube篇~~

2009-04-17
Youtube貌似被封好久了,十分郁悶,无奈只好翻墙了,Firefox有一个插件Gladder可以自动用代理访问被GFW墙了的网站,不过进得了Youtube却还是看不到视频,欺人太甚了!不能在线播,我下载来看!一搜发现一个Youtube视频下载服务KeepHD,哈哈下载速度还不错~~



这个主仆颠倒的国家,什么时候连我们判断是非利害的权力都让给那些狗奴才了?凭什么你们说封就封说禁就禁?
This country needs a little change.

How to use Erlang SSL Socket

2009-04-15
首先要用OpenSSL生成私鑰和證書:
openssl genrsa -des3 -out privkey.pem 2048
生成一個長2048 bit的私鑰,并用密碼以DES算法加密該私鑰,最后輸出為名為privkey.pem的文件。
如果加密了該私鑰,則每次使用的時候都需要提供密碼,如果不需要密碼保謢,去掉-des3參數就可以了~~

openssl req -new -x509 -key privkey.pem -out cert.pem -days 1020
生成一個自簽的測試用證書,輸出為文件cert.pem,有效期為1020天。

然后用Erlang的SSL模塊實現一個ssl socket server和client:

-module(test).
-export([start/0, connect/0]).


%啟動SSL服務
start() ->
application:start(ssl),
ssl:seed("seed-every-time"),
{ok, spawn(fun() -> start_server() end)}.


%運行SSL SOCKET SERVER
start_server() ->

%創建socket server
{ok, ListenSocket} = ssl:listen(51020, [
binary, {active, true},
{keyfile, "privkey.pem"},
{certfile, "cert.pem"}]),
io:format("SSL socket server is ready\n", []),

%接受客戶端連接
{ok, Sock} = ssl:transport_accept(ListenSocket),
ok = ssl:ssl_accept(Sock),
io:format("New ssl client is connected\n", []),
loop_client(Sock).


%處理來自客戶端的信息
loop_client(Sock) ->
receive
{ssl, Sock, Data} ->
io:format("Receive : ~p\n", [Data]),
ssl:send(Sock, Data),
loop_client(Sock);
{ssl_closed, Sock} -> io:format("Client ~p is discionnected\n", [Sock]);
{ssl_errlr, Sock, Reason} -> io:format("ERROR : ~p\n", [Reason])
end.


%客戶端進行連接
connect() ->
{ok, Sock} = ssl:connect("localhost", 51020, [bianry]),
io:format("connected\n", []),
ssl:send(Sock, "this is just a test"),
receive
Any ->
io:format("client receive : ~p\n", [Any])
end,
ssl:close(Sock),
io:format("client closed\n", []).

測試一下能否正常運行:
1> c(test).
{ok,test}

2> test:start().
{ok,<0.46.0>}
SSL server socket is ready

3> test:connect().
New ssl client connected
connected
Receive : <<"this is just a test">>
client receive : {ssl,{sslsocket,6,<0.50.0>},<<"this is just a test">>}
client closed
ok
Client {sslsocket,7,<0.49.0>} is disconnected

哈哈,一切正常~~

Erlang Term Comparisons

2009-04-13
Erlang的比较运算符如下:
== 等于
/= 不等于
=< 小于或等于
< 小于
>= 大于或等于
> 大于
=:= 完全等于
=/= 完全不等于

Erlang list item searching

2009-04-12
使用lists:keysearch函数可以方便的对一个[{key, value}, ....]列表里的指定key进行搜索,函数定义如下:
keysearch(Key, N, TupleList) -> {value, Tuple} | false
Key = term()
N = 1..tuple_size(Tuple)
TupleList = [Tuple]
Tuple = tuple()

比如说我们有这样的一个列表:
> Attributes = [{to, "jojo@verse.com"},
{from, "oscar@verse.com/school"},
{type, "chat"},
{'xml:lang', "en"}].

> lists:keysearch(from, 1, Attributes).
{value, {from, "oscar@verse.com/school"}}

若指定Key不存在则返回false
> lists:keysearch(not_exist, 2, Attributes).
false

-____- |||

2009-04-11
又一次要用这无语的表情做标题,楼下那恶心的工行ATM机,逢一三五故障二四六吞卡周末必坏.... 西区上万人就放两台ATM,让大家经常大排长龙,远在教学区边缘鸟都不飞去的野地里就放一堆,还有人工服务台,实在是让人郁闷~~

最近~~

自从找到工作之后,体重一直直线上升,上年年底面试时买的西裤,现在穿上已经有点勉强了....学习越忙工作越多越是觉得空虚,可能系因为离开家、离开你,已经太长时间了~~难得周末夜,喝口咖啡,放松一下吧~~

Mnesia 事件处理

Mnesia会产生两类事件:系统事件(system events)和表事件(table events),用户可创建一个进程来订阅Mnesia所产生的事件,有如下两个相关的函数:

mnesia:subscribe(EventCategory)
当有事件发生时发送一个事件的copy给调用此函数的进程。

mnesia:unsubscribe(EventCategory)
取消事件订阅,不再接收Mnesia产生的事件。

EventCategory可以是system、{table, Tab, simple}或{table, Tab, detailed}。事件会以进程间消息的形式发送给订阅了Mnesia事件的进程,系统事件的格式为{mnesia_system_event, Event},表事件的格式则为{mnesia_table_event, Event}。

System Events
系统事件有如下几种:

{mnesia_up, Node}
Mnesia在节点Node上被启动;

{mnesia_down, Node}
Mnesia在节点Node上停止了运行;

{mnesia_checkpoint_activated, Checkpoint}
一个与本节点相关的checkpoint被激活了;

{mnesia_checkpoint_deactivated, Checkpoint}
一个与本节点相关的checkpoint被激死(:P)了~~

{mnesia_overload, Details}
本节点上的Mnesia负载过重,可能因为对硬碟的读写请求超过了Mnesia的处理能力,如果不做任何处理可能会导致硬碟空间不足;也可能是Mnesia处理事务的速度跟不上事务的请求速度,事务堆栈可能会越来越大,直至耗尽内存或请求速度减缓。对存在异地备份或位于远程节点上的表发起大量的脏操作(dirty updates)时也可能会引发此事件;

{inconsistent|_database, Context, Node}
Mnesia发现数据库可能存在不一致的情况;

{mnesia_fatal, Format, Args, BinaryCore}
Mnesia发生了致命错误,即将在短时间内终止;

{mnesia_info, Format, Args}
调试系统时,Mnesia发现了一些应用程序可能感兴趣的东东;

{mnesia_error, Format, Args}
Mnesia发生了错误;

{mnesia_user, Event}
某应用程序触发了函数mnesia:report_event(Event),可用于调试Mnesia。

Table Events
表事件在数据表被更新时发生,分为simple和detailed两种,simple事件包括:

{write, NewRecord, ActivityId}
有新记录被写入;

{delete_object, OldRecord, ActivityId}
记录OldRecord可能被删除了(可能?),如果表类型为bag,则可能还存在其他与OldRecord主键相同的记录;

{delete, {Tab, Key}, ActivityId}
一个或多个记录可能被删除了(可能??),表里所有主键为Key的记录都已被删除;

detailed事件包括:

{write, Table, NewRecord, [OldRecords], ActivityId}
有新记录被写入;

{delete, Table, What, [OldRecords], ActivityId}
可能有记录被删除(又是可能....),What可能是{Table, Key}或{RecordName, Key, ....}

用qlc进行数据排序与筛选

Erlang有一个类似SQL的数据检索机制,叫做QLC(Query Interface to Mnesia, ETS, Dets, etc),可用于对Mnesia、ETS/DETS的数据表进行数据检索,包括常见的按指定域排序(ORDER BY)、按条件筛选(WHERE...)以及限制返回的结果长度(COUNT)。看个简单的例子:
-module(test).
-export([start/0]).
-include_lib("stdlib/include/qlc.hrl").

start() ->
QH1 = qlc:q([{X, Y} || X <- [8,3,6,2], Y <- [b,a,c]]),
%以第一个值作为key进行排序
QH2 = qlc:keysort(1, QH1, [{order, ascending}]),
QC = qlc:cursor(QH2),
%获取前两条记录
qlc:next_answers(QC, 2),
%获取剩余的其他记录
qlc:next_answers(QC, all_remainning).

Supervisor也崩溃了??

今天测试程序的时候出现了一个怪问题:我用一个supervisor(sup)去监督一个gen_server(test)的工作,test崩溃后被重启了,但随即sup也跟着崩溃了,导致整棵监督树倒了...郁闷,检查错误报告(见下)发现test仅仅被重启了一次,不是因为重启次数过多而使得sup崩溃~~
=ERROR REPORT==== 11-Apr-2009::01:51:47 ===
** Generic server test terminating
** Last message in was error
** When Server state == []
** Reason for termination ==
** error
gen_server test is started (test重启时所输出的信息)
** exception exit: {error,{gen_server,call,[test,error]}}
in function gen_server:call/2
13>
=ERROR REPORT==== 11-Apr-2009::01:51:47 ===
** Generic server sup terminating
** Last message in was {'EXIT',<0.54.0>,
{{error,{gen_server,call,[test,error]}},

可见sup系因为收到‘EXIT’信息才崩溃的,大白天谁无缘无故发个‘EXIT’信息出来?Google一番后在Google Groups里找到解答如下:

在erlshell里测试,需要unlink supervisor,这是因为在shell里运行时,进程层次状况如下:
shell
eval_loop
sup

这几个进程是link起来的,如不unlink,在出错时eval_loop会退出重启,导致sup收到{'EXIT', Pid, _}而发生退出。

OK,据此稍为修改监督者的启动函数即可:
{ok, Pid} = supervisor:start_link({local, sup}, sup, []),
unlink(Pid).

动态增删Mnesia数据库节点

2009-04-10
Mnesia支持动态的增删节点以进行数据转移或备份:
首先启动新节点上面的mnesia
> rpc:call(Node, mnesia, start, []).
添加新节点
> {ok, _} = mnesia:change_config(extra_db_nodes, [Node]).
修改新节点上的schema表的类型
> {atomic, ok} = mnesia:change_table_copy_type(schema, Node, disc_copies).

OK,现在可以在新节点上添加表备份、分块等等了,如果不再需要某个节点,可以这样删除节点:
首先关闭该节点上的mnesia
> rpc:call(Node, mnesia, stop, []).
删掉该节点上的schema表
> {atomic, ok} = mnesia:del_table_copy(schema, Node).
> ok = mnesia:delete_schema([Node]).

record_info不能接受參數?

Q:有如下函數:
1> rd(test, {key, value}).
2> Arg = test.
3> record_info(fields, test).
[key, value]
4> record_info(fields, Arg).
* 1: illegal record info

Arg跟記錄名test系一樣的,為什么传入record_info里會提示错误呢??

A:转Google Goups - [erlang-questions]上的一段解答:
record_info is not a proper function. It only exists during compilation, which means that it cannot take variable arguments:

record_info(fields, address) %% works
Rec = address,
record_info(fields, Rec) %% doesn't work!

Of course it only works if the address record is defined in that module. If you do want a function that uses record_info, try to use a macro instead.

定義成Macro就可以?馬上試試先:
-record(test, {key, value}).
-define(FIELDS(Record), record_info(fields, Record)).
....
?FIELDS(test).
> [key, value]

哇哇,果然OK哦~~

Local Content Tables

分布於多個節點上的同一個表,在各節點的備份上都保持有相同的數据,不過呢有時可能需要同一個表在不同節點備份上有不同的內容,這時就需要用到Local Content Tables了~~

如果我們創建表的時候將其屬性設置為{local_content, true},表同樣會分布多個備份到我們所指定的節點上,但寫操作僅會影響本地節點,不會同步到其他節點的備份上。另外當mnesia啟動時,這樣的表也僅會從本地讀取初始化數据,不會等待其他節點上的備份~~

感覺有點像表的分塊哦~~

Mnesia Table Fragmentation

The Concept
Mnesia支持將一個龐大的數据表分割為多個分塊,每個分塊跟一個普通的表一樣,可以備份可以創建索引等。讀寫數据的時候,Mnesia通過mnesia_frag模塊(一個mnesia_access callback behaviour)來定位實際數据位于哪個分塊里:首先mnesia_frag根据記錄的key記算一個hash值,然后根据該hash值決定表分塊的名稱,最后用普通的數据庫讀寫函數來堆該表分塊進行讀寫。若事前不知道記錄的key,則mnesia會搜索所有的分塊來尋找匹配的記錄。

下面的代碼描述如何將一個已存在的mnesia數据表轉化為一個分塊的表,以及如何動態的添加新分塊:
(a@oscar) 1> mnesia:start().
ok

當前有a、b、c三個節點正在運行
(a@oscar) 2> mnesia:system_info(running_db_nodes).
[b@oscar, c@oscar, a@oscar]

創建一個表dictionary
(a@oscar) 3> Tab = dictionary.
(a@oscar) 4> mnesia:create_table(Tab, [{ram_copies, [a@oscar, b@oscar]}]).
{atomic, ok}
(a@oscar) 5> Write = fun(Keys) -> [mnesia:write({Tab, K, -K}) || K <- Keys], ok end.
#Fun<....>
(a@oscar) 6> mnesia:activity(sync_dirty, Write, [lists:seq(1, 256)], mnesia_frag).
ok

啟動表分塊
(a@oscar) 7> mnesia:change_table_frag(Tab, {activate, []}).
{atomic, ok}

查看一下与分塊相關屬性
(a@oscar) 8> mnesia:table_info(Tab, frag_properties).
[{base_table, dictionary},
{foreign_key, undefined},
{n_doubles, 0},
{n_fragments, 1},
{next_n_to_split, 1},
{node_pool, [a@oscar, b@oscar, c@oscar]}]

添加分塊
(a@oscar) 9> Info = fun(Item) -> mnesia:table_info(Tab, Item) end.
#Fun<....>
(a@oscar) 10> Dist = mnesia:activity(sync_dirty, Info, [frag_dist], mnesia_frag).
[{c@oscar,0}, {a@oscar,1}, {b@oscar, 1}]
(a@oscar) 11> mnesia:change_table_frag(Tab, {add_frag, Dist}).
{atomic, ok}

再添加兩個分塊
(a@oscar) 12> Dist2 = mnesia:activity(sync_dirty, Info, [frag_dist], mnesia_frag).
[{b@oscar, 1}, {c@oscar, 1}, {a@oscar, 2}]
(a@oscar) 13> mnesia:change_table_frag(Tab, {add_frag, Dist2}).
{atomic, ok}
(a@oscar) 14> Dist3 = mnesia:activity(sync_dirty, Info, [frag_dist], mnesia_frag).
[{a@oscar, 2}, {b@oscar, 2}, {c@oscar, 2}]
(a@oscar) 15> mnesia:change_table_frag(Tab, {add_frag, Dist3}).
{atomic, ok}

從分塊里讀取數据
(a@oscar) 16> Read = fun(Key) -> mnesia:read({Tab, Key}) end.
#Fun<....>
(a@oscar) 17> mnesia:activity(transaction, Read, [12], mnesia_frag).
[{dictionary, 12, -12}]

查看各分塊所包含的數据量
(a@oscar) 18> mnesia:activity(sync_dirty, Info, [frag_size], mnesia_frag).
[{dictionary, 64},
{dictionary_frag2, 64},
{dictionary_frag3, 64},
{dictionary_frag3, 64}]
(哇,這么均衡~~)

Fragmentation Properties
可調用mnesia:table_info(Tab, frag_properties)函數來查看指定表的分塊清況,包含如下屬性(不全):

{n_fragments, Int}:當前表包含有多少個分塊,此屬性可在創建表時設定,或由{add_frag, NodesOrDist}、del_frag等操作所修改,默認值為1。

{node_pool, List}:分塊節點池,可在表創建時指定,或由{add_node, Node}、{del_node, Node}等操作所修改,在創建數据表的時候,mnesia會將表的分塊平均的分布到節點池里的各個節點上,默認為mnesia:system_info(db_nodes)的返回值。

{n_ram_copies, Int}:設定每個分塊應該有多少個ram_copies型備份,默認為0,但若n_disc_copies和n_disc_only_copies都為0,則此屬性會默認為1。

{n_disc_copies, Int}:設定每個分塊應有多少個disc_copies型備份,默認為0。

{n_disc_only_copies, Int}:設定每個分塊應有多少個disc_only_copies型備份,默認為0。

{hash_module, Atom}:此屬性允許用戶指定一個散列模式(hashing schema),此模塊必須實現mnesia_frag_hash回調行為(callback behaviour),默認為mnesia_frag_hash。

Management of Fragmented Tables
函數mnesia:change_table_frag(Tab, Change)用于修改分塊表的配置,其中Change可能為如下值:

{activate, FragProps}:激活已存在表的分塊机制,FragProps應為{node_pool, Nodes}或[]。

deactivate:關閉表的分塊机制,其分塊數必須為1。

{add_frag, NodesOrDist}:向分塊表添加一個新的分塊,其中一個舊分塊里的數据會被重新散列,它們中的約一半會被搬到新的分塊上去。所有其他含有指向該表的外鍵的分塊表都會自動得到一個新分塊,并按同樣的方式重新分布數据。
NodesOrDist可以是一列節點,或函數mnesia:table_info(Tab, frag_dist)的返回值,NodesOrDist應該是一個有序的數組,其中最适合放置新分塊的節點放在最前面的位置,新的分塊會得到与第一個分塊同樣數量的備份。

del_frag:從分塊表里刪除一個分塊,最后一個分塊的所有數据都會被搬到另一個分塊里。

{add_node, Node}:向node_pool里添加一個新節點,新的節點池會改變函數mnesia:table_info(Tab, frag_dist)的返回值。

{del_node, Node}:從node_pool里刪除一個節點,同樣也會改變函數mnesia:table_info(Tab, frag_dist)的返回值。

Erlang OTP - Supervisor

2009-04-09
supervisor behaviour用于实现层状监督树机制,包含一套标准的接口函数和追踪错误等的功能,监督者负责启动、停止和监督其子进程的工作,子进程可以是一个工作进程,也可以是一个子监督者,监督者机制的基本理念是通过自动重启子进程而保证其能正常持续的对外提供服务。

监督者的子进程由child specifications列表定义,当监督者启动时,其子进程将按该列表顺序从左到右启动,监督者终止运行时,其子进程将按相反方向关闭,最后终止监督者自己。子进程列表定义如下:
child_spec() = {Id, StartFunc, Restart, Shutdown, Type, Modules}
Id = term()
StartFunc = {M, F, A}
M = F = atom()
A = [term()]
Restart = permanent | transient | temporary
Shutdown = brutal_kill | int()>=0 | infinity
Type = worker | supervisor
Modules = [Module] | dynamic
Module = atom()

其中Id是监督者内部标识该子进程的名称,StartFunc定义了启动该子进程所需调用的函数,这个启动函数必须创建并连接到一个子进程,然后返回{ok, Child}或{ok, Child, Info},Child是子进程的pid,Info会被监督者所忽略;如果子进程因某些原因未能启动可返回ignore;如果发生了错误也可以返回一个{error, Error}。

Restart定义了子进程何时将会被重启:
permanent:无论子进程因何终止都应被重启;
temporary:无须重启子进程;
transient:仅在子进程因非正常原因终止时才重启子进程。

Shutdown定义了子进程应被如何终止:
brutal_kill:使用exit(Child, kill)强制终止;
int()>=0:监督者调用exit(Child, shutdown)告知子进程终止并等待其终止信号,若超过指定时间尚未收到再调用exit(Child, kill)来强制终止;
infinity:如果子进程为一个子监督者,则应将超时时间设定为infinity,以让其有充足时间来终止其子监督树。

Type定义了子进程是一个监督者还是一个工作进程。

监督者对子进程的重启方式(Restart Strategy)有如下几种:
one_for_one:如果一个子进程终止了,则只需重启该子进程;
one_for_all:如果一个子进程终止了,所有其他子进程都将被终止,然后重启所有子进程;
rest_for_one:如果一个子进程终止了,则按启动顺序比其晚启动的其他子进程都将被终止,然后该子进程和被终止掉的其他子进程都会被重启;
simple_one_for_one:是one_for_one的简化版,该监督者的所有子进程都是动态加入的,并且每个子进程类型都相同,都运行一样的代码。

为了避免监督者陷入子进程被不停的终止-重启的死循环,每个监督者都有一个最大重启频度(maximum restart frequency),由参数MaxR和MaxT所决定,如果在MaxT秒内发生多于MaxR次子进程重启,则监督者会终止其所有子进程和它本身。

Callback Module
supervisor behaviour的回调模块僅包含一个回调函数init/1:
init(_Args) ->
{ok, {{RestartStrategy, MaxR, MaxT}, [ChildSpec]}}


Starting a Supervisor
监督者通过调用函数supervisor:start_link/2来启动,如下:
supervisor:start_link(MODULE, []).

MODULE为该监督者的回调模块,第二个参数为传入到回调函数init/1里的参数。如果想为该监督者注册个名字,也可以用下面这个函数来启动:
supervisor:start_link({local, Name}, Module, Args) 或
supervisor:start_link({global, Name}, Module, Args)

启动后,监督者会调用回调模块里的init/1函数,并根据该函数的返回值启动其子进程,注意supervisor:start_link这函数不是异步的,要直到所有子进程都启动后才会返回。

Adding a Child Process
监督树可以是静态的,也可以是动态的,我们可以随时加入新的子进程到监督树中:
supervisor:start_child(Sup, ChildSpec)

其中Sup是监督者的pid或注册名,ChildSpec是一个子进程声明。动态增添的子进程和其他子进程一样,但若监督者死掉然后被重新启动后,所有动态添加的子进程都会丢失。

Stopping a Child Process
所有子进程无论动态静态,都可以按照关闭设置被停止运行:
supervisor:terminate_child(Sup, Id)

可用如下函数删除被停止运行的子进程的进程声明:
supervisor:delete_child(Sup, Id)

同样,监督者本身重启后,删除静态子进程的操作将失效。

Mnesia写入数据错误?

2009-04-08
Q:我创建了如下一个Mnesia表,为何写入数据时会出现bad type错误呢??
> rd(message, {to, from, content}).
> mnesia:create_table(msg, [{attributes, record_info(fields, message)}]).

> mnesia:dirty_write({message, jojo, oscar, <<"missing you">>}).
** exception exit: {aborted,{no_exists,message}}
in function mnesia:abort/1

> mnesia:dirty_write(msg, {message, jojo, oscar, <<"test">>}).
** exception exit: {aborted,{bad_type,
#message{to = jojo, from = oscar, content = <<"test">>}}}
in function mnesia:abort/1

A:第一次执行dirty_write时提示没有找到message表,当表名和表所存储的record名相同时,写入数据时可以忽略表名,但现在表名并非message而是msg,好吧我们指定一个表名再写入试试,这次却出现了第二个错误提示类型错误,郁闷,原来当表名和record名不同时,创建表的时候应另外添加一个参数{record_name, ...},即:

> mnesia:create_table(msg, [
{attributes, record_info(fields, message)},
{record_name, message}]).

这样就可以正常插入数据了~~

I am back ~~

2009-04-07
祭祖归来~~学校校园网验证更新后,Ubuntu依然没有可用的客户端连接网络,无耻的网管!Youtube依然未能访问,无耻的GFW!go2居然说暂停对国内提供代理服务,要“增加过滤技术”,唉,还有什么话好说的呢~~

This country needs a little change ....

我不高兴~~

2009-04-04
今天报纸上看到几句言论比较精彩,特意记下来:

“在一个法治上轨道的国家里,人是有权生气的。....不要以为你是大学教授,所以做研究比较重要;不要以为你是杀猪的,所以没有人会听你的话;也不要以为你是个大学生,不够资格管社会的事。你今天不生气,不站出来的话,明天--还有我、还有你我的下一代,就要成为沉默的牺牲者、受害人!如果你有种、有良心,你现在就去告诉你的公仆‘立法委员’、告诉卫生署、告诉环保局:你受够了,你很生气!你一定要很大声地说。”--龙应台

“在一个代议制国家,我的利益被人堂而皇之地代表。然而,代表我利益的人姓甚名谁我竟不知晓--即使是那隐姓埋名的佐罗,我也知道他的刀剑与相貌。他们从来没有向我询问过我需要什么利益。为此,我不高兴。”

“17世纪英国中央政府给社会带来的经济负担不到5%,想到2007年中国财政收入占GDP比例占到了20.80%,而且财政收入的1/3被用于‘三公消费’,大学生不去创业,而是争考公务员。国富民穷,权利诱人。为此,我不高兴。”

回想一下,我见识过两次人大代表的选举方式:一次是在我家乡珠海,村委们拿出一张所谓选票,上面有几个人名,老实说我是一个也不认识,也从来没见过他们出来宣传自己,告诉大家他会为大家争取些什么做些什么,随便打一个勾把票投了,就可以换来10块钱,实在是神奇,至于投完票后结果如何、点票是否公正、甚至说到底有没点票,实在是无人知晓;另一次是在广州读大学时,学校贴出个公告,说我们学校某某老师要参选人大,要求同学“踊跃支持”,但我为什么要支持一个我素未谋面毫无了解的人做自己的代表呢?学校的公告里貌似只给出了因为他是我们学校的老师这一条原因 -____- ||| 所以,我两次都没有参与~~

郁闷~~

2009-04-03
鬱悶啊!今天校園網要強製換用新的客戶端,卻只提供了Windows版,平時上網慢的難以忍受也就忍過去了,現在Linux是完全上不了网~~我還要在Ubuntu下搞畢業設計的啊!該死的牙又痛了整天....

設置Erlang最大總進程數

Erlang默認的最大進程總數隻有32768個,有時不怎么夠用,此時可以在啓動Erlang時加上參數“+P”來設置最大進程總數,如下:
erl +P 100000

看看是否設置成功:

1> erlang:system_info(process_limit).
100000

不過雖說在Erlang里進程不用錢,但也不應該想開多少就開多少,不然內存資源會被迅速耗盡死機的~~比如說我們創建1百萬個進程,看看繫統資源佔用情況:



黑色框部分就是創建1百萬個進程時的內存消耗情況,我那1.5GB的內存被迅速用光,交換區也被猛吃了一截....

Toothache ....



嗚嗚~~牙好痛好痛~~

利用List Comprehension過慮列表里的項目

2009-04-02
Erlang的數据結構十分容易進行匹配和過慮,下面演示個例子:
首先定義一個結構體
1> rd(person, {name, gender}).

創建一個person列表
2> List=[
2> {person, oscar, male},
2> {person, jojo, female},
2> {person, god, male},
2> {person, jessica, female}].

我們現在過慮出所有gender為male的人的名字
3> [P#person.name || P <- List, P#person.gender==male].
[oscar, god]

再將名為jojo的項目過慮出來
4> [P || P <- List, P#person.name==jojo].
[#person{name = jojo, gender = female}]

很簡便吧~~有興趣的可詳細參見這里

M-OSCAR | Powered by Blogger | Entries (RSS) | Comments (RSS) | Designed by MB Web Design | XML Coded By Cahayabiru.com