ERLANG学习总结<一>

来源:互联网 时间:1970-01-01

一、基础知识
      1、基础语法的学习
            ERLANG是自己接触的第一门函数式语言, 其面向进程的优良特性让人深深为之着迷。
            在公司进行了大约一周的基础知识学习,对ERLANG的基本数据类型、变量不变特性、函数可为一切、逻辑控制语法、优雅而又方便的并发方式等ERLANG的基本特性进行了了解及编程实践。在这个过程中,渐渐熟悉了可爱的明朝,认识着一位位洋溢着青春活力的同事。在ERLANG中,最终要的数据类型就是数列和元组了,这两种优雅的结构构成了大部分的ERLANG世界,列表的解析技术方便而又快捷,可以让人仅仅使用两行代码完成其他语言需要几十行而又难以理解的快速排序算法。刚开始接触ERLANG语言,和我所学的C/C++最让人惊讶的是没有了循环结构,for、while循环在C/C++中算的上不能割舍的技术了,而在ERLANG中,循环由自己控制,尾递归技术以其优雅的姿态让人耳目一新。一句spawn就轻松的完成一次并发,一切就这么简单,在ERLANG的世界里,进程才是王者。
      2、威力强大的尾递归
            在递归的世界里,一切迭代结构被消除,我们自由的编写着递归函数,不仅仅是更加容易让人理解了,更重要的是在我们的递归中没有效率问题。通过尾递归,系统自动帮我们优化函数问题,解决栈空间的的垃圾,余下的就是和while一样高效的迭代结构。刚开始编写尾递归函数时,完全没有章法,只能通过输出来一步步的修正最后的结果,通过学习,渐渐的对尾递归有了一定的经验,参看样本、基准情况、数据的形态、假设现有的函数可用、终止性证明、基准情况最小化。通过这样的种种办法,当然,在尾递归技术中参数肯定是要比正常的递归多的,不过这并不影响效率。
      3、TCP套接字
            从书上的一个最简单的TCP服务器开始,慢慢的学着去使用gen_tcp模块的函数。遇到问题的时候就去翻看文档,需要注意的是其中的一个套接字选项,必须保证其准确性,否则数据就无法正确被接收到,在套接字的知识中,阻塞、非阻塞、半阻塞的接收方式适合不同的生产环境。
      4、Mnesia数据库
            以前所接触和使用的数据库都是基于SQL的,不过Mnesia是专属于ERLANG的数据库,它能够内置任意的ERLANG数据结构,于是我们再也不用担心在存储过程中格式问题了,数据库本身已经帮我们做了最好的实现。相比较于MSSQL的数据库操作方式,Mnesia给人一种方便简洁的感觉,能够快速实现稳定的数据储存方式,可以自由选择让我们的数据库存在于内存抑或是硬盘当中,或者是备份存储,从而大大的保证了数据的安全性。
      5、OTP模型初接触
            OTP是一个平台,一个用于构建大规模的分布式容错系统的平台,OTP才是ERLANG生产应用最关键的地方,OTP中最重要的是行为,行为当中存在着行为模式,通过回调模块我们来自由的定制这些模式,在OTP的基础上,我们不需要在考虑容错、扩容、热代码替换等事务,这一切由系统帮你搞定。在近期的学习中,通过对OTP中gen_server的学习,逐渐开始接触到生产应用级别的ERLANG程序涉及到的基本技术。
      6、gen_server剖析
            这是一个基于进程来实现具体特定功能的模块,这是一个面向功能的模块,其中由我们程序员来实现的回调模块就是用于处理服务器模块发来的请求以及给出客户端的调用的常规接口,这两种东西在其他语言中基本都是分散在不同的模块中的,而在ERLANG中,在OTP的规则下,他们完美的结合在了一起,于是就产生了强的的服务器程序。
二、聊天Demo的实现
      1、随功能增加不断变化的通讯协议
            在经历了一段时间的基础学习后,为了更深入的对ERLANG进行掌握,于是开始在导师的指导下开展一个聊天Demo的改进工作,在开始动手之前,动手设计通讯协议,以前只是见过已经成为系统规范的一些通讯协议,刚开始的时候我在通讯协议中加入了包长的概念,结果在后面的一段时间内没有用到包长的数据,就将其移除了,但是后来遭遇了粘包问题,就又在通讯协议中增加了包长的字节,同时随着功能的不断增多,协议号在编程的过程中不断的增加,在导师的指导下,渐渐的将一些数值从编码中移除,转而使用宏定义的方式来实现,这样就增加了代码的可读性和可维护性。
      2、关于随机数的一些想法
            在位置信息的随机过程中,ERLANG语言的伪随机数产生算法的种子是根据当前时间来获得的,如果并发的时间相同,那么两个不同的进程所产生的随机数是相同的,这个时候就需要使用seed()函数来初始化种子信息,其参数取当前的系统时间,这样就能保证不同的进程产生不同的随机数。同时,对于random:uniform(25)函数而言,产生的随机数是1到25,而在AS中以Math.round(Math.random() * 25) 的方式产生的随机数是0到25。必须注意其中的区别
      3、gen_tcp中关于send的问题
            遭遇一个特殊的Bad value on output port 'tcp_inet'错误,刚开始以为是TCP发送函数的问题,后来通过百度,知道了gen_tcp:send(Socket,L)中的L必须是iodata()数据格式,继续深究iodata(),发现其是二进制数和iolist()的合集,再通过去看二进制格式的数据和List数据的格式,发现时因为我发送的数据中存在大于255的单个原子,由此出现了数据格式的错误,在程序中,不能通过list将大于255的数据发送出去,要注意。
      4、ERLANG的编码方式
            ERLANG中字符的编码方式是Latin-1,但是AS中字符的编码方式是Unicode。刚开始想改变ERLANG的系统语言来实现输出一些中文字符,但是在深入了解之后,发现与其让ERLANG显示中文,不如直接将数据通过列表的处理后直接传出去,结果就是在AS中可以实现正确的编码。在一系列的测试中io:format()函数在输出数据时候,需要在数据外加上[]符号确保数据变成列表格式,对于一些复杂的格式,可以通过binary_to_list(list_to_binary(Data))来实现将其转化为标准的列表格式。
      5、阶段性总结
            通过第一个完整的ERLANG OTP程序的训练,使我对这门语言有了更加深入的认识,在此基础上能够对TCP套接字编程进行一定的实践,通过导师、同事、书本、网络的帮助使我能够开始独立的解决遇到的一些问题,并能够根据问题进行深入的学习。
