庄周梦蝶

生活、程序、未来
   :: 首页 ::  ::  :: 聚合  :: 管理

    Fel是最近javaeye比较火的关键词,这是由网友lotusyu开发的一个高性能的EL,从作者给出的数据来看,性能非常优异,跟前段时间温少开源的Simple EL有的一拼。首先要说,这是个好现象,国内的开源项目越来越多,可以看出开发者的水平是越来越高了,比如我最近还看到有人开源的类似kestel的轻量级MQ——fqueue也非常不错,有兴趣可以看下我的分析《fqueue初步分析》。

    进入正文,本文是尝试分析下Fel的实现原理,以及优缺点和aviator——我自己开源的EL之间的简单比较。

    Fel的实现原理跟Simple EL是类似,都是使用template生成中间代码——也就是普通的java代码,然后利用javac编译成class,最后运行,当然,这个过程都是动 态的。JDK6已经引入了编译API,在此之前的版本可以调用sun的类来编译,因为javac其实就是用java实现的。回到Fel里 面,FelCompiler15就是用 com.sun.tools.javac.Main来编译,而FelCompiler16用标准的javax.tools.JavaCompiler来编译的。

    文法和语法解释这块是使用antlr这个parse generator生成的,这块不多说,有兴趣可以看下antlr,整体一个运行的过程是这样:

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

    这个思路我在实现aviator之前就想过,但是后来考虑到API需要用的sun独有的类,而且要求classpath必须有tools.jar这个依赖包,就放弃了这个思路,还是采用ASM生成字节码的方式。题外,velocity的优化可以采用这个思路,我们有这么一个项目是这么做的,也准备开源了。

 

    看看Fel生成的中间代码,例如a+b这样的一个简单的表达式,假设我一开始不知道a和b的类型,编译是这样:

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

    我稍微改了下FEL的源码,让它打印中间生成的java代码,a+b生成的中间结果为:

    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));  
        }  
    } 

     可见,FEL对表达式解析和解释后,利用template生成这么一个普通的java类,而a和b都从context中获取并转化为Object类型,这里没有做任何判断就直接认为a和b是要做字符串相加,然后拼接字符串并返回。

 

     问题出来了,因为没有在编译的时候传入context(我们这里是null),FEL会将a和b的类型默认都为java.lang.Object,a+b解释为字符串拼接。但是运行的时候,我完全可以传入a和b都为数字,那么结果就非常诡异了:

     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的结果,作为字符串拼接就是13.14,而不是我们想要的4.14。如果将表达式换成a*b,就完全运行不了

    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: 运算符 * 不能应用于 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

 

    这个问题对于Simple EL同样存在,如果没有在编译的时候能确定变量类型,这无法生成正确的中间代码,导致运行时出错,并且有可能造成非常诡异的bug。

 

    这个问题的本质是因为Fel和Simple EL没有自己的类型系统,他们都是直接使用java的类型的系统,并且必须在编译的时候确定变量类型,才能生成高效和正确的代码,我们可以将它们称为“强类型的EL“。

 

    现在让我们在编译的时候给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都强制转为double类型了,做数值相加,结果也正确了:

    4.140000000000001 

    Simple EL我没看过代码,这里猜测它的实现也应该是类似的,也应该有同样的问题。

    相比来说,aviator这是一个弱类型的EL在编译的时候不对变量类型做任何假设,而是在运行时做类型判断和自动转化。过去提过,我给aviator的定位是一个介于EL和script之间的东西,它有自己的类型系统。 例如,3这个数字,在java里可能是long,int,short,byte,而aviator统一为AviatorLong这个类型。为了在这两个类 型之间做适配,就需要做很多的判断和box,unbox操作。这些判断和转化都是运行时进行的,因此aviator没有办法做到Fel这样的高效,但是已 经做到至少跟groovy这样的弱类型脚本语言一个级别,也超过了JXEL这样的纯解释EL,具体可以看这个性能测试

 

   强类型还是弱类型,这是一个选择问题,如果你能在运行前就确定变量的类型,那么使用Fel应该可以达到或者接近于原生java执行的效率,但是失去了灵活性;如果你无法确定变量类型,则只能采用弱类型的EL。

 

   EL涌现的越来越多,这个现象有点类似消息中间件领域,越来越多面向特定领域的轻量级MQ的出现,而不是原来那种大而笨重的通用MQ大行其道,一方面是互 联网应用的发展,需求不是通用系统能够满足的,另一方面我认为也是开发者素质的提高,大家都能造适合自己的轮子。从EL这方面来说,我也认为会有越来越多 特定于领域的,优点和缺点一样鲜明的EL出现,它们包含设计者自己的目标和口味,选择很多,就看取舍。

