文档章节

Elixir 与 Go 对比 [翻译]

nicozhang
 nicozhang
发布于 03/18 19:14
字数 6168
阅读 5942
收藏 42
点赞 4
评论 25

阅读时间: 16 分钟

之前一直做 Python 和 Rails(暂时代替 Ruby) 的开发。 这两种语言都有自己的优势,应用都很广泛,但也不乏有缺陷,最突出的问题莫过于性能了。所以,在高并发到来之前,需要寻找高性能的语言来做替补,主要用来替代 Rails 的角色。Golang 是首先想到的,但在 Rails 社区 Elixir可以说高性能的代名词。在查找这两门语言对比的时候,发现了这篇文章。原文链接


在过去的几年里, Elixir 和 Go 的流行度均有巨大的提高。这两种语言通常能满足开发人员寻找的高并发方案。它们遵循许多相似的原则,但两者都对一些特殊场景下的应用做了折中。

下面通过它们的背景、编程风格及并发的处理对比这两门语言。

起源

Go/Golang自2009年起由Google研发,以二进制文件( 编译后)的形式运行在部署的平台上。 最开始的时候,它被作为一种尝试去 创建一种新的编程语言, 以解决其他编程语言的主要弊端,并保持其优势。

Go在实现开发速度、并发性、性能、稳定性、可移植性和可维护性的平衡方面做得非常出色。因此,Docker和InfluxDB都是用Go构建的,包括谷歌、Netflix、Uber、Dropbox、SendGrid和SoundCloud在内的许多大公司都在使用它来做分类工具。

Elixir自2011年由Jose Valim在Plataformatec的时候开发的,运行在BEAM VM, 也就是 Erlang VM。

Erlang自1986由爱立信开发,用于高可用分布式电话系统。它已经扩展到诸如网络服务器等许多其他领域,并已经实现了9个9的可用性(31毫秒/年的停机时间)。

Elixir的设计目标是在保持与Erlang生态系统兼容性的同时,还能够在Erlang VM中实现更高的可扩展性和生产力。 它通过在Elixir代码中使用Erlang库来实现这个目标,反之亦然。

为了避免重复,我们将Elixir / Erlang / BEAM统称为“Elixir”。

许多公司已经在生产环境中使用 Elixir, 这其中包括 Discord 、 Bleacher Report 、 Teachers Pay Teachers、 Puppet Labs 、Seneca Systems 和 FarmBot。 以及其他很多项目也是使用Erlang构建的,包括WhatsApp,Facebook的聊天服务,Amazon的CloudFront CDN,Incapsula,Heroku的路由和日志记录层,CouchDB,Riak,RabbitMQ以及全球约一半的电话系统。

编程风格

理解每个运行时的核心原理,才能对 Elixir 和 Go 做可靠对比,因为这些构建块是语言的基础。

Go是一种对传统C系编程背景的人来说更容易熟悉的语言, 尽管它做了一些有利于函数式编程的设计。Go的静态类型,指针和结构体,会让你有种似曾相识的感觉。

函数可以附加到结构体类型,这种组合方式随着时间的推移更能促进项目的增长。函数可以在任何地方创建并附加到结构体,而不是将其嵌入到必须扩展的对象中。

如果一个方法需要被多种类型的结构体调用时,可以为这个方法定义一个接口以便于提供更大的灵活性。典型的面向对象的编程语言必须首先定义一个对象来实现一个特定的接口, 与之不同的是Go中的接口将被自动应用于与之相匹配的任何事物。Go的接口实例

Elixir 更倾向于函数式风格, 但融合了一些面向对象语言的原理,使它的这种过渡不显得那么违和。

变量是不可变的,由于使用消息传递,就不需要传递指针,这意味着函数调用是非常直接的。 传入参数,返回结果,没有其他影响。 这简化了一些例如测试和代码可读性方面的问题。

由于数据不可变,诸如for循环之类的常见操作是不可用的,因为无法创建一个递增的计数器。尽管Enum(枚举)库以一种简单的方式提供了常见的迭代模式,但递归通常被用来代替这类操作。

由于递归使用频繁,因此Elixir还专门做了尾部调用优化。如果函数的最后一次调用的是自己,则可以防止调用堆栈的增长,从而避免堆栈溢出错误。

