背景
我們所有產(chǎn)品在初期的時(shí)候都使用的Java語言作為后端開發(fā)語言屿储,整個(gè)架構(gòu)在演進(jìn)了幾次之后形成了基于微服務(wù)的一個(gè)復(fù)雜架構(gòu)狀態(tài)栖茉,包括了Restful Service作為Web層抽象颁糟,基于Netty的分布式抓取框架冗锁,基于Quartz的定時(shí)服務(wù)庞萍,基于Storm的分布式實(shí)時(shí)計(jì)算框架,還有一些附加的日志處理佑刷、消息轉(zhuǎn)發(fā)莉擒、Websocket消息推送服務(wù)等等,多個(gè)服務(wù)之間通過Http協(xié)議瘫絮、或者TCP/UDP協(xié)議涨冀,或者基于AMQP的消息隊(duì)列來進(jìn)行通信÷笥總體來看整體的解決方案嚴(yán)重依賴Java語言或者說JVM語言鹿鳖,在開發(fā)的過程中這些解決方案無疑都是強(qiáng)大的扁眯,不管是Netty還是Storm都是當(dāng)時(shí)效率比較高且成熟的基礎(chǔ)組件,可以說從IO和計(jì)算層面我們盡可能的都選用了JVM生態(tài)系統(tǒng)里邊當(dāng)時(shí)最好的翅帜,比如說考慮到Netty本身的多路復(fù)用和多線程模型的優(yōu)秀姻檀,我們盡可能的在處理IO上采用類似的結(jié)構(gòu),或者直接使用Netty作為入口涝滴,包括JAX-RS的容器都是直接采用的Netty作為IO和協(xié)議解析的框架绣版,而沒有采用其他的雖然現(xiàn)在也支持NIO的Tomcat或者Jetty,分布式抓取之前的通信也是采用Netty,Storm之間的通信也依賴于Netty(官方聲明測(cè)試結(jié)果比之前的ZeroMQ的解決方案要好不少)歼疮,也是在我們技術(shù)棧內(nèi)部實(shí)現(xiàn)了某種層面的統(tǒng)一杂抽,所有的IO或者通信都采用的Netty,不過也在這幾年的開發(fā)中發(fā)現(xiàn)了一些問題韩脏,導(dǎo)致我們一直在用Java的過程中也想找到一種合適的語言來替代如日中天的Java缩麸。
Java語言的問題
1. 生產(chǎn)力問題
開發(fā)調(diào)試慢,整體解決方案復(fù)雜赡矢。這只是相對(duì)而言杭朱,對(duì)于熟練的開發(fā)這也許不是問題,作為企業(yè)級(jí)解決方案而言济竹,Java語言確實(shí)比較繁重痕檬,即使依賴了很多自動(dòng)編譯、動(dòng)態(tài)加載送浊、打包并部署的持續(xù)集成工具梦谜,調(diào)試速度依然很慢,與現(xiàn)在的快節(jié)奏的開發(fā)要求有明顯矛盾袭景,舉例來說唁桩,如果我們要開發(fā)一個(gè)小的項(xiàng)目,就是包含一個(gè)Web api服務(wù)耸棒,這時(shí)候我們需要新建一個(gè)工程荒澡,寫自己的pom文件,組織好工程的依賴和配制文件与殃,然后呢就是基于某種框架单山,可以是servlet-api也可以是restful風(fēng)格,或者是spring等等幅疼,然后選擇一種嵌入式的容器以便不想要部署到哪里就可以自己?jiǎn)?dòng)服務(wù)米奸,然后代碼可能很簡(jiǎn)單,寫一個(gè)接口然后打包爽篷,編譯悴晰,一般情況下生成一個(gè)可以自己運(yùn)行的Jar包以及他所依賴的jar包目錄,將這個(gè)目錄上傳,啟動(dòng)并測(cè)試铡溪,假如說需要修改的話就需要改動(dòng)然后重啟整個(gè)服務(wù)漂辐,等著新的代碼提交并觸發(fā)編譯部署重啟才能生效,為了避免中斷服務(wù)也需要做一些HA的策略等等棕硫,即使做一個(gè)很小的事情也需要想對(duì)比較重量級(jí)的一套東西來作為支撐髓涯,互聯(lián)網(wǎng)時(shí)代的開發(fā)節(jié)奏要求更加敏捷,Java的狀態(tài)有點(diǎn)令人著急饲帅,相對(duì)來說動(dòng)態(tài)語言在這方面確實(shí)得天獨(dú)厚复凳,用來開發(fā)產(chǎn)品或者服務(wù)的原型速度更快瘤泪,之后如果有性能問題(一般也確實(shí)會(huì)有)可以考慮用靜態(tài)類型語言重構(gòu)灶泵。
2. 表達(dá)能力問題
作為一個(gè)上世紀(jì)的語言,一個(gè)面向?qū)ο蟮植皇羌兊拿嫦驅(qū)ο笳Z言对途,本身的抽象能力是不錯(cuò)的赦邻,在很多大型的服務(wù)系統(tǒng)里邊我們能夠使用一些模式來進(jìn)行很好的抽象,使整個(gè)架構(gòu)優(yōu)美实檀,可讀性和擴(kuò)展性都能夠良好的體現(xiàn)出來惶洲。但總體來說Java語言的編寫過程更傾向于過程式的開發(fā),在上一層面上封裝了面向?qū)ο蟮奶卣骱托袨樯庞蹋Z言的設(shè)計(jì)是上個(gè)世紀(jì)九十年代的風(fēng)格恬吕,不是說語言本身不好是其抽象能力不夠,即使到了Java8也只是對(duì)Lambda表達(dá)式進(jìn)行了支持须床,因此引入了Functional Interface也即只有一個(gè)方法的接口铐料,和接口里邊的帶具體實(shí)現(xiàn)的方法(為了兼容以前的代碼不得不作出的讓步)。Java語言發(fā)展到現(xiàn)在其語言特性龐大豺旬,如果要完全了解需要幾百頁的文檔钠惩,在其發(fā)展過程中又只做加法沒又減法,語言慢慢風(fēng)格混雜族阅,變成了現(xiàn)在這種四不像的狀態(tài)篓跛,函數(shù)式的特性硬生生的嫁接在原來的面向?qū)ο筇匦灾稀?/p>
3. 資源消耗問題
Java語言號(hào)稱一次編譯,處處運(yùn)行坦刀,就在于它基于一個(gè)需要首先先安裝到他所謂的處處的JDK,通過JVM解析編譯完成后的字節(jié)碼來運(yùn)行愧沟,跟操作系統(tǒng)的接口也是在JVM托管的。這樣的好處是JVM可以在實(shí)時(shí)運(yùn)行的時(shí)候?qū)ψ止?jié)碼進(jìn)行進(jìn)一步的優(yōu)化鲤遥,也就是大名鼎鼎的JIT沐寺,問題是所有的機(jī)器上都要安裝可以兼容你的應(yīng)用程序的JDK,同時(shí)JVM啟動(dòng)消耗的資源不少,起碼數(shù)百M(fèi)渴频,且啟動(dòng)速度緩慢芽丹,同樣的直接編譯成目標(biāo)操作系統(tǒng)二進(jìn)制可執(zhí)行程序的服務(wù),啟動(dòng)起來消耗的資源小很多且速度快了很多卜朗。因此拔第,我個(gè)人更喜歡編譯語言而不是某種VM上運(yùn)行的語言咕村,在當(dāng)前差異化的芯片結(jié)構(gòu)中,像C蚊俺、GO懈涛、RUST這種能直接運(yùn)行于操作系統(tǒng)之上不基于某些龐大繁重的VM之上還是很有必要的,比如物聯(lián)網(wǎng)的控制芯片泳猬,通常內(nèi)存也只有幾百K批钠,適用性更強(qiáng)一些,而且現(xiàn)在LLVM架構(gòu)的編譯器能夠帶來性能的大幅優(yōu)化得封,所以編譯依然是一個(gè)很好的選擇埋心,除非JIT能夠逆天的達(dá)到解釋執(zhí)行的極限忙上,因此假如我們看到某些語言有Java語言的開發(fā)能力和內(nèi)存安全特性拷呆,依然是可以考慮的。
最近幾年所關(guān)注的語言
以上說了一些我們雖然完全在使用Java語言開發(fā)但是也不得不承受的一些問題疫粥,隨著時(shí)代的發(fā)展有一些新的語言進(jìn)入大家的視線茬斧,首先要說的是,我們都不是研究語言的科學(xué)家梗逮,不是嚴(yán)格的從語言的語法設(shè)計(jì)和類型系統(tǒng)的別致程度來評(píng)價(jià)一門語言项秉,更多的是從我們作為使用者實(shí)際應(yīng)用中來考慮哪個(gè)更有利于我們整個(gè)團(tuán)隊(duì)的進(jìn)化和生產(chǎn)力的提高,如有疏漏和不嚴(yán)謹(jǐn)?shù)牡胤娇锻赋觥?br>
??曾經(jīng)在考慮之中的語言包括了Haskell, Go, Scala, Rust四種娄蔼,主要考慮的因素包括了語言的能力、開發(fā)效率瞬欧、商業(yè)化程度贷屎、學(xué)習(xí)曲線、招聘市場(chǎng)人才儲(chǔ)備狀態(tài)艘虎。
??后邊三中都是比較新的語言了唉侄,為什么包含了Haskell呢,他雖然很老但是一直是作為學(xué)院派函數(shù)式語言的代表野建,其純函數(shù)式的特性和簡(jiǎn)潔漂亮的語法(糖)讓人看了非常舒服属划,在接觸了面向過程和面向?qū)ο蟮拈_發(fā)后,如果要學(xué)習(xí)一種新的寫代碼的思路候生,面向函數(shù)式的語言是目前最好的選擇了同眯,而Haskell有是函數(shù)式語言的先驅(qū)和集大成者,很多函數(shù)式語言的語法都是從Haskell借鑒來的唯鸭。下邊我分開說一下我自己的體驗(yàn)须蜗,通過學(xué)習(xí)和寫一些實(shí)際的項(xiàng)目所體會(huì)到的一些優(yōu)點(diǎn)和缺點(diǎn)。
1. Haskell
作為純函數(shù)式語言,Haskell將必然會(huì)產(chǎn)生Side-Effect的代碼比如IO操作放到了一起明肮,也即monad風(fēng)格的部分菱农,而其他的函數(shù)可以保證完全的函數(shù)式特征,對(duì)于同樣的輸入無論運(yùn)行多少次結(jié)果都是一樣的柿估,跟數(shù)學(xué)中函數(shù)的定義一樣嚴(yán)格循未,函數(shù)式是一種CPU友好的語言,在當(dāng)前多核計(jì)算機(jī)發(fā)展?fàn)顩r下秫舌,函數(shù)式可以讓程序非常安全的在多個(gè)核心上并發(fā)而不用擔(dān)心大量的數(shù)據(jù)交互和side-effect的妖, 從而在語言編譯過程中能夠針對(duì)并發(fā)進(jìn)行大幅的優(yōu)化。語言本身的很多寫法也跟數(shù)學(xué)中的定義很接近足陨,比如定義一個(gè)集合
ghci> [x*2 | x <- [1..10]] [2,4,6,8,10,12,14,16,18,20]
看起來很像數(shù)學(xué)定義嫂粟,語言可謂優(yōu)雅漂亮,看著很舒服钠右。作為學(xué)院派語言赋元,語言自身設(shè)計(jì)的要求不可謂不嚴(yán)格,完美的闡述了函數(shù)式是什么意思飒房,但是語言的復(fù)雜度較高,學(xué)習(xí)曲線很陡峭媚值,很難保證團(tuán)隊(duì)成員的接收程度狠毯,也很難招到相關(guān)的技術(shù)人才。從效率上來講褥芒,Haskell可以優(yōu)化的跟C語言的級(jí)別類似嚼松,但如果對(duì)某些特性不熟悉稍微改動(dòng)一些就會(huì)造成性能的大幅下降,對(duì)新手不算友好锰扶。同時(shí)在函數(shù)式不那么擅長(zhǎng)的領(lǐng)域Haskell的商業(yè)化程度很低献酗,我們不可能都用Haskell來寫一些語法解釋或者正則解析等,涉及IO的分布式存儲(chǔ)和計(jì)算都相對(duì)很初級(jí)坷牛,尤其是對(duì)于我們比較感興趣的數(shù)據(jù)挖掘機(jī)器學(xué)習(xí)領(lǐng)域沒有成熟的解決方案罕偎,對(duì)于Web項(xiàng)目支持的尚可,有優(yōu)秀的Yesod框架作為代表京闰⊙占埃總的來說,我們最終將Haskell定義為值的學(xué)習(xí)但不會(huì)在大型的生產(chǎn)環(huán)境中使用的語言蹂楣。
2. Scala
Scala語言的出現(xiàn)目的很明確俏站,感覺就是為了替代Java而存在,在Java語言越來越力不從心的今天痊土,能夠有一門語言既繼承了它廣大的生態(tài)系統(tǒng)肄扎,又能夠在表達(dá)能力和開發(fā)效率大大改進(jìn)的情況,可以說是很有希望的。
- Scala從一開始就是一門設(shè)計(jì)良好的語言犯祠,幾乎完美的集合了函數(shù)式的特性和面向?qū)ο蟮奶匦悦日桑m然他的函數(shù)式不是純函數(shù)式。其面向?qū)ο蟮母杏X更像Ruby而不是Java雷则,所有的東西都是對(duì)象辆雾,包括簡(jiǎn)單類型例如Int,以及函數(shù)本身都是一種對(duì)象月劈,這樣在這個(gè)層面實(shí)現(xiàn)了面向?qū)ο蠛秃瘮?shù)式的統(tǒng)一度迂。具體的我們會(huì)在接下來的文章中介紹。
- 龐大的Java生態(tài)系統(tǒng)猜揪,Scala運(yùn)行于JVM之上惭墓,能夠無縫的使用所有的原來Java語言所開發(fā)的各種庫,語言上作為Java的超集而姐,遷移過來只會(huì)更強(qiáng)大而不會(huì)打折腊凶。
- Java8的出現(xiàn)雖然增加了針對(duì)集合的stream api以及Lambda表達(dá)式這種函數(shù)式特性的支持,但只會(huì)讓人們覺得Java與Scala更像了拴念,即使Java在以后的發(fā)展過程中擁有了所有的Scala的能力钧萍,我也不會(huì)考慮,打個(gè)比方一塊歪歪扭扭的經(jīng)過各種后期焊接所建造起來的機(jī)器和一個(gè)一開始就有目的的設(shè)計(jì)出來的結(jié)構(gòu)精密政鼠、風(fēng)格統(tǒng)一风瘦、表達(dá)高效的機(jī)器比較,后者更像前者的重構(gòu)公般,而前者雖然如日中天但已經(jīng)是暮年的四不像万搔,不停的往身上增加各種各樣的功能,也許Java9會(huì)有進(jìn)步官帘,但現(xiàn)在我看到Java8后反而更傾向于Scala瞬雹。
- 函數(shù)式特性
- Pattern Matching,異常強(qiáng)大的模式匹配系統(tǒng)刽虹,可以出現(xiàn)在match語言里邊的case表達(dá)式中酗捌,可以出現(xiàn)在構(gòu)造函數(shù)中,也可以出現(xiàn)在變量初始化状婶,通過模式匹配大大減輕賦值的復(fù)雜度意敛,簡(jiǎn)化了代碼,專注于處理邏輯膛虫〔菀觯可以說在基本上所有的函數(shù)式語言中都有Pattern Matching特性,通過模式匹配來構(gòu)造的函數(shù)體表達(dá)能力更強(qiáng)稍刀,也更安全撩独。
- Actor Model敞曹,Scala的并發(fā)除了使用Java原生的線程之外還實(shí)現(xiàn)了一個(gè)Actor Model的并發(fā)模型,跟Erlang語言的并發(fā)模型相同综膀,主流的并發(fā)通信模型還有CSP澳迫,比如GO語言就采用的這種模型來組織goroutin以及他們的通信channel。Scala的Actor Model不是語言原生帶的剧劝,而是一個(gè)叫Akka的擴(kuò)展包所實(shí)現(xiàn)橄登,這也是Scala語言的一個(gè)特點(diǎn),除了自身核心的語言特性外讥此,其他的擴(kuò)展都是在其語言的基礎(chǔ)上實(shí)現(xiàn)拢锹,保持語言自身language specification足夠的精簡(jiǎn),不像Java一樣不停的集成新的特性到語言自身萄喳。Actor Model自身的最小執(zhí)行單元也就是一個(gè)個(gè)的Actor本身比線程還要輕量級(jí)卒稳,只是一種邏輯上的抽象而不是物理上的更小的線程,對(duì)于操作系統(tǒng)而言能夠分配時(shí)間片的最小的執(zhí)行單位依然是線程他巨,當(dāng)然不同的操作系統(tǒng)的線程概念會(huì)有一些差異充坑,這里只是籠統(tǒng)的說,Actor本身在JVM里邊依然是基于一個(gè)線程池來作為自己的執(zhí)行容器染突,默認(rèn)情況下選擇的是最適合多核并發(fā)的ForkJoinPool捻爷,允許執(zhí)行快速的隊(duì)列從另外的隊(duì)列尾部偷任務(wù)。使用Actor而不是直接使用線程的好處也比較明顯觉痛。
- Actor更加輕量級(jí)役衡,在一個(gè)進(jìn)程中啟動(dòng)上千個(gè)Actor完全沒有問題,而線程要重量級(jí)的多薪棒,如果一個(gè)進(jìn)程中啟動(dòng)的線程過多會(huì)增加上下文切換的頻率以及操作系統(tǒng)CPU調(diào)度的浪費(fèi),我們?cè)?jīng)寫過很多的多線程的服務(wù)榕莺,有些同學(xué)因?yàn)橹粚W⒂谧约旱臉I(yè)務(wù)邏輯俐芯,有時(shí)候會(huì)在不知道全局線程狀態(tài)的情況下再啟動(dòng)記得線程池,這樣在一些情況會(huì)發(fā)現(xiàn)CPU的負(fù)載迅速爬升钉鸯,經(jīng)常出現(xiàn)一個(gè)進(jìn)程占用十個(gè)核心的狀況吧史。而由于在一個(gè)ActorSystem中多個(gè)Actor都共用一個(gè)線程池,Actor之間的執(zhí)行分配通過自身的dispatcher來調(diào)度唠雕,這樣減少了底層的線程數(shù)量贸营,變相提高了CPU利用率。實(shí)際上這也是現(xiàn)在語言的一個(gè)趨勢(shì)岩睁,不直接操作線程而選擇更小的執(zhí)行單元钞脂,比如Go語言中的goroutine,鼎鼎大名的Java語言所寫的Netty框架也是通過高效的利用線程池而隱藏了具體的多路復(fù)用細(xì)節(jié)捕儒,使開發(fā)專注于具體的處理邏輯冰啃,這也是Netty可以處理大規(guī)模并發(fā)連接的秘訣邓夕,如果為每個(gè)連接都分配一個(gè)線程,那負(fù)載會(huì)高出很多阎毅,效率可想而知焚刚。當(dāng)然直接使用Actor也是有弊端的,對(duì)于延遲要求很高的場(chǎng)景扇调,需要馬上為當(dāng)前運(yùn)行邏輯分配線程執(zhí)行矿咕,而不是再等待ActorSystem的dipatcher去分配,這種情況還是要直接使用自己的線程池狼钮,對(duì)于大規(guī)模高并發(fā)吞吐量要求高但是對(duì)單個(gè)請(qǐng)求響應(yīng)不需要太迅速的情況下可以使用碳柱,如果仔細(xì)規(guī)劃Actor的數(shù)量,其實(shí)也是可以滿足直接使用線程的情況燃领,只是沒有操作線程那么的直接士聪。
- Stateful,在實(shí)現(xiàn)有限狀態(tài)機(jī)的時(shí)候很方便
- 事件驅(qū)動(dòng)猛蔽,類似于Netty NIO的框架剥悟,所有的操作都是通過異步的事件驅(qū)動(dòng),最大限度的利用線程資源曼库,對(duì)于Idol的Actor是不會(huì)占用CPU資源的区岗,同時(shí)使用上也要注意盡量不要阻塞Actor里邊的Receive方法,這樣會(huì)導(dǎo)致該Actor所占用的線程池里邊的某個(gè)線程被阻塞毁枯,非常影響整個(gè)系統(tǒng)的執(zhí)行效率慈缔,因此一旦考慮使用Actor Model,從思路上就要轉(zhuǎn)換為全異步的執(zhí)行方式种玛,比如提交一個(gè)任務(wù)給Spark執(zhí)行藐鹤,這時(shí)候這個(gè)消息就處理完了,而不是阻塞著等待Spark執(zhí)行結(jié)果返回赂韵,當(dāng)Spark執(zhí)行完成后通過異步的消息通知給當(dāng)前系統(tǒng)的某個(gè)Actor娱节,將消息發(fā)送給能夠處理這個(gè)事件的Actor,簡(jiǎn)單說就是保證每個(gè)消息的處理過程都是非阻塞的祭示,盡量減少處理時(shí)間肄满。
- DSL Scala核心的語法特點(diǎn)可以讓他支持對(duì)自身的擴(kuò)展,極少的保留字符可以讓開發(fā)人員按照自己的喜好定義一種新的編寫方式和習(xí)慣质涛,比如Akka的Actor包就定義了自己的Actor DSL稠歉,用!來替代tell 汇陆?替代ask巧娱,可以更有效的來操作Actor阿迈,再比如Scala-Graph API也包含了大量的DSL端蛆,通過 1 ~> 2來表示連接兩個(gè)node,~>本身是這個(gè)擴(kuò)展包自己定義的操作纠炮,而不是scala原生的保留操作符〉坪可以說我們通過scala可以擴(kuò)展出另外一種跟scala完全不同的語言恢口,這是java語言很難做到的。Scala的元編程能力可以讓他修改自己的語言定義穷躁,不只是實(shí)現(xiàn)某些業(yè)務(wù)邏輯耕肩,這樣從符號(hào)層面上,scala可以做到自洽问潭,除了核心的一些規(guī)則猿诸,其他的都可以被自己根據(jù)狀態(tài)調(diào)整所修改,這種能力可以極大的擴(kuò)展語言自身的能力狡忙,當(dāng)然也帶來了一些負(fù)面效果梳虽,每學(xué)習(xí)一種新的包不只是了解他的API,而是學(xué)習(xí)了一種新的語言灾茁,風(fēng)格可能跟scala大不相同窜觉。
- 強(qiáng)有力的證明,大數(shù)據(jù)生態(tài)系統(tǒng)代表-Spark&Kafka北专,一個(gè)是分布式計(jì)算一個(gè)是分布式大規(guī)模數(shù)據(jù)吞吐禀挫,都證明了Scala的開發(fā)能力和效率。
- Scala的問題其實(shí)也有跟Java類似的地方拓颓,首先這個(gè)語言雖然是重新設(shè)計(jì)的语婴,但是使用起來復(fù)雜度依然很高,對(duì)于范型的繼承驶睦,+-等范型標(biāo)注不好理解砰左,
3. Go
Go語言目前呈現(xiàn)了很火爆的趨勢(shì),由于其簡(jiǎn)單场航,整個(gè)語言的specification也不過十幾頁菜职,最多半天就能夠完全了解并上手寫一些小工具。GO語言最初是希望替代C和C++成為新的系統(tǒng)語言旗闽,自帶GC垃圾回收,不過最終更多的是替代了python來開發(fā)一些服務(wù)或者工具蜜另,并沒有成為系統(tǒng)級(jí)別的語言适室。
??Go語言有很多的優(yōu)點(diǎn),編譯速度快举瑰,有協(xié)程和Channel做并發(fā)支持和通信捣辆,有很多官方的網(wǎng)絡(luò)協(xié)議的庫,非常適合于寫一些網(wǎng)絡(luò)服務(wù)此迅,啟動(dòng)一個(gè)http的接口服務(wù)只需要幾行代碼汽畴。目前github上也有大量的第三方項(xiàng)目使用go語言來開發(fā)應(yīng)用或者擴(kuò)展go的功能旧巾,在使用的時(shí)候直接import即可。Go的多返回機(jī)制也還不錯(cuò)忍些,節(jié)省了大量的無意義數(shù)據(jù)結(jié)構(gòu)和不可讀的Map的使用鲁猩,總的來說Go在其擅長(zhǎng)的領(lǐng)域生產(chǎn)力很高,寫起來比較流暢罢坝,靜態(tài)類型也足夠的安全廓握。目前Docker生態(tài)系統(tǒng)里邊的各種工具都是Go來寫的。最新發(fā)布的1.5版本使得交叉編譯更加容易嘁酿,靜態(tài)鏈接庫的方式使生成的可執(zhí)行文件在相同CPU架構(gòu)的操作系統(tǒng)都能運(yùn)行隙券,減少了額外查找依賴的問題,對(duì)我們現(xiàn)在基本同構(gòu)的Linux服務(wù)器而言闹司,也打到了一次編譯處處運(yùn)行的目的娱仔。同時(shí)Go語言在運(yùn)行時(shí)消耗的資源也比Java要小,啟動(dòng)速度更快游桩,確實(shí)是輕量級(jí)服務(wù)的優(yōu)選牲迫。
??在公司內(nèi)部我們也使用Go語言做了一些項(xiàng)目,包括了日志分析众弓、日志收集恩溅、小規(guī)模的產(chǎn)品項(xiàng)目實(shí)現(xiàn),開發(fā)的過程中也找到了一些優(yōu)秀的框架谓娃,包括revel, goin等脚乡,由于使用簡(jiǎn)單上手容易,需求的實(shí)現(xiàn)速度很快滨达,同時(shí)部署也比較容易奶稠,就一個(gè)二進(jìn)制文件和對(duì)應(yīng)的配制文件即可。既保持了生產(chǎn)力捡遍,也沒有犧牲運(yùn)行效率锌订,處理速度很快,goroutine很好用画株。但是同時(shí)我們也發(fā)現(xiàn)了一些問題辆飘,在往大規(guī)模的系統(tǒng)架構(gòu)上去演進(jìn)的時(shí)候,go語言的抽象能力有限谓传,除了interface我們沒有太多的手段去讓整個(gè)工程的層次更清晰蜈项,只能依靠interface之間的繼承、包含關(guān)系续挟,讓實(shí)現(xiàn)有一定程度的分離紧卒,可是對(duì)于結(jié)構(gòu)復(fù)雜的程序抽想出來的效果不夠好,也許是我們對(duì)Go語言的一些開發(fā)模式還不夠熟悉诗祸,經(jīng)常出現(xiàn)一個(gè)復(fù)雜的功能都在一個(gè)文件里邊且代碼上千行跑芳。對(duì)于小規(guī)模的開發(fā)轴总,Go語言速度尚可,也相對(duì)比較成熟了博个,Go語言語法簡(jiǎn)單的好處還在于實(shí)現(xiàn)一個(gè)東西往往只有一種方法怀樟,不同習(xí)慣的人看不同的代碼也容易看懂,簡(jiǎn)單直接坡倔。
??如果說Go語言最讓人煩的地方漂佩,目前我感覺就是對(duì)于error的處理了,基本上所有的多返回的api都會(huì)習(xí)慣性的返回一個(gè)error罪塔,在一些開發(fā)調(diào)用的方法內(nèi)部可能是一層一層的往上返回投蝉,在應(yīng)用程序級(jí)別的時(shí)候就需要對(duì)這些error做一些判斷處理了,經(jīng)常發(fā)現(xiàn)在一個(gè)處理過程中出現(xiàn)了好多個(gè)if err != nil 的判斷征堪,如果全部都panic出去可能會(huì)導(dǎo)致進(jìn)程不穩(wěn)定瘩缆,導(dǎo)致當(dāng)前運(yùn)行的goroutine掛掉,進(jìn)而使整個(gè)進(jìn)程崩潰佃蚜。
4. Rust
Rust由于1.0版本release的時(shí)間太晚庸娱,目前沒有在實(shí)際的項(xiàng)目中使用過,通過目前的了解谐算,Rust應(yīng)該是可以替代C語言在系統(tǒng)級(jí)開發(fā)的地位熟尉,同時(shí)由于一些新的語言特性使得Rust在進(jìn)行應(yīng)用開發(fā)的時(shí)候也依然高效,目前看起來整個(gè)語言的適用范圍非常的廣洲脂,只是現(xiàn)在成熟程度太低斤儿,沒有大規(guī)模的應(yīng)用開發(fā)實(shí)例。另外直觀的感覺恐锦,Rust的代碼看起來很丑往果,遠(yuǎn)不如Haskell的優(yōu)美,可能對(duì)于面向生產(chǎn)環(huán)境的語言跟學(xué)院派風(fēng)格差異確實(shí)比較大一铅,但也太丑了一點(diǎn)陕贮。
最終的選擇和原因
通過上邊的列舉,從篇幅上也能看出我們最終的選擇是Scala潘飘, 真正在生產(chǎn)中經(jīng)過開發(fā)測(cè)試的就是Go和Scala肮之,初期我們優(yōu)先考慮了Go,畢竟編譯成原生的可執(zhí)行程序理論上能夠達(dá)到比JVM語言更快的運(yùn)行速度并且節(jié)省資源消耗卜录,如果一門語言有C的效率和Java的生產(chǎn)力局骤,那最好不過了,不過后期使用中也發(fā)現(xiàn)了雖然開發(fā)足夠糙快猛暴凑,但是純粹的面向過程開發(fā),代碼平鋪直敘的有點(diǎn)冗長(zhǎng)赘来,不容易抽象现喳,不是說不能做凯傲,而是做起來不那么方便,沒有范型嗦篱,更是缺乏現(xiàn)在的一些高級(jí)語言的特點(diǎn)冰单。在以后的技術(shù)演進(jìn)中,整個(gè)技術(shù)椌拇伲可能是異構(gòu)的诫欠,那Go會(huì)占其中一部分,但主體可能不是浴栽。
??匯總下這兩個(gè)語言在目前我們公司的技術(shù)棧下進(jìn)行切換的優(yōu)缺點(diǎn):
- Go的優(yōu)點(diǎn)
- 輕量級(jí)
- 編譯速度快
- 部署方便荒叼,生成的可執(zhí)行文件相對(duì)較小
- 多返回值
- 簡(jiǎn)單的import
- 原生的goroutine&channel
- Github大量的第三方組件支持,有大規(guī)模應(yīng)用開發(fā)實(shí)例典鸡,比如Docker
- 目前的版本執(zhí)行效率很快
- Go的缺點(diǎn)
- 面向過程被廓,抽象能力有限,語法太簡(jiǎn)單萝玷,不支持Pattern matching嫁乘,也無法實(shí)現(xiàn)DSL
- 在分布式計(jì)算領(lǐng)域不夠成熟,寫一些簡(jiǎn)單的服務(wù)可以球碉,復(fù)雜的代碼結(jié)構(gòu)會(huì)不清晰
- 與當(dāng)前我們的技術(shù)棧差異較大蜓斧,只能通過遠(yuǎn)程服務(wù)調(diào)用的形式使用
- Scala的優(yōu)點(diǎn)
- 與當(dāng)前Java技術(shù)棧無縫結(jié)合
- 表達(dá)能力強(qiáng),可以實(shí)現(xiàn)一些自定義的DSL睁冬,在我們最新的一個(gè)項(xiàng)目需要這個(gè)能力
- 具有函數(shù)式特性挎春,適合多核情況下并發(fā)優(yōu)化,Actor Model抽象高效且使用方便痴突。
- 我們以前的項(xiàng)目就較多的利用了Akka和Spark搂蜓,使用Scala來寫部分工程能夠讓大家更好的了解Spark,同構(gòu)的情況下結(jié)合的更好一些辽装,能夠利用更多的特性帮碰。
- 在大數(shù)據(jù)領(lǐng)域可謂i久經(jīng)考驗(yàn)
- Scala的缺點(diǎn)
- 語法復(fù)雜,不利于新手拾积,其實(shí)本身的風(fēng)格與Java相差甚遠(yuǎn)殉挽,特別要注意immutable和mutable的同樣名字的集合類的使用,每個(gè)庫都有自己的DSL拓巧,需要學(xué)一種新的語言斯碌,這也是表達(dá)力強(qiáng)的一個(gè)副作用,寫一個(gè)東西通常有多種方式肛度,達(dá)到相同的效果傻唾。
- 依賴于JVM,依然有資源消耗的問題,啟動(dòng)慢
- implicit是個(gè)坑冠骄,經(jīng)常需要自己診斷使用的到底是哪個(gè)實(shí)例
- 目前人才缺乏伪煤,需要自己培養(yǎng)
??可見,雖然我們從商業(yè)化的角度選擇了Scala作為下一代的替換語言凛辣,這樣以前的技術(shù)棧依然有效抱既,目前的團(tuán)隊(duì)也能比較容易的過渡,但是也不會(huì)馬上的把所有的項(xiàng)目都由Scala來實(shí)現(xiàn)扁誓,而是一些偏分布式計(jì)算的核心框架防泵,大部分依然使用java語言,經(jīng)過較長(zhǎng)的一段過渡期(與團(tuán)隊(duì)能力相關(guān))蝗敢,再?zèng)Q定是否完全轉(zhuǎn)換為Scala語言捷泞。
??本文所有的觀點(diǎn)都是在當(dāng)前時(shí)間的一些了解寫的,隨著時(shí)間推進(jìn)前普,很多東西會(huì)發(fā)生變化肚邢,不再有效,請(qǐng)注意甄別拭卿。