posted @ 2011-09-17 12:52 dennis 阅读(9900) | 评论 (5)编辑 收藏


    fqueue是国产的一个类似memcacheq,kestrel这样的支持memcached协议的轻量级开源MQ。它的项目主页:
http://code.google.com/p/fqueue/downloads/list,介绍和特点都可以看主页,我就不废话了。

    今天老大提到, co了源码看了下,写个初步分析报告。

    首先是它的存储层,主要是一个FQueue这么一个抽象队列,内部实现是FSQueue,也就是基于文件的FIFO队列。这个队列是多个文件组成的。每个文件默认大小在150M,超过即切换一个新文件来写。读的时候如果读到尾部,则查找下一个文件进行读取。数据文件名以idb为后缀,并且从编号1开始递增,除了数据文件外,每个队列还有个db为后缀的索引文件,记录当前写和读的数据文件编号和偏移量。目录结构大概是这样:
    --fqueue
        --fqueuedata_1.idb
        --fqueuedata_2.idb
        --……
        --icqueue.db

    文件的存储比较有特色,采用MappedByteBuffer做文件读写,MappedByteBuffer是java nio引入的文件内存映射方案,读写性能极高,但是也有一定的问题,比如说内存占用,以及数据刷入设备的不确定性和关闭问题。在fqueue中,每隔10毫秒会强制force一次buffer,将修改过的数据刷入设备。对于关闭问题,则采用那个技巧,示例代码:
/**
     * 关闭索引文件
     
*/
    
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还有一个问题是map的代价比较高,可能在切换文件的时候fqueue会有一定程度的阻塞现象。

    存储的性能,我在我的机器测试了下,似乎没有作者宣称的那么高,我的机器是5400转的普通SATA盘,写入1K数据的平均QPS在8000左右。我估计fqueue的性能跟磁盘有很大关系,如果使用15000转的SAS盘应该能有很大改观。

    网络层直接使用了jmemcached的实现,jmemcached是一个java实现的memcached,通常用于单元测试之类。看情况fqueue也支持memcached的二进制协议了。网络框架使用了netty3,这些就不多说了。自己看都明白。额外提一下,作者做的单元测试使用了xmemcached,咔咔,广而告之。

    总体来说fqueue是一个整体上很清爽和轻量级的MQ实现,适合一些特定的场景,至于性能,我们下周准备做个压测,到时候再谈吧。

posted @ 2011-09-16 20:10 dennis 阅读(9202) | 评论 (7)编辑 收藏

    开源的java memcached client—— xmemcached发布1.3.4版本,主要改进如下:

 

1、修复一个相对严重的bug,在解析二进制协议时如果遇到从服务端返回的错误信息,会导致连接异常断开;如果你没有使用binary协议,不会遇到此问题。建议使用xmemcached并且使用二进制协议的朋友升级到此版本

2、允许XMemcachedClientFactoryBean配置opTimeout选项。

3、添加RoundRobinMemcachedSessionLocator,轮询的连接选择器,仅用于kestrel或者memcacheq集群,这些应用都不要求同一个key要保存在固定的服务器上,而仅是作为集群分担负载。

4、KetamaMemcachedSessionLocator添加额外选项,允许配置是否兼容 nginx-upstream-consistent,这个是网友 wolfg1969贡献的patch。如果要使得xmc的一致性哈希算法兼容nginx-upstream-consistent,只要设置cwNginxUpstreamConsistent为true即可,示范代码:

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

5、修复bug,包括issue 132 , issue 142 , issue 133 , issue 139 , issue 142 , issue 145 ,issue 150等。

 

