2012年6月4日 随笔档案 -凯发k8网页登录

生活、程序、未来
   :: 凯发k8网页登录首页 ::  ::  :: 聚合  :: 管理

2012年6月4日


很久没有更新博客,没想到更新是搬迁公告。这个博客累计的访问量突破百万,是我建立的时候完全没有想过的事情。博客对我来说更多是记录、记忆的地方,我时常因为想不起某个东西,来翻自己的博客,查找旧知,发现新知。阅读很多人的博客,也是我跟踪、学习新知的主要方式。虽然微博兴起,不过博客作为更系统性的记录的地方,不会过时。

非常感谢blogjava提供这么优秀的平台。只是我今年给自己的一个目标是建立自己的博客,因此现在要搬迁,加上其实现在也写的少,其实搬迁不搬迁,意义也不大了。算是一个通告,有兴趣的可以订阅我的新博客,没兴趣的请自行略过,谢谢大家。

新博客地址:
rss地址:

新博客的第一篇记忆是《》,从现在开始,这个博客将不再发布任何新的文章,已有的也不会删除,部分可能会导到我的知识库上去。

最后,祝福blogjava越办越好。

posted @ 2012-12-10 01:24 dennis 阅读(11764) | 评论 (6)编辑 收藏

it's my weekend project——node-shorten: url shortener just like t.cn,goo.gl etc.

is is written in ,using  for mvc framework,and using mysql for storage and redis for caching.

a demo online:

the project is at

feel free to modify and use it.have fun.

posted @ 2012-11-25 20:31 dennis| 编辑 收藏

很久没写博客,一是工作忙,二是没有太多的事情可说。

最近在公司大佬的支持下,建立了一个clojure语言中文方面的博客和问答网站,欢迎任何对clojure这门基于jvm之上的函数式语言感兴趣的童鞋贡献原创文章或者资料,申请帐号请看。

博客地址:  
问答网站:  

欢迎转发和注册使用,谢谢。

邮件列表仍然使用google group:

posted @ 2012-09-25 12:51 dennis 阅读(12023) | 评论 (4)编辑 收藏

home: 

a clojure library designed to render velocity template for ring in clojure.

usage

adds dependency in leiningen project.clj:

  [ring.velocity "0.1.0-snapshot"] 

create a directory named templates in your project directory to keep all velocity templates.

create a template templates/test.vm:

  hello,$name,your age is $age. 

