奇迹私服1.04local修改ip地址有什么影响什么

请教高手怎么制作奇迹MU1.02W的单机版!请高手最好提供制作步骤!越详细越好!_百度知道
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。
请教高手怎么制作奇迹MU1.02W的单机版!请高手最好提供制作步骤!越详细越好!
我有更好的答案
补丁里面有小地图图片。5、加入了SEASON4新装备,你在服务端要找个原来的SEASON4的端,修正了装备的MVP属性乱码,兼容官方客户端;kor/item(new).txt覆盖进去。对了还有一个验证码,遨游魔神之铠等等,如果你不喜欢,修正了套装名乱码2,修正了大师等级乱码.rar" target="_blank">http.04Xplus、免费送个登陆器.bmd修改就可以了.cfg修改自己的连接信息,就可以用了.其实关键还是启动的main.exe :如果能正常进行那个1.04X版的奇迹,那么这个补丁就可以不用打了。客户端补丁地址:迅雷专用高速下载
<a href="http://aymk,修正了不能起中文角色名,就是那串乱麻,你自己也要改,然后打我这个补丁,再把我的main,你把data/local/item申明.exe 里面的ip修改就可以进1.04X游戏了。6.com/aymkMU1,修正了上方浮动公告乱码,里面你在logindata,不过很多新装备不知道怎么翻译,我都是加了我自己的名称,譬如遨游王者之杖:)7、这个补丁还有一个比较好的地方就是,修正了打字弹网页,修正了召唤师夜的术和光波术不能用的问题。4、加入了小地图、main.exe 是从韩文的版本拿来的、修正了召唤师装备数据,我是和官方同步,但是效果是以服务器的设置为主,比较纯。3,把那个lang&#47,你从官方下载 圣女降临 客户端:///aymkMU1.04Xplus.rar更新内容:1、100%中文修正
现在SF这么多
你一个人玩单机的有什么意思啊
为您推荐:
其他类似问题
单机版的相关知识
等待您来回答user=&&(.substring&"hello"&3)
user=&&(.substring&"hello"&0&3)
& & 上面的例子是在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)
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源码级别,请参考piler.InstanceMethodExpr类的构造函数和emit方法。最后,附送是否使用type hint生成substr函数的字节码之间的差异对比:
未使用type hint
使用type hint
& // access flags 1
& public invoke(Ljava/lang/OLjava/lang/OLjava/lang/O)Ljava/lang/O
& & LINENUMBER 14 L0
& & LINENUMBER 14 L1
& & ALOAD 1
& & ACONST_NULL
& & ASTORE 1
& & LDC "substring"
& & ICONST_2
& & ANEWARRAY java/lang/Object
& & ICONST_0
& & ALOAD 2
& & ACONST_NULL
& & ASTORE 2
& & AASTORE
& & ICONST_1
& & ALOAD 3
& & ACONST_NULL
& & ASTORE 3
& & AASTORE
& & INVOKESTATIC clojure/lang/Reflector.invokeInstanceMethod (Ljava/lang/OLjava/lang/S[Ljava/lang/O)Ljava/lang/O
& & LOCALVARIABLE this Ljava/lang/O L0 L2 0
& & LOCALVARIABLE s Ljava/lang/O L0 L2 1
& & LOCALVARIABLE begin Ljava/lang/O L0 L2 2
& & LOCALVARIABLE end Ljava/lang/O L0 L2 3
& & ARETURN
& & MAXSTACK = 0
& & MAXLOCALS = 0
public invoke(Ljava/lang/OLjava/lang/OLjava/lang/O)Ljava/lang/O
& & LINENUMBER 15 L0
& & 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/O)I
& & ALOAD 3
& & ACONST_NULL
& & ASTORE 3
& & CHECKCAST java/lang/Number
& & INVOKESTATIC clojure/lang/RT.intCast (Ljava/lang/O)I
& & INVOKEVIRTUAL java/lang/String.substring (II)Ljava/lang/S
& & LOCALVARIABLE this Ljava/lang/O L0 L2 0
& & LOCALVARIABLE s Ljava/lang/O L0 L2 1
& & LOCALVARIABLE begin Ljava/lang/O L0 L2 2
& & LOCALVARIABLE end Ljava/lang/O L0 L2 3
& & ARETURN
& & MAXSTACK = 0
& & MAXLOCALS = 0
& & 对比很明显,没有使用type hint,调用clojure.lang.Reflector的invokeInstanceMethod方法,使用反射调用(具体见clojure.lang.Reflector.java),而使用了type hint之后,则直接使用invokevirtual指令(其他方法可能是invokestatic或者invokeinterface等指令)调用该方法,避免了反射。
& & 参考:
& & 是淘宝的聚石写的一个非常优秀的Java进程运行时诊断和调试工具,如果你接触过btrace,那么HouseMD也许你应该尝试下,它比btrace更易用,不需要写脚本,类似strace的方式attach到jvm进程做跟踪调试。
& & 基本的安装和使用请看这篇文档《》,恕不重复。以下内容都假设你正确安装了housemd。
& & 本文主要介绍下怎么用housemd诊断跟踪clojure进程。Clojure的java实现也是跑在JVM里,当然也可以用housemd。
& & 我们以一个简单的例子开始,假设我们有如下clojure代码:
(loop&[x&1]
&&(Thread/sleep&1000)
&&(recur (inc x)))
& & 这段很简单,只是间隔一秒不断地打印递增的数字x。我们准备用housemd跟踪这个程序的运行,首先运行这个程序,你可以用lein,也可以直接java命令运行:
java&-cp&clojure.jar&clojure.main&test.clj
& & 运行时不断地在控制台打印数字,通过jps或者ps查询到该进程的id,假设为pid,使用housemd连接到该进程:
housemd&&pid&
& & 顺利进入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的编译器会将clojure代码编译成java类并运行。对于JVM来说,clojure生成的类,跟java编译器生成类没有什么不同。
& & 具体到上面的clojure代码,会生成一个名为user$eval1的类,user是默认的namespace,而eval1是clojure编译器自动生成的一个标示类名,通过loaded命令查询类的加载情况:
housemd&&loaded&user$eval1&-h
user$eval1&-&&null
&&&&-&clojure.lang.DynamicClassLoader&#64;1d25d06e
&&&&&&&&-&clojure.lang.DynamicClassLoader&#64;1d96f4b5
&&&&&&&&&&&&-&sun.misc.Launcher$AppClassLoader&#64;a6eb38a
&&&&&&&&&&&&&&&&-&sun.misc.Launcher$ExtClassLoader&#64;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$prnclojure.core$prn&-&&/Volumes/HDD/Users/apple/clojure/clojure.jar&&&&-&sun.misc.Launcher$AppClassLoader&#64;a6eb38a&&&&&&&&-&sun.misc.Launcher$ExtClassLoader&#64;69cd2e5f& &注意,不需要完整的namespace&#8212;&#8212;clojure.core,直接core$prn即可。其他也是类似。小技巧:如果你实在不知道clojure编译器生成的类名,你可以利用jvm自带的jmap命令来查询。& &接下来,我们尝试用trace命令跟踪方法的运行,例如例子中的clojure代码用到了loop和recur两个sepcial form,我们跟踪下loop:housemd&&trace&-t&5&core$loopINFO&:&probe&class&clojure.core$loopcore$loop.doInvoke(Object,&Object,&Object,&Object)&&&&sun.misc.Launcher$AppClassLoader&#64;a6eb38a&&&&&&&&&&&&0&&&&&&&&&&&&-ms&&&&nullcore$loop.getRequiredArity()&&&&&&&&&&&&&&&&&&&&&&&&&&sun.misc.Launcher$AppClassLoader&#64;a6eb38a&&&&&&&&&&&&0&&&&&&&&&&&&-ms&&&&nullcore$loop.doInvoke(Object,&Object,&Object,&Object)&&&&sun.misc.Launcher$AppClassLoader&#64;a6eb38a&&&&&&&&&&&&0&&&&&&&&&&&&-ms&&&&nullcore$loop.getRequiredArity()&&&&&&&&&&&&&&&&&&&&&&&&&&sun.misc.Launcher$AppClassLoader&#64;a6eb38a&&&&&&&&&&&&0&&&&&&&&&&&&-ms&&&&nullcore$loop.doInvoke(Object,&Object,&Object,&Object)&&&&sun.misc.Launcher$AppClassLoader&#64;a6eb38a&&&&&&&&&&&&0&&&&&&&&&&&&-ms&&&&nullcore$loop.getRequiredArity()&&&&&&&&&&&&&&&&&&&&&&&&&&sun.misc.Launcher$AppClassLoader&#64;a6eb38a&&&&&&&&&&&&0&&&&&&&&&&&&-ms&&&&nullcore$loop.doInvoke(Object,&Object,&Object,&Object)&&&&sun.misc.Launcher$AppClassLoader&#64;a6eb38a&&&&&&&&&&&&0&&&&&&&&&&&&-ms&&&&nullcore$loop.getRequiredArity()&&&&&&&&&&&&&&&&&&&&&&&&&&sun.misc.Launcher$AppClassLoader&#64;a6eb38a&&&&&&&&&&&&0&&&&&&&&&&&&-ms&&&&nullcore$loop.doInvoke(Object,&Object,&Object,&Object)&&&&sun.misc.Launcher$AppClassLoader&#64;a6eb38a&&&&&&&&&&&&0&&&&&&&&&&&&-ms&&&&nullcore$loop.getRequiredArity()&&&&&&&&&&&&&&&&&&&&&&&&&&sun.misc.Launcher$AppClassLoader&#64;a6eb38a&&&&&&&&&&&&0&&&&&&&&&&&&-ms&&&&nullINFO&:&Ended&by&timeoutINFO&:&reset&class&clojure.core$loop& & 在5秒内,clojure.core$loop类有两个方法各被调用了5次,doInvoke是实际的调用,而getRequiredArity用来查询loop所需要的参数个数。trace还可以跟踪到具体的方法,例如我们跟踪prn函数的调用情况:housemd&&trace&-t&5&core$prn.doInvokeINFO&:&probe&class&clojure.core$prncore$prn.doInvoke(Object)&&&&sun.misc.Launcher$AppClassLoader&#64;a6eb38a&&&&&&&&&&&&1&&&&&&&&&&&&1ms&&&&clojure.core$prn&#64;3e4ac866core$prn.doInvoke(Object)&&&&sun.misc.Launcher$AppClassLoader&#64;a6eb38a&&&&&&&&&&&&2&&&&&&&&&&&&1ms&&&&clojure.core$prn&#64;3e4ac866core$prn.doInvoke(Object)&&&&sun.misc.Launcher$AppClassLoader&#64;a6eb38a&&&&&&&&&&&&3&&&&&&&&&&&&1ms&&&&clojure.core$prn&#64;3e4ac866core$prn.doInvoke(Object)&&&&sun.misc.Launcher$AppClassLoader&#64;a6eb38a&&&&&&&&&&&&4&&&&&&&&&&&&1ms&&&&clojure.core$prn&#64;3e4ac866core$prn.doInvoke(Object)&&&&sun.misc.Launcher$AppClassLoader&#64;a6eb38a&&&&&&&&&&&&5&&&&&&&&&&&&1ms&&&&clojure.core$prn&#64;3e4ac866INFO&:&Ended&by&timeoutINFO&:&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/&pid&&#64;host目录下。调用堆栈的输出类似:example$square.invoke(Long)&call&by&thread&[main]&&&&example$eval9.invoke(test.clj:11)&&&&piler.eval(Compiler.java:6465)&&&&piler.load(Compiler.java:6902)&&&&piler.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的调用情况
我们在维护的淘宝开源消息中间件的的,今天发布了1.4.2版本,主要做了如下改进:1.支持发送和订阅分离,可以细粒度地控制Broker或者某个Topic是否接收消息和接受订阅。服务端添加新选项acceptPublish和acceptSubscribe。2.更友好地关闭Broker,梳理关闭流程并通过JMX调用方法关闭替代原来简单的kill。3.更新到0.2版本,可以通过pip安装: &pip install metaq4.发布ruby语言客户端&0.1版本。5.其他小改进:升级gecko到1.1.1版本,升级quartz到2.1.4版本,添加集成测试工程和内部重构等。6.新文档简介:下载:文档:
& & 你有个任务,需要用到某个开源项目;或者老大交代你一个事情,让你去了解某个东西。怎么下手呢?如何开始呢?我的习惯是这样:1.首先,查找和阅读该项目的博客和资料,通过google你能找到某个项目大体介绍的博客,快速阅读一下就能对项目的目的、功能、基本使用有个大概的了解。2.阅读项目的文档,重点关注类似Getting started、Example之类的文档,从中学习如何下载、安装、甚至基本使用该项目所需要的知识。3.如果该项目有提供现成的example工程,首先尝试按照开始文档的介绍运行example,如果运行顺利,那么恭喜你顺利开了个好头;如果遇到问题,首先尝试在项目的FAQ等文档里查找答案,再次,可以将问题(例如异常信息)当成关键词去搜索,查找相关的解决办法,你遇到了,别人一般也会遇到,热心的朋友会记录下解决的过程;最后,可以将问题提交到项目的邮件列表,请大家帮你看看。在没有成功运行example之前,不要尝试修改example。4.运行了第一个example之后,尝试根据你的理解和需要修改example,测试高级功能等。5.在了解基本使用后,需要开始深入的了解该项目。例如项目的配置管理、高级功能以及最佳实践。通常一个运作良好的项目会提供一份从浅到深的用户指南,你并不需要从头到尾阅读这份指南,根据时间和兴趣,特别是你自己任务的需要,重点阅读部分章节并做笔记(推荐evernote)。6.如果时间允许,尝试从源码构建该项目。通常开源项目都会提供一份构建指南,指导你如何搭建一个用于开发、调试和构建的环境。尝试构建一个版本。7.如果时间允许并且有兴趣,可以尝试阅读源码:(1)阅读源码之前,查看该项目是否提供架构和设计文档,阅读这些文档可以了解该项目的大体设计和结构,读源码的时候不会无从下手。(2)阅读源码之前,一定要能构建并运行该项目,有个直观感受。(3)阅读源码的第一步是抓主干,尝试理清一次正常运行的代码调用路径,这可以通过debug来观察运行时的变量和行为。修改源码加入日志和打印可以帮助你更好的理解源码。(4)适当画图来帮助你理解源码,在理清主干后,可以将整个流程画成一张流程图或者标准的UML图,帮助记忆和下一步的阅读。(5)挑选感兴趣的&#8220;枝干&#8221;代码来阅读,比如你对网络通讯感兴趣,就阅读网络层的代码,深入到实现细节,如它用了什么库,采用了什么设计模式,为什么这样做等。如果可以,debug细节代码。(6)阅读源码的时候,重视单元测试,尝试去运行单元测试,基本上一个好的单元测试会将该代码的功能和边界描述清楚。(7)在熟悉源码后,发现有可以改进的地方,有精力、有意愿可以向该项目的开发者提出改进的意见或者issue,甚至帮他修复和实现,参与该项目的发展。8.通常在阅读文档和源码之后,你能对该项目有比较深入的了解了,但是该项目所在领域,你可能还想搜索相关的项目和资料,看看有没有其他的更好的项目或者解决方案。在广度和深度之间权衡。& & 以上是我个人的一些习惯,我自己也并没有完全按照这个来,但是按照这个顺序,基本上能让你比较高效地学习和使用某个开源项目。
& & 很久没更新博客了,在北京工作,忙碌并且充实。目前来说,Clojure最好的开发编辑器应该是Emacs + 的组合,利用这个项目,加上clojure-mode,可以完美地运行slime。编译、运行、跳转、文档和引用查看甚至都可以搞定。具体配置恕不重复,看swank-clojure的文档即可自己安装起来,或者这篇,。
& & 分享几个Tip,也期待大家分享你们的使用心得。
& & 首先是自动在打开clj后缀文件的时候启动执行clojure-jack-in与slime连接,可以在emacs配置里加上个callback:
(eval-after-load&"clojure-mode"&&'(progn&&&&&(require&'slime)&&&&&(require&'clojure-mode)&&&&&(unless&(slime-connected-p)&&&&&&&(save-excursion&(clojure-jack-in)))))
& & 这样在打开clj为后缀的文件的时候,将自动启动clojure-mode执行clojure-jack-in函数并且连接slime。& & 将clj后缀的文件自动关联到clojure-mode:
(setq&auto-mode-alist&(cons&'("\\.clj$"&.&clojure-mode)&auto-mode-alist))
& & 通常来说如果你是利用安装的,会自动关联的。& & 另外,启动自动匹配括号、字符串引号等的paredit模式一定要启动:(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模式,括号再也不是问题。括号匹配提示一般是开启的,如果没有,强制开启:;;&&&&显示括号匹配(show-paren-mode&t)(setq&show-paren-style&'parentheses)& & slime更多配置,启用IO重定向(多线程IO输出都定向到SLIME repl)以及设置通讯字符编码等:(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)))& & 细心的朋友可能注意到我注释了slime-fuzzy-complete的配置,这是一个支持更好的自动补全功能的SLIME插件(可以用缩写来自动补全),可惜在我机器上没有尝试配置成功,有兴趣你可以尝试下。& & 在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-mode(在其他mode里这些快捷键不会起作用)里配置快捷键可以这样:(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-/作为自动补全的快捷键,因为meta键在我的Mac机器上设置为command键,因此自动补全的操作习惯就跟Eclipse类似。而slime-selector是一个非常有用的函数,用来跳转到slime的一系列buffer,因此我绑定了C-c s快捷键。& & 额外一提,在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)& & 最后,期待大家不吝分享你的心得。& &&
& & My weekend project clj.monitor is beta release,it's a clojure DSL for monitoring system and applications based on SSH.
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&""}])
;;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&#"&:to&"yourname&#")]
&:monitors&[mysql-monitor])
API document:
It is just a beta release,if you have any questions or find issues ,please let me know,thanks.
& & 在维护的淘宝开源消息中间件的的github分支,今天发布了1.4.2版本,主要做了如下改进:
& & 1.添加了大量的使用和原理文档,参见。& & 2.合并tools和server-wrapper工程,提供统一的脚本来管理Broker,管理Broker的工作变得非常容易,全部工作都可以通过metaServer.sh的脚本来执行。同时提供了bat启动脚本,用于在windows上启动Broker做测试。& & 3.新功能:& &(1)新的客户端API用来获取topic的分区列表& &(2)新的客户端API用来获取Broker的统计信息& &(3)异步复制的Slave可以自动获取Master的配置变更,例如Master在配置文件中新增或者删除了topic并顺利reload热加载成功后,slave可自动复制或者移除变更的topic,无需重启。& &(4)新的统计项目,可以通过'stats config'协议获取Broker的配置文件。& & 4.添加meta-python项目,一个python的客户端,暂时仅支持发送消息功能。& & 5.其他小改进,如统计信息的优化、构建工具的整合等。
& & 更详细的发行日志请看。
& & 下载地址: && & 入门指南: &《》& & 更多文档请看。
& & 我发现很多人没办法高效地解决问题的关键原因是不熟悉工具,不熟悉工具也还罢了,甚至还不知道怎么去找工具,这个问题就大条了。我想列下我能想到的一个Java程序员会用到的常用工具。
一、编码工具
1.IDE:或者,熟悉尽可能多的快捷键,《》
(1) ,在release之前进行一次静态代码检查是必须的
(2) ,关心你的单元测试覆盖率
(3)&&代码风格检查
3.构建和部署工具:或者,现在主流都是maven了吧,,再加上持续集成。代码质量不用愁。
4.版本管理工具: 或者
6.设置你的eclipse或者IDEA,如formatter,以及code template等。代码风格,直接用google的也可以啊。《》
7.掌握一个文本编辑器,Emacs或者VIM,熟悉常用快捷键。这在你需要在线编辑代码,或者编写其他语言代码时候特别有用。《》
二、JDK相关
1.jstat : 观察GC情况,如:
jstat&-gcutil&pid&2000
2.jmap,查看heap情况,如查看存活对象列表:
jmap&-histo:live&pid&|grep&pany&|less&
或者dump内存用来分析:
jmap&-dump:file=test.bin&pid
3.分析dump的堆文件,可以用jhat:
jhat&test.bin
& 分析完成后可以用浏览器查看堆的情况。这个工具的分析结果还比较原始,你还可以用插件进行图形化分析,或者IBM的.
4.jvisualvm和jconsole: JVM自带的性能分析和监控工具,怎么用?
5.jstack:分析线程堆栈,如
jstack&pid&&&thread_dump
& & 查看CPU最高的线程在干什么的方法结合top和jstack:
6.更多JVM工具,参见官方文档:7.学习使用btrace分析java运行时问题。《》
8.GC日志分析工具:、或者
9.性能分析工具,除了自带的jvisualvm外,还可以用商业的。
11.《》,iteye神贴。
三、Linux工具
3.使用替换top。
4.熟悉下甚至p来分析问题。
5.自动化部署脚本,或者自荐下我的。
1.掌握一门脚本语言,或者,高效解决一些需要quick and dirty的任务:比如读写文件、导入导出数据库、网页爬虫等。注意不是,咔咔。
2.使用Linux或者Mac os系统作为你的开发环境。
3.升级你的&#8220;硬件工具&#8221;,双屏大屏显示器、SSD、8G内存甚至更多。4.你懂的:
五、如何查找工具?
1.搜索引擎,google或者baidu,《》
2.万能的stack overflow:
3.虚心问牛人。六、最重要的是&#8943;&#8943;一颗永不停止学习的心。
& & 最近陆陆续续补充了不少的文档,部分是直接从官方文档里摘抄出来,放在了,有兴趣了解甚至使用meta的可以仔细阅读下,一份目录:
& & Developer
:使用metamorphosis作为twitter storm的spout源
: metamorphosis的python语言客户端。目前只支持发送消息功能。
& & 后续还会继续补充。
& & Java世界里有这样的神器,可以让你避免很多&#8220;简单愚蠢&#8221;的bug。同样,Clojure世界里也有相应的替代品,这就是今天要介绍的。不过kibit现在还比较年轻,判断的规则较少,但是已经可以使用起来做clojure代码的静态检查。
项目主页:
1.安装lein插件:
lein&plugin&install&jonase/kibit&0.0.2
2.在项目的根目录运行
lein&kibit
kibit会分析项目里所有clojure源码,每个namespace分别分析,例如我分析的输出:
==&mands&==
==&control.core&==
[186]&Consider&(zero?&(:status&(ssh&host&user&cluster&(str&"test&-e&"&file))))&instead&of&(=&(:status&(ssh&host&user&cluster&(str&"test&-e&"&file)))&0)
==&control.main&==
==&leiningen.control&==
[null]&Consider&Integer/parseInt&instead&of&(fn*&[p1__61444#]&(Integer/parseInt&p1__61444#))
[null]&Consider&Integer/parseInt&instead&of&(fn*&[p1__65254#]&(Integer/parseInt&p1__65254#))
& & 显然,kibit一个一个namespace分析过去,并且按照规则对它认为有问题的地方打印出来,并提出建议。例如这里它建议我用(zero?&(:status&(ssh&host&user&cluster&(str&"test&-e&"&file))))
& & 替换control.core里186行的:&(=&(:status&(ssh&host&user&cluster&(str&"test&-e&"&file)))&0)& & 目前kibit大多数是这类代码风格上的检查,还没有做到类似findbugs那样更丰富的检查,例如NPE异常检查等。此外kibit还提供反射检查,任何有反射调用的地方都给出警告。& & kibit是基于实现的,它的规则都放在了,通过defrules宏来定义检查规则,源码中对算术运算的规则定义:(defrules&rules&&[(+&?x&1)&(inc&?x)]&&[(+&1&?x)&(inc&?x)]&&[(-&?x&1)&(dec&?x)]&&[(*&?x&(*&.&?xs))&(*&?x&.&?xs)]&&[(+&?x&(+&.&?xs))&(+&?x&.&?xs)])& && & 第一个规则,任何对类似(+ 1 x)的代码,都建议替换成(inc x),后面的与此类似。理论上你也可以自定义规则,并提交给官方。总体上说kibit仍然是比不上findbugs的,期待未来发展的更好。
& & 我们经常需要在程序中测量某段代码的性能,或者某个函数的性能,在Java中,我们可能简单地循环调用某个方法多少次,然后利用System.currentTimeMillis()方法测量下时间。在Ruby中,一般都是用做测试,提供了更详细的报告信息。
& & 同样,在Clojure里你可以做这些事情,你仍然可以使用System.currentTimeMillis()来测量运行时间,例如:
user=&&(defn&sum1&[&&args]&(reduce&+&0&args))
#'user/sum1
user=&&(defn&sum2&[&&args]
&&&&&&&&&&&&&(loop&[rt&0
&&&&&&&&&&&&&&&&&&&&args&args]
&&&&&&&&&&&&&&&&&(if&args
&&&&&&&&&&&&&&&&&&&(recur&(+&rt&(first&args))&(next&args))
&&&&&&&&&&&&&&&&&&&rt)))
#'user/sum2
user=&&(defn&bench&[sum&n]
&&&&&&&&&(let&[start&(System/currentTimeMillis)
&&&&&&&&&&&&&&&nums&(range&0&(+&n&1))]
&&&&&&&&&&&(dotimes&[_&n]&(apply&sum&nums))
&&&&&&&&&&&(println&(-&(System/currentTimeMillis)&start))))
user=&&(bench&sum1&10000)
user=&&(bench&sum2&10000)
& & 定义两个求和函数sum1和sum2,一个是利用reduce,一个是自己写loop,然后写了个bench函数循环一定次数执行sum函数并给出执行时间,利用System.currentTimeMillis()方法。显然sum1比sum2快了一倍多。为什么更快?这不是我们的话题,有兴趣可以自己看reduce函数的实现。
& & 除了用System.currentTimeMillis()这样的java方式测量运行时间外,clojure还提供了time宏来包装这一切:
user=&&(doc&time)
-------------------------
clojure.core/time
&&Evaluates&expr&and&prints&the&time&it&took.&&Returns&the&value&of
& & time宏用的不是currentTimeMillis方法,而是JDK5引入的nanoTime方法更精确。重写bench函数:
user=&&(defn&bench&[sum&n]
&&&&&&&&&&&&&(time&(dotimes&[_&n]&(apply&sum&(range&0&(+&n&1))))))
#'user/bench
user=&&(bench&sum1&10000)
"Elapsed&time:&&msecs"
user=&&(bench&sum2&10000)
"Elapsed&time:&&msecs"
& & &尽管精度不一致,仍然可以看出来sum1比sum2快。
& & &这样的测试仍然是比较粗糙的,真正的性能测试需要考虑到JVM JIT、warm up以及gc带来的影响,例如我们可能需要预先执行函数多少次来让JVM&#8220;预热&#8221;这些代码。庆幸的是clojure世界里有一个开源库Criterium帮你自动搞定这一切,它的项目主页也在github上:
& & &首先在你的项目里添加criterium依赖:
:dependencies&[[org.clojure/clojure&"1.3.0"]
&&&&&&&&&&&&&&&&&&&&&&&&[criterium&"0.2.0"]])
& & &接下来引用criterium.core这个ns,因为criterium主要宏也叫bench,因此我们原来的bench函数不能用了,换个名字叫bench-sum:
user=&&(use&'criterium.core)
user=&&(defn&bench-sum&[sum&n]
&&&&&&&&&&&&&&(with-progress-reporting&(bench&(apply&sum&(range&0&(+&1&n)))&:verbose)))
#'user/bench-sum
& & &调用criterium的bench宏执行测试,使用with-progress-reporting宏包装测试代码并汇报测试进展,测试进展会打印在标准输出上。请注意,我这里并没有利用dotimes做循环测试,因为criterium会自己计算应该运行的循环次数,我们并不需要明确指定,测试下结果:
user=&&(bench-sum&sum1&10000)
Cleaning&JVM&allocations&
Warming&up&for&JIT&optimisations&
Estimating&execution&count&
Running&with&sample-count&60&exec-count&1417&
Checking&GC
Cleaning&JVM&allocations&
Finding&outliers&
Bootstrapping&
Checking&outlier&significance
x86_64&Mac&OS&X&10.7.3&4&cpu(s)
Java&HotSpot(TM)&64-Bit&Server&VM&20.4-b02-402
Runtime&arguments:&-pile.path=/Users/apple/programming/avos/logdashboard/test/classes&-Dtest.version=1.0.0-SNAPSHOT&-Dclojure.debug=false
Evaluation&count&&&&&&&&&&&&&:&85020
&&&&&&&&&&&&&Execution&time&mean&:&722.730169&us&&95.0%&CI:&(722.552670&us,&722.957586&us)
&&&&Execution&time&std-deviation&:&1.042966&ms&&95.0%&CI:&(1.034972&ms,&1.054015&ms)
&&&&&&&&&Execution&time&lower&ci&:&692.122089&us&&95.0%&CI:&(692.122089&us,&692.260198&us)
&&&&&&&&&Execution&time&upper&ci&:&768.239944&us&&95.0%&CI:&(768.239944&us,&768.305222&us)
Found&2&outliers&in&60&samples&(3.3333&%)
&&&&low-severe&&&&&2&(3.3333&%)
&Variance&from&outliers&:&25.4066&%&Variance&is&moderately&inflated&by&outliers
user=&&(bench-sum&sum2&10000)
Cleaning&JVM&allocations&
Warming&up&for&JIT&optimisations&
Estimating&execution&count&
Running&with&sample-count&60&exec-count&917&
Checking&GC
Cleaning&JVM&allocations&
Finding&outliers&
Bootstrapping&
Checking&outlier&significance
x86_64&Mac&OS&X&10.7.3&4&cpu(s)
Java&HotSpot(TM)&64-Bit&Server&VM&20.4-b02-402
Runtime&arguments:&-pile.path=/Users/apple/programming/avos/logdashboard/test/classes&-Dtest.version=1.0.0-SNAPSHOT&-Dclojure.debug=false
Evaluation&count&&&&&&&&&&&&&:&55020
&&&&&&&&&&&&&Execution&time&mean&:&1.070884&ms&&95.0%&CI:&(1.070587&ms,&1.071136&ms)
&&&&Execution&time&std-deviation&:&1.057659&ms&&95.0%&CI:&(1.050688&ms,&1.062877&ms)
&&&&&&&&&Execution&time&lower&ci&:&1.024195&ms&&95.0%&CI:&(1.024164&ms,&1.024195&ms)
&&&&&&&&&Execution&time&upper&ci&:&1.145664&ms&&95.0%&CI:&(1.145664&ms,&1.145741&ms)
Found&1&outliers&in&60&samples&(1.6667&%)
&&&&low-severe&&&&&1&(1.6667&%)
&Variance&from&outliers&:&19.0208&%&Variance&is&moderately&inflated&by&outliers
& & 这个报告是不是相当专业?不是搞统计还不一定读的懂。大概解读下,sample-count是取样次数,默认是60次,exec-count是测试的执行次数(不包括前期warm up和JIT在内),CI是可信区间,这里取95%,Execution time mean是平均执行时间,而lower和upper是测试过程中执行时间的最小和最大值。而outliers是这一组测试中的异常值,比如执行sum1测试发现了2组异常结果。从结果来看,sum1的平均执行时间是722微秒,而sum2的平均执行时间是1.07毫秒,因此还是sum1更快一些。
& & 总结下,如果只是在开发过程中做一些小块代码的简单测试,可以直接利用内置的time宏,如果你希望做一次比较标准的性能测试,那么就应该利用这个优秀的开源库。
& & 继续Clojure世界之旅,介绍下我今天的探索成果,使用clojure生成clojure项目的API文档。在java里,我们是利用javadoc生成API文档,各种build工具都提供了集成,例如maven和ant都提供了javadoc插件或者task。在Clojure世界里,同样有一系列工具帮助你从源码中自动化生成API文档。今天主要介绍三个工具。不过我不会介绍怎么在clojure里写doc,具体怎么做请看一些开源项目,或者直接看clojure.core的源码。
& & 首先是,使用相当简单,我们这里都假设你使用作为构建工具,在project.clj里添加codox依赖:
& :dev-dependencies&&&&[[codox&"0.5.0"]]
& & 解下执行lein doc命令即可生成文档,生成的文档放在doc目录,在浏览器里打开index.html即可观察生成的文档。我给生成的codox文档可以看,效果还是不错的。
& & 第二个要介绍的工具是,使用方法类似codox,首先还是添加依赖:
:dev-dependencies&[lein-marginalia&"0.7.0"]
&&&&执行lein deps处理依赖关系,然后执行lein&marg命令即可在docs目录生成文档,与codox不同的是marginalia只生成一个html文件,没有带js和css,但是效果也不错,可以看我给clojure-control生成的。marginalia生成的文档说明和源码左右对照,很利于阅读源码。
& &最后要介绍的就是Clojure.org自己在使用的,如果你喜欢clojure.org上的API文档格式可以采用这个工具。并且autodoc可以跟github pages结合起来,生成完整的项目文档并展示在github上。例如以clojure-control为例来介绍整个过程。首先你需要配置你的github pages,参照这个链接。
& & 第一步,仍然是在project.clj添加依赖:
:dev-dependencies&[[lein-autodoc&"0.9.0"]]
& & & &第二步,在你的.gitignore里忽略autodoc目录:
autodoc/**
& & & &将这些更改提交到github上,接下来在你的项目目录clone一份项目源码到&project&/autodoc目录:
&git&clone&git&#:&user&name&/&project&name&.git&autodoc
& & & &进入autodoc目录,执行下列命令创建一个gh-pages分支:
&$&cd&autodoc
&$&git&symbolic-ref&HEAD&refs/heads/gh-pages
&$&rm&.git/index
&$&git&clean&-fdx
& & &回到项目根目录后,执行lein autodoc命令在autodoc目录生成文档:
lein&autodoc
& & &接下来将生成的文档推送到github分支上:
&$cd autodoc&
&$&git&add&-A
&$&git&commit&-m"Documentation&update"
&$&git&push&origin&gh-pages
& & 等上几分钟,让github渲染你的文档,最终的效果看这个链接&& &&& & autodoc和marginalia都支持maven,具体使用请看他们的文档。
& & 前面一篇博客介绍了我在github上的一个分支,今天下午写了个metaq的python客户端,目前仅支持发送消息功能,不过麻雀虽小,五脏俱全,客户端和zookeeper的交互和连接管理之类都还具备,不出意外,我们会首先用上。第一次正儿八经地写python代码,写的不好的地方请尽管拍砖,多谢。& & 项目叫meta-python,仍然放在github上:& & 使用需要先安装zkpython这个库,具体安装,使用很简单,发送消息:& &&from&metamorphosis&import&Message,MessageProducer,SendResult&&&&p=MessageProducer("topic")&&&&message=Message("topic","message&body")&&&&print&p.send(message)&&&&p.close()& &&MessageProducer就是消息发送者,它的构造函数接受至少一个topic,默认的zk_servers为localhost:2181,可以通过zk_servers参数指定你的zookeeper集群:p=MessageProducer("topic",zk_servers="192.168.1.100:.1.101:2181")更多参数请直接看源码吧。一个本机的性能测试(meta和客户端都跑在我的机器上,机器是Mac MC700,osx 10.7,磁盘没有升级过):from&metamorphosis&import&Message,MessageProducerfrom&time&import&timep=MessageProducer("avos-fetch-tasks")message=Message("avos-fetch-tasks","")start=time()for&i&in&range(0,10000):&&&&sent=p.send(message)&&&&if&not&sent.success:&&&&&&&&print&"send&failed"finish=time()secs=finish-startprint&"duration:%s&seconds"&%&(secs)print&"tps:%s&msgs/second"&%&(10000/secs)p.close()&结果:duration:1.&secondstps:9&msgs/second
& & 开源的memcached Java客户端&#8212;&#8212;发布1.3.6版本。
& & 主要改进如下:&
1. &为MemcachedClientBuilder添加两个新方法用于配置:
public&void&setConnectTimeout(long&connectTimeout);&&
public&void&setSanitizeKeys(boolean&sanitizeKeys);
2. &用于hibernate的XmemcachedClientFactoryd添加了connectTimeout属性,感谢网友&Boli.Jiang的贡献。
3. &添加新的枚举类型&net.rubyeye.pressionMode,用于指定Transcoder的压缩类型,默认是ZIP压缩,可选择GZIP压缩。Transcoder接口添加setCompressionMode方法。
4. &修改心跳规则,原来是在连接空闲的时候发起心跳,现在变成固定每隔5秒发起一次心跳检测连接。
5. &修改默认参数,默认禁用nagle算法,默认将批量get的合并因子下降到50。
6. &修复bug和改进,包括:、、、、、、和。
项目主页:
项目文档:
Maven依赖:
&&dependency&&&
&&&&&groupId&com.googlecode.xmemcached&/groupId&&&
&&&&&artifactId&xmemcached&/artifactId&&&
&&&&&version&1.3.6&/version&&&
&/dependency&&
& & 最后感谢所有提出issue和改进意见的朋友们。
& & 加入已经一个月多一点,很荣幸加入这个充满活力的跨国团队。去了趟北京,跟我的同事们终于当面认识了下,并且顺利举办了cn-clojure的第二次聚会。不过很悲剧的是在北京一周都在生病,哪也没去成,要见的人也没见到,要凑的饭局也没吃到,非常遗憾,希望以后再弥补。不过我暂时因为家庭的因素还在杭州远程办公,沟通都是通过gtalk和google+ hangout,很多东西对我来说都是全新的经历。远程办公有利有弊,其实我还是更喜欢能在一个办公室里工作,交流方便,只要转个椅子就好。& & 正式介绍下我们公司,大boss是youtube创始人查德&#8226;赫利(Chad Hurley)和陈士骏(Steve Chen),我们在中国的boss是前google员工/耶鲁的博士,更多关于团队成员的介绍请看,我的同事们真的很强大,我要学习的地方很多。我们做的产品是从雅虎手上买下来的,不过中国团队运作的是美味书签&#8212;&#8212;。我们刚在3月1号开始做public beta,预计会在4月份的时候正式对外开放注册。我们的团队博客在。& & 我在团队里做的事情还是偏向后端,这一个月来做的事情更偏向运维之类,搞搞solr复制、mysql复制、程序监控之类,将原来只是简单了解过的东西动手做了一遍,能亲手实践的感觉不错。在此过程中要感谢锋爷和刘帅哥的帮助,再次感谢。淘宝的同事们开源了metaq和gecko,我也做了点工作,都在。几个维护的开源项目都没有太大进展,很惭愧,还被人催发新版本,能承诺的是周末发xmemcached的新版本,主要还是修bug。本来要写个clojure世界的系列文章,因为响应寥寥,也不是很有动力写下去。杂七杂八读了几本书,都没读完,这一个月杭州太冷了,雨下个不停,不过终于这一周开始晴天了,希望老天爷别再掉眼泪。& & 收拾收拾心情,整装待发,希望新的一年里能做出点不同的东西。& &&
& & 上周我在淘宝的同事开源了一个消息中间件,放在了上。我从淘蝌蚪的svn上fork了一个github的分支,放在了这里:
&1.主体工程:
&2.示例项目:
&3.Twitter storm的spout项目:
& & 主要做了一些pom文件的简化,发布1.4.0.2版本到maven central仓库,并且写了几个简单的入门文档,提供了一个完整打包可运行的下载,有兴趣的自己看github页面吧。 Wiki文档放在:
& & &客户端Maven依赖包括,可自行选择添加:
&dependency&
&&&&&groupId&com.taobao.metamorphosis&/groupId&
&&&&&artifactId&metamorphosis-client&/artifactId&
&&&&&version&1.4.0.2&/version&
&/dependency&
&dependency&
&&&&&groupId&com.taobao.metamorphosis&/groupId&
&&&&&artifactId&metamorphosis-client-extension&/artifactId&
&&&&&version&1.4.0.2&/version&
&/dependency&
&dependency&
&&&&&groupId&com.taobao.metamorphosis&/groupId&
&&&&&artifactId&storm-metamorphosis-spout&/artifactId&
&&&&&version&1.0.0&/version&
&/dependency&
& & &ps.我开通了新浪微博,有兴趣相互关注下:,你看,偏见是可以改变的。
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:
&& 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:
&&& XML处理也是个常见的编程工作,虽然说在Clojure里你很少使用XML做配置文件,但是跟遗留系统集成或者处理和其他系统通讯,可能都需要处理XML。&&& Clojure的标准库clojure.xml就是用来干这个事情的。一个简单的例子如下,首先我们要解析的是下面这个简单的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方法即可,该方法返回一个clojure.xml/element这个struct-map组成的一棵树:user=&&(use&'[clojure.xml])niluser=&&(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"]}]}]}这是一个嵌套的数据结构,每个节点都是clojure.xml/element结构,element包括:(defstruct&element&:tag&:attrs&:content)&& tag、attrs和content属性,tag就是该节点的标签,attrs是一个属性的map,而content是它的内容或者子节点。element是一个struct map,它也定义了三个方法来分别获取这三个属性:user=&&(def&x&(parse&"test.xml"))#'user/xuser=&&(tag&x):booksuser=&&(attrs&x)niluser=&&(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节点是root node,它的content就是三个book子节点,子节点组织成一个vector,我们可以随意操作:user=&&(tag&(first&(content&x))):bookuser=&&(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做解析的。同样它还有个方法,可以将解析出来的结构还原成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&&&&& 如果你要按照深度优先的顺序遍历xml,可以利用xml-seq将解析出来的树构成一个按照深度优先顺序排列节点的LazySeq,接下来就可以按照seq的方式处理,比如利用for来过滤节点:user=&&(for&[node&(xml-seq&x)&&&&&&&&&&&&&&&&&&:when&(=&:author&(:tag&node))]&&&&&&&&&&&&(first&(:content&node)))("Michael&Fogus&/&Chris&House"&"Stuart&Halloway"&"Luke&Van&der&Hart")&&& 通过:when指定了条件,要求节点的tag是author,这样就可以查找出所有的author节点的content,是不是很方便?就像写英语描述一样。&&& 更进一步,如果你想操作parse解析出来的这棵树,你还可以利用clojure.zip这个标准库,它有xml-zip函数将xml转换成zipper结构,并提供一系列方法来操作这棵树:user=&(def&xz&(xml-zip&x))#'user/xzuser=&&(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,来查找节点的邻近节点,可以通过node来得到节点本身。一切显得那么自然。更进一步,你还可以&#8220;编辑&#8220;这棵树,比如删除The joy of clojure这本书:user=&&&(def&loc-in-new-tree&(remove&(down&xz)))#'user/loc-in-new-treeuser=&&(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更改节点等。因此编辑XML并重新生成,你一般可以利用clojure.zip来更改树,最后利用clojure.xml/emit将更改还原为xml。&&& 生成xml除了emit方法,还有一个contrib库,也就是,这个库的clojure 1.3版本有人维护了一个分支,在。主要方法就是prxml,它可以将clojure的数据结构转换成xml:user=&(prxml&[:p&[:raw!&"&i&here&&&gone&/i&"]])&p&&i&here&&&gone&/i&&/p&&&& 显然,它也可以用于生成HTML。&&& xpath的支持可以使用这个开源库,遗憾的是它目前仅支持clojure 1.2。&&& 转载请注明出处:&&&
&&& 文件读写是日常编程中最经常使用的操作之一。这篇blog将大概介绍下Clojure里对文件操作的常用类库。&&& 首先介绍标准库,这是最经常用的IO库,定义了常见的IO操作。&&& 首先,直接看一个例子,可以熟悉下大多数常用的函数:(ns&io&&(:use&[clojure.java.io]));;file函数,获取一个java.io.File对象(def&f&(file&"a.txt"));;拷贝文件使用copy(copy&f&(file&"b.txt"));;删除文件,使用delete-file(delete-file&f);;更经常使用reader和writer(def&rdr&(reader&"b.txt"&:encoding&"utf-8"))(def&wtr&(writer&"c.txt"&:append&true));;copy可以接受多种类型的参数(copy&rdr&wtr&:buffer-size&4096);;关闭文件(.close&wtr)(.close&rdr)&&& 这个例子基本上说明了大多数常见的操作。但是有些问题需要解释下。&&& 首先,通过file这个函数可以将各种类型的对象转化为java.io.File对象,file可以接受String,URL,URI以及java.io.File本身作为参数,并返回java.io.File。有了File对象,你就可以调用java.io.File类中的各种方法,比如判断文件是否存在:(.exists&(file&"a.txt"))&=&&true&or&false&&& 其次,可以通过delete-file来删除一个文件,它是调用File的delete方法来执行的,但是File.delete会返回一个布尔值告诉你成功还是失败,如果返回false,那么delete-file会抛出IO异常,如果你不想被这个异常打扰,可以让它&#8220;保持安静&#8221;:&&& (delete-file&f&true)&&& 拷贝文件很简单,使用copy搞定,copy也可以很&#8220;宽容&#8221;,也可以接受多种类型的参数并帮你自动转换,input可以是InputStream, Reader, File, byte[] 或者String,而output可以是OutputStream, Writer或者File。是不是很给力?这都是通过Clojure的protocol和defmulti做到的。但是,copy并不帮你处理文件的关闭问题,假设你传入的input是一个File,output也是一个File,copy会自动帮你打开InputStream和OutputStream并建立缓冲区做拷贝,但是它不会帮你关闭这两个流,因此你要小心,如果你经常使用copy,这可能是个内存泄漏的隐患。&&& 更常用的,我们一般都是用reader和writer函数来打开一个BufferedReader和BufferedWriter做读写,同样reader和writer也可以接受多种多样的参数类型,甚至包括Socket也可以。因为writer打开的通常是一个BufferedWriter,所以你如果用它写文件,有时候发现write之后文件还是没有内容,这是因为数据暂时写到了缓冲区里,没有刷入到磁盘,可以明确地调用(.flush wtr)来强制写入;或者在wtr关闭后系统帮你写入。reader和writer还可以传入一些配置项,如:encoding指定读写的字符串编码,writer可以指定是否为append模式等。&&& Clojure并没有提供关闭文件的函数或者宏,你简单地调用close方法即可。clojure.java.io的设计很有原则,它不准备将java.io都封装一遍,而是提供一些最常用方法的简便wrapper供你使用。&&& 刚才提到copy不会帮你关闭打开的文件流,但是我们可以利用with-open这个宏来自动帮你管理打开的流:(with-open&[rdr&(reader&"b.txt")&&&&&&&&&&&&wtr&(writer&"c.txt")]&&(copy&rdr&wtr))&& with-open宏会自动帮你关闭在binding vector里打开的流,你不再需要自己调用close,也不用担心不小心造成内存泄漏。因此我会推荐你尽量用reader和writer结合with-open来做文件操作,而不要使用file函数。file函数应该用在一些判断是否存在,判断文件是否为目录等操作上。&&& 在clojure.core里,还有两个最常用的函数slurp和spit,一个吃,一个吐,也就是slurp读文件,而spit写文件,他们类似Ruby的File里的read和write,用来完整地读或者写文件:&&& (prn&(slurp&"c.txt"))&&& (spit&"c.txt"&"hello&world")&& 用法简单明了,slurp将文件完整地读出并返回字符串作为结果,它还接受:encoding参数来指定字符串编码,你猜的没错,它就是用reader和with-open实现的。spit同样很简单,将content转化为字符串写入文件,也接受:encoding和:append参数。&&& 深度优先遍历目录,可以使用file-seq,返回一个深度优先顺序遍历的目录列表,这是一个LazySeq:(user=&&(file-seq&(java.io.File.&"."))(#&File&.&&#&File&./.gitignore&&#&File&./.lein-deps-sum&&#&File&./b.txt&&#&File&./c.txt&&#&File&./classes& &#8943;&#8943; )&&& 上面的介绍已经足以让你对付大多数需求了。接下来,介绍下几个开源库。首先是这个库,它封装了java.io.File类的大多数方法,让你用起来很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")&#8943;&#8943;&&& 更多介绍请看它的。读写二进制文件也是一个很常见的需求,Clojure有几个DSL库干这个事情,可以很直观地定义二进制格式来encode/decode,比如这个库,看看它的例子: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)&&& 相当直观和给力吧。是一个更强大的DSL库,非常适合做网络通讯的协议处理。这里就不多做介绍了,你可以自己看它的例子和文档。&&& &&& 转载请注明出处:&&&
&&& 单元测试也是一个开发中最常见的需求,在Java里我们用JUnit或者TestNG,在clojure里也内置了单元测试的库。标准库的,以及第三方框架。这里我将主要介绍clojure.test这个标准库,midje是个更加强大的测试框架,广告下,midje的介绍在第二次cn-clojure聚会上将有个Topic,我就不画蛇添足了。通常来说,clojure.test足够让你对付日常的测试。&&& 首先看一个最简单的例子,定义一个函数square来计算平方,然后我们测试这个函数:;;引用clojure.test(ns&example&&(:use&[clojure.test&:only&[deftest&is&run-tests]]));;定义函数(defn&square&[x]&&(*&x&x));;测试函数(deftest&test-square &&(is&(=&4&(square&2)))&&(is&(=&9&(square&-3))));;运行测试(run-tests&'example)&&& 执行输出:Testing&exampleRan&1&tests&containing&2&assertions.0&failures,&0&errors.&&& 这个小例子基本说明了clojure.test的主要功能。首先是断言is,类似JUnit里的assertTrue,用来判断form是否为true,它还可以接受一个额外的msg参数来描述断言:&(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"的平方,这会抛出一个java.lang.ClassCastException,一个运行时异常,并且异常信息为java.lang.String cannot be cast to java.lang.Number。我们可以通过上面的方式来测试这种意外情况。clojure.test还提供了另一个断言are,用来判断多个form:&(testing&"test&zero&or&one"&&&&(are&&&&&(=&0&(square&0))&&&&&(=&1&(square&1))))&&& are接受多个form并判断是否正确。这里还用了testing这个宏来添加一段字符串来描述测试的内容。&&& 其次,我们用deftest宏定义了一个测试用例,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运行就不能简单地传入一个ns,而需要定义一个test-ns-hook指定要跑的测试用例,否则组合的用例如上面的addition和subtraction会运行两次。我们马上谈到。&&& 定义完用例后是运行测试,运行测试使用run-tests,可以指定要跑测试的ns,run-tests接受可变参数个的ns。刚才提到,组合tests的时候会有重复运行的问题,要防止重复运行,可以定义一个test-ns-hook的函数:(defn&test-ns-hook&[]&&(test-square)&&(arithmetic))&&& 这样run-tests就会调用test-ns-hook按照给定的顺序执行指定的用例,避免了重复执行。&&& 在你的测试代码里明确调用run-tests执行测试是一种方式,不过我们在开发中更经常使用的是来管理project,会将src和test分开,将你的测试代码组织在专门的test目录,类似使用maven的时候我们将main和test分开一样。这时候就可以简单地调用:&&& &&& lein&test命令来执行单元测试,而不需要明确地在测试代码里调用run-tests并指定ns。更实用的使用例子可以看一些开源项目的组织。&&& 单元测试里做mock也是比较常见的需求,在clojure里做mock很容易,原来clojure.contrib有个mock库,基本的原理都是利用binding来动态改变被mock对象的功能,但是在clojure 1.3里,binding只能改变标注为dynamic的变量,并且clojure.contrib被废弃,部分被合并到core里面,Allen Rohner编译了一个可以用于clojure 1.3的clojure.contrib,不过需要你自己install到本地仓库,具体看。不过clojure.contrib.mock哪怕使用1.2的编译版本其实也是可以的。&&& clojure.contrib最重要的是expect宏,它类似EasyMock里的expect方法,看一个例子:(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的参数是不是number,times用来指定预期调用的次数,而returns用来返回mock值,是不是很像EasyMock?因为我们这个测试只调用了square一次,所以这个用例将失败:Testing&example"Unexpected&invocation&count.&Function&name:&square&expected:&2&actual:&1"&& 这个例子要在Clojure 1.3里运行,需要将square定义成dynamic:(defn&^:dynamic&square&[x]&&(*&x&x))&& 否则会告诉你没办法绑定square:actual:&java.lang.IllegalStateException:&Can't&dynamically&bind&non-dynamic&var:&example/square&&& 额外提下,还有个轻量级的测试框架可以看一下,类似Ruby Facker的库提供一些常见的模拟数据,如名称地址等。&&& &&&& 转载请注明出处:&&&
&&& Clojure的REPL非常方便,可以随时随地试验你的想法,REPL是read-eval-print-loop的简称。默认clojure.contrib有带一个shell脚本来启动REPL,具体看。你也可以用JLine来增强REPL:
java&-cp&"%CLOJURE_DIR%\jline-VERSION.%CLOJURE_JAR%"&jline.ConsoleRunner&clojure.main
&&& 不过,其实你还可以用这个GNU库来增强clojure REPL。使用它有如下好处:
1.Tab completion,使用tab做代码提示。
2.括号匹配
3.历史记录,哪怕你重启REPL
4.通过来绑定vi或者emacs
&&& 具体操作步骤如下:
1.首先,你需要在你的机器上安装rlwrap,你可以通过apt或者port,homebrew等工具安装或者自己下载安装:
sudo&port&install&rlwrap
2.在你的home目录下创建一个clojure目录作为clojure home,并拷贝clojure.jar进去:
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.创建一个shell脚本名为clj,并放入你的path变量,脚本内容:
breakchars="(){}[],^%$#&#64;\"\";:''|\\"
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&-c&-b&"$breakchars"&\
&&&-f&"$HOME"/.clj_completions&\
&&&-t&"Clojure&REPL"&\
&&&-p&red&\
&&&-H&"$CLOJURE_DIR"/.repl_history&-s&1000\
&&&java&"$JAVA_OPTS"&&-cp&"$CLOJURE_JAR"&clojure.main
&&&&&&&&&exec&java&-cp&"$CLOJURE_JAR"&clojure.main&$1&"$&#64;"
我们将命令历史输出到~/clojure/.repl_history文件中,并限制数目为1000。
4.clj脚本中通过-f选项指定了completions文件为~/.clj_completions,执行下列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))))
这个程序只生成clojure.core的completions文件,如果你想将所有ns都加入进去,注释掉第一行,使用第二行程序。
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启动clojure REPL了,可以用tab做代码提示了,可以用Ctrl + r来搜索历史命令,运行截图:
参考:转载请注明出处:
&&& 使用http client提交表单或者下载网页也是非常常见的任务,比如使用Java的时候可以用标准库的HttpURLConnection,也可以选择。在clojure里也有这样的类库,这里我将介绍三个各有特色的http client实现。&&& 首先,我最先推荐使用clj-http这个类库,它是Apache HttpClient的clojure wrapper,是一个提供同步API的简单易用的Http Client。名称: clj-http主页:依赖:[clj-http&"0.3.1"]例子:(require&'[clj-http.client&:as&client])(client/get&"")结果:=& {:cookies {"NID" {:domain "..hk", :expires #&Date Tue Aug 14 18:20:38 CST 2012&, :path "/", :value "56=qn2OWtODE2D3fUKi_vbi44jZepOeLI9xC4Ta1JQLEicqUvIZAqr7TCmft_hq8i_FRwnFXdTK1jV2S5IrSZFyYhlAN2KcQEXgWX1iK36gM2iYPaKPihuUZDCqgiAamDOl", :version 0}, "PREF" {:domain "..hk", :expires #&Date Wed Feb 12 18:20:38 CST 2014&, :path "/", :value "ID=8b73a654ff0a2783:FF=0:NW=1:TM=:LM=:S=uEM4SsFuHlkqtVhp", :version 0}},&&& :status&200&&&&:headers&{"date"&"Sun,&01&Aug&:49&GMT"&&&&&&&&&&&&&&"cache-control"&"private,&max-age=0"&&&&&&&&&&&&&&"content-type"&"text/&charset=ISO-8859-1"&&&&&&&&&&&&&&}&&&&:body&"&!doctype&html&"&&&&:trace-redirects&[""&"/"&"http://www.google.fr/"]}更多例子:(client/get&"/resources/3"&{:accept&:json});;&Various&options:(client/post&"/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&"e/redirects-somewhere"&{:follow-redirects&false});;&Only&follow&a&certain&number&of&redirects:(client/get&"e/redirects-somewhere"&{:max-redirects&5});;&Throw&an&exception&if&redirected&too&many&times:(client/get&"e/redirects-somewhere"&{:max-redirects&5&:throw-exceptions&true});;&Send&form&params&as&a&urlencoded&body(client/post&"http//"&{: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&"/protected"&{:basic-auth&["user"&"pass"]})(client/get&"/protected"&{:basic-auth&"user:pass"});;&Query&parameters(client/get&"/search"&{:query-params&{"q"&"foo,&bar"}})&&& clj-http的API相当的简洁漂亮,使用起来非常便利,强烈推荐。题外,学习clojure的一个好方法就是为现有的java类库实现一些方便的clojure wrapper。&&& 如果你需要异步的http client,我会推荐http.async.client这个类库,它的API是异步形式的类似 Java的Future模式,对于clojure程序员来说应该更像是agent。名称: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.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&""&:body&"hello&world"&:timeout&3000)(c/DELETE&client&"")(c/POST&client&""&:body&"hello&world"&:auth&{:type&:basic&:user&"admin"&:password&"admin"})请注意,这些方法都是异步调用的,你需要通过await来等待调用完成,或者通过done?来判断调用是否完成。http.async.client有个比较重要的特性就是对Http Chunked编码的支持,分别通过LazySeq和callback的方式支持,首先看将Http chunked变成一个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))))这里非常关键的一点是stream-seq返回的chunk序列,每取一个就少一个(通过first函数),也就是说每次调用first取到的chunk都不一样,是顺序递增,不可重复获取的。通过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&&#64;parts&&&&))自己传入一个callback函数接收chunk,比如这里用一个ref累积。http.async.client的详细文档看这里:最后,有兴趣还可以看下这个异步通讯的框架,它支持Http协议,也提供了http server和client的实现。不过它的API就没有那么简单明了,它的模型是类似go语言里利用channel做异步通讯的模型,http只是它的一个模块罢了,这是另一个话题了。转载请注明出处:
&&& 处理日志是任何一个产品级的程序都需要仔细处理的模块。在Java中,我们经常使用的是log4j就是一个日志框架。在clojure里,同样有一套日志框架&#8212;&#8212;clojure.tools.logging,它不仅提供了常用的日志输出功能,还屏蔽了Java各种日志框架之间的差异,如slf4j,commons-logging,log4j,java.util.logging等,让你可以透明地使用这些框架来处理日志。名称: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等常用宏,分别对应相应的日志级别。这些方法会自动判断当前logger的级别是否有效,有效的前提下才会输出日志。也就是说在Java里,你经常需要这样:if&(logger.isDebugEnabled())&{&&&&logger.debug(x&+&"&plus&"&+&y&+&"&is&"&+&(x&+&y));}在使用 tools.logging的时候是不需要的,因为这些宏帮你做了这个判断。另外,我们在使用log4j的时候需要指定log的namespace,在tools.logging里不需要,默认会取当前的namespace也就是*ns*。最后,info还有个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宏,同时输出表达式的form和结果,例如(spy&(+1&2))输出日志2012-02-12&20:11:47,415&DEBUG&log:&(+&1&2)=&&33.with-logs宏可以在将*out*和*err*流重定向到日志的情况下求值表达式,例如:(with-logs&*ns*&(prn&"hello&world"))输出日志:2012-02-12&20:17:32,592&INFO&&log:&"hello&world"with-logs需要明确指定log-ns,默认out的输出级别是info,而err的级别是error,可以指定输出级别(with-logs [*ns* :info :error] ......)4.事务中(dosync中)的日志输出,tools.logging做了特殊处理,默认情况下当且仅当事务成功提交的时候并且日志级别是warn或者info会通过agent异步写入日志。tools.logging定义了一个全局的agent&#8212;&#8212;*logging-agent*。当判断当前是在事务中调用log宏,并且日志级别在集合*tx-agent-levels*内,就会在事务提交成功的时候将日志发送给*logging-agent*异步处理。可以通过*tx-agent-levels*改变使用agent输出日志的级别范围,默认是#{:info :warn}。还可以通过改变*force*变量来强制使用direct或者agent的方式输出日志,*force*可以为:agent或者:direct。(binding&[*force*&:agent]&&(log&:info&"hello&world"))这里特别使用了log宏,需要明确指定日志级别为info。5.默认日志框架的是从classpath查找的,查找的顺序是sl4j,commons-logging,log4j,java.util.logging,找到哪个可用就用哪个。如果你的classpath里存在多个日志框架,如同时存在sl4j和commons-logging,那么如果你希望强制使用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.每个日志框架的配置跟使用java没有什么两样,比如你用log4j,就需要在classpath下放置一个log4j.properties等。如果你希望用编程的方式配置,可以使用。转载请注明出处:
&&& 年前一篇blog提过,写了一个stm-profiler用于统计clojure STM的运行状况,放在了github上:
&& STM的事务在遇到写冲突(多个事务写同一个ref的时候)就会回滚事务并重试,通过stm-profiler你可以查看事务的重试次数,重试原因,以及每个reference的使用情况。使用很简单,在lein的project.clj引用stm-profiler:
[stm-profiler&"1.0.2-SNAPSHOT"]
注意,目前stm profiler仅支持clojure 1.3。
我们写一个简单例子:
(use&'stm)
(def&a&(ref&1))
(def&b&(ref&2))
(dotimes&[_&100]&(future&(dosync&(alter&a&+&1)&(alter&b&-&1))))
(Thread/sleep&1000)
(prn&&#64;a)
(prn&&#64;b)
(Thread/sleep&1000)
(prn&"stm&statistics"&(stm-stats))
(prn&"reference&a&statistics"&(ref-stats&a))
(prn&"reference&b&statistics"&(ref-stats&b))
定义了两个ref:a和b,然后用future启动100个线程并发地发起同一个事务操作,对a加一,对b减一。最后打印a和b的值,使用stm-stats函数获取stm的统计信息并打印,使用ref-stats获取a和b两个reference的统计信息并打印。
运行这个例子,在启动的时候会有些警告信息,忽略即可(主要是因为stm profiler重新定义了一些跟STM相关的函数和宏,如dosync等,但是仅仅是添加了统计功能,并没有修改他们原本的功能)。
在我机器上的一次输出:
"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的结果都没问题。重点看打印的统计信息,(stm-stats)的输出结果是:
{"(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}}
这个结果是一个map,key是事务的form,而value就是该form的统计信息,也是一个map,具体各项的含义如下:
total-cost
所有事务的总耗时
100个事务耗时1233毫秒
total-times
事务运行次数
average-cost
平均每个事务耗时
平均一个事务耗时12毫秒
average-retry
平均每个事务的重试次数
&平均每个事务重试了5次才成功
not-running
&当前事务不处于running状态,可能是被其他事务打断(barge),需要重试
&因为not-running的原因重试了11次
&读取ref值的时候没有找到read point之前的值,被认为是一次读错误,需要重试
&因为读ref错误重试了44次
barge-fail
&打断其他事务失败次数,需要重试
&尝试打断其他事务失败而重试了224次
change-committed
&在本事务read point之后有ref值获得提交,则需要重试
&因为ref值被其他事务提交而重试了227次
&&& 从输出结果来看,这么简单的一个事务操作,每次事务要成功平均都需要经过5次的重试,最大的原因是因为ref的值在事务中被其他事务更改了,或者尝试打断其他正在运行的事务失败而重试。关于clojure STM的具体原理推荐看这篇文章《》。STM不是完美的,事务重试和保存每个reference的历史版本的代价都不低。&&& 再看(ref-stats&a)的输出:{"(alter&a&+&1)(alter&b}

我要回帖

更多关于 修改ip地址有什么影响 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信