Elixir广泛的采用模式匹配,这与Go利用接口的方式非常相似。 使用Elixir,函数可以定义为:

def example_pattern(%{ "data" => %{ "nifty" => "bob", "other_thing" => other}}) do
  IO.puts(other)
end

使用 map 模式作为函数的参数,只有当传入的 map 包含键为 data, 值为嵌套的 map, 且值中又包含键为 nift 值为 bob 和 另外一个键为 other_thing的时候,改函数才会被调用。而此时,变量other才会被赋值。

从函数参数到变量赋值,尤其是递归,都会用到模式匹配。这里有些例子,可以感受一下。 结构可以被定义为类型,然后也可以在模式匹配中使用。

从原理上讲,这两种方法是非常相似的。两者都将数据结构和数据操作分开。同时也都是通过匹配来定义函数的调用,Go 通过接口,而 Elixir 通过模式匹配。

尽管 Go 允许函数通过特定的类型调用, g.area(), 但实际上这跟 area(g)是一样的。两者之间的唯一区别,在Elixir中 area()需要返回结果,而 Go 则是在内存里实现了一次引用。

由于采用这种方法,这两种语言的组合性变得非常强,在项目的整个生命周期中,不用去控制、扩展、注入以及重建大的继承树。这对大项目来说是个重大的利好。

此处最大的区别是,使用 Go 的时候,为了重用这种模式会在函数外定义,但如果组织不好,可能导致创建大量重复的接口。Elixir 不能简单的复用模式,但模式总是在使用它的地方定义。

Elixir使用"strong" typing(强类型)而不是static typing(静态类型), 而且大部分是推断出来的。在 Elixir 中, 没有符号重载,所以不能用+连接两个字符创会让你感觉很困惑。在 Elixir 中,<>可以用来连接字符串。

如果不理解背后的原因,这会让你感觉憋得慌。编译器能够通过显式的运算符推断出加号两边必须都是数字。同样,<>任何一边都必须是字符串。

强类型本质上是指动态类型, 其中编译器通过透析器(dialyzer)可以捕捉每种类型, 模式匹配中歧义参数除外(避免为_ 指代的不会被用到的变量或参数分配内存,就像Go一样,只是用来占位) 。 代码注释可以用来定义这些异常情况下的类型。 这样做的好处是,你可以获得静态类型的大部分优势,又不会失去动态类型所带来的灵活性和元编程特权。

Elixir的文件可以使用.ex作为编译代码的后缀,也可以用.exs 作为运行时编译的脚本后缀,例如 shell 脚本等。Go 总是被编译的, 然而 Go 的编译器是如此之快,以至于庞大的代码块也能在瞬间完成编译。

并发

并发性是本次比较的关键。现在你已经对语言风格有了一个基本的了解,其余部分会更有意义。

传统意义上,线程所涉及的并发是比较重量的。最近,一些编程语言开始使用"轻线程"或"绿线程", 实际上它是在单个线程里使用一个调度器来管理不同逻辑的轮流执行。

这类的并发模型是内存高效的,但依赖于运行时指定的执行流程。JavaScript 已经使用这种风格很多年了。举个简单的🌰, 当你听到JavaScript 的 “非阻塞I/O”时,意味着在线程中执行的代码需要执行 I/O操作的时候,会将控制权交还给调度器。

合作式调度对比抢占式调度

Elixir和Go都使用了调度器来实现它们的并发模型,这两门语言天生支持多核,但JavaScript不支持。

Elixir和Go 分别用不同的方式实现了调度。Go 使用了协作式调度, 也就是说运行时代码必须交还控制权给调度器,以便其他操作轮流执行。Elixir使用抢占式调度,这种方式会为每个操作强制预设一个执行窗口。

协作式调度在基准测试方面更高效, 抢占式调度会因为开始的强制执行而产生额外开销。但抢占式调度的的一致性更好,这能确保数以百万的小型操作不会因一个不放弃执行权限的大型操作所拖延。

Go 程序员可以在代码中插入runtime.Goshed(), 强制调度器执行更多校验, 以作为应对潜在问题代码的预防措施。运行时强制性的允许相信更多第三方库和实时系统。

Goroutines对比"进程"

