erlang浅谈
优点:
1.面向并发,有成熟而且久经考验的框架可供使用,网络部分已经经过了良好封装
2.内存缓存解决方案进程字典,前者的读写速度是50NS-100Ns级别的
3.对二进制数据解析的语法是直观,简单,强大(游戏中有大量的二进制数据要处理
4.没有共享内存! 没有锁!(我们在代码中没有过显示使用锁)
缺点
1.从一种语言过渡到另一种语言,会有各种不爽:
2.控制逻辑简单只有if 和 case ,而且有if没有else,没有continue break goto
3.包括kernel库和standlib库在内,很多函数和变量的命名和传统语言不一样
一.
1. 结构: 应用--<>模块--<>函数--<>语句--<>子语句--<>表达式--<>子语句..
2. 函数式编程
a) 函数式编程,本身不存放状态,对它的影响只有输入参数,而它的影响就是输出函数。
b) 函数式编程,是分布式应用中,高并发,代码热替换的基石
c) Erlang应用就是由这些函数构成的,它的重要理念,everything is function,所有的函数都有返回值。(相当于默认最后一句就是return的值)
3. 变量
a) Erlang是面向函数式编程的,进程本身没有全局变量。
b) 在函数上下文中,可有局部变量,所有的局部变量都是readonly,及只能赋值一次
4. 尾递归优化
a) Erlang没有循环,如果要实现循环只能通过递归来实现。
b) Erlang对函数调回,如果该函数后面没有下文,则被优化,栈空间不会被占用
先看一个例子,对前面的一个总结
raw_read_loop(File, Acc) -> %%函数
case file:read(File, 10) of
{ok, Bytes} ->
raw_read_loop(File, [Acc | Bytes]); %%递归实现循环,尾递归优化
eof ->
file:close(File), %%表达式直接用逗号隔开
{ok, iolist_to_binary(Acc)}; %%子语句用分号隔开
{error, Reason} ->
file:close(File),
erlang:error(Reason)
end.
5. 模式匹配
a) 这个是横行于Erlang和区别其他语言的重要特性
b) 通过模式匹配把函数拆分成多个子句
convert_to_celsius({Name, {c, Temp}}) ->
{Name, {c, Temp}}; %%子句
convert_to_celsius({Name, {f, Temp}}) ->
{Name, {c, (Temp - 32) * 5 / 9}}.
调用convert_to_celsius("ABC", {f, 1234}}) . 根据(f),将匹配第二句
c) 分支判断
case catch onecached_storage:get_item(Storage, Key) of
{ok, {Flags, Data}} ->
...
none ->
ok;
Other ->
...
end.
d) 协议匹配
case Packet of
<<Len:16, PayLoad:Len/binary, 16#ef>> ->
{body, PayLoad};
_ ->
{error, invalid_packet}
end.
总结:
主要说他的限制把,Erlang不是GSL(通用语言),因此使用性和流行度上有天生的限制。流行度不够,所以发展也相对缓慢。其纯函数式编程,对那些复杂业务编程不太适合。
二.
1. Erlang系统特点
Erlang本身内置支持分布式应用里边的,高并发,分布式,代码热替换,容错性。
1.1. 高并发
a) 进程
i. Erlang的最小执行单元是进程,进程的最大特点是隔离性好,(有自己的独立内存空间,Crash不会影响其他工作单元)。
ii. Erlang的进程特点
1. 协程
a) 自己控制执行切换
b) 高效。由于不频繁切换系统上下文(页表等),效率比直接使用OS的高。
2. 成本极低
a) 一个Erlang进程,默认开销2K多一点(主要是栈和默认堆)
b) 对应的一个OS线程,一般栈空间都不少于512K
c) 由于是协程,进程切换直接成本极低
3. CPU亲和性
a) 协程,当然可以自己控制ERLANG进程绑定在特定的OS线程之上
b) 这个特性大大避免了锁开销
4. 零锁编程
a) 函数式编程本身理念就是无需锁的
b) 没有全局变量,进程本身状态也只有自己可以访问,因此找不到锁的用武之地
c) 不是绝对的,比如依赖外部异构资源,这个语言本身无法解决
b) Erlang的进程成本如此之低,因此我们可以以同步的方式来构建我们的异步系统。它的实现也是如此的,对耗时操作都会引起当前进程的trap.
1.2. 容错性
a) 内置的容错性
i. 基于进程的,隔离性好
ii. 支持异常捕获
iii. 进程直接可以建立关系
发生了Link关系的两个进程可以感知另外一个进程的结束,不处理默认就是一起exit了
iv. 进程和节点监控
有内置的监控机制去监控某个进程或者节点,可以感知他们down
b) 内置支持失败重启
以-heart Cmd 标志启动,当前节点crash可以自动重启。这样你无须自己去写监控脚本。
c) OTP的Supervisor模式
Supervisor可以把根据依赖关系,把进程搭建成树状结构,高层的节点或者叶节点被低层监控。
监控节点可以根据参数决定子节点的重启策略
- Restart: permanent | transient | temporary 永远重启|异常退出才重启|永远不重启
- 监控节点退出子进程的shutdown 策略
子进程的返回中控制:
- l 当前进程挂掉了重启间隔时间
- l 指定时间内重启最大频率
- RestarStrategy: l 重启时是重启自己 || 所有的进程 || 配置时排在当前进程之后
1.3. 分布式支持
1. 节点位置透明
根据节点名可以连到节点,无需指定具体的端口号
2. 自动连接
发生通讯的进程,如果处于两个节点上,这两个节点可以自动连接
3. 连接协议可配置
节点之间的连接协议是可以配置的,内置支持TCP和SSL,也可以开发自己的协议
4. 全联通
A连接了B,如果A在连接了C,这时B和C之间也可以互相感知了(nodes()和monitor都可以感知到)
5. 透明路由
进程之间的通讯不论是同一节点上,跨节点,或者跨机器,都可以以统一的Pid!Message的方式进行通讯。
因此,我们可以以统一的方式来构建我们的系统。
6. TakeOver
可以配置多个节点来运行指定应用,系统会根据顺序选定当前服务节点。如果节点挂了,后面的依次被选择。那些优先节点恢复后会抢夺运行权,重新由它提供服务
7. 代码中心
可以指定在指定机器运行代码。
它的具体实现是通过ssh在指定机器上运行erlang并把要运行的代码复制过去,然后执行。
1.4. 代码热升级
由于没有变量,而且都是基于函数式的实现,因而可以实现代码热升级。(执行的入口改了,所有的状态都在进程上下文中,下次调用时携带到参数里就行了)
1.5. Port管道
Erlang是DSL,不适合构建太复杂业务的系统。他提供了一种异构系统互通的机制,就是Port管道。
可以直接调用外部应用,跟外部应用stdin和stdout对接。也可以开发自己driver(可以做更加精细的控制,比如常驻内存select输入,输出,不同阶段有不同的回调)
三.
1. OTP
OTP把分布式应用开发的一些常用场景,进程依赖管理,反应式,状态机,事件驱动模式框架化;另外,提供了一个分布式存储。
1.1. 框架
1. supervisor
监控树。a) 配置要管理进程们启动参数
进程的所处模块,启动函数(也支持匿名函数),启动参数
b) 定制子进程的重启策略
- l 当前进程挂掉了重启间隔时间
- l 指定时间内重启最大频率
- l 重启时是重启自己 || 所有的进程 || 配置时排在当前进程之后
2. gen_server
反应式框架。自身一个进程。
支持同步的call和异步的cast消息投递,也支持Pid!Message的原语方式(这点可以让其跟很多异步接口对接起来)。
3. gen_fsm
有限状态机(本身没有状态,只能是有限)。自身一个进程。
State(S) x Event(E) -> Actions(A), State(S')
发送消息给该进程,它会回调{CurrentStateName}/2函数,CurrentStateName就是当前状态;如果是调用all_state相关的接口,会回调专门的handle_event等相关函数。
4. gen_event
消息中心。
运行的过程:
a) 启动交换器进程,监听指定消息
b) 注册消息回调
c) 发送消息
d) 交换器进程回调注册的模块相应接口
1.2. mnesia
分布式的数据库管理
1. 支持多个列
2. 支持多个副本
副本的策略有:
- ram (所有数据都在内存)
- ram_disk(数据存在内存和磁盘)
- disk(数据只放在磁盘)
3. 支持表分片
4. 使用时不用指定副本位置
自动选择合适的副本
限制
处理大文件有一定的性能问题