<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    莊周夢蝶

    生活、程序、未來
       :: 首頁 ::  ::  :: 聚合  :: 管理

        Clojure-control is a clojure DSL for system admin and deployment with many remote machines via ssh. 
        
        I am pleased to annoucment that clojure-control 0.3.0 is out.It adds some  powerful features in this release ,includes:
    • ssh and scp both have a new option :sudo  to be executed as root on remote machines,for example:
      (ssh "/etc/init.d/ssh restart" :sudo true)
    • scp has a new  option :mode to change file modes copied from local: 
      (scp "start.sh" "/etc/init.d/start.sh" :sudo true :mode 755)
    • A  new function "exists?" to test if a file exists on remote machines:  
      (if (not (exists? (str "/home/deploy/.ssh")))
            (ssh (sudo (str "mkdir -p /home/deploy/.ssh"))))
    • Call other task in deftask with "call" function:
      (deftask :ps "A task to grep process" [process]
              (ssh (str "ps aux | grep " process)))
      (deftask :start_ha []
              (ssh "/etc/init.d/haproxy start")
              (call :ps "haproxy"))
    • A new function "append" to append a line to a file on remote machines:
      (ssh (append "/etc/hosts" "192.168.1.100 web" :sudo true))
    • A new function "sed" to replace lines in a file on remote machines,and comm/uncomm to comment/uncomment a line in a file:
            (sed <file> <before> <after> :flags <flags> :limit <limit> :backup <backup>)
      

      Equivalent to

            sed -i<backup> -r -e "<limit> s/<before>/<after>/<flags>g <filename>"
    • Limits max output line to 10000.
    • Adds more documents in wiki: https://github.com/killme2008/clojure-control/wiki 
       You can install the new version by :
        lein plugin install control 0.3.0           #For clojure 1.3
        lein plugin install control 0.3.1           #For clojure 1.2

        More information please visit it on github: https://github.com/killme2008/clojure-control

    posted @ 2012-02-18 22:08 dennis 閱讀(3457) | 評論 (0)編輯 收藏

        XML處理也是個(gè)常見的編程工作,雖然說在Clojure里你很少使用XML做配置文件,但是跟遺留系統(tǒng)集成或者處理和其他系統(tǒng)通訊,可能都需要處理XML。

        Clojure的標(biāo)準(zhǔn)庫clojure.xml就是用來干這個(gè)事情的。一個(gè)簡單的例子如下,首先我們要解析的是下面這個(gè)簡單的XML:
    <?xml version="1.0" encoding="UTF-8"?>
    <books>
      
    <book>
        
    <title>The joy of clojure</title>
        
    <author>Michael Fogus / Chris House</author>
      
    </book>
      
    <book>
        
    <title>Programming clojure</title>
        
    <author>Stuart Halloway</author>
      
    </book>
      
    <book>
        
    <title>Practical clojure</title>
        
    <author>Luke Van der Hart</author>
      
    </book>
    </books>

        解析xml用clojure.xml/parse方法即可,該方法返回一個(gè)clojure.xml/element這個(gè)struct-map組成的一棵樹:
    user=> (use '[clojure.xml])
    nil
    user
    => (parse "test.xml")
    {:tag :books, :attrs nil, :content
     [{:tag :book, :attrs nil, :content [{:tag :title, :attrs nil, :content [
    "The joy of clojure"]} {:tag :author, :attrs nil, :content ["Michael Fogus / Chris House"]}]}
     {:tag :book, :attrs nil, :content [{:tag :title, :attrs nil, :content [
    "Programming clojure"]} {:tag :author, :attrs nil, :content ["Stuart Halloway"]}]}
     {:tag :book, :attrs nil, :content [{:tag :title, :attrs nil, :content [
    "Practical clojure"]} {:tag :author, :attrs nil, :content ["Luke Van der Hart"]}]}]}

    這是一個(gè)嵌套的數(shù)據(jù)結(jié)構(gòu),每個(gè)節(jié)點(diǎn)都是clojure.xml/element結(jié)構(gòu),element包括:
    (defstruct element :tag :attrs :content)
       tag、attrs和content屬性,tag就是該節(jié)點(diǎn)的標(biāo)簽,attrs是一個(gè)屬性的map,而content是它的內(nèi)容或者子節(jié)點(diǎn)。element是一個(gè)struct map,它也定義了三個(gè)方法來分別獲取這三個(gè)屬性:
    user=> (def x (parse "test.xml"))
    #'user/x
    user=> (tag x)
    :books
    user
    => (attrs x)
    nil
    user
    => (content x)
    [{:tag :book, :attrs nil, :content [{:tag :title, :attrs nil, :content [
    "The joy of clojure"]} {:tag :author, :attrs nil, :content ["Michael Fogus / Chris House"]}]}
    {:tag :book, :attrs nil, :content [{:tag :title, :attrs nil, :content [
    "Programming clojure"]} {:tag :author, :attrs nil, :content ["Stuart Halloway"]}]}
    {:tag :book, :attrs nil, :content [{:tag :title, :attrs nil, :content [
    "Practical clojure"]} {:tag :author, :attrs nil, :content ["Luke Van der Hart"]}]}]
       books節(jié)點(diǎn)是root node,它的content就是三個(gè)book子節(jié)點(diǎn),子節(jié)點(diǎn)組織成一個(gè)vector,我們可以隨意操作:
    user=> (tag (first (content x)))
    :book
    user
    => (content (first (content x)))
    [{:tag :title, :attrs nil, :content [
    "The joy of clojure"]} {:tag :author, :attrs nil, :content ["Michael Fogus / Chris House"]}]
    user
    => (content (first (content (first (content x)))))
    [
    "The joy of clojure"]

         額外提下,clojure.xml是利用SAX API做解析的。同樣它還有個(gè)方法,可以將解析出來的結(jié)構(gòu)還原成xml,通過emit:
    user=> (emit x)

    <?xml version='1.0' encoding='UTF-8'?>
    <books>
    <book>
    <title>
    The joy of clojure
    </title>
    <author>
    Michael Fogus / Chris House
    </author>
    </book>
    <book>

         如果你要按照深度優(yōu)先的順序遍歷xml,可以利用xml-seq將解析出來的樹構(gòu)成一個(gè)按照深度優(yōu)先順序排列節(jié)點(diǎn)的LazySeq,接下來就可以按照seq的方式處理,比如利用for來過濾節(jié)點(diǎn):
    user=> (for [node (xml-seq x)
                      :when (= :author (:tag node))]
                (first (:content node)))
    ("Michael Fogus / Chris House" "Stuart Halloway" "Luke Van der Hart")

        通過:when指定了條件,要求節(jié)點(diǎn)的tag是author,這樣就可以查找出所有的author節(jié)點(diǎn)的content,是不是很方便?就像寫英語描述一樣。

        更進(jìn)一步,如果你想操作parse解析出來的這棵樹,你還可以利用clojure.zip這個(gè)標(biāo)準(zhǔn)庫,它有xml-zip函數(shù)將xml轉(zhuǎn)換成zipper結(jié)構(gòu),并提供一系列方法來操作這棵樹:
    user=>(def xz (xml-zip x))
    #'user/xz
    user=> (node (down xz))
    {:tag :book, :attrs nil, :content [{:tag :title, :attrs nil, :content [
    "The joy of clojure"]} {:tag :author, :attrs nil, :content ["Michael Fogus / Chris House"]}]}
    user
    => (-> xz down right node)
    {:tag :book, :attrs nil, :content [{:tag :title, :attrs nil, :content [
    "Programming clojure"]} {:tag :author, :attrs nil, :content ["Stuart Halloway"]}]}
    user
    => (-> xz down right right node)
    {:tag :book, :attrs nil, :content [{:tag :title, :attrs nil, :content [
    "Practical clojure"]} {:tag :author, :attrs nil, :content ["Luke Van der Hart"]}]}
    user
    => (-> xz down right right lefts)
    ({:tag :book, :attrs nil, :content [{:tag :title, :attrs nil, :content [
    "The joy of clojure"]} {:tag :author, :attrs nil, :content ["Michael Fogus / Chris House"]}]}
     {:tag :book, :attrs nil, :content [{:tag :title, :attrs nil, :content [
    "Programming clojure"]} {:tag :author, :attrs nil, :content ["Stuart Halloway"]}]})

      是不是酷得一塌糊涂?可以通過up,down,left,right,lefts,rights,來查找節(jié)點(diǎn)的鄰近節(jié)點(diǎn),可以通過node來得到節(jié)點(diǎn)本身。一切顯得那么自然。更進(jìn)一步,你還可以“編輯“這棵樹,比如刪除The joy of clojure這本書:
    user=>  (def loc-in-new-tree (remove (down xz)))
    #'user/loc-in-new-tree
    user=> (root loc-in-new-tree)
    {:tag :books, :attrs nil, :content
    [{:tag :book, :attrs nil, :content [{:tag :title, :attrs nil, :content [
    "Programming clojure"]} {:tag :author, :attrs nil, :content ["Stuart Halloway"]}]}
    {:tag :book, :attrs nil, :content [{:tag :title, :attrs nil, :content [
    "Practical clojure"]} {:tag :author, :attrs nil, :content ["Luke Van der Hart"]}]}]}

       ok,只剩下兩本書了,更多方法還包括replace做替換,edit更改節(jié)點(diǎn)等。因此編輯XML并重新生成,你一般可以利用clojure.zip來更改樹,最后利用clojure.xml/emit將更改還原為xml。

        生成xml除了emit方法,還有一個(gè)contrib庫,也就是prxml,這個(gè)庫的clojure 1.3版本有人維護(hù)了一個(gè)分支,在這里。主要方法就是prxml,它可以將clojure的數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)換成xml:
    user=>(prxml [:p [:raw! "<i>here & gone</i>"]])
    <p><i>here & gone</i></p>
        顯然,它也可以用于生成HTML。

        xpath的支持可以使用clj-xpath這個(gè)開源庫,遺憾的是它目前僅支持clojure 1.2。

        轉(zhuǎn)載請注明出處:http://www.tkk7.com/killme2008/archive/2012/02/18/370233.html   

    posted @ 2012-02-18 12:21 dennis 閱讀(4871) | 評論 (0)編輯 收藏

        文件讀寫是日常編程中最經(jīng)常使用的操作之一。這篇blog將大概介紹下Clojure里對文件操作的常用類庫。

        首先介紹標(biāo)準(zhǔn)庫clojure.java.io,這是最經(jīng)常用的IO庫,定義了常見的IO操作。

        首先,直接看一個(gè)例子,可以熟悉下大多數(shù)常用的函數(shù):
    (ns io
      (:use [clojure.java.io]))

    ;;file函數(shù),獲取一個(gè)java.io.File對象
    (
    def f (file "a.txt"))

    ;;拷貝文件使用copy
    (copy f (file 
    "b.txt"))

    ;;刪除文件,使用delete
    -file
    (delete
    -file f)

    ;;更經(jīng)常使用reader和writer
    (
    def rdr (reader "b.txt" :encoding "utf-8"))
    (
    def wtr (writer "c.txt" :append true))

    ;;copy可以接受多種類型的參數(shù)
    (copy rdr wtr :buffer
    -size 4096)

    ;;關(guān)閉文件
    (.close wtr)
    (.close rdr)



        這個(gè)例子基本上說明了大多數(shù)常見的操作。但是有些問題需要解釋下。

        首先,通過file這個(gè)函數(shù)可以將各種類型的對象轉(zhuǎn)化為java.io.File對象,file可以接受String,URL,URI以及java.io.File本身作為參數(shù),并返回java.io.File。有了File對象,你就可以調(diào)用java.io.File類中的各種方法,比如判斷文件是否存在:
    (.exists (file "a.txt")) => true or false

        其次,可以通過delete-file來刪除一個(gè)文件,它是調(diào)用File的delete方法來執(zhí)行的,但是File.delete會(huì)返回一個(gè)布爾值告訴你成功還是失敗,如果返回false,那么delete-file會(huì)拋出IO異常,如果你不想被這個(gè)異常打擾,可以讓它“保持安靜”:
        (delete-file f true)

        拷貝文件很簡單,使用copy搞定,copy也可以很“寬容”,也可以接受多種類型的參數(shù)并幫你自動(dòng)轉(zhuǎn)換,input可以是InputStream, Reader, File, byte[] 或者String,而output可以是OutputStream, Writer或者File。是不是很給力?這都是通過Clojure的protocol和defmulti做到的。但是,copy并不幫你處理文件的關(guān)閉問題,假設(shè)你傳入的input是一個(gè)File,output也是一個(gè)File,copy會(huì)自動(dòng)幫你打開InputStream和OutputStream并建立緩沖區(qū)做拷貝,但是它不會(huì)幫你關(guān)閉這兩個(gè)流,因此你要小心,如果你經(jīng)常使用copy,這可能是個(gè)內(nèi)存泄漏的隱患。

        更常用的,我們一般都是用reader和writer函數(shù)來打開一個(gè)BufferedReader和BufferedWriter做讀寫,同樣reader和writer也可以接受多種多樣的參數(shù)類型,甚至包括Socket也可以。因?yàn)閣riter打開的通常是一個(gè)BufferedWriter,所以你如果用它寫文件,有時(shí)候發(fā)現(xiàn)write之后文件還是沒有內(nèi)容,這是因?yàn)閿?shù)據(jù)暫時(shí)寫到了緩沖區(qū)里,沒有刷入到磁盤,可以明確地調(diào)用(.flush wtr)來強(qiáng)制寫入;或者在wtr關(guān)閉后系統(tǒng)幫你寫入。reader和writer還可以傳入一些配置項(xiàng),如:encoding指定讀寫的字符串編碼,writer可以指定是否為append模式等。

        Clojure并沒有提供關(guān)閉文件的函數(shù)或者宏,你簡單地調(diào)用close方法即可。clojure.java.io的設(shè)計(jì)很有原則,它不準(zhǔn)備將java.io都封裝一遍,而是提供一些最常用方法的簡便wrapper供你使用。

        剛才提到copy不會(huì)幫你關(guān)閉打開的文件流,但是我們可以利用with-open這個(gè)宏來自動(dòng)幫你管理打開的流:
    (with-open [rdr (reader "b.txt")
                wtr (writer 
    "c.txt")]
      (copy rdr wtr))

       with-open宏會(huì)自動(dòng)幫你關(guān)閉在binding vector里打開的流,你不再需要自己調(diào)用close,也不用擔(dān)心不小心造成內(nèi)存泄漏。因此我會(huì)推薦你盡量用reader和writer結(jié)合with-open來做文件操作,而不要使用file函數(shù)。file函數(shù)應(yīng)該用在一些判斷是否存在,判斷文件是否為目錄等操作上。

        在clojure.core里,還有兩個(gè)最常用的函數(shù)slurp和spit,一個(gè)吃,一個(gè)吐,也就是slurp讀文件,而spit寫文件,他們類似Ruby的File里的read和write,用來完整地讀或者寫文件:
        (prn (slurp "c.txt"))
        (spit "c.txt" "hello world")

       用法簡單明了,slurp將文件完整地讀出并返回字符串作為結(jié)果,它還接受:encoding參數(shù)來指定字符串編碼,你猜的沒錯(cuò),它就是用reader和with-open實(shí)現(xiàn)的。spit同樣很簡單,將content轉(zhuǎn)化為字符串寫入文件,也接受:encoding和:append參數(shù)。

        深度優(yōu)先遍歷目錄,可以使用file-seq,返回一個(gè)深度優(yōu)先順序遍歷的目錄列表,這是一個(gè)LazySeq:
    (user=> (file-seq (java.io.File. "."))


    (
    #<File .> #<File ./.gitignore> #<File ./.lein-deps-sum> #<File ./b.txt> #<File ./c.txt> #<File ./classes> ⋯⋯ )

        上面的介紹已經(jīng)足以讓你對付大多數(shù)需求了。接下來,介紹下幾個(gè)開源庫。首先是fs這個(gè)庫,它封裝了java.io.File類的大多數(shù)方法,讓你用起來很clojure way,很舒服,例如:
    (exists? "a.txt")
    (directory? 
    "file")
    (file? 
    "file")
    (name 
    "/home/dennis/.inputrc")
    (mkdir 
    "/var/data")
    (rename 
    "a.txt" "b.txt")
    (
    def tmp (temp-dir))
    (glob 
    #".*test.*")
    (chmod 744 "a.txt")

    ⋯⋯

        更多介紹請看它的源碼。讀寫二進(jìn)制文件也是一個(gè)很常見的需求,Clojure有幾個(gè)DSL庫干這個(gè)事情,可以很直觀地定義二進(jìn)制格式來encode/decode,比如byte-spec這個(gè)庫,看看它的例子:
    defspec basic-spec
             :a :int8
             :b :int16
             :c :int32
             :d :float32
             :e :float64
             :f :string)

    ;; An object to serialize
    (
    def foo {:a 10 :b 20 :c 40 :d 23.2 :e 23.2 :f "asddf"})

    ;; And serialize it to a byte array like this:
    (spec
    -write-bytes basic-spec foo) ;; => [bytes]

    ;; reading 
    in a byte array with the basic-spec format works like this:
    (spec
    -read-bytes basic-spec bytes)
        相當(dāng)直觀和給力吧。Gloss是一個(gè)更強(qiáng)大的DSL庫,非常適合做網(wǎng)絡(luò)通訊的協(xié)議處理。這里就不多做介紹了,你可以自己看它的例子和文檔。
       
        轉(zhuǎn)載請注明出處:http://www.tkk7.com/killme2008/archive/2012/02/16/370144.html
       

    posted @ 2012-02-16 22:38 dennis 閱讀(7096) | 評論 (3)編輯 收藏

        單元測試也是一個(gè)開發(fā)中最常見的需求,在Java里我們用JUnit或者TestNG,在clojure里也內(nèi)置了單元測試的庫。標(biāo)準(zhǔn)庫的clojure.test,以及第三方框架midje。這里我將主要介紹clojure.test這個(gè)標(biāo)準(zhǔn)庫,midje是個(gè)更加強(qiáng)大的測試框架,廣告下,midje的介紹在第二次cn-clojure聚會(huì)上將有個(gè)Topic,我就不畫蛇添足了。通常來說,clojure.test足夠讓你對付日常的測試。

        首先看一個(gè)最簡單的例子,定義一個(gè)函數(shù)square來計(jì)算平方,然后我們測試這個(gè)函數(shù):
    ;;引用clojure.test
    (ns example
      (:use [clojure.test :only [deftest 
    is run-tests]]))
    ;;定義函數(shù)
    (defn square [x]
      (
    * x x))
    ;;測試函數(shù)
    (deftest test
    -square
      (
    is (= 4 (square 2)))
      (
    is (= 9 (square -3))))
    ;;運(yùn)行測試
    (run
    -tests 'example)

        執(zhí)行輸出:

    Testing example

    Ran 
    1 tests containing 2 assertions.
    0 failures, 0 errors.

        這個(gè)小例子基本說明了clojure.test的主要功能。首先是斷言is,類似JUnit里的assertTrue,用來判斷form是否為true,它還可以接受一個(gè)額外的msg參數(shù)來描述斷言:
     (is (= 4 (square 2)) "a test")
        它還有兩種變形,專門用來判斷測試是否拋出異常:
     (is (thrown? RuntimeException (square "a")))
     (
    is (thrown-with-msg? RuntimeException #"java.lang.String cannot be cast to java.lang.Number"  (square "a")))
        上面的例子故意求"a"的平方,這會(huì)拋出一個(gè)java.lang.ClassCastException,一個(gè)運(yùn)行時(shí)異常,并且異常信息為java.lang.String cannot be cast to java.lang.Number。我們可以通過上面的方式來測試這種意外情況。clojure.test還提供了另一個(gè)斷言are,用來判斷多個(gè)form:
     (testing "test zero or one"
        (are
         (
    = 0 (square 0))
         (
    = 1 (square 1))))
        are接受多個(gè)form并判斷是否正確。這里還用了testing這個(gè)宏來添加一段字符串來描述測試的內(nèi)容。

        其次,我們用deftest宏定義了一個(gè)測試用例,deftest定義的測試用例也可以組合起來:
       (deftest addition
         (
    is (= 4 (+ 2 2)))
         (
    is (= 7 (+ 3 4))))
       (deftest subtraction
         (
    is (= 1 (- 4 3)))
         (
    is (= 3 (- 7 4))))
       (deftest arithmetic
         (addition)
         (subtraction))

        但是組合后的tests運(yùn)行就不能簡單地傳入一個(gè)ns,而需要定義一個(gè)test-ns-hook指定要跑的測試用例,否則組合的用例如上面的addition和subtraction會(huì)運(yùn)行兩次。我們馬上談到。

        定義完用例后是運(yùn)行測試,運(yùn)行測試使用run-tests,可以指定要跑測試的ns,run-tests接受可變參數(shù)個(gè)的ns。剛才提到,組合tests的時(shí)候會(huì)有重復(fù)運(yùn)行的問題,要防止重復(fù)運(yùn)行,可以定義一個(gè)test-ns-hook的函數(shù):
    (defn test-ns-hook []
      (test
    -square)
      (arithmetic))
        這樣run-tests就會(huì)調(diào)用test-ns-hook按照給定的順序執(zhí)行指定的用例,避免了重復(fù)執(zhí)行。

        在你的測試代碼里明確調(diào)用run-tests執(zhí)行測試是一種方式,不過我們在開發(fā)中更經(jīng)常使用的是lein來管理project,lein會(huì)將src和test分開,將你的測試代碼組織在專門的test目錄,類似使用maven的時(shí)候我們將main和test分開一樣。這時(shí)候就可以簡單地調(diào)用:
            lein test
    命令來執(zhí)行單元測試,而不需要明確地在測試代碼里調(diào)用run-tests并指定ns。更實(shí)用的使用例子可以看一些開源項(xiàng)目的組織。

        單元測試?yán)镒鰉ock也是比較常見的需求,在clojure里做mock很容易,原來clojure.contrib有個(gè)mock庫,基本的原理都是利用binding來動(dòng)態(tài)改變被mock對象的功能,但是在clojure 1.3里,binding只能改變標(biāo)注為dynamic的變量,并且clojure.contrib被廢棄,部分被合并到core里面,Allen Rohner編譯了一個(gè)可以用于clojure 1.3的clojure.contrib,不過需要你自己install到本地倉庫,具體看這里。不過clojure.contrib.mock哪怕使用1.2的編譯版本其實(shí)也是可以的。

        clojure.contrib最重要的是expect宏,它類似EasyMock里的expect方法,看一個(gè)例子:
    (use [clojure.contrib.mock :only [times returns has-args expect]])

    (deftest test
    -square2
      (expect [square (has
    -args [number?] (times 2 (returns 9)))]
              (
    is (= 9 (square 4)))))

        has-args用來檢測square的參數(shù)是不是number,times用來指定預(yù)期調(diào)用的次數(shù),而returns用來返回mock值,是不是很像EasyMock?因?yàn)槲覀冞@個(gè)測試只調(diào)用了square一次,所以這個(gè)用例將失敗:
    Testing example
    "Unexpected invocation count. Function name: square expected: 2 actual: 1"
       這個(gè)例子要在Clojure 1.3里運(yùn)行,需要將square定義成dynamic:
    (defn ^:dynamic square [x]
      (
    * x x))
       否則會(huì)告訴你沒辦法綁定square:
    actual: java.lang.IllegalStateException: Can't dynamically bind non-dynamic var: example/square


        額外提下,還有個(gè)輕量級的測試框架expections可以看一下,類似Ruby Facker的facker庫提供一些常見的模擬數(shù)據(jù),如名稱地址等。
       
         轉(zhuǎn)載請注明出處:http://www.tkk7.com/killme2008/archive/2012/02/15/370040.html

       

    posted @ 2012-02-15 19:39 dennis 閱讀(5062) | 評論 (0)編輯 收藏

        Clojure的REPL非常方便,可以隨時(shí)隨地試驗(yàn)?zāi)愕南敕ǎ琑EPL是read-eval-print-loop的簡稱。默認(rèn)clojure.contrib有帶一個(gè)shell腳本來啟動(dòng)REPL,具體看這里。你也可以用JLine來增強(qiáng)REPL:
    java -cp "%CLOJURE_DIR%\jline-VERSION.jar;%CLOJURE_JAR%" jline.ConsoleRunner clojure.main

        不過,其實(shí)你還可以用rlwrap這個(gè)GNU庫來增強(qiáng)clojure REPL。使用它有如下好處:
    1.Tab completion,使用tab做代碼提示。
    2.括號匹配
    3.歷史記錄,哪怕你重啟REPL
    4.通過.inputrc來綁定vi或者emacs

        具體操作步驟如下:

    1.首先,你需要在你的機(jī)器上安裝rlwrap,你可以通過apt或者port,homebrew等工具安裝或者自己下載安裝:
    sudo port install rlwrap

    2.在你的home目錄下創(chuàng)建一個(gè)clojure目錄作為clojure home,并拷貝clojure.jar進(jìn)去:
    mkdir ~/clojure
    cp .m2
    /repository/org/clojure/clojure/1.3.0/clojure-1.3.0.jar ~/clojure/clojure.jar
    我是從maven的本地倉庫里拷貝了clojure 1.3的jar包過去,重命名為clojure.jar

    3.創(chuàng)建一個(gè)shell腳本名為clj,并放入你的path變量,腳本內(nèi)容:
    #!/bin/sh
    breakchars
    ="(){}[],^%$#@\"\";:''|\\"
    CLOJURE_DIR
    =~/clojure
    CLOJURE_JAR
    ="$CLOJURE_DIR"/clojure.jar
    JAVA_OPTS
    ="-Xmx512m -XX:MaxPermSize=256m -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:+CMSClassUnloadingEnabled"
    if [ $# -eq 0 ]; then 
        exec rlwrap 
    --remember --"$breakchars" \
       
    -"$HOME"/.clj_completions \
       
    -"Clojure REPL" \
       
    -p red \
       
    -"$CLOJURE_DIR"/.repl_history -1000\
       java 
    "$JAVA_OPTS"  -cp "$CLOJURE_JAR" clojure.main
    else
             exec java 
    -cp "$CLOJURE_JAR" clojure.main $1 "$@"
    fi
    我們將命令歷史輸出到~/clojure/.repl_history文件中,并限制數(shù)目為1000。

    4.clj腳本中通過-f選項(xiàng)指定了completions文件為~/.clj_completions,執(zhí)行下列clojure程序生成此文件:
    (def completions (keys (ns-publics (find-ns 'clojure.core))))
    ;(def completions (mapcat (comp keys ns
    -publics) (all-ns)))
    (
    with-open [f (java.io.BufferedWriter. (java.io.FileWriter. (str (System/getenv "HOME""/.clj_completions")))]
      (.write f (apply str (interpose \newline completions))))
    這個(gè)程序只生成clojure.core的completions文件,如果你想將所有ns都加入進(jìn)去,注釋掉第一行,使用第二行程序。

    5.最后,配置下~/.inputrc文件:
    set editing-mode emacs
    tab: complete
    set completion
    -query-items 150
    set completion
    -ignore-case on
    set blink
    -matching-paren on
    set bell
    -style visible
    我綁定為emacs,你可以選擇vi。

    6.一切搞定,接下來你可以敲入命令clj來使用rlwrap啟動(dòng)clojure REPL了,可以用tab做代碼提示了,可以用Ctrl + r來搜索歷史命令,運(yùn)行截圖:


    參考:http://en.wikibooks.org/wiki/Clojure_Programming/Getting_Started#Enhancing_Clojure_REPL_with_rlwrap
    轉(zhuǎn)載請注明出處:http://www.tkk7.com/killme2008/archive/2012/02/14/369976.html

    posted @ 2012-02-14 19:05 dennis 閱讀(3726) | 評論 (1)編輯 收藏


        使用http client提交表單或者下載網(wǎng)頁也是非常常見的任務(wù),比如使用Java的時(shí)候可以用標(biāo)準(zhǔn)庫的HttpURLConnection,也可以選擇Apache Http Client。在clojure里也有這樣的類庫,這里我將介紹三個(gè)各有特色的http client實(shí)現(xiàn)。

        首先,我最先推薦使用clj-http這個(gè)類庫,它是Apache HttpClient的clojure wrapper,是一個(gè)提供同步API的簡單易用的Http Client。

    名稱: clj-http
    主頁:https://github.com/dakrone/clj-http
    依賴:
    [clj-http "0.3.1"]

    例子:
    (require '[clj-http.client :as client])
    (client/get "http://google.com")
    結(jié)果:
    => {:cookies {"NID" {:domain ".google.com.hk", :expires #<Date Tue Aug 14 18:20:38 CST 2012>, :path "/", :value "56=qn2OWtODE2D3fUKi_vbi44jZepOeLI9xC4Ta1JQLEicqUvIZAqr7TCmft_hq8i_FRwnFXdTK1jV2S5IrSZFyYhlAN2KcQEXgWX1iK36gM2iYPaKPihuUZDCqgiAamDOl", :version 0}, "PREF" {:domain ".google.com.hk", :expires #<Date Wed Feb 12 18:20:38 CST 2014>, :path "/", :value "ID=8b73a654ff0a2783:FF=0:NW=1:TM=1329128438:LM=1329128438:S=uEM4SsFuHlkqtVhp", :version 0}},
        :status 
    200
        :headers {
    "date" "Sun, 01 Aug 2010 07:03:49 GMT"
                  
    "cache-control" "private, max-age=0"
                  
    "content-type" "text/html; charset=ISO-8859-1"
                  }
        :body 
    "<!doctype html>"
        :trace
    -redirects ["http://google.com" "http://www.google.com/" "http://www.google.fr/"]}
    更多例子:
    (client/get "http://site.com/resources/3" {:accept :json})

    ;; Various options:
    (client
    /post "http://site.com/api"
      {:basic
    -auth ["user" "pass"]
       :body 
    "{\"json\": \"input\"}"
       :headers {
    "X-Api-Version" "2"}
       :content
    -type :json
       :socket
    -timeout 1000
       :conn
    -timeout 1000
       :accept :json})

    ;; Need to contact a server with an untrusted SSL cert
    ?
    (client
    /get "https://alioth.debian.org" {:insecure? true})

    ;; If you don
    't want to follow-redirects automatically:
    (client/get "http://site.come/redirects-somewhere" {:follow-redirects false})

    ;; Only follow a certain number of redirects:
    (client
    /get "http://site.come/redirects-somewhere" {:max-redirects 5})

    ;; Throw an exception 
    if redirected too many times:
    (client
    /get "http://site.come/redirects-somewhere" {:max-redirects 5 :throw-exceptions true})

    ;; Send form params as a urlencoded body
    (client
    /post "http//site.com" {:form-params {:foo "bar"}})

    ;; Multipart form uploads
    /posts
    ;; a map or vector works as the multipart object. Use a vector of
    ;; vectors 
    if you need to preserve order, a map otherwise.
    (client
    /post "http//example.org" {:multipart [["title" "My Awesome Picture"]
                                                  [
    "Content/type" "image/jpeg"]
                                                  [
    "file" (clojure.java.io/file "pic.jpg")]]})
    ;; Multipart values can be one of the following:
    ;; String, InputStream, File, or a 
    byte-array

    ;; Basic authentication
    (client
    /get "http://site.com/protected" {:basic-auth ["user" "pass"]})
    (client
    /get "http://site.com/protected" {:basic-auth "user:pass"})

    ;; Query parameters
    (client
    /get "http://site.com/search" {:query-params {"q" "foo, bar"}})

        clj-http的API相當(dāng)?shù)暮啙嵠粒褂闷饋矸浅1憷瑥?qiáng)烈推薦。題外,學(xué)習(xí)clojure的一個(gè)好方法就是為現(xiàn)有的java類庫實(shí)現(xiàn)一些方便的clojure wrapper。

        如果你需要異步的http client,我會(huì)推薦http.async.client這個(gè)類庫,它的API是異步形式的類似 Java的Future模式,對于clojure程序員來說應(yīng)該更像是agent。

    名稱:http.async.client
    主頁:https://github.com/neotyk/http.async.client
    依賴:
    [http.async.client "0.4.1"]
    例子:
    (require '[http.async.client :as c])
    (with-open [client (c/create-client)]
      (let [response (c
    /GET client "http://neotyk.github.com/http.async.client/")]
        (prn (c
    /done? response))
        (c
    /await response)
        (prn (c
    /string response))
        (prn (c
    /status response))
        (prn (c
    /done? response))))

    輸出:
    false
    <!DOCTYPE html 
    {:code 
    200, :msg "OK", :protocol "HTTP/1.1", :major 1, :minor 1}
    true

    更多例子:
    (c/POST client "http://example.com" :body "hello world" :timeout 3000)
    (c
    /DELETE client "http://example.com")
    (c
    /POST client "http://example.com" :body "hello world" :auth {:type :basic :user "admin" :password "admin"})

    請注意,這些方法都是異步調(diào)用的,你需要通過await來等待調(diào)用完成,或者通過done?來判斷調(diào)用是否完成。
    http.async.client有個(gè)比較重要的特性就是對Http Chunked編碼的支持,分別通過LazySeq和callback的方式支持,首先看將Http chunked變成一個(gè)lazy seq:

    (with-open [client (client/create-client)] ; Create client
      (let [resp (client
    /stream-seq client :get url)]
        (doseq [s (s
    /string resp)]
          (println s))))

    這里非常關(guān)鍵的一點(diǎn)是stream-seq返回的chunk序列,每取一個(gè)就少一個(gè)(通過first函數(shù)),也就是說每次調(diào)用first取到的chunk都不一樣,是順序遞增,不可重復(fù)獲取的。

    通過callback方式處理:
    (with-open [client (client/create-client)] ; Create client
      (let [parts (ref #{})
            resp (client
    /request-stream client :get url
                                        (fn [state body]
                                          (dosync (alter parts conj (string body)))
                                          [body :
    continue]))]
        ;; 
    do something to @parts
        ))
    自己傳入一個(gè)callback函數(shù)接收chunk,比如這里用一個(gè)ref累積。

    http.async.client的詳細(xì)文檔看這里:http://neotyk.github.com/http.async.client/docs.html

    最后,有興趣還可以看下aleph這個(gè)異步通訊的框架,它支持Http協(xié)議,也提供了http server和client的實(shí)現(xiàn)。不過它的API就沒有那么簡單明了,它的模型是類似go語言里利用channel做異步通訊的模型,http只是它的一個(gè)模塊罷了,這是另一個(gè)話題了。

    轉(zhuǎn)載請注明出處:http://www.tkk7.com/killme2008/archive/2012/02/13/369890.html

    posted @ 2012-02-13 18:57 dennis 閱讀(6249) | 評論 (1)編輯 收藏


        處理日志是任何一個(gè)產(chǎn)品級的程序都需要仔細(xì)處理的模塊。在Java中,我們經(jīng)常使用的是log4j就是一個(gè)日志框架。在clojure里,同樣有一套日志框架——clojure.tools.logging,它不僅提供了常用的日志輸出功能,還屏蔽了Java各種日志框架之間的差異,如slf4j,commons-logging,log4j,java.util.logging等,讓你可以透明地使用這些框架來處理日志。

    名稱:clojure.tools.logging
    主頁:https://github.com/clojure/tools.logging
    依賴:
    [org.clojure/tools.logging "0.2.3"]

    <dependency>
      
    <groupId>org.clojure</groupId>
      
    <artifactId>tools.logging</artifactId>
      
    <version>0.2.3</version>
    </dependency>

    使用:
    (ns example.core
      (:use [clojure.tools.logging :only (info error)]))

    (defn divide [x y]
      (try
        (info "dividing" x "by" y)
        (/ x y)
        (catch Exception ex       (error ex "There was an error in calculation"))))

    常用宏和方法:
    1.除了上面例子的info和error宏,還可以包括warn,trace,debug,fatal等常用宏,分別對應(yīng)相應(yīng)的日志級別。這些方法會(huì)自動(dòng)判斷當(dāng)前l(fā)ogger的級別是否有效,有效的前提下才會(huì)輸出日志。也就是說在Java里,你經(jīng)常需要這樣:
    if (logger.isDebugEnabled()) {
        logger.debug(x 
    + " plus " + y + " is " + (x + y));
    }
    在使用 tools.logging的時(shí)候是不需要的,因?yàn)檫@些宏幫你做了這個(gè)判斷。另外,我們在使用log4j的時(shí)候需要指定log的namespace,在tools.logging里不需要,默認(rèn)會(huì)取當(dāng)前的namespace也就是*ns*。
    最后,info還有個(gè)infof的方法,用于輸出格式化日志:
    (infof "%s is %d years old" "kid" 3)
    日志輸出:
    2012-02-12 20:23:07,394 INFO  log: kid is 3 years old
    其他方法也有類似的如warnf,debugf等。
    2.spy宏,同時(shí)輸出表達(dá)式的form和結(jié)果,例如
    (spy (+1 2))
    輸出日志
    2012-02-12 20:11:47,415 DEBUG log: (+ 1 2)
    => 3

    3.with-logs宏可以在將*out*和*err*流重定向到日志的情況下求值表達(dá)式,例如:
    (with-logs *ns* (prn "hello world"))
    輸出日志:
    2012-02-12 20:17:32,592 INFO  log: "hello world"
    with-logs需要明確指定log-ns,默認(rèn)out的輸出級別是info,而err的級別是error,可以指定輸出級別(with-logs [*ns* :info :error] ......)

    4.事務(wù)中(dosync中)的日志輸出,tools.logging做了特殊處理,默認(rèn)情況下當(dāng)且僅當(dāng)事務(wù)成功提交的時(shí)候并且日志級別是warn或者info會(huì)通過agent異步寫入日志。tools.logging定義了一個(gè)全局的agent——*logging-agent*。當(dāng)判斷當(dāng)前是在事務(wù)中調(diào)用log宏,并且日志級別在集合*tx-agent-levels*內(nèi),就會(huì)在事務(wù)提交成功的時(shí)候?qū)⑷罩景l(fā)送給*logging-agent*異步處理。可以通過*tx-agent-levels*改變使用agent輸出日志的級別范圍,默認(rèn)是#{:info :warn}。還可以通過改變*force*變量來強(qiáng)制使用direct或者agent的方式輸出日志,*force*可以為:agent或者:direct。
    (binding [*force* :agent]
      (log :info 
    "hello world"))
    這里特別使用了log宏,需要明確指定日志級別為info。

    5.默認(rèn)日志框架的是從classpath查找的,查找的順序是sl4j,commons-logging,log4j,java.util.logging,找到哪個(gè)可用就用哪個(gè)。如果你的classpath里存在多個(gè)日志框架,如同時(shí)存在sl4j和commons-logging,那么如果你希望強(qiáng)制使用commons-logging,可以通過改變*logger-factory*變量來使用:
    (ns example
      (:use [clojure.tools.logging.impl :only [cl
    -factory]]))
    (binding [
    *logger-factory* (cl-factory)]
      (info 
    "hello world"))

    *logger-factory*是dynamic變量,可以通過binding改變(前面提到的*force*等變量也一樣),如果不希望每次都用binding,而是全局改變,則需要特殊處理:
    (alter-var-root (var *logger-factory*) (constantly (cl-factory)))
    其他logger factory還包括slf4j-factory,log4j-factory,jul-factory。

    6.每個(gè)日志框架的配置跟使用java沒有什么兩樣,比如你用log4j,就需要在classpath下放置一個(gè)log4j.properties等。如果你希望用編程的方式配置,可以使用clj-logging-config

    轉(zhuǎn)載請注明出處:http://www.tkk7.com/killme2008/archive/2012/02/12/369822.html

    posted @ 2012-02-12 20:53 dennis 閱讀(4272) | 評論 (4)編輯 收藏

        年前一篇blog提過,寫了一個(gè)stm-profiler用于統(tǒng)計(jì)clojure STM的運(yùn)行狀況,放在了github上:
    https://github.com/killme2008/stm-profiler

       STM的事務(wù)在遇到寫沖突(多個(gè)事務(wù)寫同一個(gè)ref的時(shí)候)就會(huì)回滾事務(wù)并重試,通過stm-profiler你可以查看事務(wù)的重試次數(shù),重試原因,以及每個(gè)reference的使用情況。使用很簡單,在lein的project.clj引用stm-profiler:
    [stm-profiler "1.0.2-SNAPSHOT"]

    注意,目前stm profiler僅支持clojure 1.3。

    我們寫一個(gè)簡單例子:
    (use 'stm)
    (def a (ref 1))
    (def b (ref 2))

    (dotimes [_ 100] (future (dosync (alter a + 1) (alter b - 1))))
    (Thread/sleep 1000)
    (prn @a)
    (prn @b)
    (Thread/sleep 1000)
    (prn "stm statistics" (stm-stats))
    (prn "reference a statistics" (ref-stats a))
    (prn "reference b statistics" (ref-stats b))

    定義了兩個(gè)ref:a和b,然后用future啟動(dòng)100個(gè)線程并發(fā)地發(fā)起同一個(gè)事務(wù)操作,對a加一,對b減一。最后打印a和b的值,使用stm-stats函數(shù)獲取stm的統(tǒng)計(jì)信息并打印,使用ref-stats獲取a和b兩個(gè)reference的統(tǒng)計(jì)信息并打印。

    運(yùn)行這個(gè)例子,在啟動(dòng)的時(shí)候會(huì)有些警告信息,忽略即可(主要是因?yàn)閟tm profiler重新定義了一些跟STM相關(guān)的函數(shù)和宏,如dosync等,但是僅僅是添加了統(tǒng)計(jì)功能,并沒有修改他們原本的功能)。

    在我機(jī)器上的一次輸出:
    101
    -98
    "stm statistics" {"(alter a + 1)(alter b - 1)" {:not-running 11, :average-retry 5, :total-cost 1233, :get-fault 44, :barge-fail 224, :change-committed 227, :total-times 100, :average-cost 12}}
    "reference a statistics" {"(alter a + 1)(alter b - 1)" {:alter 609, :get-fault 44, :barge-fail 224, :change-committed 227}}
    "reference b statistics" {"(alter a + 1)(alter b - 1)" {:alter 114, :not-running 11}}

    a和b的結(jié)果都沒問題。重點(diǎn)看打印的統(tǒng)計(jì)信息,(stm-stats)的輸出結(jié)果是:
    {"(alter a + 1)(alter b - 1)" {:not-running 11, :average-retry 5, :total-cost 1233, :get-fault 44, :barge-fail 224, :change-committed 227, :total-times 100, :average-cost 12}}

    這個(gè)結(jié)果是一個(gè)map,key是事務(wù)的form,而value就是該form的統(tǒng)計(jì)信息,也是一個(gè)map,具體各項(xiàng)的含義如下:
    total-cost
    所有事務(wù)的總耗時(shí)
    100個(gè)事務(wù)耗時(shí)1233毫秒
    total-times
    事務(wù)運(yùn)行次數(shù)
    100次
    average-cost
    平均每個(gè)事務(wù)耗時(shí)
    平均一個(gè)事務(wù)耗時(shí)12毫秒
    average-retry
    平均每個(gè)事務(wù)的重試次數(shù)  平均每個(gè)事務(wù)重試了5次才成功
    not-running  當(dāng)前事務(wù)不處于running狀態(tài),可能是被其他事務(wù)打斷(barge),需要重試  因?yàn)閚ot-running的原因重試了11次
    get-fault
     讀取ref值的時(shí)候沒有找到read point之前的值,被認(rèn)為是一次讀錯(cuò)誤,需要重試
     因?yàn)樽xref錯(cuò)誤重試了44次
    barge-fail  打斷其他事務(wù)失敗次數(shù),需要重試  嘗試打斷其他事務(wù)失敗而重試了224次
    change-committed  在本事務(wù)read point之后有ref值獲得提交,則需要重試
     因?yàn)閞ef值被其他事務(wù)提交而重試了227次

        從輸出結(jié)果來看,這么簡單的一個(gè)事務(wù)操作,每次事務(wù)要成功平均都需要經(jīng)過5次的重試,最大的原因是因?yàn)閞ef的值在事務(wù)中被其他事務(wù)更改了,或者嘗試打斷其他正在運(yùn)行的事務(wù)失敗而重試。關(guān)于clojure STM的具體原理推薦看這篇文章《Software transactional memory》。STM不是完美的,事務(wù)重試和保存每個(gè)reference的歷史版本的代價(jià)都不低。

        再看(ref-stats a)的輸出:
    {"(alter a + 1)(alter b - 1)" {:alter 609, :get-fault 44, :barge-fail 224, :change-committed 227}}
        可以看到a在所有事務(wù)中的統(tǒng)計(jì)信息,返回的結(jié)果同樣是個(gè)map,key是使用了a的事務(wù),value是具體的統(tǒng)計(jì)信息。各項(xiàng)的含義類似上表,不過這里精確到了具體的reference。其中alter項(xiàng)是指對a調(diào)用alter函數(shù)了609次。ref-stats會(huì)輸出所有在事務(wù)中調(diào)用了a的函數(shù)的調(diào)用次數(shù)。

        通過stm profiler你可以分析具體每個(gè)事務(wù)的執(zhí)行狀況,甚至每個(gè)reference的運(yùn)行狀況,查找熱點(diǎn)事務(wù)和熱點(diǎn)reference等。stm-profiler還不完善,目前還不支持1.2(1.4測試是可以的)。希望有興趣的朋友加入進(jìn)來一起完善。

    轉(zhuǎn)載請注明出處:http://www.tkk7.com/killme2008/archive/2012/02/09/369694.html

    posted @ 2012-02-09 20:55 dennis 閱讀(3379) | 評論 (2)編輯 收藏


        去年(我靠,已經(jīng)是去年了)首次在上海組織了第一次cn-clojure的線下聚會(huì),詳細(xì)可以看這篇blog。今年,我們將在北京舉行第二次cn-clojure的聚會(huì),時(shí)間大概在2月底或者3月初,具體地點(diǎn)待定,歡迎任何對clojure語言或者Lisp語言感興趣的朋友參加,如果有想分享的技術(shù)topic更好 :D。

        如果你要參加,請參加下面的報(bào)名調(diào)查,填寫真實(shí)的姓名和郵箱。如果有想分享的topic,可以填寫調(diào)查或者直接郵件給我。
        報(bào)名鏈接:http://www.diaochapai.com/survey584561

        我們將在議程、時(shí)間和地點(diǎn)確定后發(fā)郵件給報(bào)名的朋友,確認(rèn)參會(huì)的具體時(shí)間和地點(diǎn)。
        郵件列表:http://groups.google.com/group/cn-clojure

    posted @ 2012-02-09 12:37 dennis 閱讀(2825) | 評論 (0)編輯 收藏


        很久沒寫blog了,寫寫最近做的一些工作,給感興趣的朋友做參考。
        首先是我們的kafka的“復(fù)制品”metamorphosis做了1.4版本,實(shí)現(xiàn)了同步復(fù)制方案,broker本身也做了很多優(yōu)化,總體而言meta是一個(gè)非常成熟可用的產(chǎn)品了。甚至可以說是我在淘寶做的最好的一個(gè)產(chǎn)品。有些朋友總是問我們?yōu)槭裁床恢苯佑胟afka,而要另寫一個(gè)?這里做個(gè)統(tǒng)一的解答。
    (1)kafka是scala寫的,我對scala不熟悉,也不待見,考慮到維護(hù)和語言熟悉程度,用java重寫仍然是最好的選擇。
    (2)其次,kafka的整個(gè)社區(qū)非常不活躍,發(fā)展太慢,而我又不愿意去學(xué)習(xí)scala來參與社區(qū)發(fā)展,那么唯一的出路就是自己寫。
    (3)kafka的一些工作不能滿足我們的要求,比如一開始它連producer的負(fù)載均衡都沒有,它的消費(fèi)者API設(shè)計(jì)還是比較蛋疼的。它也不支持事務(wù),沒有考慮作為一個(gè)通用的MQ系統(tǒng)來使用。并且它也沒有高可用和數(shù)據(jù)高可靠的方案。
    (4)我們做了什么呢?
    a.用java徹底重寫整個(gè)系統(tǒng),除了原理一致,整個(gè)實(shí)現(xiàn)是徹底重新實(shí)現(xiàn)的。
    b.我們提供了生產(chǎn)者的負(fù)載均衡(仍然是基于zk),重新設(shè)計(jì)了消費(fèi)者API,更符合 JMS的使用習(xí)慣。
    c.我們提供了事務(wù)實(shí)現(xiàn),包括producer和consumer端的,包括本地事務(wù)和符合XA規(guī)范的分布式事務(wù)實(shí)現(xiàn)。
    d.我們提供了兩種數(shù)據(jù)高可靠方案:類似mysql的異步復(fù)制和同步復(fù)制方案。通過將消息復(fù)制到多個(gè)節(jié)點(diǎn)上來保證數(shù)據(jù)的高可靠。
    e.我們提供了http協(xié)議的實(shí)現(xiàn),并且本身使用協(xié)議也是類似memcached的文本協(xié)議,內(nèi)部也增加了很多統(tǒng)計(jì)項(xiàng)目,可以以memcached的stats協(xié)議的方式來獲取純文本的統(tǒng)計(jì)信息。整個(gè)系統(tǒng)運(yùn)維很方便。
    f.提供了很多擴(kuò)展應(yīng)用:廣播消費(fèi)者的實(shí)現(xiàn),多種offset存儲的實(shí)現(xiàn)(默認(rèn)的zookeeper,還有文件和mysql),tail4j用于作為agent發(fā)送日志,log4j appender擴(kuò)展用于透明地使用log4j發(fā)送消息,hdfs writer用于將消息寫入hdfs,storm spout用于將消息接入storm做實(shí)時(shí)分析,基本上形成一套完整的工具鏈和擴(kuò)展。
    g.一些其他功能點(diǎn):group commit提升數(shù)據(jù)可靠性和吞吐量,連接復(fù)用,集群下的順序消息發(fā)送,消息數(shù)據(jù)的無痛遷移和水平擴(kuò)展,web管理平臺等。

        meta未來會(huì)走開源的路子,不過不會(huì)是我來推動(dòng)的,估計(jì)是在今年會(huì)有進(jìn)展。

        我最近還寫了一些小項(xiàng)目值得一提,首先是aviator這個(gè)輕量級的表達(dá)式執(zhí)行引擎發(fā)布了2.2.1版本,主要是這么幾個(gè)改進(jìn):
    (1)支持多維數(shù)組變量的訪問,如a[0][0]
    (2)添加Expression#getVariableNames()用于返回表達(dá)式的變量列表
    (3)添加AviatorEvaluator#exec方法來簡化調(diào)用
    (4)bug修正等。
        maven直接升級:
     <dependency>
                            
    <groupId>com.googlecode.aviator</groupId>
                            
    <artifactId>aviator</artifactId>
                            
    <version>2.2.1</version>
            
    </dependency>

        其次,hs4j這個(gè)handler socket的客戶端,由新浪微博的@趙鵬城實(shí)現(xiàn)了inc/dec協(xié)議,添加了incr和decr方法用于更新計(jì)數(shù),感謝他的貢獻(xiàn),如果你需要這兩個(gè)功能可以自己從github拉取源碼并構(gòu)建打包,暫時(shí)不準(zhǔn)備發(fā)布到maven。

        第三,關(guān)注高可用的Transaction Manager實(shí)現(xiàn)的可以關(guān)注下我的ewok項(xiàng)目,這是一個(gè)基于BTM這個(gè)開源JTA實(shí)現(xiàn),提供基于bookkeeper的高可用的TM項(xiàng)目。將事務(wù)日志寫到高可用的bookkeeper上,并利用zookeeper來做到故障的透明遷移,某個(gè)TM掛了,可以在其他機(jī)器上從bookkeeper拉取日志并恢復(fù)。代碼已經(jīng)穩(wěn)定并做了性能測試,沒有做進(jìn)一步的破壞性測試。BTM是一個(gè)比JOTM和atomikos更靠譜的開源JTA實(shí)現(xiàn),并且性能也好上很多,代碼質(zhì)量更不用說,建議有興趣的可以看一下。我也為它貢獻(xiàn)了一個(gè)事務(wù)日志寫入優(yōu)化的patch,日志寫入性能提升了近一倍。

        最后,我在clojure上做了一些事情,首先是為storm項(xiàng)目貢獻(xiàn)了兩個(gè)patch:利用curator做zookeeper交互和添加storm.ui.context.path選項(xiàng),前者被作者接受,后者暫時(shí)只對我們有用。前者讓storm跟zk的交互更可用,后者是為storm ui添加了可選的相對路徑。你都可以在我fork的分支上嘗試,curator的patch在storm 0.6.2上發(fā)布,現(xiàn)在還是snapshot狀態(tài)。昨天晚上牙痛睡不著,半夜寫了個(gè)clojure STM profiler,用于統(tǒng)計(jì)分析clojure STM運(yùn)行狀況,諸如事務(wù)運(yùn)行次數(shù)和時(shí)間,事務(wù)的重試原因和次數(shù)等,可以針對每個(gè)dosync的form做統(tǒng)計(jì),有興趣也可以看下。不過我其實(shí)更想將這個(gè)功能加入到clojure核心,會(huì)嘗試提交下pull request。

       還有個(gè)工作上的變遷,我將在2月1號正式從呆了近三年的淘寶離職,加入一支充滿活力的創(chuàng)業(yè)團(tuán)隊(duì)。從穩(wěn)定的大公司出來,去加入一家初創(chuàng)公司,不能說沒有風(fēng)險(xiǎn),但是我還是想去接受新的挑戰(zhàn),愿意更新我的知識結(jié)構(gòu),愿意向牛人們學(xué)習(xí)。我在某個(gè)blog上說我今年遇到了人生中最大的挑戰(zhàn)和轉(zhuǎn)折,并不是說這個(gè)事情,而是我的兒子今年患了一場重病,慶幸在很多人的幫助和關(guān)心下,他勇敢地挺了過來,度過最困難的一關(guān),現(xiàn)在還在繼續(xù)治療。我要感謝很多人,感謝淘寶,感謝我的TL華黎和鋒寒,感謝我的同事和朋友林軒,感謝我們的HR,感謝三年后打交道的很多同事。沒有他們,我今年真的過不了關(guān),沒有他們,我也不能進(jìn)入淘寶并呆上快三年。

       最后的最后,我要特別感謝我的兒子,謝謝你的降生,謝謝你今年的勇敢,謝謝你給我們?nèi)規(guī)淼目鞓罚x謝你繼續(xù)陪著我們 ,也希望你新年繼續(xù)勇敢地堅(jiān)持下去,我們必將戰(zhàn)勝一切。     
       

    posted @ 2012-01-15 12:50 dennis 閱讀(5468) | 評論 (20)編輯 收藏

    僅列出標(biāo)題
    共56頁: 上一頁 1 2 3 4 5 6 7 8 9 下一頁 Last 
    主站蜘蛛池模板: 99久久99久久精品免费观看| 亚洲av乱码一区二区三区按摩| 精品国产亚洲一区二区三区 | 国产偷国产偷亚洲清高APP| 99久久国产免费中文无字幕| 国产麻豆成人传媒免费观看| 在线涩涩免费观看国产精品| 男人都懂www深夜免费网站| 久久99精品视免费看| 亚洲免费二区三区| 在线观看免费高清视频| 成年人性生活免费视频| 全免费一级午夜毛片| 国产乱人免费视频| 亚洲国产婷婷香蕉久久久久久| 区三区激情福利综合中文字幕在线一区亚洲视频1 | 国产综合激情在线亚洲第一页| 777亚洲精品乱码久久久久久 | 亚洲爆乳少妇无码激情| 国产99久久亚洲综合精品| 又长又大又粗又硬3p免费视频| 狠狠躁狠狠爱免费视频无码| a级大片免费观看| 中文字幕免费高清视频| 成年免费大片黄在线观看岛国| 午夜电影免费观看| 亚洲精品第一国产综合精品99| 亚洲香蕉网久久综合影视| 91亚洲国产在人线播放午夜| 日本亚洲色大成网站www久久| 亚洲高清乱码午夜电影网| fc2免费人成在线| 免费无码又爽又刺激高潮视频| 91成年人免费视频| 国产一区二区免费在线| 亚洲精品无码国产| 亚洲一级毛片在线观| 国产亚洲美女精品久久| a毛片在线还看免费网站| 97热久久免费频精品99| 免费乱码中文字幕网站|