在 Go 中通过goroutine执行并发。只需要简单的在方法前面加上go就可以了, 任何方法都可以这么做。如下:

hello("Bob")
// To...
go hello("Bob")

Elixir 在这点上与 Go 非常相似。Go 用 go 创建 goroutine, Elixir 生成"进程"(spawn processes 这里的进程不是操作系统的进程)。另外请注意,函数必须在Elixir的模块里面。

# From...
HelloModule.hello("Bob")
# To...
spawn(HelloModule, :hello, ["Bob"])
# Or by passing a function
spawn fn -> HelloModule.hello("Bob") end

此处的最大区别是,go 不会返回任何东西,spawn 会返回进程的 ID。

这两个系统都通过消息队列实现了例程间的通信。Go 称它为管道(channels), Elixir 称它为进程邮箱(process mailboxes)。

在 Go 中,可以先定义一个管道,如果引用了该管道,任何东西都可以通过管道传递消息。在 Elixir 中, 消息可以通过进程 ID 或进程名字给进程传递消息。Go 中的管道需要按消息的类型进行定义,而 Elixir 使用模式匹配处理消息邮箱。

发消息给 Elixir 的进程相当于发消息给goroutine所监视管道。🌰: Go channel

messages := make(chan string) // Define a channel that accepts strings

go func() { messages <- "ping" }() // Send to messages

msg := <-messages // Listen for new messages
fmt.Println(msg)

Elixir 进程邮箱

send self(), {:hello, "world"}
receive do
  {:hello, msg} -> msg # This reciever will match the pattern
  {:world, msg} -> "won't match"
end

两者可以在监听消息时设置超时。因为 Go 有共享内存,所以 goroutine 可以直接转化成内存引用,但需要互斥锁以避免竞争。理想情况下,一个 goroutine监听一个管道来更新内存, 就不需要互斥锁了。

除了这个功能之外,事情还会扩张。

在使用并发和分布式逻辑时,Erlang定义了一套捆绑在“OTP”下最佳实践模式。多数情况下,在 Elixir 的代码中,你永远都接触不到原生的spawnsend/receive 函数,这样就推迟了功能的抽象。

Task 的封装实现了简单的async/await风格的调用。Agent为并发的进程维护和更新共享状态。GenServer 可用来实现更复杂的自定义逻辑。

为了限制一个特定的队列所能承受的最高并发,Go 管道实现了缓冲区用于接受指定数量的消息(用数量限制发送者)。默认情况下,如果还没准备好接受消息,管道一直是阻塞的,除非是设定了缓冲区。

Elixir 进程邮箱默认没有消息处理数量限制,但可以使用Task.async_stream定义一个操作的最大并发数。这跟在 channel 上设置有限缓冲区以阻止发送者的方式是一样的。

例程在这两门语言里的代价都十分小, 每个 goroutine 只有2K 的大小,而 Elixir 的每个进程只有0.5K。Elixir 的进程有自己独立的堆空间,当进程结束后会被单独回收。goroutine 使用共享内存,用一个应用范围内(application-wide)的垃圾回收器回收资源。

错误处理

错误处理是这两门语言差别最大的地方。从函数调用到panics(Go 的崩溃异常), Go 对各个层次的错误处理都很明确。Elixir的错误处理被认为有种"代码的味道"。过会再来读一遍。

它是如何工作的呢?是否还记得先前我们谈论过Elixir的spawn调用会返回一个进程ID?它不仅仅是用来发送消息的,还可以用来监控进程并检查它是否还在活动状态。

由于进程的代价小,Elixir的标准做法是一次性创建两个,一个用来运行,一个用来监督。

这种方法被成为管理者模式。Elixir的应用往往在一种监管树内运行。 监管者进程通过一个叫spawn_link的函数创建进程, 如果被创建进程崩溃,那么创建进程也会崩溃。监管者进会处理这些崩溃,并迅速重启进程。

一个例子,用被监管的进程做除法运算。被0除将进程崩溃掉,但监管进程会立即将其重启,让未来的操作得以继续执行。不是错误处理不存在,而是被监管进程处理掉了。

正好相反,Go 没有办法追踪每个独立例程的执行。所以每个层次的错误处理都需要非常明确,这就会导致写出如下的代码:

