《Elixir in Action》书评及作者问答录
《Elixir in Action》是由Manning所出版的一本新書(shū),本書(shū)為讀者介紹了Elixir這門(mén)語(yǔ)言以及Erlang虛擬機(jī),同時(shí)也討論了與并發(fā)編程、容錯(cuò)以及與高可用性相關(guān)的話(huà)題。InfoQ有幸與本書(shū)的作者Sa?a Juri?進(jìn)行了一次訪(fǎng)談。\
《Elixir in Action》的內(nèi)容源自于Juri?在Erlang方面的經(jīng)驗(yàn),他為此特意創(chuàng)建了一個(gè)博客,為來(lái)自面向?qū)ο蟊尘暗某绦騿T展現(xiàn)Erlang的優(yōu)勢(shì)。Juri?之后轉(zhuǎn)而使用Elixir,這是一種函數(shù)式的并發(fā)編程語(yǔ)言,它的目標(biāo)是提供一種比起以Prolog為啟發(fā)創(chuàng)建的Erlang更簡(jiǎn)便的語(yǔ)法,以及更高等的抽象能力。\
本書(shū)的內(nèi)容采取了一種漸進(jìn)式的方式,首先介紹了Elixir的語(yǔ)法與它的基本特性,例如宏、模式匹配、模塊、多態(tài)等等,隨后介紹了如何創(chuàng)建一個(gè)容錯(cuò)的、高可用的、并發(fā)的分布式系統(tǒng)。在全書(shū)的一些核心章節(jié)中涵蓋了Erlang平臺(tái)的大量知識(shí),其中的主題包括管理進(jìn)程、持久化、通過(guò)supervision樹(shù)進(jìn)行運(yùn)行時(shí)錯(cuò)誤處理的多種方式,以及“任其崩潰”(let it crash)等設(shè)計(jì)哲學(xué)。\
總的來(lái)說(shuō),《Elixir in Action》一書(shū)不僅為如何使用Elixir語(yǔ)言與Erlang VM,同時(shí)也為讀者如何邁進(jìn)高可用性系統(tǒng)這一領(lǐng)域打下了堅(jiān)實(shí)的基礎(chǔ)。InfoQ與Sa?a Juri?進(jìn)行了一次訪(fǎng)談,以了解這本書(shū)背后的更多知識(shí)。\
InfoQ:是什么原因促使你編寫(xiě)了本書(shū),它與現(xiàn)有的一些針對(duì)Elixir的書(shū)籍又有何不同之處呢?\
\Sa?a:《Elixir in Action》的目標(biāo)是幫助那些具有編程經(jīng)驗(yàn),但缺乏Erlang、Elixir或函數(shù)式編程背景知識(shí)的程序員開(kāi)發(fā)能夠在生產(chǎn)環(huán)境中運(yùn)行的系統(tǒng)。在這本書(shū)的構(gòu)思階段,我就假設(shè)本書(shū)的大部分讀者都來(lái)自于面向?qū)ο笳Z(yǔ)言的背景。對(duì)于這些讀者來(lái)說(shuō),書(shū)中的許多內(nèi)容都是新穎的:新的語(yǔ)言、運(yùn)行時(shí)與生態(tài)系統(tǒng),包括函數(shù)式編程與類(lèi)似于Actor的并發(fā)編程思想。讀者需要打下大量的知識(shí)基礎(chǔ),而這可能會(huì)令人望而生畏懼。\
我相信,通過(guò)一種更為專(zhuān)注的方式,初學(xué)者的學(xué)習(xí)過(guò)程將能夠得到極大的簡(jiǎn)化。因此,我并不打算編寫(xiě)一本完整的參考書(shū),而是決定專(zhuān)注于那些對(duì)于多數(shù)目標(biāo)讀者來(lái)說(shuō)有些不尋常的核心概念:函數(shù)式編程、并發(fā),以及OTP框架的核心思想。我相信一旦讀者開(kāi)始對(duì)這些主題感覺(jué)到“心意相通”,他們?cè)趯W(xué)習(xí)本書(shū)并未敘述的一些內(nèi)容時(shí)就會(huì)更容易。舉例來(lái)說(shuō),如果讀者掌握了Erlang中的并發(fā)知識(shí),并且理解了大多數(shù)最重要的OTP概念(GenServer、Supervisor、Application),那么他們就能夠較容易地自行學(xué)習(xí)其它的抽象概念,例如Task、Agent或GenEvent。\
我相信,這是目前唯一一本以這種方式進(jìn)行撰寫(xiě)的Elixir書(shū)籍。任何一位想要使用Elixir/Erlang技術(shù)打造可伸縮的、高容錯(cuò)性的分布式系統(tǒng),都必須學(xué)習(xí)Elixir in Action一書(shū)中所介紹的各種材料。當(dāng)然,你也可以通過(guò)其它資源學(xué)習(xí)這些內(nèi)容,但我認(rèn)為,本書(shū)是目前唯一一本面向Elixir介紹以上所有主題的書(shū)籍。
\InfoQ:你能否簡(jiǎn)單地介紹一下你在Elixir方面的經(jīng)驗(yàn)?它對(duì)于你實(shí)現(xiàn)工作目標(biāo)提供了哪些程度的幫助?\
\Sa?a:對(duì)我來(lái)說(shuō),Elixir最重要的一方面實(shí)際上是Erlang VM,這是一切功能的基礎(chǔ),這也是我首次接觸Erlang時(shí)對(duì)于我?guī)椭畲蟮膬?nèi)容。大約5年以前,我當(dāng)時(shí)需要實(shí)現(xiàn)一個(gè)基于長(zhǎng)輪詢(xún)的服務(wù)器推,它需要不斷地將頻繁變化的數(shù)據(jù)傳輸給數(shù)以千計(jì)的連接用戶(hù)。經(jīng)過(guò)一段時(shí)間的評(píng)估之后,我們最終選擇了Erlang,我從中也收獲了許多經(jīng)驗(yàn)。Erlang以一種結(jié)構(gòu)化的方式幫助我克服了重重阻礙:使用它創(chuàng)建解決方案的過(guò)程非常順利,即使我對(duì)它的開(kāi)發(fā)平臺(tái)還有些陌生。最終設(shè)計(jì)出的系統(tǒng)具備了高伸縮性、高效性以及靈活性。我能強(qiáng)烈地感覺(jué)到Erlang在支持著我,即使我犯下了各種錯(cuò)誤。這套系統(tǒng)能夠處理各種預(yù)料之外的狀況,而我甚至還沒(méi)有在這方面做過(guò)什么計(jì)劃。
\InfoQ:Elixir特別適合于哪些類(lèi)型的項(xiàng)目?\
\Sa?a:在我看來(lái),Elixir/Erlang適合于任何類(lèi)型的服務(wù)端系統(tǒng):即需要持續(xù)運(yùn)行,并且盡可能持續(xù)提供服務(wù)的軟件。這方面一個(gè)很明顯的示例就是基于web的系統(tǒng),用于處理傳入的HTTP請(qǐng)求,但也需要進(jìn)行其它活動(dòng),例如后臺(tái)的、周期性的作業(yè)或緩存管理。在這種系統(tǒng)中,許多活動(dòng)在某個(gè)具體時(shí)間往往處于掛起狀態(tài)。而Erlang應(yīng)對(duì)這種并發(fā)情況的途徑讓開(kāi)發(fā)者的擔(dān)子輕了許多。如果每個(gè)獨(dú)立的活動(dòng)都能夠分配至少一個(gè)獨(dú)立的Erlang進(jìn)程(不要與OS的進(jìn)程混淆了),我們就不僅能夠?qū)崿F(xiàn)可伸縮性,還能夠改善容錯(cuò)性:一個(gè)單一的進(jìn)程故障不會(huì)對(duì)整個(gè)系統(tǒng)的絕大部分產(chǎn)生影響。并且還有許多方法能夠檢測(cè)到這種故障,然后從故障中恢復(fù)。\
我發(fā)現(xiàn)這種處理方式非常直觀,并且任意一種類(lèi)型的后端系統(tǒng)都能夠從中受益。我看到某些觀點(diǎn)說(shuō)Erlang只適合于大規(guī)模系統(tǒng),或某些特定的領(lǐng)域,例如電信領(lǐng)域。我不同意這種觀點(diǎn)。Erlang能夠幫助系統(tǒng)實(shí)現(xiàn)大規(guī)模化,而這種特質(zhì)在任何類(lèi)型的生產(chǎn)系統(tǒng)中都是必要的,無(wú)論其規(guī)模與領(lǐng)域如何。因?yàn)槿绻硞€(gè)系統(tǒng)做不到高可用 ,那么它就會(huì)頻繁地產(chǎn)生故障。即使你不需要實(shí)現(xiàn)傳說(shuō)中的9個(gè)9的可用性,但你也應(yīng)當(dāng)希望讓你的系統(tǒng)停機(jī)時(shí)間盡量減短吧。實(shí)現(xiàn)這一點(diǎn)是一個(gè)艱難的挑戰(zhàn),而Erlang則能夠幫助你實(shí)現(xiàn)這個(gè)目標(biāo)。
\InfoQ:你怎樣描述Elixir與Erlang兩者之間的關(guān)系呢?\
\Sa?a:我的看法是,Elixir是在Erlang與OTP所提供的強(qiáng)大基礎(chǔ)之上所擴(kuò)展的能力,旨在提升開(kāi)發(fā)者的生產(chǎn)力。我曾經(jīng)進(jìn)行過(guò)大量的全職Erlang開(kāi)發(fā)工作,雖然我十分熱愛(ài)這門(mén)語(yǔ)言,但有許多任務(wù)也顯得過(guò)于繁瑣,我不得不無(wú)謂地處理一些低層次的機(jī)械性工作。而Elixir在這方面提供了大量實(shí)用的特性,包括語(yǔ)言(例如通過(guò)宏進(jìn)行元編程,以及通過(guò)協(xié)議實(shí)現(xiàn)多態(tài))與工具(例如構(gòu)建項(xiàng)目的多種工具搭配,以及十六進(jìn)制包管理器)兩方面,這讓我們能夠?qū)W⒂谔幚硪恍└鼮閷?shí)際的問(wèn)題。\
我個(gè)人的感受是使用Elixir進(jìn)行開(kāi)發(fā)比起使用純粹的Erlang進(jìn)行開(kāi)發(fā)要簡(jiǎn)單許多。Elixir使可伸縮性與開(kāi)發(fā)者的生產(chǎn)力之間這種刻意的權(quán)衡大大降低了,甚至是完全消除了。僅僅因?yàn)槟硞€(gè)開(kāi)發(fā)平臺(tái)允許我們創(chuàng)建高并發(fā)、可伸縮、高容錯(cuò)的分布式系統(tǒng),并不代表它就應(yīng)當(dāng)難以使用。同時(shí),Elixir的運(yùn)行時(shí)并沒(méi)有徹底遠(yuǎn)離Erlang的哲學(xué)。作為一種函數(shù)式語(yǔ)言,它的語(yǔ)義與Erlang非常接近。Elixir能夠無(wú)縫地集成各種Erlang庫(kù),因此開(kāi)發(fā)者能夠完整地訪(fǎng)問(wèn)整個(gè)Erlang生態(tài)系統(tǒng)。
\InfoQ:在決定直接使用Erlang或Elixir時(shí),這兩者有哪些缺陷是開(kāi)發(fā)者需要考慮進(jìn)去的?\
\Sa?a:我不認(rèn)為它們有任何的缺陷。它們所具有的優(yōu)勢(shì)主要來(lái)自于VM本身,以及已經(jīng)過(guò)充分測(cè)試的OTP框架,而你可以從其中任何一門(mén)語(yǔ)言中收獲這些益處。因此主要的決定因素在于其它的一些附加價(jià)值。Elixir加入了一些額外的特性,因此實(shí)際上它是一門(mén)比起Erlang更加復(fù)雜的語(yǔ)言。而這門(mén)語(yǔ)言的優(yōu)勢(shì)在于它的代碼更加簡(jiǎn)潔,在樣板代碼方面的負(fù)擔(dān)較少。與之相比,Erlang是一門(mén)更為簡(jiǎn)單的語(yǔ)言,因此所涉及的代碼更多,但它也因而顯得更為明確。\
從我個(gè)人來(lái)看,Elixir在樣板代碼的減少與語(yǔ)言的明確性方面找到了一個(gè)很好的平衡點(diǎn)。它沒(méi)有Ruby等語(yǔ)言表面上那么神奇,而仍然提供了各種實(shí)用的特性,其中最值得留意的就是元編程與多態(tài)性。
\InfoQ:創(chuàng)建一個(gè)高可用、高容錯(cuò)的并發(fā)系統(tǒng)是一項(xiàng)復(fù)雜的任務(wù),尤其是從CAP定律的角度來(lái)看。要實(shí)現(xiàn)這一目標(biāo),選擇一門(mén)合適的語(yǔ)言與運(yùn)行時(shí)環(huán)境的重要性有多高?\
\Sa?a:這實(shí)際上取決于個(gè)人的觀點(diǎn)。人們?cè)诟鞣N語(yǔ)言上都實(shí)現(xiàn)過(guò)大規(guī)模的系統(tǒng),因此不用Erlang也是完全可能的。不過(guò)對(duì)我來(lái)說(shuō),問(wèn)題不僅僅在于是否可能,還在于某個(gè)工具能夠在多大程度上幫助我們完成這一過(guò)程。畢竟,工具的目的就是為我們提供服務(wù)。\
而這也是為什么我很看重Erlang的一個(gè)原因。在我看來(lái),它為系統(tǒng)化地處理編寫(xiě)高可用系統(tǒng)所面臨的挑戰(zhàn)提供了簡(jiǎn)單而又非常強(qiáng)大的構(gòu)建塊。它的主要工具是Erlang進(jìn)程,它讓我們能夠?qū)⒐ぷ鞣纸鉃閹浊€(gè),乃至上百萬(wàn)個(gè)獨(dú)立的部分。通過(guò)使用多個(gè)進(jìn)程,我們就獲得了可伸縮性與容錯(cuò)性。它的崩潰傳遞機(jī)制能夠讓我們有機(jī)會(huì)處理這些故障:如果某個(gè)部分崩潰了,系統(tǒng)中的其它部分會(huì)收到它的通知,并進(jìn)行相應(yīng)的處理。最后,無(wú)共享并發(fā)機(jī)制能夠?qū)崿F(xiàn)分布式系統(tǒng),即使在一臺(tái)獨(dú)立的機(jī)器上也不例外。其實(shí)在本質(zhì)上,通過(guò)將整個(gè)工作分解為大量隔離的、完全獨(dú)立的實(shí)體(進(jìn)程),我們已經(jīng)實(shí)現(xiàn)了分布式工作。當(dāng)然,將系統(tǒng)在多臺(tái)機(jī)器上實(shí)現(xiàn)集群仍然不是一件簡(jiǎn)單的事,畢竟分布式系統(tǒng)在本質(zhì)上就存在著復(fù)雜性。但至少Erlang已經(jīng)為我們解決了一些低層次的工作,我們可以始終利用相同的基元功能實(shí)現(xiàn)協(xié)作,即進(jìn)程與消息傳遞。因此我們就能夠?qū)W⒂跇I(yè)務(wù)上內(nèi)在的挑戰(zhàn),而不是將大量的精力消耗在低層次的細(xì)節(jié)上。\
總的來(lái)說(shuō),我認(rèn)為Erlang能夠簡(jiǎn)化實(shí)現(xiàn)高可用性的挑戰(zhàn)。你也可以使用Erlang以外的技術(shù)應(yīng)對(duì)這些挑戰(zhàn),但很可能會(huì)因此付出更多的努力。
\InfoQ:Erlang的一個(gè)核心思想是使用非常輕量級(jí)的進(jìn)程模型,這使得上下文切換的開(kāi)銷(xiāo)非常低。另一方面,在許多系統(tǒng)中仍然用線(xiàn)程處理可伸縮性,而線(xiàn)程的可伸縮性往往會(huì)成為系統(tǒng)的瓶頸。為了避免這種瓶頸,可以使用一個(gè)完整的異步模型配合一個(gè)小型的線(xiàn)程池,這種做法也有成功案例(這里有一個(gè)參考示例)。你能否詳細(xì)地分析一下Erlang的處理方式與完整的異步方法相比所具有的優(yōu)勢(shì)?\
\Sa?a:我認(rèn)為Erlang為我們創(chuàng)建高并發(fā)的系統(tǒng)提供了一種優(yōu)秀而整潔的抽象,而任何一種需要持續(xù)處理各種不同任務(wù)的系統(tǒng)在本質(zhì)上都是并發(fā)的。Erlang的處理方式非常適合于這種類(lèi)型的問(wèn)題,你總是可以通過(guò)進(jìn)程來(lái)應(yīng)對(duì)各種類(lèi)型的任務(wù),無(wú)論是I/O密集型還是CPU密集型任務(wù),并且你可以信任VM會(huì)高效地分派工作。在使用Erlang不太會(huì)出現(xiàn)許多頓悟的情況,也不太會(huì)搬起石頭砸了自己的腳。它能夠讓減輕我們的負(fù)擔(dān),讓我們專(zhuān)注于實(shí)際的業(yè)務(wù)問(wèn)題。\
反之,如果你打算自行設(shè)計(jì)一種線(xiàn)程池,那么就不得不自己處理許多問(wèn)題。打個(gè)比方,如果你在某個(gè)線(xiàn)程中執(zhí)行一個(gè)時(shí)間很長(zhǎng)的計(jì)算,那么你會(huì)阻塞在同一個(gè)線(xiàn)程上掛起的其它活動(dòng)。如果某個(gè)線(xiàn)程因?yàn)橐粋€(gè)bug而產(chǎn)生故障,該線(xiàn)程上運(yùn)行的所有活動(dòng)都會(huì)失敗。這一點(diǎn)當(dāng)然是能夠解決的,但你或許要為投入大量的時(shí)間,以實(shí)現(xiàn)一種類(lèi)似于Erlang VM的功能。既然如此,為什么不依賴(lài)于某個(gè)已被證實(shí)的解決方案呢?如果單純的處理速度或是內(nèi)存占用確實(shí)極端重要,那么采取自定義的實(shí)現(xiàn)方案可能還有一些益處,但在我所遇到的這些情形中來(lái)看,基本都不屬于這種情況。
\InfoQ:Erlang的另一個(gè)基本原則也被Elixir保留下來(lái)了,那就是“任其崩潰”。這種做法目前已經(jīng)演變?yōu)閷⒗泄掳愕馗傻暨M(jìn)程作為一種確保系統(tǒng)能夠容忍這一事件的手段了。這種策略對(duì)于打造一個(gè)具備容錯(cuò)性的Erlang/Elixir系統(tǒng)有多大的重要性?\
\Sa?a:Erlang設(shè)計(jì)的一個(gè)前提就是在生產(chǎn)環(huán)境中的系統(tǒng)有可能產(chǎn)生錯(cuò)誤,但系統(tǒng)作為一個(gè)整體不能夠中止:它應(yīng)當(dāng)盡力保留所有的服務(wù),并盡快地從故障中進(jìn)行自我修復(fù)。\
任其崩潰在這種場(chǎng)合扮演了一個(gè)核心角色,它是一種簡(jiǎn)單的技術(shù),能夠讓我們以一種有條不紊的方式處理系統(tǒng)的錯(cuò)誤。在這種情形下,我們會(huì)選擇讓進(jìn)程崩潰,并依靠Supervisor修復(fù)問(wèn)題。這種做法的好處是該進(jìn)程的主體代碼可以不必操心錯(cuò)誤處理的問(wèn)題,例如編寫(xiě)try-catch或“if err != nil”這樣的代碼塊,而只關(guān)注主路徑上的邏輯。我們甚至還可以通過(guò)模式匹配的方式優(yōu)雅地對(duì)各種期望進(jìn)行斷言。\
在我看來(lái),這種方式比try-catch-ignore的做法更優(yōu)秀,因?yàn)橐坏┻M(jìn)程中止,它的狀態(tài)也就消失了。而問(wèn)題的根源很可能來(lái)自于有問(wèn)題的狀態(tài)。在進(jìn)程重啟之后,新的進(jìn)程會(huì)生成全新的、穩(wěn)定的狀態(tài),因此進(jìn)程能夠再次運(yùn)轉(zhuǎn)。至少它可以穩(wěn)定地運(yùn)行一段時(shí)間,直到狀態(tài)再次出現(xiàn)問(wèn)題為止。這種做法能夠讓系統(tǒng)中有問(wèn)題的地方浮現(xiàn)出來(lái),多數(shù)服務(wù)在這種方式下都會(huì)表現(xiàn)出偶爾的故障現(xiàn)象,直至問(wèn)題的根源解決為止。\
與任其崩潰相輔相成的一點(diǎn)是通過(guò)Supervisor進(jìn)行恢復(fù)。如果你建立了一個(gè)細(xì)粒度的supervision樹(shù),那么所需重啟的部分也相對(duì)較少。一旦產(chǎn)生故障,你可以試著重啟系統(tǒng)中的一小部分如果問(wèn)題仍然沒(méi)有解決,你可以逐漸增加這部分的區(qū)域,直到系統(tǒng)中有問(wèn)題的那部分被重啟為止。相反,如果你采用了try-catch-ignore方式,就有可能導(dǎo)致錯(cuò)誤的狀態(tài)始終延續(xù),最終產(chǎn)生了一個(gè)永無(wú)休止的故障循環(huán)。
\InfoQ:“任其崩潰”是僅屬于Erlang的一種獨(dú)特功能嗎?可否將其移植至其它不使用Erlang VM的環(huán)境中呢?\
\Sa?a:問(wèn)得好!首先我要強(qiáng)調(diào)一點(diǎn),OTP是用純粹的Erlang構(gòu)建的,它依賴(lài)于Erlang VM的基礎(chǔ)功能。理解這一點(diǎn)非常重要,因?yàn)槲以?jīng)看到過(guò)一些說(shuō)法,認(rèn)為OTP能夠以某種方式“移植”到其它運(yùn)行時(shí)環(huán)境中。但我認(rèn)為這是不太可能的,除非目標(biāo)運(yùn)行時(shí)平臺(tái)能夠提供一些嚴(yán)格的保障。\
具體到任其崩潰和supervisor來(lái)說(shuō), Erlang的VM為它們提供了一些重要的保障。\
前兩點(diǎn)特性能夠幫助我們將故障的后果局限在一定范圍內(nèi):如果有部分出現(xiàn)問(wèn)題,整個(gè)系統(tǒng)的大部分依然能夠繼續(xù)提供服務(wù)。第三點(diǎn)保障能夠讓我們對(duì)某個(gè)故障進(jìn)行響應(yīng),當(dāng)發(fā)生崩潰時(shí),Supervisor能夠?qū)ζ溥M(jìn)行糾正。最后兩點(diǎn)保證了適當(dāng)?shù)南到y(tǒng)清理,如果沒(méi)有這兩點(diǎn)保障,系統(tǒng)可能會(huì)產(chǎn)生孤兒進(jìn)程或是資源無(wú)法釋放的問(wèn)題。\
如果缺乏這些保障,我認(rèn)為是無(wú)法實(shí)現(xiàn)Erlang的容錯(cuò)性能力的。即使你能夠盡力接近,但永遠(yuǎn)也做不到100%的功能,總會(huì)有些隱秘的功能是你無(wú)法察覺(jué)的。這并不是說(shuō)你必須要使用Erlang VM才能夠?qū)崿F(xiàn)任其崩潰的做法,只是說(shuō)你需要一種能夠提供這些保障的VM。
\InfoQ:你是否能夠分享一下你對(duì)于Elixir目前在業(yè)界的使用情況的展望?\
\Sa?a:雖然Elixir還是一門(mén)新生的語(yǔ)言,但它的基礎(chǔ)(Erlang)已經(jīng)非常穩(wěn)定,其能力近20年來(lái)在各種不同的大型系統(tǒng)中都得到了證實(shí)。成功的案例包括WhatsApp、RabbitMQ、Riak、實(shí)時(shí)競(jìng)價(jià)(AdRoll),以及財(cái)務(wù)系統(tǒng)(Klarna)等等。至于Elixir,我已經(jīng)看到它越來(lái)越多地出現(xiàn)在各種解決方案的生產(chǎn)環(huán)境中,例如游戲的后臺(tái)或物聯(lián)網(wǎng)(IoT)。可以在這里找到在生產(chǎn)環(huán)境中使用Elixir的公司的一個(gè)列表,我很期待看到它今后的發(fā)展。
\關(guān)于本書(shū)作者
\Sa?a Juri?是一位軟件開(kāi)發(fā)者,他在使用Elixir和Erlang打造高負(fù)載、高并發(fā)的服務(wù)端系統(tǒng)方面具有豐富的經(jīng)驗(yàn)。
\查看英文原文:Elixir in Action Review and Q\u0026amp;A with the Author
與50位技術(shù)專(zhuān)家面對(duì)面20年技術(shù)見(jiàn)證,附贈(zèng)技術(shù)全景圖總結(jié)
以上是生活随笔為你收集整理的《Elixir in Action》书评及作者问答录的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Java虚拟机9:Java类加载机制
- 下一篇: MongoDB 复制集的选举原理