三、压力测试的完成
      1、ERLANG并发的威力
            在完成聊天Demo之后,开始进行压力测试,最先完成的版本在并发了几十条TCP连接的机器人的攻击下直接就倒下去了,再也站不起来了,于是通过不断的对服务端和客户端进行重新设计,使得服务端能够支持越来越多的机器人。机器人的产生通过并发原句spawn 和spawn_link来实现,简简单单1000多并发进程就被开辟出来,在ERLANG的世界中,据说进程和C++中的对象一样方便,终于见识到了,在本地机器上可以轻松的并发出10万左右的并发进程,就像一只等着你指挥的强大军队一样,伴随你的口令攻城略地。
      2、QLC和脏操作
            在最初的设计中,我使用了QLC操作来处理数据库的数据,后来在导师的指导下知道了QLC的低效性,但是不能否认QLC的事务具有很强的容错能力,不会导致数据库的同时读写问题。后来通过使用脏操作来进行数据库的读取操作,由于服务器对于数据库更大的压力在于读取,所以脏操作很适合本聊天系统,通过dirty_match_object(Data) 来进行数据匹配,其中一个问题是Data是一个元组,其首项要求必须是表的名字。
      3、gen_server中消息传递和init
            在服务端的后期版本中,并发出多个进程来处理不同的消息,当然,gen_tcp:recv在主进程中,没有进行并发操作,在recv得到消息后就将消息通过消息传递符号!将消息传递给处理的进程,在某一段时间,我在init中做了一个尾递归处理,然后就悲剧了,因为这个尾递归一直在跑,没有返回,所以系统就阻塞住了,再也不能往下进行,于是对init函数进行了一定的学习,发现这个函数实现的是一些初始化工作,必须在其返回了值以后程序才能进行下去,所以记住了绝对不能阻塞init。
      4、可恶的文件句柄
            在测算服务端可以接受多少连接的时候,设置机器人只进行TCP连接而不进行其他的动作。总是在1000多一点TCP连接的时候出现系统限制错误,后来发现这是由于文件句柄的限制,对于每一个进程而言,它能够打开的文件句柄数量是1024,而每一个TCP连接就是一个文件句柄,在网络上只发现了如何在Unix系统下突破文件句柄限制的办法,无法解决windows系统下文件句柄的问题。
      5、遭遇粘包危机
            随着测试的进行,问题不断的暴露出来,在数据的发送到达一定的速度以后,服务端和客户端都出现了粘包问题,后一个包直接就接在前一个包后面出现,为此,尝试了两种办法解决,第一种就是在数据包的包头添加包长的选项,在对数据包进行读取的过程中先读取包长,然后再根据包长来读取数据,这种办法在庆亮的博客中有提到。还有就是伟佳告诉我的标志位的方法。在一个包的后面加一个标志字符,我用的是[255]作为标志字符,在客户端按照位来读取,然后AS中调用String.fromCharCode(temp)方法来实现把字符一位一位的转变为Unicode的字符显示就是正确的了。
      6、负载均衡的思考
            在测试的过程中,发现在TCP连接数超过600后,由于系统的负载均衡没有做考虑,导致系统的性能差异很大,在峰值的情况下,有的TCP连接就会被断掉,需要下一步继续学习。
四、来个小结吧,这幸福的20多天
         最后得到的服务器程序总共有28个并发进程,其中一个主进程,用于分发TCP消息,一个世界进程,用于处理世界消息、国家消息、登录消息。25个地图进程,分别处理地图中人员的走动,本地频道的说话消息。还有一个计数器进程用于计算当前的连接数量。
         有一句话这样子说的“生命短暂,我用ERLANG”,开发速度上的优势以及并发上的超强能力使得ERLANG在生产系统中强大无比。以后的道路需要不断的加油,不断的学习,努力争取提升自己的能力,创造实际的价值,实现双赢。


相关阅读:
Top