b, err := base64.URLEncoding.DecodeString(cookie)
if err != nil {
  // Handle error
}

这样做的目的是在可能出现异常的地方都要加上异常处理,不管是不是在goroutine 里。

Goroutine 可以用相同的方式将错误传递给管道。然而,如果 panic 出现,每个 goroutine 必须有应对之策,不然整个程序都会崩溃掉。panic 不同于其他语言里的异常,它的目的是系统级别"停止任何事情"的事件。

这种异常被封装在内存不足的错误里。如果 goroutine 触发了一个内存不足的错误,由于共享内存运行时的状态,即便是有对应的错误处理也会导致程序的崩溃。

对于 Elixir, 由于每个进程都有自己独立的堆空间,也就可以各自设置堆的最大值,一旦超过这个最大值进程就会崩溃,然后会被单独做垃圾回收并重启,不会影响其他东西。

这并不是说Elixir 是无敌的。VM 依然可以通过其他方式耗尽内存, 但在进程中该问题是可控的。

并不是为了打击 Go, 所有你听说过的使用共享内存的语言都要面对该问题。这只是Erlang / Elixir设计上的一个特点。

Go的处理方式迫使开发者在可能出现错误的地方直接进行处理,这需要明确的设计思路,但 这可能会开发出精心设计的应用程序。

Elixir 的核心概念就像Joe Armstrong 所说的那样, 期望一个应用程序可以一直运行。就像用 Go 调用调度器,你也可以通过 Go 的 suture 库实现一个监管程序。

注: 你在大多数服务器上用 Go 实现的处理程序都已处理panic 错误。因此,一次 Web 请求出现的崩溃,不足以干掉一个进程。即便如此,你依然要在 goroutine 上解决它。这个解释不是为了暗示 Go 的脆弱,毕竟它也不是 :)

可变与不可变

在 Go 与 Elixir 的比较中,理解可变类型与不可变类型数据对比所产生的权衡是十分重要的。

Go 使用与大多数程序员所用过的相同的内存管理风格,通过共享内存、指针和数据结构 对内存进行更改或重新分配。在处理大的数据结构的时候,这种方式效率更高。

Elixir的不可变数据使用了写入时拷贝。在相同的堆空间中,它实际上是传递一个指向数据的指针,但只要对其执行操作,就会创建一个新的副本。

例如, 值列表会传递指向内存中不可数据的指针列表,而排序将以不同的顺序返回一个指针列表,因为内存中的值可以相信它是不变得。改变列表中的一个值就会返回一个新的列表,其中一个指针会指向新的值。如果要把这个列表传递给另一个进程,这个列表将会被复制一份到新的堆空间。

集群

另一个出自可变与与不可变对比的权衡是集群化。使用 Go, 可以无缝的执行远程调用。但由于指针和共享内存的存在,如果你在远程的终端调用一个引用了本地参数的方法,这就不会像预期那样执行了。

对于 Elixir,所有的东西都是通过消息传递的,所以整个应用程序栈可以在任意数量的机器上集群化。数据被传入返回响应的函数中。任何函数函数调用都不会发生内存中的转换,这就允许Elixir像其他函数调用它自己的堆堆栈一样,可以在不同的堆栈、不同的机器、或完全不同的数据中心。

许多应用不需要集群化,但也有很多应用从中受益,例如用户从不同的机器连接的聊天程序或水平分布的数据库都会用到的通信系统。两者分别可以使用Phoenix 框架的管道和 Erlang 的 Mnesia数据库的解决方案。集群对于任何应用程序的横向扩展能力都是十分重要的,而不依赖于有性能瓶颈的中间节点。

Go 拥有一个广泛标准库,允许大多数开发者在不需要第三库的情况多做任何事。

Elixir标准库更显得简洁一些,但也包含Erlang 的库,它的库更全面,还包括三个内置的数据库 ETS/DETS/Mnesia。其他的包,必须从其他的三方库中提取。

Elixir 和 Go 都有大量的第三方库可以用。Go 使用go get引入远程的包。Elixir 使用 Mix, 一个构建工具,它会像用户所熟悉的其他语言的包管理方式调用Hex包。

Go仍在努力实现一个完整的包管理方案。三方库与标准库相比,Go 社区的大多数人在可能的情况下更倾向于使用标准库。目前 已经有几个包管理工具可以用了。