如果你使用maven,只要简单升级版本即可: 

  <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 阅读(3267) | 评论 (3)编辑 收藏


    这篇blog迟到了很久,本来是想写另一个跟网络相关bug的查找过程,偷偷懒,写下最近印象比较深刻的bug。这个bug是我的同事水寒最终定位到的。
    前几个月同事报告称有一个线上MQ集群会同一时间抛出ArrayIndexOutOfBoundsException这个异常,也就是数组越界。查看源码,除去一些无关紧要的细节大概是这样子:
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);
}

   }

    很显然,这里的本意是实现一个轮询的连接选择器,返回一个选中的连接。使用AtomicInteger递增并对链表大小取模,返回结果索引位置的连接。异常抛出的位置就是我代码中标红的位置。

    显然,这里有两种可能,一种情况下是说在执行那一行代码的时候,connList的大小缩小了(也就是说连接可能被其他线程移出),那么导致取模的结果越界。另一种可能是取模的结果本身确实超过了列表范围。

    第一种情况是完全可能的,因为服务器的连接可能随时断开或者重连,但是这种情况相对非常少见,因此我们这里并没有对这个选择过程做同步,主要是从性能的角度出发,偶尔的失败可以接受。很遗憾的是,我被我的思维惯性误导了,从来没有怀疑过第二种情况,总是认为是不是真的连接恰巧断开导致这个异常,但是却无法解释这个异常发生后就一直错误下去,无法自行恢复。
    为什么说思维惯性误导呢?这里的问题其实是负数取模的问题,对一个负数进行取模,结果会是正数还是负数?答案是结果因语言而异。
    我很早以前在使用Ruby的时候做过测试,负数取模结果为正数,例如在irb里尝试下:
>> -1000%3
=> 2
>> -2001%4
=> 3

    这个印象持续至今,在clojure里结果也是这样子:
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

    这三种语言的结果完全一致,结果都为正数。这个惯性思维延续到java却不成立了,可惜我根本没做测试,让我们试下:
   public static void main(final String[] args) {
        System.out.println(
-1000 % 3);
        System.out.println(
-2001 % 4);
    }

打印结果为:
-1
-1

    果然,在java里负数取模的结果为负数,而不是我习惯性地认为是正数。因此最终的定位到的原因就是sets这个变量递增超过Integer.MAX_VALUE后越界变成负数了,取模的结果为负数,导致抛出数组越界的异常,这也解释了为什么同一个集群都在同一时间出问题,因为这个集群内的机器启动时间相邻并且调用这个方法次数相对平均。修正问题很简单,加个Math.abs就好。

    Update:加个abs是不够的,因为Math.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.

    也就是说对Integer.MIN_VALUE做abs结果仍然是负数。尽管在这个场景中失败一次可以接受,但是最好的办法还是回复中steven提到的抵消符号位的做法:
