`
cwqcwk1
  • 浏览: 87404 次
文章分类
社区版块
存档分类
最新评论

erlang开发经验谈:防坑指南

 
阅读更多
任何语言在使用中都会遇到这样那样的问题,erlang也是。这里整理下我遇到的一些问题,避免继续踩坑。说实话,“防坑指南”这个标题有点过于标新立异,不过还是希望能引起重视,避免在实际开发中重复犯这些问题。


'--' 运算与 '++'运算

1> [1,2,3,4] -- [1] -- [2].
[2,3,4]
算是erlang经典的问题了。这是从后面算起的,先算 [1] -- [2] ,得到 [1] 后被 [1,2,3,4] --,最后得到 [2,3,4]
'++'运算也是一样的,也是从后面开始算起。
2> [1,2,3,4] -- [1] ++ [2,3,4].
[]
另外,以下这种情况也要注意,只会减去遇到的第一个元素。
3> [1,2,3,2] -- [2].
[1,3,2]



erlang:function_exported()

这个接口是用来检查模块函数是否导出,但是,如果模块没加载过,这个函数返回值就是false
3> erlang:function_exported(odbc,start,0).
false
4> odbc:start().
ok
5> erlang:function_exported(odbc,start,0).
true
我之前也讨论过这个问题,解决方案看这里


erlang:list_to_binary()

如果参数是多层嵌套结构,就会被扁平化掉,使用 binary_to_list 不能转成原来的数据,也就是不可逆的。
6> list_to_binary([1,2,[3,4],5]) .
<<1,2,3,4,5>>
如果想可逆,可以使用 erlang:term_to_binary
7> binary_to_term(term_to_binary([1,2,[3,4],5])).
[1,2,[3,4],5]



erlang:list_to_atom()

这个函数用于字符串转原子,本身没什么问题,但是erlang系统有原子总数限制,除非整个节点关闭了,否则原子就算没用到了也不会被gc。而 lists_to_atom() 如果要转换的原子不存在,将会自动重新生成。可以换成另外一个函数 list_to_existing_atom ,只会生成已经存在的原子
2> list_to_atom("erlang").
erlang
3> list_to_existing_atom("erlang").
erlang
4> list_to_existing_atom("hello").
** exception error: bad argument
in function list_to_existing_atom/1
called as list_to_existing_atom("hello")
也不是说lists_to_atom 不能使用,避免直接将远程传过来的字符串转成原子。
除了这里提到的,还有 binary_to_atom() , binary_to_term() 也有这个问题。对于binary_to_atom() 可以换成binary_to_existing_atom() , 但binary_to_term () 有点不同,参考 erlang binary数据转换的问题解决这个问题


erlang:process_info()

这个函数用以查看任意进程的内部数据,比如进程字典数据,消息队列长度,占用内存,当前调用栈等。但是要注意一个问题,如果是查看其他进程,数据将会复制一份到本进程,特别是对于进程字典数据,当前进程将会保存一份副本。如果该进程的进程字典数据特别庞大,有几个G,那将是多么恐怖的事情。
erlang:process_info(Pid, dictionary)


random:uniform()

这个用于生成随机数,返回一个随机数浮点数。但是,这个函数的随机初始种子是个定值,而且种子就放在进程字典,就是说每个进程生成的随机数都是一样的。坑爹啊。
10> spawn(fun() -> io:format("~w ~w~n",[random:uniform(),random:uniform()]) end)
,ok.
0.44358461744572030.7230402056221108
ok
11> spawn(fun() -> io:format("~w ~w~n",[random:uniform(),random:uniform()]) end)
,ok.
0.44358461744572030.7230402056221108
ok
所以,解决的方法就是进程启动后要重置随机数种子,然后再使用这个函数。
13> random:seed(erlang:now()), random:uniform().
0.4691405130019146
或者重写掉这个函数:
uniform() ->
{A1, A2, A3} = case get(random_seed) of
undefined ->erlang:now();
Tuple -> Tuple
end,
B1 = (A1*171) rem 30269,
B2 = (A2*172) rem 30307,
B3 = (A3*170) rem 30323,
put(random_seed, {B1,B2,B3}),
R = A1/30269 + A2/30307 + A3/30323,
R - trunc(R).


try... catch

try1(ErrType) ->
try
case ErrType of
error -> erlang:error(error);
exit -> erlang:exit (exit);
throw -> erlang:throw(throw)
end
catch
Err -> Err
end.
8> test:try1(error).
** exception error: error
in function test:try1/1 (test.erl, line 7)
9> test:try1(exit).
** exception exit: exit
in function test:try1/1 (test.erl, line 8)
10> test:try1(throw).
throw
以上结果只有 throw可以匹配,如果想要全部匹配到要改写这个函数:
try1(ErrType) ->
try
case ErrType of
error -> erlang:error(error);
exit -> erlang:exit (exit);
throw -> erlang:throw(throw)
end
catch
_:Err -> Err
end.


匿名函数(fun)

匿名函数的使用场景很多,经常被用作回调函数,比如lists模块就大量使用匿名函数做回调。在现在的erlang版本中,匿名函数会在编译期优化成本地函数。如果把匿名函数以进程字典保存,或者收到其他进程发来的匿名函数消息但是没有处理,就可能导致进程被kill。这是因为模块热更新时会检查旧代码是否被使用,如果有进程使用,就会杀掉进程。
如何保存匿名函数,而且不怕热更新时进程被杀掉?
有两种做法:
1. 以MFA方式,然后这样调用 M:F(A) 或者 apply(M, F, A)
2. 以fun M:F/A 形式, 如 fun m:test/0

断言(guard)

check(A) when length(A) > 9 ->
error;
check(_) ->
ok.
检查list长度,但如果传入的数据不是list,结果又会怎样?
11> test:check(hack).
ok
在 guard 模式下,原本会抛出异常的代码就不会报错了,而是结束当前计算并返回false。
顺道说下以下几种情况:
check1(A) when length(A) > 9 ; true->
error;
check1(_) ->
ok.
check2(A) when length(A) > 9 orelse true->
error;
check2(_) ->
ok.
check3(A) when length(A) > 9 , true->
error;
check3(_) ->
ok.
check4(A) when length(A) > 9 andalso true->
error;
check4(_) ->
ok.
12> test:check1(hack).
error
13> test:check2(hack).
ok
14> test:check3(hack).
ok
15> test:check4(hack).
ok
如此可见,orelse 和 ',' 运算还是有区别的。


io_lib:char_list()

这个函数在R15和R16下运行结果可能是相反的。

R15下:
Eshell V5.9.3.1 (abort with ^G)
1> io_lib:char_list([10100,10600,20100]).
false

R16下:
Eshell V5.10.1 (abort with ^G)
1> io_lib:char_list([10100,10600,20100]).
true
结果是截然相反,可想而知,如果写了这段代码,换个版本就可能运行有问题了。erlang的版本过渡还是有点粗放。


gen_server:cast()

gen_server在跨节点下发消息使用 noconnect ,这是正确的选择,可以参考这篇文章
但是,gen_server:cast 使用中可能会有问题。以下是 cast 的实现代码:
do_cast(Dest, Request) ->
do_send(Dest, cast_msg(Request)),
ok.
do_send(Dest, Msg) ->
case catch erlang:send(Dest, Msg, [noconnect]) of
noconnect ->
spawn(erlang, send, [Dest,Msg]);
Other ->
Other
end.
当连接不成功时,cast还会新起一个进程去发消息。这就算了,而且不带 noconnect,就是说还会尽最大可能把消息发出去。本来这里是没有不好,但是一般使用gen_server不会意识到有这个问题。在网络不理想的情况,就会莫名其妙多了很多进程,进程本身有最大数量限制。另外,消息发不出去,还是堆积在内存中。


不同类型数据比较

不同类型的数据也可以比较大小
16> hack >1.
true
17> [] > 1.
true
18> [] > {a,b,c}.
true
比较公式为:number < atom < reference < fun < port < pid < tuple < list < bit string


receive .. after ..

这个是匹配消息超时的语法结构,after 后面带数字表示超时的时间,单位为毫秒,但这个数值不能超过32位,即最大4294967295
test() ->
receive
after 4294967296 ->
ok
end.
最致命一点是,以上代码在编译时不会抛出异常,而是在运行时才抛出。
18> m:test().
** exception error: bad receive timeout value
in function m:test/0 (m.erl, line 8)


系统限制

这些有点老生常谈了,不过开发中还是要注意。
1. mnesia或dets有2G限制(无法定制)
2. ets表最大数量默认1400(可用 ERL_MAX_ETS_TABLES 定制)
3. 原子最大数量默认 1048576 (可用 +t 定制)
4. 进程最大数量默认 32768 (可用 +P 定制, 范围1024-134217727)
5. 端口/文件句柄最大数量默认 16384(可用 +Q 定制, 范围1024-134217727)


2015/3/4 补充erlang随机数函数random:uniform()
2015/3/6 补充字符串转原子函数list_to_atom()
2015/4/23 补充匿名函数导致进程被kill情况
2015/8/9 补充进程信息调试函数process_info的说明
参考:http://blog.csdn.net/mycwq/article/details/43775285
分享到:
评论

相关推荐

    erlang-23.2.1-1.el7.x86-64.rpm

    Erlang:RabbitMQ 是用 Erlang 编写的,因此需要 Erlang 运行时。确保安装了兼容的 Erlang 版本;Erlang:RabbitMQ 是用 Erlang 编写的,因此需要 Erlang 运行时。确保安装了兼容的 Erlang 版本;Erlang:RabbitMQ ...

    erlang入门级练习:LeetCode OJ问题的部分erlang 源码

    我自己在新学erlang,在LeetCode OJ上找了题目练习,题目很适合新手熟悉语言,但是LeetCode OJ里面只有几门主流语言的答案,下面是已完成的erlang源代码,后续有空再做其他问题续传,题目包含:(源码开头都有题目...

    Centos7安装RabbitMQ的文档和安装包(包含erlang安装包).rar

    RabbitMQ基于Erlang编程语言,因此在安装RabbitMQ之前,我们需要先安装Erlang环境。本文将涵盖以下几个关键知识点: 1. **Erlang安装**: Erlang是RabbitMQ的基础,因为RabbitMQ是用Erlang编写的。首先,我们需要...

    Erlang安装包,版本:otp_win64_24.1.7.exe

    otp_win64_24.1.7.exe

    《Erlang之父:为什么面向对象很糟糕》PDF

    《Erlang之父:为什么面向对象很糟糕》PDF 《Erlang之父:为什么面向对象很糟糕》PDF 《Erlang之父:为什么面向对象很糟糕》PDF 《Erlang之父:为什么面向对象很糟糕》PDF 《Erlang之父:为什么面向对象很糟糕》PDF

    Erlang趣学指南

    这是一本讲解Erlang编程语言的入门指南,内容通俗易懂,插图生动幽默,示例短小清晰,结构安排合理。书中从Erlang的基础知识讲起,融汇所有的基本概念和语法。 这是一本讲解Erlang编程语言的入门指南,内容通俗易懂...

    RabbitMQ3.9.13和ErLang24.2版本

    总的来说,RabbitMQ 3.9.13与Erlang 24.2结合使用,为开发高效、可靠的分布式系统提供了一个强大的消息中间件解决方案。它们的组合不仅提供了高性能的消息传递,还具备高可用性和可扩展性,是现代云原生应用的关键...

    二郎助手erlang开发工具、erlang编辑器

    【二郎助手:Erlang开发的利器】 在IT领域,Erlang是一种强大的编程语言,尤其在并发处理和分布式系统方面表现出色。为了更好地支持Erlang的开发,二郎助手应运而生,它是一款专为Elang语言定制的开发工具,旨在...

    并行编程语言Erlang:Erlang OTP框架及其应用开发指南

    最后,文档通过具体的代码示例演示了如何使用Erlang OTP框架开发电话系统、即时通讯系统和分布式数据库应用,涵盖了从安装配置到具体实现的全过程。 适合人群:具备编程基础的技术人员,特别是对并行编程和分布式...

    Erlang应用优化指南

    Erlang应用优化指南 Erlang应用优化指南

    erlang趣学指南

    这本书的作者Fred Hébert是一位在一线拥有丰富实战经验的工程师,他通过轻松幽默的文风和清晰的讲解,向读者介绍了Erlang的模块、函数、类型、递归、错误处理、数据结构、并行编程、多处理、事件处理以及Erlang的...

    程序员开发erlang的资料

    6. **Eclipse插件Erlide**:Erlide是Erlang开发的Eclipse集成开发环境插件,提供代码编辑、调试、重构和项目管理等功能,帮助开发者提高Erlang编码效率。 7. **Erlang实践**:从文件名erlang_practice.pdf来看,...

    Erlang开发环境搭建

    Erlang开发环境搭建 Erlang是一种功能强大且灵活的编程语言,广泛应用于 telecommunications、金融、电子商务等领域。然而,要充分发挥Erlang的潜力,需要搭建合适的开发环境。本文将指导读者如何在Windows平台下...

    Erlang开发及应用

    Erlang开发及应用

    erlang文献及资料汇总

    开发经验: 面对软件错误构建可靠的分布式系统 编写分布式的 Erlang 程序:陷阱和对策 硝烟中的Erlang 深入底层: erlang VM基于多核处理器的可伸缩性特征 erlang VM内部数据共享机制 erlang 消息传递机制 文章地址...

    eclipse erlang 开发工具插件 2011-3 月 the stable releases

    Eclipse Erlang 开发工具插件是为Eclipse IDE量身定制的一款扩展,旨在提供一个高效、便捷的环境来编写、测试和调试Erlang代码。...无论是初学者还是经验丰富的开发者,都可以借助Erlide更高效地进行Erlang项目开发。

    Erlang编程指南

    “即便我已经使用Erlang多年,在编程的时候仍然需要参考《Erlang编程指南》。不同层次的Erlang程序员都会发现本书是有价值的学习和参考资料。”, ——Steve Vinoski,《IEEE Internet Computing》专栏作家, 《Erlang...

    Erlang游戏程序学习完整PDF手册

    这份"Erlang游戏程序学习完整PDF手册"是一份全面介绍Erlang在游戏开发中应用的学习资料,包含了Erlang的基础知识、并发原理以及在游戏开发中的实践案例。 Erlang语言的设计理念源自于Ericsson公司为解决电信系统中...

    erlang开发简介

    erlang

    Erlang and OTP in Action MEAP May 2010

    Erlang and OTP in Action Martin Logan, Eric Merritt, and Richard Carlsson MEAP Began: August 2008 Softbound print: May 2010 (est.) | 500 pages ISBN: 1933988789 Part One: Getting Past Pure Erlang; ...

Global site tag (gtag.js) - Google Analytics