莊周夢(mèng)蝶

          生活、程序、未來(lái)
             :: 首頁(yè) ::  ::  :: 聚合  :: 管理

              Fel是最近javaeye比較火的關(guān)鍵詞,這是由網(wǎng)友lotusyu開發(fā)的一個(gè)高性能的EL,從作者給出的數(shù)據(jù)來(lái)看,性能非常優(yōu)異,跟前段時(shí)間溫少開源的Simple EL有的一拼。首先要說(shuō),這是個(gè)好現(xiàn)象,國(guó)內(nèi)的開源項(xiàng)目越來(lái)越多,可以看出開發(fā)者的水平是越來(lái)越高了,比如我最近還看到有人開源的類似kestel的輕量級(jí)MQ——fqueue也非常不錯(cuò),有興趣可以看下我的分析《fqueue初步分析》。

              進(jìn)入正文,本文是嘗試分析下Fel的實(shí)現(xiàn)原理,以及優(yōu)缺點(diǎn)和aviator——我自己開源的EL之間的簡(jiǎn)單比較。

              Fel的實(shí)現(xiàn)原理跟Simple EL是類似,都是使用template生成中間代碼——也就是普通的java代碼,然后利用javac編譯成class,最后運(yùn)行,當(dāng)然,這個(gè)過(guò)程都是動(dòng) 態(tài)的。JDK6已經(jīng)引入了編譯API,在此之前的版本可以調(diào)用sun的類來(lái)編譯,因?yàn)閖avac其實(shí)就是用java實(shí)現(xiàn)的?;氐紽el里 面,F(xiàn)elCompiler15就是用 com.sun.tools.javac.Main來(lái)編譯,而FelCompiler16用標(biāo)準(zhǔn)的javax.tools.JavaCompiler來(lái)編譯的。

              文法和語(yǔ)法解釋這塊是使用antlr這個(gè)parse generator生成的,這塊不多說(shuō),有興趣可以看下antlr,整體一個(gè)運(yùn)行的過(guò)程是這樣:

              expression string -> antlr -> AST -> comiple -> java source template -> java class -> Expression 

              這個(gè)思路我在實(shí)現(xiàn)aviator之前就想過(guò),但是后來(lái)考慮到API需要用的sun獨(dú)有的類,而且要求classpath必須有tools.jar這個(gè)依賴包,就放棄了這個(gè)思路,還是采用ASM生成字節(jié)碼的方式。題外,velocity的優(yōu)化可以采用這個(gè)思路,我們有這么一個(gè)項(xiàng)目是這么做的,也準(zhǔn)備開源了。

           

              看看Fel生成的中間代碼,例如a+b這樣的一個(gè)簡(jiǎn)單的表達(dá)式,假設(shè)我一開始不知道a和b的類型,編譯是這樣:

              FelEngine fel = new FelEngineImpl();  
              Expression exp 
          =  fel.compile("a+b"null); 

              我稍微改了下FEL的源碼,讓它打印中間生成的java代碼,a+b生成的中間結(jié)果為:

              package com.greenpineyu.fel.compile;  
                
              
          import com.greenpineyu.fel.common.NumberUtil;  
              
          import com.greenpineyu.fel.Expression;  
              
          import com.greenpineyu.fel.context.FelContext;  
              
          import org.apache.commons.lang.ObjectUtils;  
              
          import org.apache.commons.lang.StringUtils;  
                
              
          public class Fel_0  implements Expression{  
                
                  
          public Object eval(FelContext context) {  
                      java.lang.Object var_1 
          = (java.lang.Object)context.get("b");   //b  
                      java.lang.Object var_0 = (java.lang.Object)context.get("a");   //a  
                      return (ObjectUtils.toString(var_0))+(ObjectUtils.toString(var_1));  
                  }  
              } 

               可見,F(xiàn)EL對(duì)表達(dá)式解析和解釋后,利用template生成這么一個(gè)普通的java類,而a和b都從context中獲取并轉(zhuǎn)化為Object類型,這里沒有做任何判斷就直接認(rèn)為a和b是要做字符串相加,然后拼接字符串并返回。

           

               問(wèn)題出來(lái)了,因?yàn)闆]有在編譯的時(shí)候傳入context(我們這里是null),F(xiàn)EL會(huì)將a和b的類型默認(rèn)都為java.lang.Object,a+b解釋為字符串拼接。但是運(yùn)行的時(shí)候,我完全可以傳入a和b都為數(shù)字,那么結(jié)果就非常詭異了:

               FelEngine fel = new FelEngineImpl();  
                
              Expression exp 
          = fel.compile("a+b"null);  
              Map
          <String, Object> env=new HashMap<String, Object>();  
              env.put(
          "a"1);  
              env.put(
          "b"3.14);  
              System.out.println(exp.eval(
          new MapContext(env))); 

          輸出:

              13.14 

              1+3.14的結(jié)果,作為字符串拼接就是13.14,而不是我們想要的4.14。如果將表達(dá)式換成a*b,就完全運(yùn)行不了

              com.greenpineyu.fel.exception.CompileException: package com.greenpineyu.fel.compile;  
                
              
          import com.greenpineyu.fel.common.NumberUtil;  
              
          import com.greenpineyu.fel.Expression;  
              
          import com.greenpineyu.fel.context.FelContext;  
              
          import org.apache.commons.lang.ObjectUtils;  
              
          import org.apache.commons.lang.StringUtils;  
                
              
          public class Fel_0  implements Expression{  
                
                  
          public Object eval(FelContext context) {  
                      java.lang.Object var_1 
          = (java.lang.Object)context.get("b");   //b  
                      java.lang.Object var_0 = (java.lang.Object)context.get("a");   //a  
                      return (var_0)*(var_1);  
                  }  
              }  
                
              [Fel_0.java:
          14: 運(yùn)算符 * 不能應(yīng)用于 java.lang.Object,java.lang.Object]  
                  at com.greenpineyu.fel.compile.FelCompiler16.compileToClass(FelCompiler16.java:
          113)  
                  at com.greenpineyu.fel.compile.FelCompiler16.compile(FelCompiler16.java:
          87)  
                  at com.greenpineyu.fel.compile.CompileService.compile(CompileService.java:
          66)  
                  at com.greenpineyu.fel.FelEngineImpl.compile(FelEngineImpl.java:
          62)  
                  at TEst.main(TEst.java:
          14)  
              Exception in thread 
          "main" java.lang.NullPointerException  
                  at TEst.main(TEst.java:
          18

           

              這個(gè)問(wèn)題對(duì)于Simple EL同樣存在,如果沒有在編譯的時(shí)候能確定變量類型,這無(wú)法生成正確的中間代碼,導(dǎo)致運(yùn)行時(shí)出錯(cuò),并且有可能造成非常詭異的bug。

           

              這個(gè)問(wèn)題的本質(zhì)是因?yàn)镕el和Simple EL沒有自己的類型系統(tǒng),他們都是直接使用java的類型的系統(tǒng),并且必須在編譯的時(shí)候確定變量類型,才能生成高效和正確的代碼,我們可以將它們稱為“強(qiáng)類型的EL“。

           

              現(xiàn)在讓我們?cè)诰幾g的時(shí)候給a和b加上類型,看看生成的中間代碼:

              FelEngine fel = new FelEngineImpl();  
              fel.getContext().set(
          "a"1);  
              fel.getContext().set(
          "b"3.14);  
              Expression exp 
          = fel.compile("a+b"null);  
              Map
          <String, Object> env = new HashMap<String, Object>();  
              env.put(
          "a"1);  
              env.put(
          "b"3.14);  
              System.out.println(exp.eval(
          new MapContext(env))); 

              查看中間代碼:

              package com.greenpineyu.fel.compile;  
                
              
          import com.greenpineyu.fel.common.NumberUtil;  
              
          import com.greenpineyu.fel.Expression;  
              
          import com.greenpineyu.fel.context.FelContext;  
              
          import org.apache.commons.lang.ObjectUtils;  
              
          import org.apache.commons.lang.StringUtils;  
                
              
          public class Fel_0  implements Expression{  
                
                  
          public Object eval(FelContext context) {  
                      
          double var_1 = ((java.lang.Number)context.get("b")).doubleValue();   //b  
                      double var_0 = ((java.lang.Number)context.get("a")).doubleValue();   //a  
                      return (var_0)+(var_1);  
                  }  
              } 

          可以看到這次將a和b都強(qiáng)制轉(zhuǎn)為double類型了,做數(shù)值相加,結(jié)果也正確了:

              4.140000000000001 

              Simple EL我沒看過(guò)代碼,這里猜測(cè)它的實(shí)現(xiàn)也應(yīng)該是類似的,也應(yīng)該有同樣的問(wèn)題。

              相比來(lái)說(shuō),aviator這是一個(gè)弱類型的EL,在編譯的時(shí)候不對(duì)變量類型做任何假設(shè),而是在運(yùn)行時(shí)做類型判斷和自動(dòng)轉(zhuǎn)化。過(guò)去提過(guò),我給aviator的定位是一個(gè)介于EL和script之間的東西,它有自己的類型系統(tǒng)。 例如,3這個(gè)數(shù)字,在java里可能是long,int,short,byte,而aviator統(tǒng)一為AviatorLong這個(gè)類型。為了在這兩個(gè)類 型之間做適配,就需要做很多的判斷和box,unbox操作。這些判斷和轉(zhuǎn)化都是運(yùn)行時(shí)進(jìn)行的,因此aviator沒有辦法做到Fel這樣的高效,但是已 經(jīng)做到至少跟groovy這樣的弱類型腳本語(yǔ)言一個(gè)級(jí)別,也超過(guò)了JXEL這樣的純解釋EL,具體可以看這個(gè)性能測(cè)試。

           

             強(qiáng)類型還是弱類型,這是一個(gè)選擇問(wèn)題,如果你能在運(yùn)行前就確定變量的類型,那么使用Fel應(yīng)該可以達(dá)到或者接近于原生java執(zhí)行的效率,但是失去了靈活性;如果你無(wú)法確定變量類型,則只能采用弱類型的EL。

           

             EL涌現(xiàn)的越來(lái)越多,這個(gè)現(xiàn)象有點(diǎn)類似消息中間件領(lǐng)域,越來(lái)越多面向特定領(lǐng)域的輕量級(jí)MQ的出現(xiàn),而不是原來(lái)那種大而笨重的通用MQ大行其道,一方面是互 聯(lián)網(wǎng)應(yīng)用的發(fā)展,需求不是通用系統(tǒng)能夠滿足的,另一方面我認(rèn)為也是開發(fā)者素質(zhì)的提高,大家都能造適合自己的輪子。從EL這方面來(lái)說(shuō),我也認(rèn)為會(huì)有越來(lái)越多 特定于領(lǐng)域的,優(yōu)點(diǎn)和缺點(diǎn)一樣鮮明的EL出現(xiàn),它們包含設(shè)計(jì)者自己的目標(biāo)和口味,選擇很多,就看取舍。

          posted @ 2011-09-17 12:52 dennis 閱讀(9953) | 評(píng)論 (5)編輯 收藏


              fqueue是國(guó)產(chǎn)的一個(gè)類似memcacheq,kestrel這樣的支持memcached協(xié)議的輕量級(jí)開源MQ。它的項(xiàng)目主頁(yè):
          http://code.google.com/p/fqueue/downloads/list,介紹和特點(diǎn)都可以看主頁(yè),我就不廢話了。

              今天老大提到, co了源碼看了下,寫個(gè)初步分析報(bào)告。

              首先是它的存儲(chǔ)層,主要是一個(gè)FQueue這么一個(gè)抽象隊(duì)列,內(nèi)部實(shí)現(xiàn)是FSQueue,也就是基于文件的FIFO隊(duì)列。這個(gè)隊(duì)列是多個(gè)文件組成的。每個(gè)文件默認(rèn)大小在150M,超過(guò)即切換一個(gè)新文件來(lái)寫。讀的時(shí)候如果讀到尾部,則查找下一個(gè)文件進(jìn)行讀取。數(shù)據(jù)文件名以idb為后綴,并且從編號(hào)1開始遞增,除了數(shù)據(jù)文件外,每個(gè)隊(duì)列還有個(gè)db為后綴的索引文件,記錄當(dāng)前寫和讀的數(shù)據(jù)文件編號(hào)和偏移量。目錄結(jié)構(gòu)大概是這樣:
              --fqueue
                  --fqueuedata_1.idb
                  --fqueuedata_2.idb
                  --……
                  --icqueue.db

              文件的存儲(chǔ)比較有特色,采用MappedByteBuffer做文件讀寫,MappedByteBuffer是java nio引入的文件內(nèi)存映射方案,讀寫性能極高,但是也有一定的問(wèn)題,比如說(shuō)內(nèi)存占用,以及數(shù)據(jù)刷入設(shè)備的不確定性和關(guān)閉問(wèn)題。在fqueue中,每隔10毫秒會(huì)強(qiáng)制force一次buffer,將修改過(guò)的數(shù)據(jù)刷入設(shè)備。對(duì)于關(guān)閉問(wèn)題,則采用那個(gè)技巧,示例代碼:
          /**
               * 關(guān)閉索引文件
               
          */
              
          public void close() {
                  
          try {
                      mappedByteBuffer.force();
                      AccessController.doPrivileged(
          new PrivilegedAction<Object>() {
                          
          public Object run() {
                              
          try {
                                  Method getCleanerMethod 
          = mappedByteBuffer.getClass().getMethod("cleaner"new Class[0]);
                                  getCleanerMethod.setAccessible(
          true);
                                  sun.misc.Cleaner cleaner 
          = (sun.misc.Cleaner) getCleanerMethod.invoke(mappedByteBuffer,
                                          
          new Object[0]);
                                  cleaner.clean();
                              } 
          catch (Exception e) {
                                  log.error(
          "close logindexy file error:", e);
                              }
                              
          return null;
                          }
                      });
                      fc.close();
                      dbRandFile.close();
                      mappedByteBuffer 
          = null;
                      fc 
          = null;
                      dbRandFile 
          = null;
                  } 
          catch (IOException e) {
                      log.error(
          "close logindex file error:", e);
                  }
              }

              利用反射,并且使用了sun特有的類,不具有可移植性。MappedByteBuffer還有一個(gè)問(wèn)題是map的代價(jià)比較高,可能在切換文件的時(shí)候fqueue會(huì)有一定程度的阻塞現(xiàn)象。

              存儲(chǔ)的性能,我在我的機(jī)器測(cè)試了下,似乎沒有作者宣稱的那么高,我的機(jī)器是5400轉(zhuǎn)的普通SATA盤,寫入1K數(shù)據(jù)的平均QPS在8000左右。我估計(jì)fqueue的性能跟磁盤有很大關(guān)系,如果使用15000轉(zhuǎn)的SAS盤應(yīng)該能有很大改觀。

              網(wǎng)絡(luò)層直接使用了jmemcached的實(shí)現(xiàn),jmemcached是一個(gè)java實(shí)現(xiàn)的memcached,通常用于單元測(cè)試之類??辞闆rfqueue也支持memcached的二進(jìn)制協(xié)議了。網(wǎng)絡(luò)框架使用了netty3,這些就不多說(shuō)了。自己看都明白。額外提一下,作者做的單元測(cè)試使用了xmemcached,咔咔,廣而告之。

              總體來(lái)說(shuō)fqueue是一個(gè)整體上很清爽和輕量級(jí)的MQ實(shí)現(xiàn),適合一些特定的場(chǎng)景,至于性能,我們下周準(zhǔn)備做個(gè)壓測(cè),到時(shí)候再談吧。

          posted @ 2011-09-16 20:10 dennis 閱讀(9232) | 評(píng)論 (7)編輯 收藏

              開源的java memcached client—— xmemcached發(fā)布1.3.4版本,主要改進(jìn)如下:

           

          1、修復(fù)一個(gè)相對(duì)嚴(yán)重的bug,在解析二進(jìn)制協(xié)議時(shí)如果遇到從服務(wù)端返回的錯(cuò)誤信息,會(huì)導(dǎo)致連接異常斷開;如果你沒有使用binary協(xié)議,不會(huì)遇到此問(wèn)題。建議使用xmemcached并且使用二進(jìn)制協(xié)議的朋友升級(jí)到此版本

          2、允許XMemcachedClientFactoryBean配置opTimeout選項(xiàng)。

          3、添加RoundRobinMemcachedSessionLocator,輪詢的連接選擇器,僅用于kestrel或者memcacheq集群,這些應(yīng)用都不要求同一個(gè)key要保存在固定的服務(wù)器上,而僅是作為集群分擔(dān)負(fù)載。

          4、KetamaMemcachedSessionLocator添加額外選項(xiàng),允許配置是否兼容 nginx-upstream-consistent,這個(gè)是網(wǎng)友 wolfg1969貢獻(xiàn)的patch。如果要使得xmc的一致性哈希算法兼容nginx-upstream-consistent,只要設(shè)置cwNginxUpstreamConsistent為true即可,示范代碼:

              MemcachedClientBuilder builder = new XMemcachedClientBuilder(  
                              AddrUtil.getAddresses(servers));  
                
              builder.setSessionLocator(
          new KetamaMemcachedSessionLocator(  
                              
          true)); 

          5、修復(fù)bug,包括issue 132 , issue 142 , issue 133 , issue 139 , issue 142 , issue 145 ,issue 150等。

           

          如果你使用maven,只要簡(jiǎn)單升級(jí)版本即可: 

            <dependency>  
                   
          <groupId>com.googlecode.xmemcached</groupId>  
                   
          <artifactId>xmemcached</artifactId>  
                   
          <version>1.3.4</version>  
              
          </dependency> 

          下載地址:

          http://code.google.com/p/xmemcached/downloads/list

          posted @ 2011-09-08 18:55 dennis 閱讀(3283) | 評(píng)論 (3)編輯 收藏


              這篇blog遲到了很久,本來(lái)是想寫另一個(gè)跟網(wǎng)絡(luò)相關(guān)bug的查找過(guò)程,偷偷懶,寫下最近印象比較深刻的bug。這個(gè)bug是我的同事水寒最終定位到的。
              前幾個(gè)月同事報(bào)告稱有一個(gè)線上MQ集群會(huì)同一時(shí)間拋出ArrayIndexOutOfBoundsException這個(gè)異常,也就是數(shù)組越界。查看源碼,除去一些無(wú)關(guān)緊要的細(xì)節(jié)大概是這樣子:
          public class ConnectionSelector{
              
          private AtomicInteger sets=new AtomicInteger(0);

             
          public void selectConnection(List<Connection> connList){
                    
          if(connList==null){
                          
          return null;
                     }
                    
          final int size = connList.size();
                      
          if (size == 0) {
                          
          return null;
                      }
                     
          return connList.get(sets.incrementAndGet() % size);
          }

             }

              很顯然,這里的本意是實(shí)現(xiàn)一個(gè)輪詢的連接選擇器,返回一個(gè)選中的連接。使用AtomicInteger遞增并對(duì)鏈表大小取模,返回結(jié)果索引位置的連接。異常拋出的位置就是我代碼中標(biāo)紅的位置。

              顯然,這里有兩種可能,一種情況下是說(shuō)在執(zhí)行那一行代碼的時(shí)候,connList的大小縮小了(也就是說(shuō)連接可能被其他線程移出),那么導(dǎo)致取模的結(jié)果越界。另一種可能是取模的結(jié)果本身確實(shí)超過(guò)了列表范圍。

              第一種情況是完全可能的,因?yàn)榉?wù)器的連接可能隨時(shí)斷開或者重連,但是這種情況相對(duì)非常少見,因此我們這里并沒有對(duì)這個(gè)選擇過(guò)程做同步,主要是從性能的角度出發(fā),偶爾的失敗可以接受。很遺憾的是,我被我的思維慣性誤導(dǎo)了,從來(lái)沒有懷疑過(guò)第二種情況,總是認(rèn)為是不是真的連接恰巧斷開導(dǎo)致這個(gè)異常,但是卻無(wú)法解釋這個(gè)異常發(fā)生后就一直錯(cuò)誤下去,無(wú)法自行恢復(fù)。
              為什么說(shuō)思維慣性誤導(dǎo)呢?這里的問(wèn)題其實(shí)是負(fù)數(shù)取模的問(wèn)題,對(duì)一個(gè)負(fù)數(shù)進(jìn)行取模,結(jié)果會(huì)是正數(shù)還是負(fù)數(shù)?答案是結(jié)果因語(yǔ)言而異。
              我很早以前在使用Ruby的時(shí)候做過(guò)測(cè)試,負(fù)數(shù)取模結(jié)果為正數(shù),例如在irb里嘗試下:
          >> -1000%3
          => 2
          >> -2001%4
          => 3

              這個(gè)印象持續(xù)至今,在clojure里結(jié)果也是這樣子:
          Clojure 1.2.1
          user
          => (mod -1000 3)
          2
          user
          => (mod -2001 4)
          3

              可以再試試python:
          Python 2.7.1 (r271:86832, Jun 16 201116:59:05
          [GCC 
          4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2335.15.00)] on darwin
          Type 
          "help""copyright""credits" or "license" for more information.
          >>> -10000%3
          2
          >>> -2001%4
          3

              這三種語(yǔ)言的結(jié)果完全一致,結(jié)果都為正數(shù)。這個(gè)慣性思維延續(xù)到j(luò)ava卻不成立了,可惜我根本沒做測(cè)試,讓我們?cè)囅拢?br />
             public static void main(final String[] args) {
                  System.out.println(
          -1000 % 3);
                  System.out.println(
          -2001 % 4);
              }

          打印結(jié)果為:
          -1
          -1

              果然,在java里負(fù)數(shù)取模的結(jié)果為負(fù)數(shù),而不是我習(xí)慣性地認(rèn)為是正數(shù)。因此最終的定位到的原因就是sets這個(gè)變量遞增超過(guò)Integer.MAX_VALUE后越界變成負(fù)數(shù)了,取模的結(jié)果為負(fù)數(shù),導(dǎo)致拋出數(shù)組越界的異常,這也解釋了為什么同一個(gè)集群都在同一時(shí)間出問(wèn)題,因?yàn)檫@個(gè)集群內(nèi)的機(jī)器啟動(dòng)時(shí)間相鄰并且調(diào)用這個(gè)方法次數(shù)相對(duì)平均。修正問(wèn)題很簡(jiǎn)單,加個(gè)Math.abs就好。

              Update:加個(gè)abs是不夠的,因?yàn)镸ath.abs的javadoc提醒了:
          Note that if the argument is equal to the value of Integer.MIN_VALUE, the most negative representable int value, the result is that same value, which is negative.

              也就是說(shuō)對(duì)Integer.MIN_VALUE做abs結(jié)果仍然是負(fù)數(shù)。盡管在這個(gè)場(chǎng)景中失敗一次可以接受,但是最好的辦法還是回復(fù)中steven提到的抵消符號(hào)位的做法:
          (sets.incrementAndGet() & 0x7FFFFFFF% size
             
              這個(gè)問(wèn)題更詳細(xì)的討論后來(lái)我找到這篇博客,作者討論幾種語(yǔ)言和計(jì)算器的這個(gè)問(wèn)題的結(jié)果,給出了一些結(jié)論。不過(guò)我覺的這個(gè)結(jié)論可能也不是那么可靠,特別是對(duì)c/c++來(lái)說(shuō),很大程度上應(yīng)該還是依賴于實(shí)現(xiàn),最可靠的辦法還是強(qiáng)制結(jié)果為正。

              這個(gè)bug的幾個(gè)教訓(xùn):
          1、首先是第一次出現(xiàn)的時(shí)候沒有引起足夠重視,重啟解決問(wèn)題后沒有深究。有句玩笑話:99%的程序問(wèn)題都可以通過(guò)重啟解決。但是事實(shí)上問(wèn)題仍然存在,該發(fā)生的終究還會(huì)發(fā)生。不管你信不信,它就是發(fā)生了,這是一個(gè)奇跡。
          2、注意大腦的思維慣性,經(jīng)驗(yàn)主義和教條主義都不可取。最近在讀一本好書《暗時(shí)間》,大腦誤導(dǎo)我們的手段可是多種多樣。
          3、最后就是這個(gè)負(fù)數(shù)取模的結(jié)果因語(yǔ)言而異,不要依賴于特定實(shí)現(xiàn)。
             

          posted @ 2011-09-02 00:02 dennis 閱讀(4134) | 評(píng)論 (5)編輯 收藏

              沒有耐心看經(jīng)過(guò)的請(qǐng)直接拉到末尾看slide列表。

              這個(gè)聚會(huì)是由江宏首先提議的,我參與協(xié)助。目的是讓長(zhǎng)三角地區(qū)對(duì)clojure語(yǔ)言感興趣,或者正在使用的朋友當(dāng)面認(rèn)識(shí)和交流一下。會(huì)議的組織過(guò)程一波三折,首先是會(huì)議地點(diǎn)本來(lái)定在了上海google辦公室,但是后來(lái)google那邊又說(shuō)不讓過(guò)去,我再聯(lián)系了原來(lái)淘寶網(wǎng)的同事火狐,經(jīng)過(guò)他的努力和幫助,最終將地點(diǎn)確定在了上海大眾點(diǎn)評(píng)。要感謝大眾點(diǎn)評(píng)網(wǎng)和火狐的幫助,不然這次活動(dòng)估計(jì)就黃了。會(huì)議的日期選定也比較偶然,跟七夕撞在了一天,并且8月6號(hào)這天說(shuō)臺(tái)風(fēng)“梅花”要在江浙一帶登陸,上海要刮多少級(jí)多少級(jí)的大風(fēng),加上我和杭州的幾個(gè)朋友過(guò)去要坐高鐵,那心里就七上八下了,搞不好就要被“掩埋”了。我們還開玩笑說(shuō)最好買火車中段車廂的票為妙。

              8月6日一早,和同在淘寶的楊冬,加上兩位做ROR的朋友一起出發(fā),天氣沒有想象的糟糕,高鐵一個(gè)小時(shí)就到了,轉(zhuǎn)地鐵到大眾點(diǎn)評(píng)網(wǎng)大概也才中午12點(diǎn)左右。打了電話給火狐,一起吃了飯然后就直奔大眾點(diǎn)評(píng)網(wǎng)。大眾點(diǎn)評(píng)網(wǎng)的前臺(tái)大廳裝修也是非常熟悉的橙色,很意外周6有很多人,后來(lái)才知道是在搞招聘會(huì)和培訓(xùn)。這時(shí)候,江宏他們也從昆山趕到了,火狐幫我們定的會(huì)議室很大,足夠容納20號(hào)人左右。陸續(xù)有人達(dá)到,到約定的1點(diǎn)的時(shí)候,我記的是來(lái)了大概11還是12個(gè)人,還有幾個(gè)朋友在路上,因此我們決定推遲到1點(diǎn)半再開始。最終來(lái)的人估計(jì)有15個(gè)以上,估計(jì)報(bào)名的都來(lái)了。

              1點(diǎn)半正式開始,首先是我來(lái)講《clojure概覽》這個(gè)topic,主要是一個(gè)clojure語(yǔ)言的介紹。這個(gè)是我上周開始準(zhǔn)備的,在去年《clojure的魅力》的基礎(chǔ)上做了刪減和增加,聽取江宏的意見增加了示例和引子。上周也在我們的團(tuán)隊(duì)講過(guò)一次。輕車熟路,也為了給后面的topic留出時(shí)間,我講的比較快,大概40分鐘就結(jié)束了。

              接下來(lái)是孫寧(sunng87)講《clojure開發(fā)的生命周期管理》,我對(duì)clojure的周邊工具并不熟悉,趁機(jī)更好地了解了很多 clojure開發(fā)過(guò)程中用到的工具和資料,推薦對(duì)clojure開發(fā)感興趣的朋友看下。嘗試了下clooj,比我預(yù)期的要好,遺憾的是還沒有語(yǔ)法高亮,推薦初學(xué)clojure的朋友可以嘗試下這個(gè)輕量級(jí)的IDE。目前最好的clojure IDE還是idea里的La Clojure插件。最后孫寧順便廣告了下lein-control插件,這是孫寧構(gòu)建的一個(gè)clojure-control的lein插件,他還貢獻(xiàn)了一個(gè)類似python里fabric的clojure DSL實(shí)現(xiàn),讓clojure-control更易用。

              接下來(lái)是江宏介紹他們開發(fā)trakrapp.com這個(gè)純clojure實(shí)現(xiàn)的網(wǎng)站中使用的技術(shù),以及遇到的問(wèn)題和經(jīng)驗(yàn)。這個(gè)網(wǎng)站基于compojure這個(gè)框架實(shí)現(xiàn)的,前端采用backbone.js,后端是MongoDB和postgresql,可以說(shuō)都是非常“新潮”的技術(shù)。他在談?dòng)龅絾?wèn)題的時(shí)候,提到clojure的stack trace又長(zhǎng)又丑,這一點(diǎn)深有體會(huì),clojure的異常堆棧包含了java和clojure的,整個(gè)調(diào)用鏈相對(duì)較長(zhǎng),非常不利于問(wèn)題的排查,不知道后續(xù)clojure會(huì)不會(huì)對(duì)這一點(diǎn)做出改進(jìn)。

              接下來(lái)是林晴介紹他們一個(gè)用scala實(shí)現(xiàn)的類似domino的企業(yè)OA系統(tǒng),不過(guò)他這個(gè)例子給我的感覺更多是發(fā)揮了mongodb的schema free的特點(diǎn),沒有體現(xiàn)出使用scala的好處來(lái)。我對(duì)scala的觀點(diǎn)一直很明確,scala想做JVM上的c++,從個(gè)人角度不喜歡這種多范式的語(yǔ)言,并且語(yǔ)法不符合我的胃口,特別是類型系統(tǒng)這塊特別復(fù)雜,我怕我在寫scala的時(shí)候還要參考一本厚厚的reference,這不是我想要的。而clojure的核心就非常小,相對(duì)符合我的期望。

              作為東道主的火狐介紹了大眾點(diǎn)評(píng)網(wǎng)的新架構(gòu)以及他們從.net往java遷移的經(jīng)驗(yàn),他們的新架構(gòu)也是做服務(wù)化和中心化,對(duì)于.net和java平臺(tái)來(lái)說(shuō),遷移更多是從人力成本和一些其他因素決定的,當(dāng)然,遷移最重要的還是要有公司高層的全力支持,特別重要的一點(diǎn)是如何讓老員工也參與這個(gè)過(guò)程。因?yàn)槔蠁T工對(duì)現(xiàn)有系統(tǒng)和業(yè)務(wù)最熟悉,將他們排除在外閉門造車是注定要失敗的。

              最后是同樣來(lái)自昆山文石的吳哲介紹如何在半天內(nèi)實(shí)現(xiàn)一個(gè)HTML 5的游戲,他介紹的processing.js非常有趣,processing本身是一門編程語(yǔ)言,有人將它移植到了js上,可以直接在支持html5上瀏覽器展現(xiàn),效果相當(dāng)cool。巧合的是我在回去后的第二天去書店的時(shí)候,竟然在某個(gè)角落看到《processing互動(dòng)編程藝術(shù)》這本書,買了下來(lái)準(zhǔn)備了解下。做數(shù)據(jù)圖形化的同學(xué)可以關(guān)注下。

              總體來(lái)講,這次聚會(huì)的效果超過(guò)我的預(yù)期,在超強(qiáng)臺(tái)風(fēng)的陰影下和七夕愛情的感召下還有這么多人趕過(guò)來(lái),作為組織者之一非常感動(dòng)。并且topic講座也讓我學(xué)習(xí)了一些東西,最重要的是當(dāng)面認(rèn)識(shí)了一些網(wǎng)上交流過(guò)的朋友,給我印象深刻的是看起來(lái)非常老成的孫寧,完全不像個(gè)85后。還有個(gè)印象深刻的細(xì)節(jié)是現(xiàn)場(chǎng)的5,6臺(tái)mbp,這里面還是因?yàn)橛型瑢W(xué)是在搞ROR的因素。

              最后,給下slide的鏈接如下:

          1,我的《clojure概覽》,源碼在github上。
          2,孫寧的《Clojure開發(fā)的生命周期管理》,lein-controlclojure-control
          3,江宏的《Clojure web development》,他們開發(fā)的網(wǎng)站
          4,吳哲的《How to build a html5 game in half a day
          5,火狐的《大眾點(diǎn)評(píng)網(wǎng)新架構(gòu)
          6,cn-clojure主頁(yè)
           

          posted @ 2011-08-09 14:09 dennis 閱讀(7585) | 評(píng)論 (3)編輯 收藏

              最近看了篇在google reader里分享非常多的文章,我表示很無(wú)語(yǔ),文章在這里,題目是《Peter Norvig:編程語(yǔ)言的選擇并不重要》。簡(jiǎn)單來(lái)講這文章就是鼓吹python的,然后舉了很多例子說(shuō)python描述算法比Lisp容易。這個(gè)無(wú)需多說(shuō),圖靈模型本來(lái)就比lambda演算更適合描述算法。

              我想說(shuō)的是,文中明明提了,Peter norvig說(shuō)的是:
          就更一般意義上的編程而言,在Google和其他地方,我認(rèn)為語(yǔ)言的選擇并不如其他方面的選擇那么重要:如果你有了正確的總體架構(gòu)、正確的程序員團(tuán)隊(duì)、正確的開發(fā)過(guò)程(能夠快速開發(fā)、持續(xù)改善),那么很多語(yǔ)言都能勝任;但如果以上的東西你沒有,那無(wú)論選擇什么語(yǔ)言,你都會(huì)陷入一團(tuán)糟。

             這句話的意思很明顯,在google或者其他什么地方,編程語(yǔ)言的選擇,比之正確的架構(gòu),正確的團(tuán)隊(duì)以及正確的開發(fā)過(guò)程,對(duì)最終任務(wù)的完成影響不是那么大。但并非所謂"編程語(yǔ)言的選擇不重要“,這種斷章取義的題目除了吸引眼球外,沒有任何益處。
              很多編程語(yǔ)言都可以勝任你要完成的編程任務(wù),你完全可以用C去寫CGI,用匯編去寫消息中間件,只要你有正確的架構(gòu),正確的團(tuán)隊(duì)和開發(fā)過(guò)程,你應(yīng)該總能完成任務(wù)。但是選擇適當(dāng)?shù)木幊陶Z(yǔ)言可以讓你事半功倍,更少的代碼,更高的開發(fā)效率。從ROR以及動(dòng)態(tài)語(yǔ)言的流行來(lái)看,選擇編程語(yǔ)言,真的很重要。

              除非你的規(guī)模達(dá)到google的程度,性能意味著美元,一秒的優(yōu)化意味著成千甚至上億的dollar的時(shí)候,也許你可以說(shuō)下編程語(yǔ)言的選擇不重要。

              最后,我還想鄙視下分享這篇文章的大爺們,你們真的看了文章嗎?還只是沖著這標(biāo)題,急急忙忙地獻(xiàn)寶式地分享了?咱們淡定點(diǎn)行不?

          posted @ 2011-08-06 23:51 dennis 閱讀(6008) | 評(píng)論 (10)編輯 收藏

          轉(zhuǎn)自 http://hjiang.net/archives/484

          Clojure-CN要組織周期性的線下技術(shù)交流活動(dòng)了。如果你熱愛程序設(shè)計(jì)的相關(guān)技術(shù),并且住在長(zhǎng)三角一帶,歡迎來(lái)參與活動(dòng)。只要填一下這個(gè)調(diào)查表就可以:
           http://www.diaochapai.com/survey548296

          更多:
              關(guān)注我的blog的朋友應(yīng)該都知道我這一年都一直在關(guān)注clojure這門語(yǔ)言,后來(lái)還搞了個(gè)cn-clojure的google grouphjiang的公司在使用clojure做商業(yè)項(xiàng)目,他們公司可能是國(guó)內(nèi)唯一在使用clojure的商業(yè)團(tuán)體,他上周跟我提起想搞這么個(gè)活動(dòng),促進(jìn)對(duì)clojure學(xué)習(xí)和使用的交流,并且不局限在clojure語(yǔ)言本身。今年其實(shí)給自己一個(gè)目標(biāo)也是去嘗試推動(dòng)一些事情,我對(duì)clojure純粹是技術(shù)上的興趣,未來(lái)也不排除去找一份專職寫clojure的工作,如果你或者他(她)對(duì)clojure語(yǔ)言(或者函數(shù)式語(yǔ)言)感興趣,歡迎來(lái)參加這次聚會(huì),填寫下這個(gè)調(diào)查表: http://www.diaochapai.com/survey548296。

              我們?cè)谡{(diào)查完成后統(tǒng)計(jì)下大家的興趣點(diǎn)和地理分布,最后決定在哪里舉辦,以及確定talk列表和聚會(huì)形式等。

              我還申請(qǐng)了一個(gè)域名 http://cnlojure.org,在github上建了個(gè)page,這件事的進(jìn)展會(huì)放到這個(gè)網(wǎng)頁(yè)上。

          posted @ 2011-07-27 16:19 dennis 閱讀(3772) | 評(píng)論 (1)編輯 收藏


              格式化源碼是很常見的需求,emacs有個(gè)indent-region函數(shù)用于格式化選定的代碼,前提是你處在某個(gè)非text mode下,如c-mode或者java-mode之類。如果要格式化整個(gè)文件,你需要先選定整個(gè)文件(C-x-h),然后調(diào)用indent-region(或者 C-M-\ )。兩個(gè)命令總是麻煩,我們可以定義個(gè)函數(shù)搞定這一切,并綁定在一個(gè)特定鍵上,實(shí)現(xiàn)一鍵格式化:

          ;;格式化整個(gè)文件函數(shù)
          (defun indent
          -whole ()
            (interactive)
            (indent
          -region (point-min) (point-max))
            (message 
          "format successfully"))
          ;;綁定到F7鍵
          (global
          -set-key [f7] 'indent-whole)

              將這段代碼添加到你的emacs配置文件(~/.emacs),重啟emacs,以后格式化源碼都可以用F7一鍵搞定。

          posted @ 2011-07-26 11:24 dennis 閱讀(9504) | 評(píng)論 (4)編輯 收藏


          1.選定宿主語(yǔ)言,最好選用元編程能力強(qiáng)悍的語(yǔ)言作為宿主語(yǔ)言。
          2.確定DSL的樣子,讓腦袋空白,不去考慮任何實(shí)現(xiàn)問(wèn)題,純粹思考你想要實(shí)現(xiàn)的dsl是什么樣子
          3.用你想要的DSL寫一個(gè)最基本的例子,只包括最基本的功能
          4.開始實(shí)現(xiàn)DSL,盡快讓你的DSL例子以dirty and quick的方式跑起來(lái)。
          5.寫更多DSL的例子,慢慢包括你想要的所有功能,并一一實(shí)現(xiàn),在這個(gè)過(guò)程中你可能改變DSL的樣子,原來(lái)模糊的東西漸漸清楚。
          6.大功告成,review你的代碼并添加自動(dòng)化測(cè)試,將代碼中dirty和bad smell的部分一一剔除。
          7.讓你的DSL接受實(shí)際應(yīng)用的考驗(yàn)吧。

          posted @ 2011-07-25 19:30 dennis 閱讀(3529) | 評(píng)論 (1)編輯 收藏

          update: Allow passing command line arguments to task now.

          1.What is clojure-control?

              The idea came from node-control.
              Define clusters and tasks for system administration or code deployment, then execute them on one or many remote machines.
              Clojure-control depends only on OpenSSH and clojure on the local control machine.Remote machines simply need a standard sshd daemon.

          2.Quick example

          Get the current date from the two machines listed in the 'mycluster'  config with a single command:

               (ns samples
               (:use [control.core :only [task cluster scp ssh begin]]))
               ;;define clusters
               (cluster :mycluster
                           :clients [
                                         { :host 
          "a.domain.com" :user "alogin"}
                                         { :host 
          "b.domain.com" :user "blogin"}
                                       ])
               ;;define tasks
               (task :date 
          "Get date"
                        []
                        (ssh 
          "date"))
              ;;start running
              (begin)

              If saved in a file named "controls.clj",run with   

          java -cp clojure.jar:clojure-contrib.jar:control-0.1-SNAPSHOT.jar clojure.main controls.clj mycluster date

              Each machine execute "date" command ,and the output form the remote machine is printed to the console.Exmaple console output

           

              Performing mycluster
              Performing date 
          for a.domain.com
              a.domain.com:ssh: date
              a.domain.com:stdout: Sun Jul 
          24 19:14:09 CST 2011
              a.domain.com:exit: 
          0
              Performing date 
          for b.domain.com
              b.domain.com:ssh: date
              b.domain.com:stdout: Sun Jul 
          24 19:14:09 CST 2011
              b.domain.com:exit: 
          0

              Each line of output is labeled with the address of the machine the command was executed on. The actual command sent and the user used to send it is displayed. stdout and stderr output of the remote process is identified as well as the final exit code of the local ssh command.


          3.How to scp files?
              Let's define a new task named deploy

            (task :deploy "scp files to remote machines"
                  []
                  (scp (
          "release1.tar.gz" "release2.tar.gz""/home/alogin/"))

              Then it will copy release1.tar.gz and release2.tar.gz to remote machine's /home/alogin directory.

          4.Where is it?

              It's on github,https://github.com/killme2008/clojure-control

              Any suggestion or bug reports welcomed.

          posted @ 2011-07-24 21:48 dennis 閱讀(3170) | 評(píng)論 (0)編輯 收藏

          僅列出標(biāo)題
          共56頁(yè): 上一頁(yè) 1 2 3 4 5 6 7 8 9 下一頁(yè) Last 
          主站蜘蛛池模板: 买车| 苏尼特右旗| 峡江县| 阿拉善右旗| 额济纳旗| 松滋市| 广饶县| 惠安县| 伊川县| 屯门区| 长宁区| 江口县| 米泉市| 枝江市| 乡城县| 杭州市| 邳州市| 东海县| 东阳市| 内丘县| 扶沟县| 泉州市| 龙口市| 张家川| 泽库县| 玉溪市| 安平县| 河津市| 公安县| 葵青区| 成武县| 清涧县| 高邮市| 祁东县| 金溪县| 康马县| 平顺县| 巴彦淖尔市| 新竹市| 苗栗县| 原平市|