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

  Actors模型(Actor model)首先是由Carl Hewitt在1973定义, 由Erlang OTP
(Open Telecom Platform) 推广,其 消息传递更加符合面向对象的原始意图。
Actors属于并发组件模型
,通过组件方式定义并发编程范式的高级阶段,避免使用者直接接触多线程并发或线程池等基础概念。

澳门新葡萄京所有网站 1

  
传统多数流行的语言并发是基于多线程之间的共享内存,使用同步方法防止写争夺,Actors使用消息模型,每个Actors在同一时间处理最多一个消息,可以发送消息给其他Actors,保证了单独写原则 。从而巧妙避免了多线程写争夺。

  • 现在开始接触 Elixir

  • 对编程语言选择的一点看法

  • Elixir 的元编程 (meta programming) 和
    DSL

  • 很多有用的链接

   Actors模型的特点是:

Elixir 并不是一个最近出现的语言。但是近期 Elixir
的生态逐渐完善,越来越多的专家开始关注这门语言,并且 给予
Elixir
好评。

  • 隔离计算实体
  • “Share nothing”
  • 没有任何地方同步
  • 异步消息传递
  • 不可变的消息 消息模型类似mailbox / queue

现在开始接触 Elixir

一个小的 Elixir 例子

并行处理 JSON
字符串输入,并且解析成可用的变量,计算每秒处理的速度并输出。

https://github.com/doubaokun/exsample

澳门新葡萄京所有网站 2

用 entop 监控 Elixir 应用状态

澳门新葡萄京所有网站 3

  
AKKA框架是一个实现Actors模型的Scala或Java平台,灵感来自ERlang,能更轻松地开发可扩展,实现多线程安全应用。

对编程语言选择的一点看法

作为个语言发烧友,之前接触过
Java、Erlang、Scala、PHP、JavaScript、C#、C、Python、Ruby
等一大堆各种风格的编程语言。有人说,学那么多编程语言是想做”翻译”吗?其实事情并不那么简单。

不同的语言背后是风格截然不同的类库群、技术堆栈、生态和工具链。不同的语言针对了不同类型的问题。某些语言解决某些问题的成本会比其他语言低非常多。回归本质,学习编程语言还是为了低成本高效的解决实际的业务问题。

  Actors是一个轻量级的对象,通过发送消息实现交互。每个Actors在同一时间处理最多一个消息,可以发送消息给其他Actors。在同一时间可以于一个Java虚拟机存在数以百万计的参与者,构架是一个分层的父层(管理)

子层,其中父层监控子层的行为。还可以很容易地扩展Actor运行在集群中各个节点之间

  • 无需修改一行代码。每个演员都可以有内部状态(字段/变量)
    ,但通信只能通过消息传递,不会有共享数据结构(计数器,队列)
    。Akka框架支持两种语言Java和Scala,

 

WhatsApp成功背后的Erlang语言

Actor模型和CSP模型的区别

高级并发:Akka Actors和JavaEE7的EJB比较

Akka教程

AKKA框架介绍

使用Akka发送1000万消息

使用Akka实现MapReduce的单词计数

Akka+ZeroMQ消息生产者和消费者

软件事务内存STM

澳门新葡萄京所有网站 ,RXJAVA的Observable和AKKA ACTOR综合使用

为什么Actor模型是高并发事务的终极解决方案?

actor并发模型&基于共享内存线程模型

Spray + Akka高性能异步IO并发

使用Scala的Akka HTTP,Akka Stream和Reactive
Mongo建立REST服务

单独写原则

LMAX架构

go reactive宣言

Scala入门之函数编程

Scala入门之基本概念

vertx入门教程

使用Kafka和ZeroMQ实现分布式Quasar Actor

 

个人喜欢的编程语言风格

相关参考

Actors模型更多专题

AKKA更多专题

reactive编程

Scala专题

Disruptor专题

并发编程

性能专题

EDA事件驱动专题

 

 

过去十几年CPU一直遵循着摩尔定律发展,单核频率越来越快,但是最近这几年,摩尔定律已然失效,CPU的工艺制程和发热稳定性之间难以取舍,取而代之的策略则是增加核心数量,目前家用电脑四核已经非常常见,服务器更是达到了32核64线程。为了有效地利用多核CPU,我们在代码层面就应该考虑到并发性。十几年的痛苦开发经历告诉我们,threads并不是获取并发性的好方法,往往会带来难以查找的bug,但是不用害怕,今天我们有很多其他方法来获得易用的并发性,比如我们接下来介绍的Actor模型。

