Erlang-China

erlang 中文社区

Erlang Gtalk 讨论群8月份聊天记录


Gtalk 讨论群上面都是比较活跃的,大家的讨论也不乏亮点,现在整理出技术讨论的部分,方便大伙


Erlang: Let’s talk to java


试了一下传说中的 JInterface ,使用 OtpErlang.jar 的整个过程其实非常简单,似乎比 JMS 的程序都简单。

首先,我们要用 java 实现的原始 erlang 程序如下,没错,就是巨简单的 echo ,我们的目标是要把它用 java 来改写,不仅写服务端,也要写客户端。


【转】erlang 网络调优实战


原文网址:erlang网络编程的几个性能调优和注意点
原文作者:coderplay

前些天给echo_server写了个非常简单的连接压力测试程序,

  1. -module(stress_test)
  2.  
  3. -export([start/0, tests/1])
  4.  
  5. start() -> 
  6.     tests(12345)
  7.  
  8. tests(Port) -> 
  9.     io:format("starting~n")
  10.     spawn(fun() -> test(Port) end)
  11.     spawn(fun() -> test(Port) end)
  12.     spawn(fun() -> test(Port) end)
  13.     spawn(fun() -> test(Port) end)
  14.  
  15. test(Port) -> 
  16.      case gen_tcp:connect("192.168.0.217", Port, [binary,{packet, 0}]) of 
  17.     {ok, _} -> 
  18.             test(Port)
  19.     _ -> 
  20.         test(Port) 
  21.     end.

一开始我的这个stress_test客户端运行在windows上面, echo_server服务器端运行在linux上面。结果接受了1016个连接就停止了. 于是我用ulimit -n 改了服务器端的文件描述符数量为10240. 接着还是如此,折腾了几天,最终还是没有搞明白。

于是就求助于公司的linux编程牛人,结果让我一倒… 客户端没有修改文件描述符个数. windows上得在注册表里面改.


【转】公布 mryufeng 同学的 Erlang 秘笈


原文地址:这里 这里 这里 这里
原文作者:mryufeng

上次说请 mryufeng 同学多写写研究心得,还推说不大爱写长东西。隔不了几天,跑到他的 blog 上一看,乖乖龙的东!整了一大堆秘笈,原来是要藏私!说不得,对于这样同志,只能揪出来示众了。注意,以下内容全部是“海贼版”,未经作者明确同意,请慎入!


为什么没用YAWS?


之前的 webmine (抱歉,现在暂时没法访问)是基于 erlang 自带的 inets httpd 而不是如雷贯耳的 YAWS 。有朋友问我,这是为什么,说来说去好像说不清,索性就 blah 一篇了。

为什么不用 yaws 呢?主要有这么几个原因:


一个简单的列表操作性能测试


Note:首先要了解,Erlang里面的列表,比如 [1,2,3,4],其实是这样的方式来存储 [1,[2,[3,[4]]]],因此在头部插入一个元素,很简单,但是在尾部插入就比较困难了。

闲来对Erlang中的2个列表操作进行了测试,先上代码:

  1. -module(test_list).
  2. -compile(export_all).
  3.  
  4. main() ->
  5. test_concat(),
  6. test_flatten(),
  7. test_append_tail(),
  8. test_append_header().
  9.  
  10. test_concat() ->
  11. statistics(wall_clock),
  12. test_concat(1000000).
  13.  
  14. test_concat(0) ->
  15. {_, Duration} = statistics(wall_clock),
  16. io:format("Concat Duration ~pms~n", [Duration]),
  17. ok;
  18. test_concat(N) ->
  19. "<title>" ++ integer_to_list(N) ++ "</title>",
  20. test_concat(N-1).
  21.  
  22. test_flatten() ->
  23. statistics(wall_clock),
  24. test_flatten(1000000).
  25.  
  26. test_flatten(0) ->
  27. {_, Duration} = statistics(wall_clock),
  28. io:format("Flatten Duration ~pms~n", [Duration]),
  29. ok;
  30. test_flatten(N) ->
  31. lists:flatten(["<title>",integer_to_list(N),"</title>"]),
  32. test_flatten(N-1).
  33.  
  34. test_append_tail() ->
  35. statistics(wall_clock),
  36. test_append_tail(100000).
  37.  
  38. test_append_tail(0) ->
  39. {_, Duration} = statistics(wall_clock),
  40. io:format("Append tail Duration ~pms~n", [Duration]),
  41. ok;
  42. test_append_tail(N) ->
  43. append_last([], 97, 122),
  44. test_append_tail(N-1).
  45.  
  46. append_last(List, N, N) ->
  47. List ++ [N];
  48. append_last(List, Curr, N) ->
  49. append_last(List ++ [Curr], Curr+1, N).
  50.  
  51.  
  52. test_append_header() ->
  53. statistics(wall_clock),
  54. test_append_header(100000).
  55.  
  56. test_append_header(0) ->
  57. {_, Duration} = statistics(wall_clock),
  58. io:format("Append header Duration ~pms~n", [Duration]),
  59. ok;
  60. test_append_header(N) ->
  61. append_header_and_reverse([], 97, 122),
  62. test_append_header(N-1).
  63.  
  64. append_header_and_reverse(List, N, N) ->
  65. lists:reverse([N|List]);
  66. append_header_and_reverse(List, Curr, N) ->
  67. append_header_and_reverse([Curr|List], Curr+1, N).