部署

Go 的部署很简单。一个 Go 应用程序被编程成一个包含所有依赖的二进制文件,可以在本地运行,也可以夸平台。Go 的编译器可以针对任何架构的框架进行编译,而不管运行的计算机是什么样的。这是 Go 最大的优势。

Elixir 实际上有很多配置选项,但首选的方法还是通过优秀的distillery包。它将应用程序封装成一个包含所依赖(能在目标系统一并部署)二进制文件。

两者的区别在于, Elixir 的编译所用的架构必须与目标系统的架构相同才行。这些文档(这里应该是 链接)包含了这个场景的几个解决方法,但最简单的方法是在具有目标架构的Docker容器中构建发行。

使用这两种解决方案,都必须要停止正在运行的代码,替换二进制文件,然后按大多数 blue-green 风格的部署方式重启应用。

热更新

Elixir 还有另外一种部署方式,BEAM 虚拟机。稍微有点复杂,但有一些特定类型的应用适合这种方案。它叫做热加载或热更新。

Distillery 使用--upgrade参数来简化你构建的命令,但这并不意味着你要一直用它。

在谈论何时用它之前,我们需要先理解它是什么。 Erlang是为电话系统而开发的,目前驱动了这个星球上一半的电话系统。它被设计成永远不会宕机, 但当系统中正在有人打电话时,部署就变成了一个复杂的问题。

如何在不断开系统中所有人的情况下进行部署?是否要禁止新来的流量并礼貌的等待每个人到通话结束?

答案是否定的,那正式需要热加载的地方。

由于进程间堆栈的隔离,版本的升级是不会打断运行中的进程。没有激活的进程直接被替换,新部署的进程与运行中的进程同时存在并接受新的流量,运行中的进程可以一直工作直到任务完成。

这可以使你部署具有数百万通话系统升级, 而且是不打断现有通话的前提下。想象一下,用一堆新的汽包替换天空中另一堆起泡,这基本上就是热加载的工作方式。老的起泡四处飘散,直到它们破裂。

了解了这一点,就可以看到一些可能排的上用场的场景:

  1. 一个用户连接到指定机器的 websocket 聊天系统
  2. 一个作业服务器, 升级系统的时候不中断正在运行的作业
  3. 一个大流量的 CDN,正在连接一个很慢的WEB 请求

尤其是 websocket, 在一个有数百万活动连接的机器上部署,不用因中断而承受瞬间的爆发式重连尝试,甚至丢失正在传输中的信息。顺便说一句,这就是为什么 Whatsapp 使用 Erlang。热加载被用来给正在飞行的飞机电脑进行更新。

热更新的缺点是,如果需要回滚将会更复杂。你可能不需要用它,除非你有一个真正需要它的场景。有个选择总是好的。

集群化也是如此,并不是一直需要它,但当你那么做的时候它是必不可少的。集群化、热更新与分布式系统齐头并进。

结论

这篇文章很长,但希望它把Elixir 和 Go 之间的区别做一个详实的描述。思考这两门语言最有效的方式是吧Elixir 看做一个系统,而 Go 是一个专门的程序。

Go 的快速和针对性的解决方案非常适合工程化。Elixir 创建一个环境,即使在部署的时候,不同的程序也可以共存,操作和交流,且互不干扰 。Go 可以用来构建单个微服务, 而一个 Elixir 的umbrella环境里可以构建多个微服务。

Go 更专注且简单易用。一旦理解 了Elixir思想,它也很简单。如果你的目标是在使用它之前掌握它,那么 OTP 的世界和 Erlang 的广阔会让人感到恐惧。

两者都是非常出色的语言,做任何编程的事情时,它们都是我的首选。

对于非常集中的代码,可移植的系统级工具,性能密集型任务和API,Go很难被击败。对于全堆栈的Web应用程序,分布式系统,实时系统或嵌入式应用程序,我会选择Elixir。

© 著作权归作者所有

共有 人打赏支持
nicozhang
粉丝 6
博文 8
码字总数 10946
作品 0
青岛
程序员
加载中

评论(25)

同城陌路人
同城陌路人
又曾go的热点
来自山卡拉的你
来自山卡拉的你

