快速瞭解Scala技術棧html
我無可救藥地成爲了Scala的超級粉絲。在我使用Scala開發項目以及編寫框架後,它就彷彿凝聚成爲一個巨大的黑洞,吸引力使我不得不飛向它,以致於開始背離Java。當然Java 8爲Java陣營增添了一絲亮色,倒是望眼欲穿,千呼萬喚始出來。而Scala程序員,卻早就在享受lambda、高階函數、trait、隱式轉換等帶來的福利了。git
Java像是一頭史前巨獸,它在OO的方向上幾乎走到了極致,硬將它拉入FP陣營,確乎有些強人所難了。而Scala則不,由於它的誕生就是OO與FP的混血兒——完美的基因融合。程序員
「Object-Oriented Meets Functional」,這是Scala語言官方網站上飄揚的旗幟。這也是Scala的野心,固然,也是Martin Odersky的雄心。github
然而,一門語言並不能孤立地存在,必須提供依附的平臺,以及圍繞它創建的生態圈。不如此,語言則不足以壯大。Ruby很優秀,但若是沒有Ruby On Rails的推進,也很難發展到今天這個地步。Scala一樣如此。反過來,當咱們在使用一門語言時,也要選擇符合這門語言的技術棧,在整個生態圈中找到適合具體場景的框架或工具。json
固然,咱們在使用Scala進行軟件開發時,亦能夠尋求龐大的Java社區支持;但是,若是選擇調用Java開發的庫,就會犧牲掉Scala給咱們帶來的福利。幸運的是,在現在,多數狀況你已沒必要如此。伴隨着Scala語言逐漸造成的Scala社區,已經開始慢慢造成相對完整的Scala技術棧。不管是企業開發、自動化測試或者大數據領域,這些框架或工具已經很是完整地呈現了Scala開發的生態系統。api
若要了解Scala技術棧,並快速學習這些框架,一個好的方法是下載typesafe推出的Activator。它提供了相對富足的基於Scala以及Scala主流框架的開發模板,這其中實則還隱含了typesafe爲Scala開發提供的最佳實踐與指導。下圖是Activator模板的截圖:tomcat
那麼,是否有渠道能夠總體地獲知Scala技術棧到底包括哪些框架或工具,以及它們的特性與使用場景呢?感謝Lauris Dzilums以及其餘在Github的Contributors。在Lauris Dzilums的Github上,他創建了名爲awesome-scala的Repository,蒐羅了當下主要的基於Scala開發的框架與工具,涉及到的領域包括:
是否有「亂花漸欲迷人眼」的感受?不是太少,而是太多!那就讓我刪繁就簡,就個人經驗介紹一些框架或工具,從持久化、分佈式系統、HTTP、Web框架、大數據、測試這六方面入手,做一次走馬觀花般的俯瞰。
歸根結底,對數據的持久化主要仍是經過JDBC訪問數據庫。可是,咱們須要更好的API接口,能更好地與Scala契合,又或者更天然的ORM。若是但願執行SQL語句來操做數據庫,那麼運用相對普遍的是框架ScalikeJDBC,它提供了很是簡單的API接口,甚至提供了SQL的DSL語法。例如:
val alice: Option[Member] = withSQL { select.from(Member as m).where.eq(m.name, name) }.map(rs => Member(rs)).single.apply()
若是但願使用ORM框架,Squeryl應該是很好的選擇。個人同事楊雲在項目中使用過該框架,體驗不錯。該框架目前的版本爲0.9.5,已經比較成熟了。Squeryl支持按慣例映射對象與關係表,至關於定義一個POSO(Plain Old Scala Object),從而減小框架的侵入。若映射違背了慣例,則能夠利用框架定義的annotation如@Column定義映射。框架提供了org.squeryl.Table[T]來完成這種映射關係。
由於能夠運用Scala的高階函數、偏函數等特性,使得Squeryl的語法很是天然,例如根據條件對錶進行更新:
update(songs)(s => where(s.title === "Watermelon Man") set(s.title := "The Watermelon Man", s.year := s.year.~ + 1) )
我放棄介紹諸如模塊化管理以及依賴注入,是由於它們在Scala社區的價值不如Java社區大。例如,咱們能夠靈活地運用trait結合cake pattern就能夠實現依賴注入的特性。所以,我直接跳過這些內容,來介紹影響更大的支持分佈式系統的框架。
Finagle的血統高貴,來自過去的寒門,如今的高門大族Twitter。Twitter是較早使用Scala做爲服務端開發的互聯網公司,於是積累了很是多的Scala經驗,並基於這些經驗推出了一些很有影響力的框架。因爲Twitter對可伸縮性、性能、併發的高要求,這些框架也極爲關注這些質量屬性。Finagle就是其中之一。它是一個擴展的RPC系統,以支持高併發服務器的搭建。我並無真正在項目中使用過Finagle,你們能夠到它的官方網站得到更多消息。
對於分佈式的支持,絕對繞不開的框架仍是AKKA。它產生的影響力如此之大,甚至使得Scala語言從2.10開始,就放棄了本身的Actor模型,轉而將AKKA Actor收編爲2.10版本的語言特性。許多框架在分佈式處理方面也選擇了使用AKKA,例如Spark、Spray。AKKA的Actor模型參考了Erlang語言,爲每一個Actor提供了一個專有的Mailbox,並將消息處理的實現細節作了良好的封裝,使得併發編程變得更加容易。AKKA很好地統一了本地Actor與遠程Actor,提供了幾乎一致的API接口。AKKA也可以很好地支持消息的容錯,除了提供一套完整的Monitoring機制外,還提供了對Dead Letter的處理。
AKKA天生支持EDA(Event-Driven Architecture)。當咱們針對領域建模時,能夠考慮針對事件進行建模。在AKKA中,這些事件模型能夠被定義爲Scala的case class,並做爲消息傳遞給Actor。借用Vaughn Vernon在《實現領域驅動設計》中的例子,針對以下的事件流:
咱們能夠利用Akka簡單地實現:
case class AllPhoneNumberListed(phoneNumbers: List[Int]) case class PhoneNumberMatched(phoneNumbers: List[Int]) case class AllPhoneNumberRead(fileName: String) class PhoneNumbersPublisher(actor: ActorRef) extends ActorRef { def receive = { case ReadPhoneNumbers => //list phone numbers actor ! AllPhoneNumberListed(List(1110, )) } } class PhoneNumberFinder(actor: ActorRef) extends ActorRef { def receive = { case AllPhoneNumberListed(numbers) => //match actor ! PhoneNumberMatched() } } val finder = system.actorOf(Prop(new PhoneNumberFinder(...))) val publisher = system.actorOf(Prop(new PhoneNumbersPublisher(finder))) publisher ! ReadPhoneNumbers("callinfo.txt")
若須要處理的電話號碼數據量大,咱們能夠很容易地將諸如PhoneNumbersPublisher、PhoneNumberFinder等Actors部署爲Remote Actor。此時,僅僅須要更改客戶端得到Actor的方式便可。
Twitter實現的Finagle是針對RPC通訊,Akka則提供了內部的消息隊列(MailBox),而由LinkedIn主持開發的Kafka則提供了支持高吞吐量的分佈式消息隊列中間件。這個頂着文學家帽子的消息隊列,可以支持高效的Publisher-Subscriber模式進行消息處理,並以快速、穩定、可伸縮的特性很快引發了開發者的關注,並在一些框架中被列入候選的消息隊列而提供支持,例如,Spark Streaming就支持Kafka做爲流數據的Input Source。
嚴格意義上講,Spray並不是單純的HTTP框架,它還支持REST、JSON、Caching、Routing、IO等功能。Spray的模塊及其之間的關係以下圖所示:
我在項目中主要將Spray做爲REST框架來使用,並結合AKKA來處理領域邏輯。Spray處理HTTP請求的架構以下圖所示:
Spray提供了一套DSL風格的path語法,可以很是容易地編寫支持各類HTTP動詞的請求,例如:
trait HttpServiceBase extends Directives with Json4sSupport { implicit val system: ActorSystem implicit def json4sFormats: Formats = DefaultFormats def route: Route } trait CustomerService extends HttpServiceBase { val route = path("customer" / "groups") { get { parameters('groupids.?) { (groupids) => complete { groupids match { case Some(groupIds) => ViewUserGroup.queryUserGroup(groupIds.split(",").toList) case None => ViewUserGroup.queryUserGroup() } } } } } ~ path("customers" / "vip" / "failureinfo") { post { entity(as[FailureVipCustomerRequest]) { request => complete { VipCustomer.failureInfo(request) } } } } }
我我的認爲,在進行Web開發時,徹底能夠放棄Web框架,直接選擇AngularJS結合Spray和AKKA,一樣可以很好地知足Web開發須要。
Spray支持REST,且Spray自身提供了服務容器spray-can,於是容許Standalone的部署(固然也支持部署到Jetty和tomcat等應用服務器)。Spray對HTTP請求的內部處理機制實則是基於Akka-IO,經過IO這個Actor發出對HTTP的bind消息。例如:
IO(Http) ! Http.Bind(service, interface = "0.0.0.0", port = 8889)
咱們能夠編寫不一樣的Boot對象去綁定不一樣的主機Host以及端口。這些特性都使得Spray可以很好地支持當下較爲流行的Micro Service架構風格。
正如前面所說,當咱們選擇Spray做爲REST框架時,徹底能夠選擇諸如AngularJS或者Backbone之類的JavaScript框架開發Web客戶端。客戶端可以處理本身的邏輯,而後再以JSON格式發送請求給REST服務端。這時,咱們將模型視爲資源(Resource),視圖徹底在客戶端。JS的控制器負責控制客戶端的界面邏輯,服務端的控制器則負責處理業務邏輯,因而傳統的MVC就變化爲VC+R+C模式。這裏的R指的是Resource,而服務端與客戶端則經過JSON格式的Resource進行通訊。
若硬要使用專有的Web框架,在Scala技術棧下,最爲流行的就是Play Framework,這是一個標準的MVC框架。另一個相對小衆的Web框架是Lift。它與大多數Web框架如RoR、Struts、Django以及Spring MVC、Play不一樣,採用的並不是MVC模式,而是使用了所謂的View First。它驅動開發者對內容生成與內容展示(Markup)造成「關注點分離」。
Lift將關注點重點放在View上,這是由於在一些Web應用中,可能存在多個頁面對同一種Model的Action。假若採用MVC中的Controller,會使得控制變得很是複雜。Lift提出了一種所謂view-snippet-model(簡稱爲VSM)的模式。
View主要爲響應頁面請求的HTML內容,分爲template views和generated views。Snippet的職責則用於生成動態內容,並在模型發生更改時,對Model和View進行協調。
大數據框架最耀眼的新星非Spark莫屬。與許多專有的大數據處理平臺不一樣,Spark創建在統一抽象的RDD之上,使得它能夠以基本一致的方式應對不一樣的大數據處理場景,包括MapReduce,Streaming,SQL,Machine Learning以及Graph等。這即Matei Zaharia所謂的「設計一個通用的編程抽象(Unified Programming Abstraction)。
因爲Spark具備先進的DAG執行引擎,支持cyclic data flow和內存計算。所以相比較Hadoop而言,性能更優。在內存中它的運行速度是Hadoop MapReduce的100倍,在磁盤中是10倍。
因爲使用了Scala語言,經過高效利用Scala的語言特性,使得Spark的總代碼量出奇地少,性能卻在多數方面都具有必定的優點(只有在Streaming方面,遜色於Storm)。下圖是針對Spark 0.9版本的BenchMark:
因爲使用了Scala,使得語言的函數式特性獲得了最棒的利用。事實上,函數式語言的諸多特性包括不變性、無反作用、組合子等,天生與數據處理匹配。因而,針對WordCount,咱們能夠如此簡易地實現:
file = spark.textFile("hdfs://...") file.flatMap(line => line.split(" ")) .map(word => (word, 1)) .reduceByKey(_ + _)
要是使用Hadoop,就沒有這麼方便了。幸運的是,Twitter的一個開源框架scalding提供了對Hadoop MapReduce的抽象與包裝。它使得咱們能夠按照Scala的方式執行MapReduce的Job:
class WordCountJob(args : Args) extends Job(args) { TextLine( args("input") ) .flatMap('line -> 'word) { line : String => tokenize(line) } .groupBy('word) { _.size } .write( Tsv( args("output") ) ) // Split a piece of text into individual words. def tokenize(text : String) : Array[String] = { // Lowercase each word and remove punctuation. text.toLowerCase.replaceAll("[^a-zA-Z0-9\\s]", "").split("\\s+") } }
雖然咱們可使用諸如JUnit、TestNG爲Scala項目開發編寫單元測試,使用Cocumber之類的BDD框架編寫驗收測試。但在多數狀況下,咱們更傾向於選擇使用ScalaTest或者Specs2。在一些Java開發項目中,咱們也開始嘗試使用ScalaTest來編寫驗收測試,乃至於單元測試。
若要我選擇ScalaTest或Specs2,我更傾向於ScalaTest,這是由於ScalaTest支持的風格更具有多樣性,能夠知足各類不一樣的需求,例如傳統的JUnit風格、函數式風格以及Spec方式。個人一篇博客《ScalaTest的測試風格》詳細介紹了各自的語法。
一個被普遍使用的測試工具是Gatling,它是基於Scala、AKKA以及Netty開發的性能測試與壓力測試工具。個人同事劉冉在InfoQ發表的文章《新一代服務器性能測試工具Gatling》對Gatling進行了詳細深刻的介紹。
ScalaMeter也是一款很不錯的性能測試工具。咱們能夠像編寫ScalaTest測試那樣的風格來編寫ScalaMeter性能測試用例,並可以快捷地生成性能測試數據。這些功能都很是有助於咱們針對代碼或軟件產品進行BenchMark測試。咱們曾經用ScalaMeter來編寫針對Scala集合的性能測試,例如比較Vector、ArrayBuffer、ListBuffer以及List等集合的相關操做,以便於咱們更好地使用Scala集合。如下代碼展現瞭如何使用ScalaMeter編寫性能測試:
import org.scalameter.api._ object RangeBenchmark extends PerformanceTest.Microbenchmark { val ranges = for { size <- Gen.range("size")(300000, 1500000, 300000) } yield 0 until size measure method "map" in { using(ranges) curve("Range") in { _.map(_ + 1) } } }
比起Java龐大的社區,以及它提供的浩如煙海般的技術棧,Scala技術棧差很少能夠說是滄海一粟。然而,麻雀雖小卻五臟俱全,況且Scala以及Scala技術棧仍然走在邁向成熟的道路上。對於Scala程序員而言,由於項目的不一樣,未必能涉獵全部技術棧,並且針對不一樣的方面,也有多個選擇。在選擇這些框架或工具時,應根據實際的場景作出判斷。爲穩妥起見,最好能運用技術矩陣地方式對多個方案進行設計權衡與決策。
咱們也不能固步自封,視Java社區而不顧。畢竟那些Java框架已經經歷了千錘百煉,並有許多成功的案例做爲佐證。關注Scala技術棧,卻又不侷限本身的視野,量力而爲,選擇合適的技術方案,纔是設計與開發的正道。
參考資料:
快速瞭解Scala技術棧:http://www.infoq.com/cn/articles/scala-technology/
twitter改用scala後用什麼框架:http://zhidao.baidu.com/link?url=9Ld-XxLLV9rXrE6i_E_IRS7mxuPQuGDpLNQYX85XiyC9sn80BmKgRnvToQNRZjy6u1Ha8qwKK_X7yr3qVFxjh9X498bJ2Pz_C2vgXnWKfIu
四種Scala的Web框架:http://www.jdon.com/idea/scala/scala-web-frameworks-web-developers.html