一个Erlang诗歌代理

让我们看一下Erlang诗歌客户端. 这次我们直接跳入工作版本而不是像在Twisted中慢慢地搭建它.同样,这不是意味着完整版本的Erlang介绍. 但如果这激起了你的兴趣,我们在本部分最后建议了一些深度阅读资料.

下面代码是 main 函数代码,与Python客户端中的 main 函数具有相同的目的:

main([]) ->

usage();

main(Args) ->

Addresses = parse_args(Args),

Main = self(),

[erlang:spawn_monitor(fun () -> get_poetry(TaskNum, Addr, Main) end) || {TaskNum, Addr}

collect_poems(length(Addresses), []).

如果你从来没有见过Prolog或者相似的语言,那么Erlang的句法将显得有一点奇怪.但是有一些人也这样认为Python.

main 函数被两个分离的句群定义,被分号分割. Erlang根据参数选择运行哪一个句群,所以第一个句群只在我们执行客户端时不提供任何命令行参数的情况下运行,并且它只打印出帮助信息.第二个句群是所有实际的行动.

Erlang函数中的每条语句被逗号分隔,所以函数以句号结尾.让我们看一看第二个句群,第一行仅仅分析命令行参数并且将它们绑定到一个变量(Erlang中所有变量必须大写).第二行使用 self 函数来获取当下正在运行的Erlang进程(而非OS进程)的ID.由于这是主函数,你可以认为它等价于Python中的 __main__ 模块. 第三行是最有趣的:

[erlang:spawn_monitor(fun () -> get_poetry(TaskNum, Addr, Main) end)

|| {TaskNum, Addr}

这个语句是对Erlang列表的理解,与Python有相似的句法.它产生新的Erlang进程,对应每个需要连接的服务器. 同时每个进程将运行相同的 get_poetry 函数, 但是根据特定的服务器用不同的参数.我们同时传递主进程的PID以便新的进程可以把诗歌发送回来(你通常需要一个进程的PID来向它发送消息)

main 函数中的最后一条语句调用 collect_poems 函数,它等待诗歌传回来和 get_poetry 进程结束.我们可以看一下其他函数,但首先你可能会对比一下Erlang的 main 函数与等价地Twisted客户端中的 main 函数.

现在让我们看一下Erlang中的 get_poetry 函数.事实上在我们的脚本中有两个函数叫 get_poetry.在Erlang中,一个函数被名字和元数同时确定,所以我们的脚本包含两个不同的函数, get_poetry/3 和 get_poetry/4,它们分别接收3个或4个参数.这里是 get_poetry/3,它是被 main 生成的:

get_poetry(Tasknum, Addr, Main) ->

{Host, Port} = Addr,

{ok, Socket} = gen_tcp:connect(Host, Port,

[binary, {active, false}, {packet, 0}]),

get_poetry(Tasknum, Socket, Main, [])

这个函数首先做一个TCP连接,就像Twisted客户端中的 get_poetry.但之后,不是返回,而是继续使用那个TCP连接,通过调用 get_poetry/4,如下:

get_poetry(Tasknum, Socket, Main, Packets) ->

case gen_tcp:recv(Socket, 0) of

{ok, Packet} ->

io:format("Task ~w: got ~w bytes of poetry from ~s\n",

[Tasknum, size(Packet), peername(Socket)]),

get_poetry(Tasknum, Socket, Main, [Packet|Packets]);

{error, _} ->

Main ! {poem, list_to_binary(lists:reverse(Packets))}

end

这个Erlang函数正在做Twisted客户端中 PoetryProtocol 的工作,不同的是它使用阻塞函数调用. gen_tcp:recv 函数等待在套接字上一些数据的到来(或者套接字关闭),无论要等多长时间.但Erlang中的”阻塞”函数仅阻塞正在运行函数的进程,而不是整个Erlang运行时.那个TCP套接字并不是一个真正的阻塞套接字(你不能在纯Erlang代码中创建一个真正的阻塞套接字).对于Erlang中的每个套接字,在运行时的某处,一个”真正的”TCP套接字被设置为非阻塞模型并且用作选择循环的一部分.

但是Erlang进程并不知道这些.它仅仅等待一些数据的到来,如果阻塞了,其他Erlang进程会代替运行.甚至一个进程从不阻塞,Erlang运行时可以在任何时刻自由地在进程间切换.换句话说,Erlang具有一个非协同并发机制.

注意 get_poetry/4,在收到一小部分诗歌后,继续递归地调用它自己.对于一个急迫的语言程序员这看起来像耗尽内存的良方,但Erlang编译器却可以优化”尾”调用(函数调用一个函数中的最后一条语句)为循环.这照亮了又一个有趣的Erlang客户端和Twisted客户端之间的平行对比.在Twisted客户端中,”虚拟”循环是被 reaactor 创建的,它一次又一次地调用相同的函数(dataReceived).同时在Erlang客户端中,”真正”的运行进程(get_poetry/4)形成通过”尾调优化“一次又一次调用它们自己的循环.感觉怎么样.

如果连接关闭了, get_poetry 做的最后一件事情是把诗歌发送到主进程.同时结束 get_poetry 正在运行的进程,因为剩下没什么可做的了.

我们Erlang客户端中剩下的关键函数是 collect_poems:

collect_poems(0, Poems) ->

[io:format("~s\n", [P]) || P

collect_poems(N, Poems) ->

receive

{'DOWN', _, _, _, _} ->

collect_poems(N-1, Poems);

{poem, Poem} ->

collect_poems(N, [Poem|Poems])

end.

这个函数被主进程运行,就像 get_poetry,它对自身递归循环.它同样阻塞. receive 告诉进程等待符合给定模式的消息到来,并且从”信箱”中提取消息.

collect_poems 函数等待两种消息: 诗歌和”DOWN”通知.后者是发送给主进程的, 当 get_poetry 进程之一由于某种原因死了的情况发送(这是 spawn_monitor 的监控部分).通过数 DOWN 消息,我们知道何时所有的诗歌都结束了. 前者是来自 get_poetry 进程的包含完整诗歌的消息.

OK,让我们运行一下Erlang客户端.首先启动3个慢服务器:

python blocking-server/slowpoetry.py --port 10001 poetry/fascination.txt

python blocking-server/slowpoetry.py --port 10002 poetry/science.txt

python blocking-server/slowpoetry.py --port 10003 poetry/ecstasy.txt --num-bytes 30

现在我们可以运行Erlang客户端了,与Python客户端有相似的命令行句法.如果你在Linux或其他UNIX-样的系统,你应该可以直接运行客户端(假设你安装了Erlang并使得它在你的PATH上).在Windows中,你可能需要运行 escript 程序,将指向Erlang客户端的路径作为第一个参数(其他参数留给Erlang客户端自身的参数).

./erlang-client-1/get-poetry 10001 10002 10003

之后,你可以看到如下输出:

Task 3: got 30 bytes of poetry from 127:0:0:1:10003

Task 2: got 10 bytes of poetry from 127:0:0:1:10002

Task 1: got 10 bytes of poetry from 127:0:0:1:10001

...

这就像之前的Python客户端之一,打印我们得到的每一小部分诗歌的信息.当所有诗歌都结束后,客户端应该打印每首诗的完整内容.注意客户端在所有服务器之间切换,这取决于哪个服务器可以发送诗歌.

图45展示了Erlang客户端的进程结构:

图45: Erlang诗歌客户端

这张图显示了3个 get_poetry 进程(每个服务器一个)和一个主进程.你可以看到消息从诗歌进程流向主进程.

那么当一个服务器失败了会发生什么呢? 让我们试试:

./erlang-client-1/get-poetry 10001 10005

上面命令包含一个活动的端口(假设你没有终止之前的诗歌服务器)和一个未激活的端口(假设你没有在10005端口运行任一服务器). 我们得到如下输出:

Task 1: got 10 bytes of poetry from 127:0:0:1:10001

=ERROR REPORT==== 25-Sep-2010::21:02:10 ===

Error in process <0.33.0> with exit value: {{badmatch,{error,econnrefused}},[{erl_eval,expr,3}]}

Task 1: got 10 bytes of poetry from 127:0:0:1:10001

Task 1: got 10 bytes of poetry from 127:0:0:1:10001

...

最终客户端从活动的服务器完成诗歌下载,打印出诗歌并退出.那么 main 函数是怎样得知那两个进程完成工作了? 那个错误消息就是线索. 这个错误源自当 get_poetry 尝试连接到服务器时没有得到期望的值({ok, Socket}),而是得到一个连接被拒绝的错误.

Erlang进程中一个未处理的异常将使其”崩溃”,这意味着进程停止运行并且它们所有资源被回收了.但主进程,它监视所有 get_poetry 进程,当任何进程无论因为何种原因停止运行时将收到一个DOWN消息.这样,我们的客户端就退出了而不是一直运行下去.

python twisted教程_Python Twisted系列教程20: Twisted和Erlang相关推荐

  1. python面向对象教程_Python入门系列教程3-面向对象

    课程咨询.获取课件.技术交流直接加入博学谷在线学习:631731828 课程简介 本课程是Python系列入门课程的第三阶段,该阶段我们会学习面向对象(OOP)这一重要的编程思想.学习的知识点主要包括 ...

  2. Python学习教程(Python学习路线_Python基础学习教程_Python视频教程):初学者新手怎样快速入门Python

    Python学习教程(Python学习路线_Python基础学习教程_Python视频教程):初学者新手怎样快速入门Python? 人生苦短,我用Python!!!短短几个字,现在在各大学习类平台随处 ...

  3. python网页填表教程_PythonSpot 中文系列教程 · 翻译完成

    原文:PythonSpot Python Tutorials 协议:CC BY-NC-SA 4.0 欢迎任何人参与和完善:一个人可以走的很快,但是一群人却可以走的更远. 在线阅读 ApacheCN 学 ...

  4. sql python 教程_Python SQLAlchemy ORM教程(3)

    由于SQLAlchemy 中文资料比较少,所以根据官网给的tutorial外加其他大佬写的中文资料整合以后准备写一个SQLAlchemy 系列的基础入门教程.本系列可能会夹杂一些个人对于python ...

  5. python 工业自动化控制_python工业自动化测试教程

    python工业自动化测试教程内容摘要 python工业自动化测试教程P>winpython基础教程,为你的店堂营造出一种艺术氛围,为男性开辟集中便捷的专门场所已为不少商场所采纳,为目标顾客提供 ...

  6. java线程 教程_Java多线程系列教程

    Java多线程系列教程 多线程是Java中不可避免的一个重要主体.从本章开始,我们将展开对多线程的学习.接下来的内容是对Java多线程内容的讲解,涉及到的内容包括,Object类中的wait(), n ...

  7. python中的gui界面编程_python应用系列教程——python的GUI界面编程Tkinter全解

    全栈工程师开发手册 (作者:栾鹏) python的GUI界面编程,常用的几个python库包含如下: Tkinter: Tkinter 模块(Tk 接口)是 Python 的标准 Tk GUI 工具包 ...

  8. python采集修改原创_python应用系列教程——python中ftp操作:连接、登录、获取目录,重定向、上传下载,删除更改...

    python中ftp操作: ftp=FTP() #设置变量 ftp.set_debuglevel(2) #打开调试级别2,显示详细信息 ftp.connect("IP"," ...

  9. python如何绘制条形图_python Matplotlib 系列教程(三)——绘制直方图和条形图

    在本章节我们将学习如何绘制条形图和直方图 条形图与直方图的区别: 首先,条形图是用条形的长度表示各类别频数的多少,其宽度(表示类别)则是固定的: 直方图是用面积表示各组频数的多少,矩形的高度表示每一组 ...

  10. python图形编程基础-Python从基础到入门系列教程

    本教程集合了Python基础&系统管理,从基础到入门,带你走进Python世界!对Python有兴趣的可以学习一下哦基础系列:1.课程简介2.Python下载和安装3.IDLE使用简介4.第1 ...

最新文章

  1. python3 socketserver模块 网络服务编程框架
  2. JavaScript中的箭头函数
  3. python画五角星代码_Python第八课 绘制五角星1.0
  4. 通讯录c语言以文本文件保存,学C三个月了,学了文件,用C语言写了个通讯录程序...
  5. openlayers属性数据mysql_OpenLayers学习笔记8——使用servlet从mysql获取数据并标注
  6. 单片机按键“消抖”的思考
  7. .NET/C#使用NPOI操作Excel
  8. ANE 在 Android 上的应用
  9. java中hashmap_Java HashMap – Java中的HashMap
  10. 诸神战纪堕落天使java_诸神战纪-堕落天使路西法
  11. 风控小白必看!主流风控模型解析
  12. 化工原理 --- 流体流动 2
  13. Linux 下使用Trickle限制下载/上传带宽
  14. 栈与队列(逆波兰式)
  15. 专著《Python与开源GIS:数据处理、空间分析》
  16. Spark读取Hdfs上的数据存储在Hbase的ETL过程
  17. Java实现任意进制转换
  18. 九图鸡汤(从繁体中文翻译为简体中文)
  19. android 弹幕动画,Android自制精彩弹幕效果
  20. 【深度学习论文翻译】基于LSTM深度神经网络的时间序列预测(Time Series Prediction Using LSTM Deep Neural Networks)

热门文章

  1. 酒店小秘:酒店行业应用再添新兵
  2. 一 python入门
  3. 海潮音排序算法之观音兰花手双理算法
  4. 《尼山萨满》一战成名,但中国功能游戏的取经之路才刚开始
  5. win7 去掉系统快捷方式小箭头
  6. 关于windows设备 \Device\HarddiskVolume3及其故障报错处理。
  7. android下保存图片到mySQL_android将图片保存进数据库
  8. 在计算机系统中 控制和管理各种资源,操作系统试题2
  9. 学会拒绝 (贾平凹)
  10. getchar函数与其在缓存区的使用(详解易懂)---读取多组值:scanf返回值的理解使用