引用来自“Schr0dingerCat”的评论

go又开始刷存在感了:kissing:
哈哈,go还是挺好用的,至少在交付应用的时候不怕被反编译,开发效率还行,如果自用的话,就可以选择其它了
LinkerLin
LinkerLin

引用来自“qgymje”的评论

希望楼主早日认清Erlang的局限,不要困在里面无法自拔

引用来自“LinkerLin”的评论

也许Erlang的抽象是对的, 但是世界都不是按照Erlang的方式来做的.

引用来自“qgymje”的评论

Eralng的诸多特性看上去是美妙的,pattern matching, recursion, process,甚至连scheduler都是那么公平,但在实践中,参数位置问题,代码可读性差,依赖register测试不方便,分布式是有一定条件的,等等等等。我只能将Erlang看做特定领域的编程语言,不能当作general purpose pl。最最最坑的一点就是,你写的代码,必须要符合Erlang的设定,一切都要抽象为process,就像模拟真实的世界怎么样?编程体验不好啊。

引用来自“LinkerLin”的评论

Erlang是典型的手里有一个锤子就把世界全部当成钉子.

引用来自“nicozhang”的评论

Erlang 确实有诟病。不过,我们不能一概而论,认为 Elixir 就是一糟粕。就像 Java 也有很多问题,但 kotlin 依然能让人耳目一新(当然,这里并不是吧 kotlin 的抽象层次类比Elixir)。有时间,我会用用Golang 和 Elixir 去实现些小项目,来满足我验证其糟粕与精华的想法 :joy:
厉害了,我还是比较推荐用通用语言.
nicozhang
nicozhang

引用来自“qgymje”的评论

希望楼主早日认清Erlang的局限,不要困在里面无法自拔

引用来自“LinkerLin”的评论

也许Erlang的抽象是对的, 但是世界都不是按照Erlang的方式来做的.

引用来自“qgymje”的评论

Eralng的诸多特性看上去是美妙的,pattern matching, recursion, process,甚至连scheduler都是那么公平,但在实践中,参数位置问题,代码可读性差,依赖register测试不方便,分布式是有一定条件的,等等等等。我只能将Erlang看做特定领域的编程语言,不能当作general purpose pl。最最最坑的一点就是,你写的代码,必须要符合Erlang的设定,一切都要抽象为process,就像模拟真实的世界怎么样?编程体验不好啊。

引用来自“LinkerLin”的评论

Erlang是典型的手里有一个锤子就把世界全部当成钉子.
Erlang 确实有诟病。不过,我们不能一概而论,认为 Elixir 就是一糟粕。就像 Java 也有很多问题,但 kotlin 依然能让人耳目一新(当然,这里并不是吧 kotlin 的抽象层次类比Elixir)。有时间,我会用用Golang 和 Elixir 去实现些小项目,来满足我验证其糟粕与精华的想法 :joy:
LinkerLin
LinkerLin

引用来自“qgymje”的评论

希望楼主早日认清Erlang的局限,不要困在里面无法自拔

引用来自“LinkerLin”的评论

也许Erlang的抽象是对的, 但是世界都不是按照Erlang的方式来做的.

引用来自“qgymje”的评论

Eralng的诸多特性看上去是美妙的,pattern matching, recursion, process,甚至连scheduler都是那么公平,但在实践中,参数位置问题,代码可读性差,依赖register测试不方便,分布式是有一定条件的,等等等等。我只能将Erlang看做特定领域的编程语言,不能当作general purpose pl。最最最坑的一点就是,你写的代码,必须要符合Erlang的设定,一切都要抽象为process,就像模拟真实的世界怎么样?编程体验不好啊。
Erlang是典型的手里有一个锤子就把世界全部当成钉子.
qgymje
qgymje

引用来自“qgymje”的评论

希望楼主早日认清Erlang的局限,不要困在里面无法自拔

引用来自“LinkerLin”的评论

也许Erlang的抽象是对的, 但是世界都不是按照Erlang的方式来做的.
Eralng的诸多特性看上去是美妙的,pattern matching, recursion, process,甚至连scheduler都是那么公平,但在实践中,参数位置问题,代码可读性差,依赖register测试不方便,分布式是有一定条件的,等等等等。我只能将Erlang看做特定领域的编程语言,不能当作general purpose pl。最最最坑的一点就是,你写的代码,必须要符合Erlang的设定,一切都要抽象为process,就像模拟真实的世界怎么样?编程体验不好啊。
c
coderwu