use ring.velocity in your namespace:

  (use '[ring.velocity.core :only [render]]) 

use render function to render template with vars:

  (render "test.vm" :name "dennis" :age 29) 

the test.vm will be interpreted equals to:

  hello,dennis,your age is 29. 

use ring.velocity in :

  (defroutes app-routes      
(get "/" [] (render "test.vm" :name "dennis" :age 29))
(route/not-found "not found"))

use ring.velocity in :

  (use '[ring.util.response])   
(response (render "test.vm" :name "dennis" :age 29))

custom velocity properties,just put a file named ring-velocity.properties to your classpath or resource paths.the default velocity properties is in .

license

凯发天生赢家一触即发官网 copyright © 2012 dennis zhuang[killme2008@gmail.com]

distributed under the eclipse public license, the same as clojure.

home: 

posted @ 2012-07-18 00:07 dennis 阅读(9420) | 评论 (0)编辑 收藏


    clojure的一大优点就是跟java语言的完美配合,clojure和java之间可以相互调用,clojure可以天然地使用java平台上的丰富资源。在clojure里调用一个类的方法很简单,利用dot操作符:

user=> (.substring "hello" 3)
"lo"
user=> (.substring "hello" 0 3)
"hel"

    上面的例子是在clojure里调用string的substring方法做字符串截取。clojure虽然是一门弱类型的语言,但是它的lisp reader还是能识别大多数常见的类型,比如这里hello是一个字符串就可以识别出来,3是一个整数也可以,通过这些类型信息可以找到最匹配的substring方法,在生成字节码的时候避免使用反射,而是直接调用substring方法(invokevirtual指令)。

    但是当你在函数里调用类方法的时候,情况就变了,例如,定义substr函数:
(defn substr [s begin end] (.substring s begin end))

    我们打开*warn-on-reflection*选项,当有反射的时候告警:

user=> (set! *warn-on-reflection* true)
true
user=> (defn substr [s begin end] (.substring s begin end))
reflection warning, no_source_path:22 - call to substring can't be resolved.
#'user/substr
   
    问题出现了,由于函数substr里没有任何关于参数s的类型信息,为了调用s的substring方法,必须使用反射来调用,clojure编译器也警告我们调用substring没办法解析,只能通过反射调用。众所周知,反射调用是个相对昂贵的操作(对比于普通的方法调用有)。这一切都是因为clojure本身是弱类型的语言,对参数或者返回值你不需要声明类型而直接使用,clojure会自动处理类型的转换和调用。ps.在里启用反射警告很简单,在project.clj里设置:

;; emit warnings on all reflection calls.
  :warn-on-reflection true
   
过多的反射调用会影响效率,有没有办法避免这种情况呢?有的,clojure提供了type hint机制,允许我们帮助编译器来生成更高效的字节码。所谓type hint就是给参数或者返回值添加一个提示:hi,clojure编译器,这是xxx类型,我想调用它的yyy方法,请生成最高效的调用代码,谢谢合作:
user=> (defn substr [^string s begin end] (.substring s begin end))
#'user/substr
     
    这次没有警告,^string就是参数s的type hint,提示clojure编译器说s的类型是字符串,那么clojure编译器会从java.lang.string类里查找名称为substring并且接收两个参数的方法,并利用invokevirtual指令直接调用此方法,避免了反射调用。除了target对象(这里的s)可以添加type hint,方法参数和返回值也可以添加type hint:
user=> (defn ^{:tag string} substr [^string s ^integer begin ^integer end] (.substring s begin end))
#'user/substr
    
    返回值添加type hint是利用tag元数据,提示substr的返回类型是string,其他函数在使用substr的时候可以利用这个类型信息来避免反射;而参数的type hint跟target object的type hint一样以^开头加上类型,例如这里begin和end都提示说是integer类型。

    问题1,什么时候应该为参数添加type hint呢?我的观点是,在任何为target object添加type hint的地方,都应该相应地为参数添加type hint,除非你事先不知道参数的类型。为什么呢?因为clojure查找类方法的顺序是这样:

1.从string类里查找出所有参数个数为2并且名称为substring方法
2.遍历第一步里查找出来的method,如果你有设置参数的type hint,则
查找最匹配参数类型的method;否则,如果第一步查找出来的method就一个,直接使用这个method,相反就认为没有找到对应的method。
3.如果第二步没有找到method,使用反射调用;否则根据该method元信息生成调用字节码。

   因此,如果substring方法的两个参数版本刚好就一个,方法参数有没有type hint都没有关系(有了错误的type hint反而促使反射的发生),我们都会找到这个唯一的方法;但是如果目标方法的有多个重载方法并且参数相同,而只是参数类型不同(java里是允许方法的参数类型重载的,clojure只允许函数的参数个数重载),那么如果没有方法参数的type hint,clojure编译器仍然无法找到合适的调用方法,而只能通过反射。
   
   看一个例子,定义get-bytes方法调用string.getbytes:

user=> (defn get-bytes [s charset] (.getbytes s charset))
reflection warning, no_source_path:26 - call to getbytes can't be resolved.
#'user/get-bytes
user=> (defn get-bytes [^string s charset] (.getbytes s charset))
reflection warning, no_source_path:27 - call to getbytes can't be resolved.
#'user/get-bytes

    第一次定义,s和charset都没有设置type hint,有反射警告;第二次,s设置了type hint,但是还是有反射警告。原因就在于string.getbytes有两个重载方法,参数个数都是一个,但是接收不同的参数类型,一个是string的charset名称,一个charset对象。如果我们明确地知道这里charset是字符串,那么还可以为charset添加type hint:
user=> (defn get-bytes [^string s ^string charset] (.getbytes s charset))
#'user/get-bytes
   
    这次才真正的没有警告了。总结:在设置type hint的时候,不要只考虑被调用的target object,也要考虑调用的方法参数。

    问题2:什么时候应该添加tag元数据呢?理论上,在任何你明确知道返回类型的地方都应该添加tag,但是这不是教条,如果一个偶尔被调用的方法是无需这样做的。这一点只对写库的童鞋要特别注意。

    type hint的原理在上文已经大概描述了下,具体到clojure源码级别,请参考clojure.lang.compiler.instancemethodexpr类的构造函数和emit方法。最后,附送是否使用type hint生成substr函数的字节码之间的差异对比:
未使用type hint 使用type hint

  // access flags 1

  public invoke(ljava/lang/object;ljava/lang/object;ljava/lang/object;)ljava/lang/object;

   l0

    linenumber 14 l0

   l1

    linenumber 14 l1

    aload 1

    aconst_null

    astore 1

    ldc "substring"

    iconst_2

    anewarray java/lang/object

    dup

    iconst_0

    aload 2

    aconst_null

    astore 2

    aastore

    dup

    iconst_1

    aload 3

    aconst_null

    astore 3

    aastore

    invokestatic clojure/lang/reflector.invokeinstancemethod (ljava/lang/object;ljava/lang/string;[ljava/lang/object;)ljava/lang/object;

   l2

    localvariable this ljava/lang/object; l0 l2 0

    localvariable s ljava/lang/object; l0 l2 1

    localvariable begin ljava/lang/object; l0 l2 2

    localvariable end ljava/lang/object; l0 l2 3

    areturn

    maxstack = 0

    maxlocals = 0

public invoke(ljava/lang/object;ljava/lang/object;ljava/lang/object;)ljava/lang/object;

   l0

    linenumber 15 l0

   l1

    linenumber 15 l1

    aload 1

    aconst_null

    astore 1

    checkcast java/lang/string

    aload 2

    aconst_null

    astore 2

    checkcast java/lang/number

    invokestatic clojure/lang/rt.intcast (ljava/lang/object;)i

    aload 3

    aconst_null

    astore 3

    checkcast java/lang/number

    invokestatic clojure/lang/rt.intcast (ljava/lang/object;)i

    invokevirtual java/lang/string.substring (ii)ljava/lang/string;

   l2

    localvariable this ljava/lang/object; l0 l2 0

    localvariable s ljava/lang/object; l0 l2 1

    localvariable begin ljava/lang/object; l0 l2 2

    localvariable end ljava/lang/object; l0 l2 3

    areturn

    maxstack = 0

    maxlocals = 0


    
    对比很明显,没有使用type hint,调用clojure.lang.reflector的invokeinstancemethod方法,使用反射调用(具体见clojure.lang.reflector.java),而使用了type hint之后,则直接使用invokevirtual指令(其他方法可能是invokestatic或者invokeinterface等指令)调用该方法,避免了反射。
      

    参考:

posted @ 2012-07-10 20:37 dennis 阅读(11864) | 评论 (1)编辑 收藏


    是淘宝的聚石写的一个非常优秀的java进程运行时诊断和调试工具,如果你接触过btrace,那么housemd也许你应该尝试下,它比btrace更易用,不需要写脚本,类似strace的方式attach到jvm进程做跟踪调试。

    基本的安装和使用请看这篇文档《》,恕不重复。以下内容都假设你正确安装了housemd。

    本文主要介绍下怎么用housemd诊断跟踪clojure进程。clojure的java实现也是跑在jvm里,当然也可以用housemd。

    我们以一个简单的例子开始,假设我们有如下clojure代码:
(loop [x 1]
  (thread/sleep 1000)
  (prn x)
  (recur (inc x)))

    这段很简单,只是间隔一秒不断地打印递增的数字x。我们准备用housemd跟踪这个程序的运行,首先运行这个程序,你可以用lein,也可以直接java命令运行:
java -cp clojure.jar clojure.main test.clj

    运行时不断地在控制台打印数字,通过jps或者ps查询到该进程的id,假设为pid,使用housemd连接到该进程:
housemd 
    顺利进入housemd的交互控制台,通过help命令可以查询支持的命令:

housemd> help

quit      terminate the process.
help      display this infomation.
trace     display or output infomation of method invocaton.
loaded    display loaded classes information.

    要用housemd调试clojure,你需要对clojure的实现有一点点了解,有兴趣可以看过去的一篇blog《clojure hacking guide》,简单来说,clojure的编译器会将clojure代码编译成java类并运行。对于jvm来说,clojure生成的类,跟java编译器生成类没有什么不同。
    具体到上面的clojure代码,会生成一个名为user$eval1的类,user是默认的namespace,而eval1是clojure编译器自动生成的一个标示类名,通过loaded命令查询类的加载情况:
housemd> loaded user$eval1 -h
user$eval1 -> null
    - clojure.lang.dynamicclassloader@1d25d06e
        - clojure.lang.dynamicclassloader@1d96f4b5
            - sun.misc.launcher$appclassloader@a6eb38a
                - sun.misc.launcher$extclassloader@69cd2e5f

    通过-h选项打印了加载user$eval1的类加载器的层次关系,因为user$eval1是动态生成的(clojure启动过程中),因此它不在任何一个class或者jar文件中。除了查询user namespace的类之外,你还可以查询clojure.core,clojure.lang,clojure.java等任何被加载进来的类,例如查询clojure.core.prn的类,在clojure里这是一个函数,在jvm看来这只是一个类:
housemd> loaded -h core$prn
clojure.core$prn -> /volumes/hdd/users/apple/clojure/clojure.jar
    - sun.misc.launcher$appclassloader@a6eb38a
        - sun.misc.launcher$extclassloader@69cd2e5f
   注意,不需要完整的namespace——clojure.core,直接core$prn即可。其他也是类似。小技巧:如果你实在不知道clojure编译器生成的类名,你可以利用jvm自带的jmap命令来查询。

   接下来,我们尝试用trace命令跟踪方法的运行,例如例子中的clojure代码用到了loop和recur两个sepcial form,我们跟踪下loop:
housemd> trace -t 5 core$loop
info : probe class clojure.core$loop
core$loop.doinvoke(object, object, object, object)    sun.misc.launcher$appclassloader@a6eb38a            0            -ms    null
core$loop.getrequiredarity()                          sun.misc.launcher$appclassloader@a6eb38a            0            -ms    null

core$loop.doinvoke(object, object, object, object)    sun.misc.launcher$appclassloader@a6eb38a            0            -ms    null
core$loop.getrequiredarity()                          sun.misc.launcher$appclassloader@a6eb38a            0            -ms    null

core$loop.doinvoke(object, object, object, object)    sun.misc.launcher$appclassloader@a6eb38a            0            -ms    null
core$loop.getrequiredarity()                          sun.misc.launcher$appclassloader@a6eb38a            0            -ms    null

core$loop.doinvoke(object, object, object, object)    sun.misc.launcher$appclassloader@a6eb38a            0            -ms    null
core$loop.getrequiredarity()                          sun.misc.launcher$appclassloader@a6eb38a            0            -ms    null

core$loop.doinvoke(object, object, object, object)    sun.misc.launcher$appclassloader@a6eb38a            0            -ms    null
core$loop.getrequiredarity()                          sun.misc.launcher$appclassloader@a6eb38a            0            -ms    null

info : ended by timeout
info : reset class clojure.core$loop

    在5秒内,clojure.core$loop类有两个方法各被调用了5次,doinvoke是实际的调用,而getrequiredarity用来查询loop所需要的参数个数。trace还可以跟踪到具体的方法,例如我们跟踪prn函数的调用情况:
housemd> trace -t 5 core$prn.doinvoke
info : probe class clojure.core$prn
core$prn.doinvoke(object)    sun.misc.launcher$appclassloader@a6eb38a            1            1ms    clojure.core$prn@3e4ac866

core$prn.doinvoke(object)    sun.misc.launcher$appclassloader@a6eb38a            2           <1ms    clojure.core$prn@3e4ac866

core$prn.doinvoke(object)    sun.misc.launcher$appclassloader@a6eb38a            3           <1ms    clojure.core$prn@3e4ac866

core$prn.doinvoke(object)    sun.misc.launcher$appclassloader@a6eb38a            4           <1ms    clojure.core$prn@3e4ac866

core$prn.doinvoke(object)    sun.misc.launcher$appclassloader@a6eb38a            5           <1ms    clojure.core$prn@3e4ac866

info : ended by timeout
info : reset class clojure.core$prn
  
   trace打印了方法的调用次数(5秒内)和每次调用的时间(毫秒级别),以及调用的target object。小技巧:没有可变参数的函数生成类最终调用的是invoke方法(参数个数可能重载),有可变参数的函数调用的是doinvoke方法。

   trace命令还支持打印调用堆栈到文件,例如:
trace -t 5 -d -s  core$prn.doinvoke

   利用-s和-d命令会将详细的调用信息输出到临时目录,临时目录的路径可以通过trace help命令查询到,在我的机器上是/tmp/trace/@host目录下。调用堆栈的输出类似:
example$square.invoke(long) call by thread [main]
    example$eval9.invoke(test.clj:11)
    clojure.lang.compiler.eval(compiler.java:6465)
    clojure.lang.compiler.load(compiler.java:6902)
    clojure.lang.compiler.loadfile(compiler.java:6863)
    clojure.main$load_script.invoke(main.clj:282)
    clojure.main$script_opt.invoke(main.clj:342)
    clojure.main$main.doinvoke(main.clj:426)
    clojure.lang.restfn.invoke(restfn.java:421)
    clojure.lang.var.invoke(var.java:405)
    clojure.lang.afn.applytohelper(afn.java:163)
    clojure.lang.var.applyto(var.java:518)
    clojure.main.main(main.java:37)

   上面这个简单的例子展示了使用housemd跟踪诊断clojure进程的方法。

   自定义ns和函数的调试与此类似,假设我们有下面的clojure代码:
(ns example)
(defn square [x]
  (* x x))

(loop [x 1]
  (thread/sleep 1000)
  (square x)
  (recur (inc x)))
 
   ns为example,自定义函数square并定期循环调用。使用housemd诊断这段代码:
loaded -h example$square     #查询square的加载情况
trace -t 10 -d -s example$square.invoke  #跟踪10秒内square的调用情况

posted @ 2012-06-15 02:52 dennis 阅读(12077) | 评论 (2)编辑 收藏

我们在维护的淘宝开源消息中间件的的,今天发布了1.4.2版本,主要做了如下改进:

1.支持发送和订阅分离,可以细粒度地控制broker或者某个topic是否接收消息和接受订阅。服务端添加新选项acceptpublish和acceptsubscribe。

2.更友好地关闭broker,梳理关闭流程并通过jmx调用方法关闭替代原来简单的kill。

3.更新到0.2版本,可以通过pip安装:  pip install metaq

4.发布ruby语言客户端 0.1版本。

5.其他小改进:升级gecko到1.1.1版本,升级quartz到2.1.4版本,添加集成测试工程和内部重构等。

6.新文档

简介:
下载:

文档:

posted @ 2012-06-04 10:03 dennis 阅读(11182) | 评论 (1)编辑 收藏

网站地图