test_concat() 和 test_flatten(),测试使用 ++ 构造一个 list,和使用 lists:flatten() 构造list的开销;test_append_tail() 和 test_append_header(), 测试使用 ++ 和 [H|T]再lists:reverse() 方式将大量字符构造成一个列表的开销。

  1. 1> c(test_list).
  2. {ok,test_list}
  3. 2> test_list:main().
  4. Concat Duration 1637ms
  5. Flatten Duration 6743ms
  6. Append tail Duration 2144ms
  7. Append header Duration 624ms
  8. ok
  9. 3> c(test_list, [native]).
  10. {ok,test_list}
  11. 4> test_list:main().
  12. Concat Duration 863ms
  13. Flatten Duration 6084ms
  14. Append tail Duration 1974ms
  15. Append header Duration 362ms
  16. ok

可见,使用 ++ 合并列表,比使用 lists:flatten() 高效;而使用[H|T]再lists:reverse()的方式比 ++ 高效。使用HIPE编译后,++操作和[H|T]操作的性能也得到了较大的提高。

经常在Erlang的程序代码里面看到类似

  1. foo([], List) ->
  2. lists:reverse(List);
  3. foo([H|T], List) ->
  4. ...
  5. foo(T, [H|List])

这样的代码,里面就是使用了头部插入,然后反转列表的方式。


写出正确的尾递归代码


你可能早已耳闻,在 erlang 中,循环变成了递归。

你很可能常常看见这样的代码,并因为它是来自于 erlang 官方的文档 getting start with erlang 而觉得这样的代码,可能就是传说中的尾递归。

  1. -module(tut1).
  2. -export([fac/1]).
  3.  
  4. fac(1) ->
  5.     1;
  6. fac(N) ->
  7.     N * fac(N - 1).

没错,一个函数自己调用自己,这就是递归。但,这真的就是传说中可以通过编译优化得“和循环一样快,没有额外开销”的尾递归么?递归和尾递归,是否存在差异呢?

我们可以做一个试验。


【转】Erlang 里面使用Remote shell


原文地址:avindev.javaeye.com
原文作者:AvinDev

今天无意中发现这个功能。。。

ejabberd.jabber.ru

在下载的 getting_started-5.0.1.pdf 那里也有这个topic:《Advanced Shell Usage - Job Control Mode》,不知道为何官方html格式的文档那里消失了。

简单来说是这样,比如节点 foo@192.168.0.2 启动了一个daemon

  1. erl -name foo@192.168.0.2 -setcookie 123456 -noshell -noinput ...

注:即,无shell,无输入,可以理解为以无界面的方式启动。
注:选项 setcookie 是指定 magic cookie ,这个参数也可以在文件中指定。这是 erlang 的“要么就都没有,要么就全权控制”的安全机制,两个 erlang 的 node 如果需要通讯,必须要有相同的 magic cookie。也就是说,你如果知道目标机器的 magic cookie 你就可以远程管理它。