引用来自“nicozhang”的评论

引用来自“Greatim”的评论

Elixir 最大的问题就是必须依赖Erlang的VM,而这个VM在开源后就一直不怎么稳定,已经没有工业级的光环了。

有相关的资料说明不稳定的方便发一下吗? 谢谢��
我也想说,不稳定的方面请麻烦说一下
Shawn90
Shawn90

引用来自“nicozhang”的评论

引用来自“Shawn90”的评论

作者你居然跟我翻译了同一篇文章。。 https://www.jianshu.com/p/b59677547b26

回复@Shawn90 : 我没注意已经有中文得了。我问了英文原作者,他同意我才翻译的。
厉害了,我偷偷就翻译了。我得去问问。
LinkerLin
LinkerLin

引用来自“qgymje”的评论

希望楼主早日认清Erlang的局限,不要困在里面无法自拔
也许Erlang的抽象是对的, 但是世界都不是按照Erlang的方式来做的.
小鱼梦想
小鱼梦想
按照Conainter的设计原则,用G跟贴切,镜像更小。
Elixir 学习资源

Elixir 官网 getting started官方入门学习资源 官方文档 hex 包管理系统 elixir sips 比较不错视频课程 Elixir China 中文论坛 官方wiki Elixir by Example Awesome Elixir Elixir Quiz 通过...

lidashuang ⋅ 2017/11/29 ⋅ 0

[Erlang 0113] Elixir 编译流程梳理

注意:目前Elixir版本还不稳定,代码调整较大,本文随时失效 之前简单演示过如何从elixir ex代码生成并运行Erlang代码,下面仔细梳理一遍elixir文件的编译过程,书接上文,从elixir的代码切入,这一...

唐玄奘 ⋅ 01/03 ⋅ 0

Unix 哲学:Elixir 将会替代 Go

本文由伯乐在线 -youth7 翻译,蒋生武 校稿。未经许可,禁止转载! 英文出处:Aaron Lebo。欢迎加入翻译组。 如果你做过Web开发,你可能会觉得我们正处于一个新时代的开端。多年以来我们一直...

伯乐在线 ⋅ 2015/08/19 ⋅ 0

Elixir: 编程语言的未来

这篇文章谈一谈最近火爆的 Elixir,同时说一下对编程语言选择的看法。同时作为 Erlang 发烧友,Elixir 不可不提。即使有了那么多编程语言 Elixir 也值得接触。 现在开始接触 Elixir 对编程语...

oschina ⋅ 2015/08/02 ⋅ 18

elixir官方入门教程 介绍

介绍 安装 交互模式 运行脚本 提出疑问 欢迎! 在本教程中我们将教给你Elixir的基础,语法,如何定义模块,如何操作常用数据结构的特性等等.本章将确保Elixir安装好了,并且你能够成功运行Elixir的...

ljzn ⋅ 2016/08/06 ⋅ 0

elixir官方入门教程 学习资料

下一步该去哪 构建你的第一个Elixir项目 元编程 社区与其它资源 Erlang基础 想要学习更多?继续阅读! 构建你的第一个Elixir项目 为了开始你的第一个项目,Elixir装载了一个叫做Mix的构建工具....

ljzn ⋅ 2016/08/06 ⋅ 0

总有你要的编程书单(GitHub )

目录 IDE IntelliJ IDEA 简体中文专题教程 MySQL 21分钟MySQL入门教程 MySQL索引背后的数据结构及算法原理 NoSQL Disque 使用教程 Neo4j .rb 中文資源 Redis 命令参考 Redis 设计与实现 The ...

汇智网 ⋅ 2017/11/22 ⋅ 0

Phoenix官方教程 (八) 模板

模板听起来像是:我们传送数据给它,以形成完整的HTTP响应。对于web应用来说,这些响应会是HTML文档。对API来说,它们大多是JSON或XML。模板文件中的主要代码通常是修饰,但也会有有一些需要...

ljzn ⋅ 2016/08/17 ⋅ 0