(sets.incrementAndGet() & 0x7FFFFFFF% size
   
    这个问题更详细的讨论后来我找到这篇博客,作者讨论几种语言和计算器的这个问题的结果,给出了一些结论。不过我觉的这个结论可能也不是那么可靠,特别是对c/c++来说,很大程度上应该还是依赖于实现,最可靠的办法还是强制结果为正。

    这个bug的几个教训:
1、首先是第一次出现的时候没有引起足够重视,重启解决问题后没有深究。有句玩笑话:99%的程序问题都可以通过重启解决。但是事实上问题仍然存在,该发生的终究还会发生。不管你信不信,它就是发生了,这是一个奇迹。
2、注意大脑的思维惯性,经验主义和教条主义都不可取。最近在读一本好书《暗时间》,大脑误导我们的手段可是多种多样。
3、最后就是这个负数取模的结果因语言而异,不要依赖于特定实现。
   

posted @ 2011-09-02 00:02 dennis 阅读(4114) | 评论 (5)编辑 收藏

    没有耐心看经过的请直接拉到末尾看slide列表。

    这个聚会是由江宏首先提议的,我参与协助。目的是让长三角地区对clojure语言感兴趣,或者正在使用的朋友当面认识和交流一下。会议的组织过程一波三折,首先是会议地点本来定在了上海google办公室,但是后来google那边又说不让过去,我再联系了原来淘宝网的同事火狐,经过他的努力和帮助,最终将地点确定在了上海大众点评。要感谢大众点评网和火狐的帮助,不然这次活动估计就黄了。会议的日期选定也比较偶然,跟七夕撞在了一天,并且8月6号这天说台风“梅花”要在江浙一带登陆,上海要刮多少级多少级的大风,加上我和杭州的几个朋友过去要坐高铁,那心里就七上八下了,搞不好就要被“掩埋”了。我们还开玩笑说最好买火车中段车厢的票为妙。

    8月6日一早,和同在淘宝的杨冬,加上两位做ROR的朋友一起出发,天气没有想象的糟糕,高铁一个小时就到了,转地铁到大众点评网大概也才中午12点左右。打了电话给火狐,一起吃了饭然后就直奔大众点评网。大众点评网的前台大厅装修也是非常熟悉的橙色,很意外周6有很多人,后来才知道是在搞招聘会和培训。这时候,江宏他们也从昆山赶到了,火狐帮我们定的会议室很大,足够容纳20号人左右。陆续有人达到,到约定的1点的时候,我记的是来了大概11还是12个人,还有几个朋友在路上,因此我们决定推迟到1点半再开始。最终来的人估计有15个以上,估计报名的都来了。

    1点半正式开始,首先是我来讲《clojure概览》这个topic,主要是一个clojure语言的介绍。这个是我上周开始准备的,在去年《clojure的魅力》的基础上做了删减和增加,听取江宏的意见增加了示例和引子。上周也在我们的团队讲过一次。轻车熟路,也为了给后面的topic留出时间,我讲的比较快,大概40分钟就结束了。

    接下来是孙宁(sunng87)讲《clojure开发的生命周期管理》,我对clojure的周边工具并不熟悉,趁机更好地了解了很多 clojure开发过程中用到的工具和资料,推荐对clojure开发感兴趣的朋友看下。尝试了下clooj,比我预期的要好,遗憾的是还没有语法高亮,推荐初学clojure的朋友可以尝试下这个轻量级的IDE。目前最好的clojure IDE还是idea里的La Clojure插件。最后孙宁顺便广告了下lein-control插件,这是孙宁构建的一个clojure-control的lein插件,他还贡献了一个类似python里fabric的clojure DSL实现,让clojure-control更易用。

    接下来是江宏介绍他们开发trakrapp.com这个纯clojure实现的网站中使用的技术,以及遇到的问题和经验。这个网站基于compojure这个框架实现的,前端采用backbone.js,后端是MongoDB和postgresql,可以说都是非常“新潮”的技术。他在谈遇到问题的时候,提到clojure的stack trace又长又丑,这一点深有体会,clojure的异常堆栈包含了java和clojure的,整个调用链相对较长,非常不利于问题的排查,不知道后续clojure会不会对这一点做出改进。

    接下来是林晴介绍他们一个用scala实现的类似domino的企业OA系统,不过他这个例子给我的感觉更多是发挥了mongodb的schema free的特点,没有体现出使用scala的好处来。我对scala的观点一直很明确,scala想做JVM上的c++,从个人角度不喜欢这种多范式的语言,并且语法不符合我的胃口,特别是类型系统这块特别复杂,我怕我在写scala的时候还要参考一本厚厚的reference,这不是我想要的。而clojure的核心就非常小,相对符合我的期望。

    作为东道主的火狐介绍了大众点评网的新架构以及他们从.net往java迁移的经验,他们的新架构也是做服务化和中心化,对于.net和java平台来说,迁移更多是从人力成本和一些其他因素决定的,当然,迁移最重要的还是要有公司高层的全力支持,特别重要的一点是如何让老员工也参与这个过程。因为老员工对现有系统和业务最熟悉,将他们排除在外闭门造车是注定要失败的。

    最后是同样来自昆山文石的吴哲介绍如何在半天内实现一个HTML 5的游戏,他介绍的processing.js非常有趣,processing本身是一门编程语言,有人将它移植到了js上,可以直接在支持html5上浏览器展现,效果相当cool。巧合的是我在回去后的第二天去书店的时候,竟然在某个角落看到《processing互动编程艺术》这本书,买了下来准备了解下。做数据图形化的同学可以关注下。

    总体来讲,这次聚会的效果超过我的预期,在超强台风的阴影下和七夕爱情的感召下还有这么多人赶过来,作为组织者之一非常感动。并且topic讲座也让我学习了一些东西,最重要的是当面认识了一些网上交流过的朋友,给我印象深刻的是看起来非常老成的孙宁,完全不像个85后。还有个印象深刻的细节是现场的5,6台mbp,这里面还是因为有同学是在搞ROR的因素。

    最后,给下slide的链接如下:

1,我的《clojure概览》,源码在github上。
2,孙宁的《Clojure开发的生命周期管理》,lein-controlclojure-control
3,江宏的《Clojure web development》,他们开发的网站
4,吴哲的《How to build a html5 game in half a day
5,火狐的《大众点评网新架构
6,cn-clojure主页
 

posted @ 2011-08-09 14:09 dennis 阅读(7535) | 评论 (3)编辑 收藏

    最近看了篇在google reader里分享非常多的文章,我表示很无语,文章在这里,题目是《Peter Norvig:编程语言的选择并不重要》。简单来讲这文章就是鼓吹python的,然后举了很多例子说python描述算法比Lisp容易。这个无需多说,图灵模型本来就比lambda演算更适合描述算法。

    我想说的是,文中明明提了,Peter norvig说的是:
就更一般意义上的编程而言,在Google和其他地方,我认为语言的选择并不如其他方面的选择那么重要:如果你有了正确的总体架构、正确的程序员团队、正确的开发过程(能够快速开发、持续改善),那么很多语言都能胜任;但如果以上的东西你没有,那无论选择什么语言,你都会陷入一团糟。

   这句话的意思很明显,在google或者其他什么地方,编程语言的选择,比之正确的架构,正确的团队以及正确的开发过程,对最终任务的完成影响不是那么大。但并非所谓"编程语言的选择不重要“,这种断章取义的题目除了吸引眼球外,没有任何益处。
    很多编程语言都可以胜任你要完成的编程任务,你完全可以用C去写CGI,用汇编去写消息中间件,只要你有正确的架构,正确的团队和开发过程,你应该总能完成任务。但是选择适当的编程语言可以让你事半功倍,更少的代码,更高的开发效率。从ROR以及动态语言的流行来看,选择编程语言,真的很重要。

    除非你的规模达到google的程度,性能意味着美元,一秒的优化意味着成千甚至上亿的dollar的时候,也许你可以说下编程语言的选择不重要。

    最后,我还想鄙视下分享这篇文章的大爷们,你们真的看了文章吗?还只是冲着这标题,急急忙忙地献宝式地分享了?咱们淡定点行不?

posted @ 2011-08-06 23:51 dennis 阅读(5989) | 评论 (10)编辑 收藏

转自 http://hjiang.net/archives/484

Clojure-CN要组织周期性的线下技术交流活动了。如果你热爱程序设计的相关技术,并且住在长三角一带,欢迎来参与活动。只要填一下这个调查表就可以:
 http://www.diaochapai.com/survey548296

更多:
    关注我的blog的朋友应该都知道我这一年都一直在关注clojure这门语言,后来还搞了个cn-clojure的google grouphjiang的公司在使用clojure做商业项目,他们公司可能是国内唯一在使用clojure的商业团体,他上周跟我提起想搞这么个活动,促进对clojure学习和使用的交流,并且不局限在clojure语言本身。今年其实给自己一个目标也是去尝试推动一些事情,我对clojure纯粹是技术上的兴趣,未来也不排除去找一份专职写clojure的工作,如果你或者他(她)对clojure语言(或者函数式语言)感兴趣,欢迎来参加这次聚会,填写下这个调查表: http://www.diaochapai.com/survey548296

    我们在调查完成后统计下大家的兴趣点和地理分布,最后决定在哪里举办,以及确定talk列表和聚会形式等。

    我还申请了一个域名 http://cnlojure.org,在github上建了个page,这件事的进展会放到这个网页上。

posted @ 2011-07-27 16:19 dennis 阅读(3761) | 评论 (1)编辑 收藏


    格式化源码是很常见的需求,emacs有个indent-region函数用于格式化选定的代码,前提是你处在某个非text mode下,如c-mode或者java-mode之类。如果要格式化整个文件,你需要先选定整个文件(C-x-h),然后调用indent-region(或者 C-M-\ )。两个命令总是麻烦,我们可以定义个函数搞定这一切,并绑定在一个特定键上,实现一键格式化:

;;格式化整个文件函数
(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 阅读(9485) | 评论 (4)编辑 收藏


1.选定宿主语言,最好选用元编程能力强悍的语言作为宿主语言。
2.确定DSL的样子,让脑袋空白,不去考虑任何实现问题,纯粹思考你想要实现的dsl是什么样子
3.用你想要的DSL写一个最基本的例子,只包括最基本的功能
4.开始实现DSL,尽快让你的DSL例子以dirty and quick的方式跑起来。
5.写更多DSL的例子,慢慢包括你想要的所有功能,并一一实现,在这个过程中你可能改变DSL的样子,原来模糊的东西渐渐清楚。
6.大功告成,review你的代码并添加自动化测试,将代码中dirty和bad smell的部分一一剔除。
7.让你的DSL接受实际应用的考验吧。

posted @ 2011-07-25 19:30 dennis 阅读(3508) | 评论 (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 阅读(3135) | 评论 (0)编辑 收藏

仅列出标题
共56页: 上一页 1 2 3 4 5 6 7 8 9 下一页 Last