HouseMD 是淘宝的聚石写的一个非怼U的Javaq程q行时诊断和调试工具Q如果你接触qbtraceQ那么HouseMD也许你应该尝试下Q它比btrace更易用,不需要写脚本Q类似strace的方式attach到jvmq程做跟t调试?br />
基本的安装和使用Lq篇文?a >UserGuide》,恕不重复。以下内定w假设你正安装了housemd?br />
本文主要介绍下怎么用housemd诊断跟踪clojureq程。Clojure的java实现也是跑在JVM里,当然也可以用housemd?br />
我们以一个简单的例子开始,假设我们有如下clojure代码Q?br />
(loop [x 1]
(Thread/sleep 1000)
(prn x)
(recur (inc x)))
q段很简单,只是间隔一U不断地打印递增的数字x。我们准备用housemd跟踪q个E序的运行,首先q行q个E序Q你可以用leinQ也可以直接java命oq行Q?br />
java -cp clojure.jar clojure.main test.clj
q行时不断地在控制台打印数字Q通过jps或者ps查询到该q程的idQ假设ؓpidQ用housemdq接到该q程Q?br />
housemd <pid>
利q入housemd的交互控制台Q通过help命o可以查询支持的命令:
housemd> help
quit terminate the process.
help display this infomation.
trace display or output infomation of method invocaton.
loaded display loaded classes information.
要用housemd调试clojureQ你需要对clojure的实现有一点点了解Q有兴趣可以看过ȝ一blog?a href="http://www.aygfsteel.com/killme2008/archive/2010/07/11/325775.html">clojure hacking guide》,单来_clojure的编译器会将clojure代码~译成javacdƈq行。对于JVM来说Qclojure生成的类Q跟java~译器生成类没有什么不同?br />
具体C面的clojure代码Q会生成一个名?strong>user$eval1的类Quser是默认的namespaceQ而eval1是clojure~译器自动生成的一个标C类名,通过loaded 命o查询cȝ加蝲情况Q?br />
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的类加蝲器的层次关系Q因为user$eval1是动态生成的Qclojure启动q程中)Q因此它不在M一个class或者jar文g中。除了查询user namespace的类之外Q你q可以查询clojure.core,clojure.lang,clojure.java{Q何被加蝲q来的类Q例如查询clojure.core.prn的类,在clojure里这是一个函敎ͼ在jvm看来q只是一个类Q?br />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
注意Q不需要完整的namespace——clojure.coreQ直接core$prn卛_。其他也是类伹{?strong>技巧:如果你实在不知道clojure~译器生成的cdQ你可以利用jvm自带的jmap命o来查询?/strong> 接下来,我们试用trace命o跟踪Ҏ的运行,例如例子中的clojure代码用到了loop和recur两个sepcial formQ我们跟t下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
?U内Qclojure.core$loopcL两个Ҏ各被调用?ơ,doInvoke是实际的调用Q而getRequiredArity用来查询loop所需要的参数个数。traceq可以跟t到具体的方法,例如我们跟踪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打印了方法的调用ơ数Q?U内Q和每次调用的时_毫秒U别Q,以及调用的target object。小技巧:没有可变参数的函数生成类最l调用的是invokeҎQ参C数可能重载)Q有可变参数的函数调用的是doInvokeҎ?br /> trace命oq支持打印调用堆栈到文gQ例如:trace -t 5 -d -s core$prn.doInvoke
利用-s?d命o会将详细的调用信息输出到临时目录Q时目录的路径可以通过trace help命o查询刎ͼ在我的机器上?tmp/trace/<pid>@host目录下。调用堆栈的输出cMQ?br />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)
上面q个单的例子展示了用housemd跟踪诊断clojureq程的方法?br /> 自定义ns和函数的调试与此cMQ假设我们有下面的clojure代码Q?br />(ns example) (defn square [x] (* x x)) (loop [x 1] (Thread/sleep 1000) (square x) (recur (inc x)))
ns为exampleQ自定义函数squareq定期@环调用。用housemd诊断q段代码Q?br />loaded -h example$square #查询square的加载情?br />trace -t 10 -d -s example$square.invoke #跟踪10U内square的调用情?/div>
]]>
分布式消息中间gMetaq发布1.4.3 http://www.aygfsteel.com/killme2008/archive/2012/06/04/379895.htmldennis dennis Mon, 04 Jun 2012 02:03:00 GMT http://www.aygfsteel.com/killme2008/archive/2012/06/04/379895.html http://www.aygfsteel.com/killme2008/comments/379895.html http://www.aygfsteel.com/killme2008/archive/2012/06/04/379895.html#Feedback 1 http://www.aygfsteel.com/killme2008/comments/commentRss/379895.html http://www.aygfsteel.com/killme2008/services/trackbacks/379895.html 我们在维护的淘宝开源消息中间g?a style="color: #006699; ">metaq?a style="color: #006699; ">github分支Q今天发布了1.4.2版本Q主要做了如下改q: 1.支持发送和订阅分离Q可以细_度地控制Broker或者某个Topic是否接收消息和接受订阅。服务端d新选项acceptPublish和acceptSubscribe?br /> 2.更友好地关闭BrokerQ梳理关闭流Eƈ通过JMX调用Ҏ关闭替代原来单的kill?br /> 3.更新python客户?/a>?.2版本Q可以通过pip安装: pip install metaq 4.发布ruby语言客户?a style="color: #006699; ">meta-ruby 0.1版本?br /> 5.其他改q:升gecko?.1.1版本Q升Uquartz?.1.4版本Q添加集成测试工E和内部重构{?br /> 6.新文?a >《用log4j扩展发送消息?/a> 介:https://github.com/killme2008/Metamorphosis/wiki/介绍 下蝲Q?a style="color: #006699; ">https://github.com/killme2008/Metamorphosis/downloads文Q?a style="color: #006699; ">https://github.com/killme2008/Metamorphosis/wiki
]]> 如何熟悉一个开源项目? http://www.aygfsteel.com/killme2008/archive/2012/05/22/378885.htmldennis dennis Tue, 22 May 2012 15:12:00 GMT http://www.aygfsteel.com/killme2008/archive/2012/05/22/378885.html http://www.aygfsteel.com/killme2008/comments/378885.html http://www.aygfsteel.com/killme2008/archive/2012/05/22/378885.html#Feedback 9 http://www.aygfsteel.com/killme2008/comments/commentRss/378885.html http://www.aygfsteel.com/killme2008/services/trackbacks/378885.html 你有个Q务,需要用到某个开源项?或者老大交代你一个事情,让你M解某个东ѝ怎么下手呢?如何开始呢Q我的习惯是q样Q?br /> 1.首先Q查扑֒阅读该项目的博客和资料,通过google你能扑ֈ某个目大体介绍的博客,快速阅M下就能对目的目的、功能、基本用有个大概的了解?br /> 2.阅读目的文,重点xcMGetting started、Example 之类的文,从中学习如何下蝲、安装、甚臛_本用该目所需要的知识?br /> 3.如果该项目有提供现成的example工程Q首先尝试按照开始文的介绍q行exampleQ如果运行顺利,那么恭喜你顺利开了个好头;如果遇到问题Q首先尝试在目?strong>FAQ{文档里查找{案Q再ơ,可以问题(例如异常信息Q当成关键词L索,查找相关的解军_法,你遇CQ别Z般也会遇刎ͼ热心的朋友会记录下解决的q程;最后,可以问题提交到目的邮件列表,请大家帮你看看?strong>在没有成功运行example之前Q不要尝试修改example?br /> 4.q行了第一个example之后Q尝试根据你的理解和需要修改exampleQ测试高U功能等?br /> 5.在了解基本用后Q需要开始深入的了解该项目。例如项目的配置理、高U功能以及最佛_c通常一个运作良好的目会提供一份从到q用户指南Q你q不需要从头到N读这份指南,Ҏ旉和兴,特别是你自己d的需要,重点阅读部分章节q做W记Q推荐evernoteQ?br /> 6.如果旉允许Q尝试从源码构徏该项目。通常开源项目都会提供一份构建指南,指导你如何搭Z个用于开发、调试和构徏的环境。尝试构Z个版本?br /> 7.如果旉允许q且有兴,可以试阅读源码Q?br />Q?Q阅L码之前,查看该项目是否提供架构和设计文Q阅读这些文可以了解该目的大体设计和l构Q读源码的时候不会无从下手?br />Q?Q阅L码之前,一定要能构建ƈq行该项目,有个直观感受?br />Q?Q阅L码的W一步是抓主qԌ试理清一ơ正常运行的代码调用路径Q这可以通过debug来观察运行时的变量和行ؓ。修Ҏ码加入日志和打印可以帮助你更好的理解源码?br />Q?Q适当d来帮助你理解源码Q在理清d后,可以整个流E画成一张流E图或者标准的UML图,帮助记忆和下一步的阅读?br />Q?Q挑选感兴趣?#8220;枝干”代码来阅读,比如你对|络通讯感兴,阅ȝl层的代码,深入到实现细节,如它用了什么库Q采用了什么设计模式,Z么这样做{。如果可以,debugl节代码?br />Q?Q阅L码的时候,重视单元试Q尝试去q行单元试Q基本上一个好的单元测试会该代码的功能和边界描述清楚?br />Q?Q在熟悉源码后,发现有可以改q的地方Q有_֊、有意愿可以向该目的开发者提出改q的意见或者issueQ甚臛_他修复和实现Q参与该目的发展?br /> 8.通常在阅L档和源码之后Q你能对该项目有比较深入的了解了Q但是该目所在领域,你可能还x索相关的目和资料,看看有没有其他的更好的项目或者解x案。在q度和深度之间权衡?br /> 以上是我个h的一些习惯,我自׃q没有完全按照这个来Q但是按照这个顺序,基本上能让你比较高效地学习和使用某个开源项目?img src ="http://www.aygfsteel.com/killme2008/aggbug/378885.html" width = "1" height = "1" /> ]]> Emacs + Clojure配置的几个Tip http://www.aygfsteel.com/killme2008/archive/2012/05/19/378535.htmldennis dennis Fri, 18 May 2012 16:57:00 GMT http://www.aygfsteel.com/killme2008/archive/2012/05/19/378535.html http://www.aygfsteel.com/killme2008/comments/378535.html http://www.aygfsteel.com/killme2008/archive/2012/05/19/378535.html#Feedback 11 http://www.aygfsteel.com/killme2008/comments/commentRss/378535.html http://www.aygfsteel.com/killme2008/services/trackbacks/378535.html
很久没更新博客了Q在北京工作Q忙ƈ且充实。目前来_Clojure最好的开发编辑器应该是Emacs + Slime 的组合,利用swank-clojure q个目Q加上clojure-modeQ可以完地q行slime。编译、运行、蟩转、文和引用查看甚至debug 都可以搞定。具体配|恕不重复,看swank-clojure的文档即可自己安装v来,或者这?a >中文博客Q?a >windows上配|?/a>?br />
分n几个TipQ也期待大家分n你们的用心得?br />
首先是自动在打开clj后缀文g的时候启动执行clojure-jack-in与slimeq接Q可以在emacs配置里加上个callbackQ?br />
(eval-after-load "clojure-mode" '(progn (require 'slime) (require 'clojure-mode) (unless (slime-connected-p) (save-excursion (clojure-jack-in)))))
q样在打开clj为后~的文件的时候,自动启动clojure-mode执行clojure-jack-in函数q且q接slime?br /> clj后缀的文件自动关联到clojure-mode:
(setq auto-mode-alist (cons '("\\.clj$" . clojure-mode) auto-mode-alist))
通常来说如果你是利用marmalade 安装的,会自动关联的?br /> 另外Q启动自动匹配括受字W串引号{的paredit模式一定要启动Q?br />(defun paredit-mode-enable () (paredit-mode 1)) (add-hook 'clojure-mode-hook 'paredit-mode-enable) (add-hook 'clojure-test-mode-hook 'paredit-mode-enable)
在用clojure-mode或者clojure-test-mode的时候自动启用paredit模式Q括号再也不是问题。括号匹配提CZ般是开启的Q如果没有,强制开启:;; 昄括号匚w (show-paren-mode t) (setq show-paren-style 'parentheses)
slime更多配置Q启用IO重定向(多线EIO输出都定向到SLIME replQ以及设|通讯字符~码{:(eval-after-load "slime" '(progn (slime-setup '(slime-repl slime-fuzzy)) ;;(setq slime-truncate-lines t) (setq swank:*globally-redirect-io* t) ;; (setq slime-complete-symbol-function ' slime-fuzzy-complete-symbol) (setq slime-net-coding-system 'utf-8-unix)))
l心的朋友可能注意到我注释了slime-fuzzy-complete的配|,q是一个支持更好的自动补全功能的SLIME插gQ可以用~写来自动补全)Q可惜在我机器上没有试配置成功Q有兴趣你可以尝试下?br /> 在REPL里支持语法高亮,一定要配置上:(add-hook 'slime-repl-mode-hook (defun clojure-mode-slime-font-lock () (require 'clojure-mode) (let (font-lock-mode) (clojure-mode-font-lock-setup))))
单独在clojure-modeQ在其他mode里这些快捷键不会起作用)里配|快捷键可以q样:(eval-after-load "clojure-mode" '(progn (require 'slime) (require 'clojure-mode) (define-key clojure-mode-map (kbd "M-/") (quote slime-complete-symbol)) (define-key clojure-mode-map (kbd "C-c s") (quote slime-selector)))
例如我这里将M-/作ؓ自动补全的快捷键Q因为meta键在我的Mac机器上设|ؓcommand键,因此自动补全的操作习惯就跟EclipsecM。?strong>slime-selector是一个非常有用的函数Q用来蟩转到slime的一pdbufferQ因此我l定了C-c s快捷键?br /> 额外一提,在Mac osx下,command作ؓmeta?;;; I prefer cmd key for meta (setq mac-option-key-is-meta nil mac-command-key-is-meta t mac-command-modifier 'meta mac-option-modifier 'none)
最后,期待大家不吝分n你的心得?br /> ]]> clj.monitor : monitoring applications in clojure based on SSH http://www.aygfsteel.com/killme2008/archive/2012/05/12/378018.htmldennis dennis Sat, 12 May 2012 14:38:00 GMT http://www.aygfsteel.com/killme2008/archive/2012/05/12/378018.html http://www.aygfsteel.com/killme2008/comments/378018.html http://www.aygfsteel.com/killme2008/archive/2012/05/12/378018.html#Feedback 5 http://www.aygfsteel.com/killme2008/comments/commentRss/378018.html http://www.aygfsteel.com/killme2008/services/trackbacks/378018.html My weekend project clj.monitor is beta release,it's a clojure DSL for monitoring system and applications based on SSH.
Home:https://github.com/killme2008/clj.monitor
An example:
(ns clj.monitor.example
(:use [clj.monitor.core]
[control.core]
[clj.monitor.tasks]))
;;define a mysql cluster
(defcluster mysql
:clients [{:user "deploy" :host "mysql.app.com"}])
;;define a monitor for mysql cluster
(defmonitor mysql-monitor
:tasks [(ping-mysql "root" "password")
(system-load :5 3)]
:clusters [:mysql])
;;start monitors
(start-monitors
:cron "* 0/5 * * * ?"
:alerts [(mail :from "alert@app.com" :to "yourname@app.com")]
:monitors [mysql-monitor])
API document: http://fnil.net/clj.monitor
It is just a beta release,if you have any questions or find issues ,please let me know,thanks.
]]>