Elixir 1.5 版本发布,函数式编程语言

Elixir 1.5 发布了,Elixir 是一种函数式编程语言,建立在Erlang虚拟机之上。它是一种动态语言,灵活的语法与宏支持,利用Erlang的能力来构建并发、分布式、容错应用程序与热代码升级。 该版本...

两味真火 ⋅ 2017/07/26 ⋅ 4

【ELIXIR】for语句的N种用法

Elixir中的for语句有许多用法: 一般用法 [2, 4, 6, 8] 多个元素池 [3,4,6,8] 附带筛选 [2, 4] 在<-符号的左边可以加上模式匹配和筛选 ["JHON", "TOM"] 处理bitstring的时候,双箭头放在最外...

ljzn ⋅ 2016/09/17 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

熊掌号收录比例对于网站原创数据排名的影响[图]

从去年下半年开始,我在写博客了,因为我觉得业余写写博客也还是很不错的,但是从2017年下半年开始,百度已经推出了原创保护功能和熊掌号平台,为此,我也提交了不少以前的老数据,而这些历史...

原创小博客 ⋅ 45分钟前 ⋅ 0

LVM讲解、磁盘故障小案例

LVM LVM就是动态卷管理,可以将多个硬盘和硬盘分区做成一个逻辑卷,并把这个逻辑卷作为一个整体来统一管理,动态对分区进行扩缩空间大小,安全快捷方便管理。 1.新建分区,更改类型为8e 即L...

蛋黄Yolks ⋅ 今天 ⋅ 0

Hadoop Yarn调度器的选择和使用

一、引言 Yarn在Hadoop的生态系统中担任了资源管理和任务调度的角色。在讨论其构造器之前先简单了解一下Yarn的架构。 上图是Yarn的基本架构,其中ResourceManager是整个架构的核心组件,它负...

p柯西 ⋅ 今天 ⋅ 0

uWSGI + Django @ Ubuntu

创建 Django App Project 创建后, 可以看到路径下有一个wsgi.py的问题 uWSGI运行 直接命令行运行 利用如下命令, 可直接访问 uwsgi --http :8080 --wsgi-file dj/wsgi.py 配置文件 & 运行 [u...

袁祾 ⋅ 今天 ⋅ 0

JVM堆的理解

在JVM中,我们经常提到的就是堆了,堆确实很重要,其实,除了堆之外,还有几个重要的模块,看下图: 大 多数情况下,我们并不需要关心JVM的底层,但是如果了解它的话,对于我们系统调优是非常...

不羁之后 ⋅ 昨天 ⋅ 0

推荐:并发情况下:Java HashMap 形成死循环的原因

在淘宝内网里看到同事发了贴说了一个CPU被100%的线上故障,并且这个事发生了很多次,原因是在Java语言在并发情况下使用HashMap造成Race Condition,从而导致死循环。这个事情我4、5年前也经历...

码代码的小司机 ⋅ 昨天 ⋅ 1

聊聊spring cloud gateway的RetryGatewayFilter

序 本文主要研究一下spring cloud gateway的RetryGatewayFilter GatewayAutoConfiguration spring-cloud-gateway-core-2.0.0.RC2-sources.jar!/org/springframework/cloud/gateway/config/G......

go4it ⋅ 昨天 ⋅ 0

创建新用户和授予MySQL中的权限教程

导读 MySQL是一个开源数据库管理软件,可帮助用户存储,组织和以后检索数据。 它有多种选项来授予特定用户在表和数据库中的细微的权限 - 本教程将简要介绍一些选项。 如何创建新用户 在MySQL...

问题终结者 ⋅ 昨天 ⋅ 0

android -------- 颜色的半透明效果配置

最近有朋友问我 Android 背景颜色的半透明效果配置,我网上看资料,总结了一下, 开发中也是常常遇到的,所以来写篇博客 常用的颜色值格式有: RGB ARGB RRGGBB AARRGGBB 这4种 透明度 透明度...

切切歆语 ⋅ 昨天 ⋅ 0

CentOS开机启动subversion

建立自启动脚本: vim /etc/init.d/subversion 输入如下内容: #!/bin/bash## subversion startup script for the server## chkconfig: 2345 90 10# description: start the subve......

随风而飘 ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部