当前位置: 首页 > 科技观察

7天假期学习Elixir,掌握函数式编程和Actor模型

时间:2023-03-13 21:07:19 科技观察

为什么要学习Elixir?Elixir是一门基于erlang开发的新语言,它复用了erlang的虚拟机和所有库(该站已经在巨人的肩膀上存活了20多年),它定义了新的语法,构建了一个必不可少的生态环境现代语言——包管理器、测试工具、格式化器等。使用Elixir,你可以轻松构建可用性高达99.9999且自然分布的程序(轻松编写代码稳定和分布式),你可以打开数千个Elixir中的独占进程秒级(轻于系统进程)级别),处理高并发请求等。Elixr/Erlang天生就支持分布式,作者JoeArmstrong在他的论文中是这样认为的:几乎所有的传统编程语言都缺乏对真正的并发的强大支持——它本质上是顺序的,语言的并发性都是由底层操作系统提供的,而不是由语言提供的。用对并发性提供良好支持的语言(即作者所说的面向并发的语言ConcurrencyOrientedLanguage)编写程序是相当容易的activitiesElixir是用什么样的语言来编写所有可以在不同消息通道中流动的消息?Elixir是函数式语言,不兼容java、C++等过程式语言,没有变量。也就是说,所有的变量都是不可变的(unchangeable)。通过学习Elixir,您可以学习另一种编程范式。这是你在python中处理列表的方式:mylist=[]mylist.append('Google')mylist.append('Facebook')printmylist#Theresultis['Google','Facebook']这就是Elixir的样子:myList=[]myList=List.insert_at(myList,0,"Google")myList=List.insert_at(myList,1,"Facebook")这在IO.inspectmyListelixir中是不正常的,但我只是用这些例子来介绍相似之处和差异。注意,在面向对象思想的语言中,处理列表的方法是使用对象的方法,mylist.append:object。要处理的操作;又因为变量在函数式方法中是不可变的,所以是List.append(mylist,xx),objectmodule.action(whichobject)去处理,会返回修改后的新对象。不可变数据的好处是,在高并发下,不会因为状态多,不断变化,调试起来异常困难——人脑不习惯多线程。immutable就是不能用for和while循环,因为不存在不断变化的变量,达到某个值就会终止循环~所以只能用递归来实现while。不过不用怕,Elixir提供了极其强大的抽象,每个函数,map函数,reduce函数,all?function(判断list的所有值是否满足这个条件),group函数(类似于数据库的group)等等,只有你想不到。相比之下,golang实在是乏善可陈。来自Go语言的创始人之一、经验丰富的Unix黑客RobPike的建议:因此,没有泛型就无法工作。..Pipeline是的,类似于linuxpipe|,将处理结果传递给下一个函数。1..100|>Enum.map(fnx->x+1end)|>Enum.filter(fnx->rem(x,2)==0end)|>Enum.filter(fnx->rem(x,3)==0end)|>Enum.filter(fnx->rem(x,5)==0end)|>IO.inspectPython与以下代码相比是否相形见绌?numbers=range(1,100)numbers=map((lambdax:x+1),numbers)numbers=filter((lambdax:x%2==0),numbers)numbers=filter((lambdax:x%3==0),numbers)numbers=filter((lambdax:x%5==0),numbers)print(numbers)再举个例子,来自DaveLong的博客PlayingwithElixirPipes[1]:代码的作用是:取出请求的headerx-twilio-signature签名,并检查它是否有效。当没有管道时,代码看起来像这样:send_resp(conn,401,"Notauthorized"))结束加管道:signature=conn|>get_req_header("x-twilio-signature")|>List.firstifconn|>url_from_conn|>Validator.validate(conn.params,signature)doconnelseconn|>send_resp(401,"Notauthorized")|>haltend逻辑很清晰。也可以这样写:signature=conn|>get_req_header("x-twilio-signature")|>List.firstconn|>url_from_conn|>Validator.validate(conn.params,signature)|>if(do:conn,else:conn|>send_resp(401,"Notauthorized")|>halt)ProcessActorModel轻量级进程在Elixir中,Elixir进程(以下简称进程,区别于系统进程)是一个轻量级进程,它与操作有关系统的概念是类似的,只是Elixir进程运行在虚拟机中。那么为什么Elixir进程更快呢?Erlang进程的堆栈是动态分配的,并且随着使用而增长。创建一个新的Erlang进程的开销比系统进程/线程的开销小得多。开销与用OO语言创建新对象一样简单。正常的进程/线程内存管理是基于页面的,页面对于一个功能来说太大了+一点零碎。实际上,操作系统分配给普通进程的初始堆栈可以达到兆字节级别。Erlang进程是隔离的,没有共享状态,所有消息都是异步的,大量已有状态不被继承。Erlang进程的调度发生在ErlangVM中,与OS层无关,正常进程/线程切换时不需要各种开销。Erlang进程切换是一种类似直接“跳转”的方法,实现复杂度为O(1)。Erlang调度程序管理这些开关,可能只需几十条指令和几十纳秒。普通线程的切换需要几百纳秒,OS调度器的运行复杂度可能是O(logn)或者O(log(logn))。如果有几万个线程,这个时间会有很大的提升。来自知乎[2]像指挥交响乐团一样,指挥你的Elixir进程对于Elixir进程,你可以很方便地使用一个进程(supervisor)来管理子进程,supervisor会根据你设置的策略处理子进程的意外挂断进程(这种情况很少见,如果错误处理没做好,就会挂掉),策略是::所有在挂起的子进程创建时间之后创建的子进程都会被重启。敲代码的老头是个飞梭!但是不行,重启就好了。本质上,这得到了论文的支持:在复杂的生产系统中,几乎所有的故障和错误都是暂时的,重试操作是一个很好的解决方案-JimGray的论文[3]中指出,平均故障间隔时间(采用该方法处理瞬态故障,系统的MTBF)提高了4倍。因此,可以创建一棵类监控树,根节点就是除了监控什么都不做的进程。其他都是它的子进程,如果不是coredump(几乎不会发生),那么根节点挂掉的可能性不大;因此其他子进程将被正确处理。当然有一个前提:5秒内重启3次以上,它就会停止重启,让进程挂掉。为什么?因为重启是为了让进程恢复到启动时的稳定状态。由于稳态不稳定,反复重启是没有意义的。这个时候,就急需人们去处理了。轻松沟通一切都是信息。进程间通信就像微风一样自然。你监管的进程的信息,你调用的库的消息,都可以自己处理,做相应的处理。甚至还有一个抽象的GenServer可以让您专门处理消息和状态逻辑。定时器?不需要,我们甚至可以自己发送消息来实现更好的定时器:process.send_after会在xx秒后向指定进程发送消息。通过这个函数,我们可以不断的给自己发送消息,实现定时器功能。请看实现:defmodulePeriodicallydorequireLoggeruseGenServerdefstart_linkdoGenServer.start_link(__MODULE__,%{})enddefinit(state)doschedule_work(:do_some_work){:ok,state}enddefhandle_info(:do_some_work,state)dodoing_now()schedule_work(:do_some_workply)st:nore什么是enddefpschedule_work(update_type)doProcess.send_after(self(),update_type,30*1000)endend与setTimeOut等相比的好处?Elixir附带了可以查看和管理所有进程状态的工具。Periodically被用作一个进程一旦启动,自然要管理它:Pletitcrash的想法的提出和实现。程序不可能处理所有的错误,所以程序员只需要尽可能地处理那些明显的错误,而让他把那些隐藏的、非直觉的错误——它们很可能是罕见的错误、频繁出现的错误那些?程序员需要手动处理它们。这是紧急情况。即使trycatchallerrors,也无法避免,因为系统已经在崩溃的边缘,再拖下去就是自欺欺人。需要注意的是,让它崩溃并不是说你用golang、C++等语言打包一个binary,用supervisorctl等工具来监控。如果出错,马上崩溃,挂了会自动重启-=-模式匹配和宏比较通常我们的赋值语言比较新,介绍的太长了。通过模式匹配参见http://szpzs.oschina.io/2017/01/30/elixir-getting-started-pattern-matching/和https://elixir-lang.org/getting-started/pattern-matching.html,我们可以避免ifelse的嵌套地狱;我们可以使用语言自带的匹配来做搜索,宏可以让你实现自定义的DSL(当然功能太强大自然会导致滥用bug),可以屏蔽掉很多不雅的细节。以上就是Elixir的简单介绍。我建议你学习Elixir并刷新你的OO和过程编程思维。本文转载自微信公众号《山尽头写东西的缓存》,可通过以下二维码关注。转载本文请联系写东西的cache公众号。