Scala實例教程:Kestrel
要學(xué)習(xí)一門新語言,***的方法之一莫過于從代碼實例學(xué)習(xí)?,F(xiàn)在Scala對于很多人來說還是一門新語言,所以有機(jī)會通過Scala實例教程多看多學(xué)都是很有幫助的。
Scala實例教程:走讀Kestrel,了解Scala
Scala的一些語言特性是很令人著迷的,尤其對于那些厭倦了異常處理和冗長的類型轉(zhuǎn)換的Java工程師來說,Scala可以把代碼寫的更加地簡潔。但是對于Scala來說,尤其是沒有 Python, Ruby 等語言基礎(chǔ)的Java工程師來說,學(xué)習(xí)Scala是一件比較痛苦的事情,而將Scala的優(yōu)勢發(fā)揮出來,則需要更多的努力。
為了讓這個事情變得不那么困難,我們可以從優(yōu)秀的一個Scala代碼的實例來開始——Kestrel。我們可以從新聞中了解到,作為Twitter核心的消息轉(zhuǎn)發(fā)機(jī)制,使用了Kestrel。也許是因為Kestrel的集群架構(gòu)實現(xiàn)簡單——Kestrel的說明中提到,只要運(yùn)行多個獨(dú)立的Kestrel,隨機(jī)set和get這些實例就能做到集群了(其實犧牲了消息隊列的時序)。
Kestrel的原型是來自于Ruby的Starling。一個兼容memcached接口的消息隊列,把這個消息隊列看成是一個基礎(chǔ)的service。客戶端可以通過queueName,向隊列添加或者獲取消息隊列中的消息,依次處理。Kestrel項目中,使用Apache Mina項目來實現(xiàn)Socket的鏈接管理。
同時,Kestrel還支持當(dāng)消息隊列過多的時候,把消息隊列存放到本地硬盤上的策略。以提高整體的穩(wěn)定性。當(dāng)然,***的情況,還是能夠及時將隊列中的消息處理完畢。在測試的過程中,會發(fā)生OutOfMemory的情況,這是因為在配置中,允許駐留在內(nèi)存中的消息數(shù)量,超過了JVM開啟的時候,所能夠申請的數(shù)量,這在實際的使用中,是需要考慮清楚的。
另外,還需要注意的是Kestrel只能在Java 1.6的環(huán)境下使用。
不過還是主要通過走讀Kestrel代碼,來了解Scala。也能從中學(xué)到一些對Scala非常好的使用方法。等到對代碼有一定認(rèn)識之后,在回來看Scala的語法手冊,可能會有更多的新發(fā)現(xiàn)。
下載編譯不多說,注意要用Java 1.6就可以了。我們先從簡單的開始,我們在Kestrel的官方網(wǎng)站上看到一個壓力測試的用例。如下文所示:
- $ ant -f tests.xml put-many-1 -Ditems=5000000
先從測試案例開始,然后再看核心代碼,入門會比較容易。在測試案例里面,我們比較容易猜出來,整個程序大概做了一些什么,通過test.xml所述,我們找到了net.lag.kestrel.load.PutMany,在src/test/net/lang/kestrel/load里面可以找到這個程序,PutMany.scala,相對來說ManyClients.scala的功能可能更全面一些,我們就拿它作為例子吧:
- package net.lag.kestrel.load
- import _root_.java.net._
- import _root_.java.nio._
- import _root_.java.nio.channels._
- import _root_.java.util.concurrent.atomic._
- import _root_.net.lag.extensions._
這段一點(diǎn)都不難懂,嗯……我們可以大膽地猜的 _ 在這里的涵義等同于 Java 里面的 *。事實上就是這樣的,在后來,你會發(fā)現(xiàn) _ 不僅僅代表的是 * ,它會出其不意地出現(xiàn)在很多地方,而在Scala中,它的涵義接近于default。
- object ManyClients {
- private val SLEEP = System.getProperty("sleep", "100").toInt
- private val COUNT = System.getProperty("count", "100").toInt
- ……
- }
類定義也很清楚,object 等同于 Java 中的 class。變量聲明中Scala的格式和Java有點(diǎn)不同,需要說明一下的是var表示變量,而val表示常量,在聲明的時候必須被賦值,不過這個常量是在創(chuàng)建的時候執(zhí)行的結(jié)果。就好像例子中寫的那樣。注意toInt,這是一個Scala的特點(diǎn),通過推斷的方式來確定變量的類型,所有在有些時候,變量的類型聲明就可以被省略了。
這種對變量類型的省略是Scala的核心理念——”Type Less, Do more”,有些事情編譯器直到就好啦。
函數(shù)來了:
- def put(socket: SocketChannel, queueName: String, n: Int) = {
- ……
- }
def 表示一個方法,每個Scala的函數(shù)都有返回,這是Scala對”=”的理解,返回的結(jié)果就是 {} 中***一條指令執(zhí)行的返回值,所以,Scala沒有return函數(shù)。返回值類型和參數(shù)的類型說明都是用 name : type 的格式來體現(xiàn)的,如果我們制定了 put 的返回是一種特殊的類型,比如Unit,那么我們的定義就是 def put ( a : Int) : Unit = …… 。這樣的表達(dá)是可以理解的,但是對于Java工程師來說,看起來很別扭,尤其是 = 后面的 {} 被省略掉之后。
接下來我們就閱讀到了main函數(shù),是的,它和Java的main函數(shù)作用是完全相同的,但是其中有一段比較難以理解的代碼,而這段代碼體現(xiàn)了Scala易用性的特點(diǎn):
- var threadList: List[Thread] = Nil
- ……
- for (i <- 0 until clientCount) {
- val t = new Thread {
- override def run = {
- val socket = SocketChannel.open(new InetSocketAddress("localhost", 22133))
- getStuff(socket, "spam")
- }
- }
- threadList = t :: threadList
- t.start
- }
- ……
- for (t <- threadList) {
- t.join
- }
先解釋一下對List的操作。var threadList : List[Thread],比較好理解,就是聲明一個List,每個成員都是Thread,類似于Java聲明ArrayList。使用var而不使用val,因為前面提到過var是會被改變的,val是常量。而 threadList = t :: threadList 是一個對List的操作,把t放在threadList的表頭……是不是很形象呢?
再解釋一下for循環(huán)的一個語法 for (t <- threadList) {} 就是遍歷所有的threadList都執(zhí)行一遍,等同于Java中的iterator,是否看起來很簡潔呢?而另一個語法 for (t <- 0 until clientCount) {} 就不用解釋了。
Scala的精彩部分之一,隆重登場了:
- val t = new Thread {
- override def run = {
- val socket = SocketChannel.open(new InetSocketAddress("localhost", 22133))
- getStuff(socket, "spam")
- }
- }
首先,t 是一個Thread類,因為 val t = new Thread ….。
然后我們發(fā)現(xiàn)這個Thread的run函數(shù)被重載了。這就是為什么def 后面那個 = 的深意,函數(shù)是可以被賦值的。然后繁瑣的創(chuàng)建線程的語句,被簡化成了簡單的幾行代碼。非常直觀。
哦,其實Javascript也有這樣的語法,這也是這一代語言的發(fā)展趨勢。在這一代語言里面,語言不再是少林寺的武功,需要從擺架子開始,而更象金庸筆下的凌波微步,走得更加輕巧。其實多生成一個類,增加一個對象,對系統(tǒng)資源的耗損并沒有想象中的那么大,所以在這里,類的重載,以及Scala的匿名函數(shù)等語法上的特性,更加鼓勵程序員,用簡潔和優(yōu)美的方式來描述數(shù)據(jù)處理的方式。
看了這個Scala實例教程,希望能夠更好的幫助你了解Scala語言。
【編輯推薦】