顯示具有 erlang 標籤的文章。 顯示所有文章
顯示具有 erlang 標籤的文章。 顯示所有文章

Use Erlang to handle xml stream

2009-06-03
写了一个模块,使用xmerl_scan:string函数流式的读取和处理XML流(边读边处理),很适合用于处理XMPP协议的XML流数据,每读取到一个完整的节点(message, presence, iq ....)就会做相应处理~~源码在这里~~

erlang 与 linked-in driver 间的通信

2009-05-31
erlang貌似没有直接向port写入整数的方法,通过port传送的全是binary数据,以下代码在erlang中通过port传送一个整数给cpp读取~~
Erlang :
send_integer(Int) ->
    Size = erlang:system_info(wordsize) * 8,
    Data = <<Int:Size/integer-little>>,
    Port ! {self(), {command, Data}},
    ....

CPP :
static void output(ErlDrvData handle, char* buff, int bufflen){
    int *num = (int *)buff
    ....

Erlang Linked in driver

2009-05-24
Erlang允许其他语言编写的程序以共享库的方式动态连接到Erlang运行时环境里运行,与Erlang Port的方式相似,这种方式称为linked-in driver(内部连接驱动??),是Erlang与其他语言程序交互的最高效的方式,但同时也是最危险的,因为一旦驱动内部出现致命异常,Erlang虚拟机也会崩溃,拖累其他Erlang进程一起挂掉,因此如非必要不推荐使用linked-in driver。

下面是C++编写的driver,具体代码结构解析请参见《Programming Erlang》
#include 
#include "erl_driver.h"
using namespace std;

struct data{
    ErlDrvPort port;
};

//PORT打开时执行
static ErlDrvData start(ErlDrvPort port, char *buff){
    data* d = (data*)driver_alloc(sizeof(data));
    d->port = port;
    return (ErlDrvData)d;
}

//PORT关闭时执行
static void stop(ErlDrvData handle){
    driver_free((char*)handle);
}

//ERLANG发送来信息时执行
static void output(ErlDrvData handle, char *buff, int bufflen){
    cout << buff << endl;
    char rs[] = "ok";
    data* d = (data*)handle;
    driver_output(d->port, rs, 2);
}

ErlDrvEntry driver_entry = {
    NULL,    //F_PTR init N/A
    start,   //L_PTR PORT打开时执行此函数
    stop,    //F_PTR PORT关闭时执行此函数
    output,  //F_PTR ERLANG发来信息时执行
    NULL,    //F_PTR ready_input, called when input descriptor ready to read
    NULL,    //F_PTR ready_output
    "test",  //DRIVER名,OPEN_PORT函数的参数
    NULL,    //F_PTR finish, called when unloaded
    NULL,    //F_PTR control, port_command callback
    NULL,    //F_PTR timeout, reserved
    NULL     //F_PTR outputv, reserved
};

extern "C"{
    DRIVER_INIT(test){
        return &driver_entry;
    }
}
然后用如下命令编译成共享库:
g++ test.cpp -I/usr/local/lib/erlang/usr/include/ -fPIC -shared -o test.so

Erlang里调用这个共享库的代码如下:
-module(test).
-compile(export_all).

start() ->
    ok = erl_ddll:load_driver(".", "test"),
    Port = open_port({spawn, "test"}, [{packet, 2}]),
    Port ! {self(), {command, "woo just a test"}},
    receive
        {Port, {data, Data}} ->
            ok = io:format("[~p/~p] ~p\n", [?MODULE, self(), Data])
    end,
    Port ! {self(), close}.

[ Erlang + ActionScript ] System Monitor

2009-05-12
在畢業設計裡加入了一個遠程的服務端負載監測功能,可以遠程查看服務端的當前CPU負載、內存分配等狀況,效果圖如下:
 前台用Flex實現,Flex的圖表組件很好用,坐標軸能自動根據數據來調整,不過數據綁定上好像有點問題,數據源更新了圖表卻不會自動刷新~~後台由Erlang提供數據,Erlang的OS_MON模塊可以提供系統CPU負載、內存狀況、硬碟空間使用情況等系統信息~~Erlang通過Socket主動推送JSON格式的數據給客戶端~~有興趣的話可以看看源碼:

客戶端源碼 / 服務端源碼

Erlang處理JSON數據~~

2009-05-09
要用Erlang處理JSON數據,先下載這個東東:
JSON and JSON-RPC for Erlang
下載解壓編譯完之後,輸出的beam文件在ebin目錄下~~

上面下載的包裡有一個rfc4627模塊,提供了JSON數據的encode、decode等方法,還可以將JSON與Erlang Record相互轉換,十分方便快捷,下面看看用法:
首先試試將字符串轉換為Json Object:
1> JsonBin = <<"{ 
1> \"username\":\"Oscar Tong\",
1> \"password\":\"123456\",
1> \"male\":true,
1> \"age\":23
1> }">>.
<<"{\n\"username\":\"Oscar Tong\",\n\"password\":\"123456\",\n\"male\":true,\n\"age\":23\n}">>
2> {ok, {obj, JsonObj}, _Rest} = rfc4627:decode(JsonBin).
{ok,{obj,[{"username",<<"Oscar Tong">>},
          {"password",<<"123456">>},
          {"male",true},
          {"age",23}]},
    []}

再轉換為JSON格式的字符串:
3> rfc4627:encode({obj,JsonObj}).
"{\"username\":\"Oscar Tong\",\"password\":\"123456\",\"male\":true,\"age\":23}"
經測試表明,JSON數據的解析與處理速度比XML要快得多,decode和encode僅耗時約40us,比xmerl模塊處理XML需要超過200us相比實在是快多了~~

gen_tcp:controlling_process BADARG error ??

2009-05-07
今晚嘗試了一下在ERLANG裡將SOCKET鏈接的控制進程轉到另一個節點上去,結果得了一個{error,badarg}錯誤,代碼如下:
....
{ok, Sock} = gen_tcp:accept(Listener),
Pid = spawn(SomeOtherNode, Mod, Fun, Arg),
ok = gen_tcp:controlling_process(Sock, Pid),
....
看來想用這種方式來將眾多SOCKET鏈接的壓力分擔到多個節點上是不行的 -____- |||

Erlang的進程信息過濾

2009-05-06
Erlang進程之間可以通過收發信息來通信,有時可能有一堆信息從各處無序的湧來,你只想處理其中部分信息,而將其他的保留在進程的收件箱裡遲點再管,那該怎麼搞呢?其實很簡單的,看下面代碼:
...
wait_for_reply() ->
    receive
        {reply, Reply} -> handle(Reply);
        _Other -> wait_for_reply()
    end.
上述函數僅處理匹配{reply, Reply}格式的信息,其他信息因為唯一的匹配模式是一個被忽略的參數_Other,因此不會被處理而繼續留在收件箱裡~~

Erlang : head mismatch ??

2009-05-02
今天編譯代碼的時候出現了一個奇怪的問題,erlang老提示某個函數有head mismatch錯誤,但我看來看去沒發現什麼不對哇,函數的定義和調用都沒錯~~最後發現原來是因為函數放置了在兩個重名函數之間,即:
出錯前:
myfun(params1) -> .... ;
myfun(params2) -> .... .

這樣就會提示錯誤了:
myfun(params1) -> .... ;
new_fun_here(params) -> .... .
myfun(params2) -> .... .
把新函數放到其他地方就OK了~~粗心之過~~

Reload Erlang Module

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

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

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

2009-04-26
可以將任意的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确保您的预期,如果发生错误,那么就大胆的表达出来。


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

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

Mnesia 事件处理

2009-04-11
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]).

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