模型 Model

Actor模型是一个概念模型,用于处理并发计算。它定义了一系列系统组件应该如何动作和交互的通用规则,最著名的使用这套规则的编程语言是Erlang。这篇文章更关注模型本身而不是它在不同语言的实现。

Actors

一个Actor指的是一个最基本的计算单元。它能接收一个消息并且基于其执行计算。

这个理念很像面向对象语言,一个对象接收一条消息(方法调用),然后根据接收的消息做事(调用了哪个方法)。

Actors一大重要特征在于actors之间相互隔离,它们并不互相共享内存。这点区别于上述的对象。也就是说,一个actor能维持一个私有的状态,并且这个状态不可能被另一个actor所改变。

聚沙成塔

One ant is no ant, one actor is no actor.
光有一个actor是不够的,多个actors才能组成系统。在actor模型里每个actor都有地址,所以它们才能够相互发送消息。

Actors有邮箱

只得指明的一点是,尽管许多actors同时运行,但是一个actor只能顺序地处理消息。也就是说其它actors发送了三条消息给一个actor,这个actor只能一次处理一条。所以如果你要并行处理3条消息,你需要把这条消息发给3个actors。

消息异步地传送到actor,所以当actor正在处理消息时,新来的消息应该存储到别的地方。Mailbox就是这些消息存储的地方。

 

澳门新葡萄京所有网站 4

Actors通过异步消息沟通,在处理消息之前消息被存放在Mailbox中

Actors做什么

当一个actor接收到消息后,它能做如下三件事中的一件:

  • Create more actors; 创建其他actors
  • Send messages to other actors; 向其他actors发送消息
  • Designates what to do with the next message.
    指定下一条消息到来的行为

前两件事比较直观,第三件却很有意思。

我之前说过一个actor能维持一个私有状态。「指定下一条消息来到做什么」意味着可以定义下条消息来到时的状态。更清楚地说,就是actors如何修改状态。

设想有一个actor像计算器,它的初始状态是数字0。当这个actor接收到add(1)消息时,它并不改变它原本的状态,而是指定当它接收到下一个消息时,状态会变为1。

容错 Fault tolerance

Erlang
引入了「随它崩溃」的哲学理念,这部分关键代码被监控着,监控者的唯一职责是知道代码崩溃后干什么(如将这个单元代码重置为正常状态),让这种理念成为可能的正是actor模型。

每段代码都运行在process中,process是erlang称呼actor的方式。这个process完全独立,意味着它的状态不会影响其他process。我们有个supervisor,实际上它只是另一个process(所有东西都是actor),当被监控的process挂了,supervisor这个process会被通知并对此进行处理。这就让我们能创建「自愈」系统了。如果一个actor到达异常状态并崩溃,无论如何,supervisor都可以做出反应并尝试把它变成一致状态,这里有很多策略,最常见的是根据初始状态重启actor。

分布式 Distribution

另一个关于actor模型的有趣方面是它并不在意消息发送到的actor是本地的或者是另外节点上的。

转念一想,如果actor只是一些代码,包含了一个mailbox和一个内部状态,actor只对消息做出响应,谁会关注它运行在哪个机器上呢?只要我们能让消息到达就行了。这允许我们基于许多计算机上构建系统,并且恢复其中任何一台。

进一步了解

这里是一个快速的概念模型回顾,其中的概念被运用到许多知名语言和库中,比如Erlang和Elixir,
Akka (for the JVM) 和 Celluloid (for Ruby)。

如果你想更深入了解actor及其背后的原理,你可以进一步阅读下面书籍和文章:

  • Seven Concurrency Models in Seven Weeks: When Threads
    Unravel
  • Programming
    Elixir
  • Elixir in
    Action
  • Actor模型
  • 为什么Actor模型是高并发事务的终极解决方案?

这个视频比较完整地讨论了Actor的概念模型。
The Actor Model (everything you wanted to
know)

Scala或Erlang的进程信箱都是一种Actor模型,也有Java的专门的Actor模型,这里是几种Actor模型比较明白了Actor模型原理,使用Disruptor这样无锁队列也可以自己实现Actor模型,让一个普通对象与外界的交互调用通过Disruptor消息队列实现,比如LMAX架构就是这样实现高频交易,从2009年成功运行至今,被Martin
Fowler推崇。