如果要管理它,可以这样:

  1. # ./start.sh -name bar@192.168.0.3 -setcookie 123456
  2. Erlang (BEAM) emulator version 5.5.4 [source] [async-threads:0] [hipe] [kernel-poll:false]
  3.  
  4. Eshell V5.5.4 (abort with ^G)
  5. (bar@192.168.0.3)1>
  6. User switch command
  7. --> h
  8. c [nn] - connect to job
  9. i [nn] - interrupt job
  10. k [nn] - kill job
  11. j - list all jobs
  12. s - start local shell
  13. r [node] - start remote shell
  14. q - quit erlang
  15. ? | h - this message
  16. --> j
  17. 1* {shell,start,[init]}
  18. --> r 'foo@192.168.0.2'
  19. --> j
  20. 1 {shell,start,[init]}
  21. 2* {'foo@192.168.0.2',shell,start,[]}
  22. --> c 2
  23. Eshell V5.5.4 (abort with ^G)
  24. (foo@192.168.0.2)1> nodes().
  25. ['bar@192.168.0.3']

比用什么rpc的方便多了
btw:Control+G无法在win下的shell里面使用~

注: Control+G 在 windows 下的 erl 中的确不能用,但在 werl (另一个 shell 界面)中可以照样用,windows 下 werl 的热键与文档保持高度一致。


【转】Erlang 字符编码模块: iconv


原文地址:avindev.javaeye.com
原文作者:AvinDev

在Erlang中,要处理字符串编码的转换,可以使用iconv,目前CEAN的发行版本,ejabberd和iconv模块里面都有iconv的库,但是都无法正常运行(提示版本不兼容),但是在ejabberd的安装包可以使用。经过比较,发现是 erl.exe,erlexec.dll,beam.dll 这三个启动程序的问题,ejabberd 的是 V5.5.2.2,而 CEAN1.2 和ErlangOTP R11B4 都是 V5.5.4,可能两个版本跟iconv的c port不兼容。

下面就以ejabberd的erlang版本来说明:

首先启动port

  1. 1> iconv:start().
  2. {ok,<0.30.0>}
  3. 2> iconv:convert("gbk", "utf-8", "你好")
  4. "浣犲ソ"

Windows下控制台编码为gbk,这里将“你好”编码为UTF8后输出,显示乱码,可以用io:format解决:

  1. 3> io:format("~w~n", [iconv:convert("gbk", "utf-8", "你好")]).
  2. [228,189,160,229,165,189]
  3. ok

反过来流程是一样的:

  1. 4> iconv:convert("utf-8", "gbk", [228,189,160,229,165,189]).
  2. "你好"

要解决在Erlang发行版下使用的问题,估计要重新编译iconv的port。有谁有更好的方法,欢迎提出。

注:貌似 N 多的 c port 代码在 cean 版本中都有问题,上次试验 linux 下的 cean yaws 也出了一个 ddll 的包不兼容的问题。而自己从源代码编译 erlang 又巨慢巨痛苦。苦恼啊。


【转】erlang module pg2


原文地址:avindev.javaeye.com
原文作者:AvinDev

《轻松实现可伸缩性,容错性,和负载平衡的大规模多人在线系统》一文里面对“Erlang的分布式进程组(Distributed Named Process Groups)”大吹特吹,就是说的pg2这个module。文档那里虽然写了支持分布式节点,但是并没有说如何如何,只提供了一个join(Name, Pid)。

看了一下openpoker的源码,原来很简单,对于连接上的node,用 which_groups() 多调用几次就可以同步过来了:

主节点先创建一个Group:

  1. (foo@localhost)1> pg2:create(group).
  2. ok
  3. (foo@localhost)2> pg2:get_members(group).
  4. []
  5. (foo@localhost)3> pg2:join(group, self()).
  6. ok
  7. (foo@localhost)4> pg2:get_members(group).
  8. [<0.35.0>]

其他节点先连上主节点,然后调用pg2:which_groups().

  1. (bar@localhost)1> net_adm:ping('foo@localhost').
  2. pong
  3. (bar@localhost)2> pg2:which_groups().
  4. []
  5. (bar@localhost)3> pg2:which_groups().
  6. [group]
  7. (bar@localhost)4> pg2:join(group, self()).
  8. ok

已经加入了:

  1. (foo@localhost)5> pg2:get_members(group).
  2. [<4835.35.0>,<0.35.0>]

使用就是那么简单。但是在pg2的底层实现上,使用了ets,global等模块,下次继续研究。

注:正如它的名称所暗示的 pg2 可以用于管理成组 process 的场景,如:每一个 game session 是一个 process ,由这些 process 形成一个 group ,那么你可以通过这个 group 获取 session 的数目(eg. 在线人数),即便你的 session counter process 死掉,那么在它被自动恢复的时候,可以连上 group 并从中找回 session 数目。