Elixir: 編程語言的未來

jopen 9年前發布 | 21K 次閱讀 Elixir

這篇文章談一談最近火爆的 Elixir,同時說一下對編程語言選擇的看法。同時作為 Erlang 發燒友,Elixir 不可不提。即使有了那么多編程語言 Elixir 也值得接觸。

  • 現在開始接觸 Elixir
  • 對編程語言選擇的一點看法
  • Elixir 的元編程 (meta programming) 和 DSL
  • 很多有用的鏈接
  • </ul>

    Elixir 并不是一個最近出現的語言。但是近期 Elixir 的生態逐漸完善,越來越多的專家開始關注這門語言,并且 給予 Elixir 好評

    現在開始接觸 Elixir

    一個小的 Elixir 例子

    并行處理 JSON 字符串輸入,并且解析成可用的變量,計算每秒處理的速度并輸出。

    https://github.com/doubaokun/exsample

    用 entop 監控 Elixir 應用狀態

    對編程語言選擇的一點看法

    作為個語言發燒友,之前接觸過 Java、Erlang、Scala、PHPJavaScript、C#、C、Python、Ruby 等一大堆各種風格的編程語言。有人說,學那么多編程語言是想做”翻譯”嗎?其實事情并不那么簡單。

    不同的語言背后是風格截然不同的類庫群、技術堆棧、生態和工具鏈。不同的語言針對了不同類型的問題。某些語言解決某些問題的成本會比其他語言低非常多。回歸本質,學習編程語言還是為了低成本高效的解決實際的業務問題。

    個人喜歡的編程語言風格

    可以近實時更新變更

    最好不需要長時間編譯才能執行、應用啟動快。

    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 語法。

    quote do: 1 + 2

    2. 執行 quote 的表達式

    Code.eval_quoted(quote do: 1 + 2)

    3. unquote 用來引用 quote 范圍之外的變量

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

    Elixir 成熟的工具鏈

    mix:項目創建、構建工具
    hex:可以和 npm 媲美的依賴和庫管理系統 https://hex.pm/
    iex: 類似 Erlang 的 erl 既是 EPRL 又是應用啟動命令
    exunit: 單元測試工具

    Tip: (Erlang\Elixir\Akka 都需要注意不要讓某一個 Actor 的 Queue 積壓過多消息成為系統瓶頸。監控 Queue 長度非常必要。)

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

    entop
    gproc
    :observer.start()
    rebar

    更多有用的 Elixir / Erlang 類庫,比如 Web 類庫 Phoenix Webframework,常見的 MySQL、Redis、MongoDB 類庫:

    https://github.com/h4cc/awesome-elixir

    來自:http://blog.eood.cn/elixir

     本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
     轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
     本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!