原文地址:
http://www.brianstorti.com/the-actor-model/

作者:时见疏星
链接:https://www.jianshu.com/p/449850aa8e82
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

 

可以近实时更新变更

最好不需要长时间编译才能执行、应用启动快。

Java、 C 编译很慢,不适合频繁修改的项目。但是 PHP 、Node.js
修改即可见,可以极大提高开发效率。最好还能 hot-reload
就像很多前端工具一样,只要源码有一点变更,不需要刷新页面自动反应在浏览器中。Play
framework 类似的自动加载功能也可以。

更进一步,能够在生产环境热加载就更好了,更新代码不影响用户。针对这一点,很多人乐了,
PHP 默认就是这样的啊,部署后刷新 APC 缓存就可以实现。
这正是无状态、短链接的 HTTP 应用的优势,虽然随之而来的是性能相对降低更多
TCP
的开销,但是把问题变得简单很多。但是很多其他语言做到这点就很难了,比如大部分
Java 应用。

Elixir、Erlang 可以做到真正的任何情况下开着跑车换轮子。

关于热加载,见另一篇文章:编程开发常用的热加载工具。

支持并发执行

人们更习惯顺序执行的思路,并且大部分业务逻辑都是顺序执行的。但是为了降低延迟、提高性能,最好能在语言层面支持并发执行。比如,一个操作开始还未返回结果,就可以开始另一个操作。
这样调用远程 API 或者远程
RPC,耗时为最慢那一个操作的耗时。从这一点看,大部分流行语言都可以做到并发调用,但
PHP 难以做到。

轻量级执行进程或者线程

由于某些限制,某些业务逻辑不可避免的会因为大量计算、网络磁盘 IO
等占用一个执行进程或者线程。所以希望这个执行体能够尽量轻量级,很少的内存占用,很快的启动时间,很少的切换消耗,最好能在
IO 执行的时候自动让出计算资源。

并发和并行

我们更多关注并发,但是比较少关注并行。因为通过增加机器数量能抗住大量用户的请求比节省机器更加简单和迫切。
这也是很多互联网公司动辄几百台上千台服务器的现状。用户和请求量的多少由于业务逻辑的不同很难比较,只能比较机器数量了。

并发之进程模型

PHP 既是典型的这种模式。曾经见过某异步 PHP 框架 CS
高居不下,甚至比业务逻辑的 CPU 使用更高。

并发之线程模型

这种模型相对于进程模型好了很多,因为线程比进程轻量很多,创建、切换也快很多。

问题:线程和内核线程的关系为多对多,内核线程有限。能够调度的用户线程有限,无法充分利用多核性能。创建新线程消耗非常大。IO
阻塞无法释放计算资源。

每个 CPU 核心只能同时运行一个线程,多个线程之间需要切换调度(CS)。如果是
CPU 密集类型的计算,没有或者很少 IO 操作,最好启动 CPU
核心数量的线程。
但是如果有 IO 操作,比如 磁盘或者网络,多余 CPU 核心数的线程有效,因为
IO 操作的时候可以切换到其他线程执行 CPU 操作。

并发之 Fork-join 轻量级进程模型:

Fork-join 创建自己的进程池来执行小粒度的任务。
相对于 Erlang 那种真正的抢占式调度的 VM
实现或者操作系统的抢占式调度,Fork-join
模型非常简单,也意味着相比之下效率相对低。
Fork-join 针对计算密集操作设计,意味着无法告诉 F/J 框架你因为 IO
等待而释放一会儿计算资源。所以,一般需要将异步 IO
操作放到另外的线程池,FJ 只处理纯计算。
基于 Scala 的 Akka 既是这种模型。所以,假如处理不当, Akka 的 Actor
很容易阻塞执行线程,如果执行线程池的线程被耗光,整个应用将会僵死在那里。而
Erlang 则没有这个问题。

并发之 Erlang 轻量级进程模型:

VM 调度线程,将计算划分为非常小的执行单元。可以支持非常多的进程。IO
阻塞可以自动释放资源。真正的抢占式调度。

类型系统

静态类型可以避免很多失误。动态类型经常会出现不可预期的结果,这有悖于
UNIX 风格的最少意外原则。
动态类型可以让开发更加快速。强静态类型系统会执行很快,比如
Java,但是也可以在有必要的时候使用反射,比如很多 RPC 框架的实现
(当然也有更进一步的字节码修改技术)。
每个语言的类型系统都有自己的特点。

丰富的内置结构或者容器类

最好能够区分 Interface、Struct 和
Implementation。能够以比较统一的模式轻松的定义自己需要的结构体。

GC 系统

除非 Erlang 无可媲美的轻量级线程级别的 GC
。否则你要么需要记住和理解复杂的 GC 调优参数、要么像 PHP
那样过一段时间将进程杀掉重来。

元编程和 DSL 扩展性

在语法级别的抽象和封装更能提高开发效率。Elixr 中如何实现 DSL。

执行速度和性能

这点和并发并行模式、以及多核利用率密切相关。

UNIX 风格

简单说就是模块化;每个模块完成相对单一的功能、复制任务由多个模块组合完成。项目设计就像搭积木。不同模块之前的输入输出可以拼接。
另外就是极简风格。

依赖和库管理系统

这点 Node.js npm 是最好的依赖管理系统了,这样导致了 Node.js
社群库数量的爆发。因为创建和发布一个库实在是太容易了,找到需要的库也非常简单。
极大提高了开发效率。

打包和发布系统

最好能打包成单一文件,容易分发和部署。比如 Java 应用打包成 Fat Jar
包到处执行,或者 Golang 那样编译成单一文件。

日志系统

真实的项目、日志非常重要。之前的文章已经提到日志的重要性。所以好的内置日志系统或者比较统一高效的日志模式非常重要。
最好支持屏幕打印、写文件等等功能。这可能不能算一个编程语言的特性了,要看这个语言是不是有很好的日志类库。
Java 的 SLF 就是一个比较好的日志系统类库。

工具链

项目构建、编译、测试工具比较完善。
比如 Java、Scala 项目的 maven、sbt 。Erlang 项目可以用 rebar ,但是
Elixir 的 mix 友好的很多倍。
另外一个好的 REPL
命令行工具非常重要,因为这可以方便的侵入应用进行调试,或者测试一条代码片段。
比如 PHP 的 php -a, sbt, Clojure 的 lein, Erlang 的 erl, Elixir 的 iex
等等。

脚本执行

这是脚本语言的一大优势。小任务可以立刻创建一个脚本执行,而不需要修改、编译部署现有运行的应用。
这点对于小任务非常重要。Erlang 和 Elixir 都支持这样运行,escript 或者
Elixir
脚本。比如,连接到集群,读取状态或者进行一次性的数据操作,然后断开。

测试系统

最好有一种比较标准的单元测试模型。比如 Java、Node.js、Scala、Elixir
等等。

说了这么多,回到 Elixir。首先 Elixir 执行和 Erlang 没有任何差别。Erlang
的优点 Elixir
完全具备。比如:真正的抢占式调度;充分利用多核心并行执行;Actor
模型;监控树;透明的分布式;
极其高的稳定性;代码的热更新部署;函数式编程;模式匹配;等等。并且很多
Erlang 下工具也是可以直接使用。比如 entop 。

另外 Elixir 比 Erlang
多出的好处在于更加友好的语法、工具链、社群。很多之前写 Ruby 的开始写
Elixir,因为他们的语法最接近。

Elixir 的元编程 (meta programming) 和 DSL

  1. quote 将代码变成 AST,很像 LISP 语法。

?

1
quote do: 1 + 2
  1. 执行 quote 的表达式

?

1
Code.eval_quoted(quote do: 1 + 2)
  1. unquote 用来引用 quote 范围之外的变量

?

1
2
number = 13
Macro.to_string(quote do: 11 + unquote(number))

Elixir 成熟的工具链

mix:项目创建、构建工具
hex:可以和 npm 媲美的依赖和库管理系统
iex: 类似 Erlang 的 erl 既是 EPRL 又是应用启动命令
exunit: 单元测试工具

Tip: (ErlangElixirAkka 都需要注意不要让某一个 Actor 的 Queue
积压过多消息成为系统瓶颈。监控 Queue 长度非常必要。)

Erlang、Elixir 一些有用的工具和库

entop
gproc
:observer.start()
rebar

更多有用的 Elixir / Erlang 类库,比如 Web 类库 Phoenix
Webframework,常见的 MySQL、Redis、MongoDB 类库:

很多有用的链接

https://en.wikipedia.org/wiki/Preemption_(computing)

https://www.subbu.org/blog/2012/03/async-io-and-fork-join

稿源: