ctrl-n class... (find by name) ctrl-shift-n file... (find by name) ctrl-shift-alt-n symbol... (find by name) ctrl-g line... (goto line) ctrl-h type hierarchy (hierarchy) ctrl-shift-h method hierarchy (hierarchy) ctrl-alt-h call hierarchy (hierarchy) ctrl-q quick documentation ctrl-alt-i auto-indent lines (indent) ctrl-alt-l reformat code (line up) ctrl-alt-o optimize imports (optimize) ctrl-/ comment with line comment (//) ctrl-shift-/ comment with block comment (/*...*/) ctrl-w select word or block (word) ctrl-d copy line (duplicate line, yyp) ctrl-x cut line (dd) ctrl-u uppercase/lowercase (upper) ctrl-j insert live template ctrl-alt-j surround with live template ctrl-alt-t surround with (template) ctrl-shift-j join lines (join) ctrl-e recent files (editions) ctrl-shift-e recently changed files (editions) alt-shift-c recent changes (changes) ctrl-b delcaration ctrl-shift-b type declaration ctrl-alt-b implementation(s) ctrl-p parameter info (parameter) ctrl-space basic completion ctrl-shift-space smart completion ctrl-alt-space completion lookup alt-enter auto-complete alt-insert generate... ctrl-shift-up/down move statement up/down alt-shift-up/down move line up/down ctrl-up/down scroll up/down alt-up/down previous/next method f2 next highlighted error shift-f2 previous highlighted error ctrl-f find ctrl-r replace f3 next match shift-f3 previous match f4 jump to source ctrl-alt-shift-t refactor this f5 refactor copy f6 refactor move ctrl-f6 refactor change signature shift-f6 refactor rename alt-delete refactor safe delete ctrl-alt-v refactor extract variable ctrl-alt-f refactor extract field ctrl-alt-p refactor extract parameter ctrl-alt-m refactor extract method ctrl-alt-n refactor inline
首先,写作促进我们思考。程序员每天的工作,不论是学习新知识新技术、理解软件需求、阅读代码/文档、设计框架、还是实现业务逻辑,都离不开思考。相信很多人都有过这样的体验: 对于某个设计或知识点,你以为自己想清楚了,但真要让你解释给别人听,你又会觉得无从下手,几轮过后回想当初,之前所看到和相信的不过是错觉,当你完整的向其他人转述一遍之后,你才算是真正想明白了。其实这个道理和书桌上放一只橡皮鸭/填充玩偶(然后向它讲述你的想法)的做法是相通的,只不过写作本身不光整理了思路,还留下了文档,同时,写作的过程也是不断加深印象、提升信心的过程,可谓一举多得。
其次,和写作一样,编程的主要目的是与世界交流,不论这个世界是指的机器的世界还是人的世界。要有效地与机器世界交流,你编写的代码必须符合一定的语法和范式,必须逻辑上讲得通,这样才有意义。而基本的写作训练可以让我们养成主动留意错别字、格式、拼写错误、逻辑错误的习惯。如c. a. r. haore所说,我们宁要明显无错误的代码,不要无明显错误的代码。这点洁癖是好程序员必须具备的修养。这也是为什么我看到有明显拼写错误的程序员简历时,会直接把他/她们拉黑。同样地,我一直坚持认为,如果程序员写不出结构优良的纯文本文档,那么我们也不必指望他/她能够写出优雅的代码。除此之外,我们还可以再稍微发散一下: 开源项目那么多,满足类似需要的往往不止一个,为什么有的很成功,有的却无人问津? 主创/灵魂人物的写作能力,不论是代码、文档、邮件还是ppt,是很重要的分野。我相信,改变世界、影响更多的人,是很多程序员梦寐以求的,而要做到这一点,离不开写作。
最后,写作通常是程序员的短板,亦即最容易低成本高产出的地方。我一直很不喜欢人为地给不同专业背景的人贴上文科和理科的标签,仿佛他们之间没有交集似的。这样做的最大问题在我看来是一方天然地觉得另一方的知识对自己没价值,以至于形成了类似"写作是文科生的菜"、"编程是理科才要学的东西"等谬误。就我的观察,计算机科班出身的同学,往往文字表达能力不够强,可能多少和这种心理暗示有关。如此明显的短板,如果能够引起广大程序员群体的重视,肯在写作上持续投入,效果一定是惊人的。
java ee应用,同时部署在两个tomcat(5.5.27)实例上,前面放了个apache(httpd-2.2.19-win32-x86-no_ssl),通过mod_jk(1.2.32)做负载均衡,同一台物理服务器,操作系统为64位的windows server 2003 sp2。现象是60 客户端,平均每个客户端每秒请求数2次,单个请求正常响应时间在500ms以内,即每秒冲进来120个请求,并发量最多在60上下,apache就已不堪"重"负,静态资源响应时间都超过10s,同时tomcat和数据库服务器均正常。凭我的经验,同样的压力直接压到tomcat也不至于这么难看。看来问题出在apache。
首先修改httpd.conf配置文件,打开status模块:
loadmodule status_module modules/mod_status.so
sethandler server-status
order deny,allow
deny from all
allow from 127.0.0.1 #需要的话也可放开为all
这样我们就能通过/status页面查看服务器当前的状态信息,结果观察到worker(即线程)数仅为64!坑爹啊!难怪撑不住。我记得以前windows下面的apache默认线程数没这么小的。找准问题,接下来就好办了:
threadsperchild 300
maxrequestsperchild 0
稍微解释一下:mpm_winnt.c是apache为windows nt提供的mpm (multi-processing module),对应到linux环境下,则有prefork.c(多进程/每进程1个线程)和worker.c(多进程 多线程)两种mpm可选。windows下面只会有父与子两个进程,因此单个子进程能同时起多少线程(threadsperchild)就成了调优的关键。另一个参数maxrequestsperchild的含义是单个子进程累计最多处理到少个请求,超过该值则退出重启,这是出于防止内存泄露慢慢拖垮整个服务器而做的防御性措施,0表示不做此限制。
新配置上线后,客户端数量顺利冲上200 。case closed.
今天帮同事解决一个邮件乱码的问题,简记于此。
邮件正文就不贴了,有一段从上下文判断明显应该是"上海",却显示成了"涓...",于是自然而然的拿出"上"字和"涓"字来分析。
通过简单的groovy脚本对这两个汉字分别按gbk和utf-8编码,并将得到的byte[]转换成二进制表示输出如下:
== 上 ==
11001001 11001111
11100100 10111000 10001010
== 涓 ==
11100100 10111000
11100110 10110110 10010011
注意"上"字的第2排前两组和"涓"字第1排的两组byte正好相同,于是问题迎刃而解,乱码是由于邮件以utf-8编码后被错误的以gbk解码(随后又重新编码成utf-8)造成的。
关键代码:
void showbytes(string input) {
println("== " input " ==")
b = input.getbytes("gbk")
b.each { print integer.tobinarystring(it).substring(24) ' ' }
println()
b = input.getbytes("utf8")
b.each { print integer.tobinarystring(it).substring(24) ' ' }
println()
}
问题起因: 帮同事排查一个svn资源库导入后无法使用的问题,checkout时报错
expected fs format '2'; found format '3'
期待文件系统(fs)格式 “2”;找到格式“3”
熟悉svn的朋友应该知道,subversion大版本更新时有可能会对其文件格式进行调整,因此内部有文件格式版本的说法,以上报错信息在网上搜一下就知道,版本2和版本3分别对应subversion的1.4.x和1.5.x,于是问题归结为如何给资源库降级,从而得到与1.4兼容的dump文件。方法很简单,记录如下,供需要的朋友参考:
首先在1.5.x的subversion环境下新建一个与1.4.x兼容的资源库
svnadmin --pre-1.5-compatible create project01
然后将之前无法正常导入1.4.x的dump文件(dumpfile.old)导入
svnadmin load project01 < dumpfile.old
最后再做一次导出即可
svnadmin dump project01 > dumpfule.new
回到1.4.x的subversion环境,新的dump文件(dumpfile.new)就可以正常导入和使用了。
设想一下这样的场景: 你出差在外,或者生病在家,有个紧急的需求要处理,涉及到数个源代码文件的改动,你亲自修改需要10分钟,电话和在公司的同伴沟通然后由他/她来修改则需要1小时。公司svn服务仅支持svn://协议,且仅限内网访问,而你只有一个ssh账号可以远程登录到公司某台linux/unix服务器。
你暗自庆幸,幸好管理员有先见之明,为你留了个ssh口子,这样至少你还可以ssh上去通过命令行的方式在服务器上做svn checkout,vim ...和svn commit。不过如果你认为这就是全部,那就太小瞧ssh了。
ssh有个命令行参数 -d [地址:]端口,含义是在某个本地地址的某个端口上开socks服务进行监听,把这个端口的数据通信以加密形式转发到ssh的另一端。你说好,我有了一个socks服务器,但我又不是要上网走代理,svn也并不天然支持socks啊,有什么用呢? 嗯,这正是tsocks的用武之地,它能透明的让普通应用程序也走socks,安装方法很简单: 主流的linux发行版,如debian、archlinux等的默认软件仓库已经自带了tsocks,通常只需要apt-get install或pacman -s即可,mac os x下则可以利用macports安装,然后修改配置文件/etc/tsocks.conf(macports会安装到/opt/local/etc目录),可以在样本文件tsocks.conf.sample的基础上修改,通常只要配置server = 127.0.0.1即可,其他都可以默认。
有了这些打底,剩下的就很简单了: 首先 ssh -d 1080 -f -n 用户名@公司服务器的公网地址 在本机的1080端口开启socks服务;然后按照你平时使用svn的习惯,只是在命令前加上tsocks,类似这样: tsocks svn up 或者 tsocks svn ci -m 'blahblahblah' 等等即可,本地的svn sandbox不需要任何修改。
这个例子可以说只是冰山一角,不论是ssh还是tsocks都还有更高级的用法,而这个通道一旦打通,它的效果就像是简化版的vpn,除了ping之类的少数命令外,几乎就跟你在公司做各种操作没有两样,所以,发挥你的想象力吧 :)
和swing应用的直接跨平台不同,swt/rcp应用要想同时支持不同平台,需要做些特殊的配置,不过并不复杂,记录在此,希望能帮到有需要的朋友。目前win32、32位linux、64位linux和mac os x基本上就覆盖了所有主流的桌面操作系统,本文将以同时支持这四种os为例来进行讲解。
首先是下载对应版本的rcp框架在不同操作系统的插件,将它们放在同一个plugins目录,比如你可以用win32为基础,然后添加针对其他平台的如下插件:
org.eclipse.core.filesystem
org.eclipse.core.filesystem.win32.x86
org.eclipse.core.filesystem.linux.x86
org.eclipse.core.filesystem.linux.x86_64
org.eclipse.core.filesystem.macosx
org.eclipse.core.net
org.eclipse.core.net.win32.x86
org.eclipse.core.net.linux.x86
org.eclipse.core.resources
org.eclipse.core.resources.win32.x86
org.eclipse.equinox.launcher
org.eclipse.equinox.launcher.win32.win32.x86
org.eclipse.equinox.launcher.gtk.linux.x86
org.eclipse.equinox.launcher.gtk.linux.x86_64
org.eclipse.equinox.launcher.carbon.macosx
org.eclipse.equinox.security
org.eclipse.equinox.security.win32.x86
org.eclipse.equinox.security.macosx
org.eclipse.swt
org.eclipse.swt.win32.win32.x86
org.eclipse.swt.gtk.linux.x86
org.eclipse.swt.gtk.linux.x86_64
org.eclipse.swt.carbon.macosx
接下来将不同平台下的eclipse可执行文件(windows下面是eclipe.exe,linux下是eclipse,mac os x下面是eclipse.app)放到不同的子目录下,当然,如果你的rcp应用有别的名称,也可以重命名eclipse可执行文件,按照不同平台的规范更换图标,然后修改.ini文件让它的-startup和-startup.libraray参数指向相对路径中正确版本的插件即可。
最后分享一下我们软件部署的机制: 按照前面介绍的方式打包的应用程序,交到用户手里并不是很友好,因为需要他/她自己再做一次判断,当前的操作系统是什么,然后打开不同的目录去点击不同的可执行文件。我们的做法是单独提供一个swing程序,在客户端自动判断os,然后自动调用不同版本的可执行文件,同时,这个swing程序被做成java web start,把整个rcp客户端的下载和同步也处理掉,这样,对用户而言,整个过程就透明了,只需要一个jnlp,剩下的工作完全自动化。
在linux或其他unix和类unix环境下,ps命令想必大家都不陌生,我相信也有不少同学写过 ps aux | grep java | grep -v grep | awk '{print $2}' 这样的管道命令来找出java进程的pid。常言道,java并非真的"跨平台",它自己就是平台。作为平台,当然也有些基本的工具,让我们可以用更简单、更统一,同时又是非侵入的方式来查询进程相关信息。今天我们就来认识一下其中的两个。
jps
顾名思义,它对应到unix的ps命令。用法如下:
jps [ options ] [ hostid ]
其中,options可以用 -q (安静) -m (输出传递给main方法的参数) -l (显示完整路径) -v (显示传递给jvm的命令行参数) -v (显示通过flag文件传递给jvm的参数) -j (和其他java工具类似用于传递参数给命令本身要调用的java进程);hostid是主机id,默认localhost。
jstat
用于输出给定java进程的统计信息。用法如下:
jstat -options 可以列出当前jvm版本支持的选项,常见的有 -class (类加载器) -compiler (jit) -gc (gc堆状态) -gccapacity (各区大小) -gccause (最近一次gc统计和原因) -gcnew (新区统计) -gcnewcapacity (新区大小) -gcold (老区统计) -gcoldcapacity (老区大小) -gcpermcapacity (永久区大小) -gcutil (gc统计汇总) -printcompilation (hotspot编译统计)
假定你要监控的java进程号是12345,那么
jstat -gcutil -t 12345 200 300 即可每200毫秒连续打印300次带有时间戳的gc统计信息。
简单解释一下: -gcutil是传入的option;必选,-t是打印时间戳,是以目标jvm启动时间为起点计算的,可选;12345是vmid/pid,和我们从jps拿到的是一样的,必选;200是监控时间间隔,可选,不提供就意味着单次输出;300是最大输出次数,可选,不提供且监控时间间隔有值的话,就是无限期打印下去。
具体输出明细的解释请参考官方文档
在vim中,我们可以通过set fencs=utf-8,gbk告诉它按照先utf-8后gbk的顺序自动识别打开文件的字符编码。emacs也有一组字符编码相关指令,整理如下:
指令全名:set-buffer-file-coding-system
指令作用:改变当前buffer的编码
调用方法:(以目标编码gbk为例)
c-x
该指令还可以用于改变当前buffer的换行习惯(编码参数用dos或unix,对应\r\n或\n)。
指令全名:universal-coding-system-argument
指令作用:指定紧随其后的命令(如c-x c-f或c-x c-w等)所采用的编码
调用方法:(以目标编码gbk为例)
c-x
指令全名:revert-buffer-with-coding-system
指令作用:用指定编码重读当前buffer(如果打开时用错了编码)
调用方法:(以目标编码gbk为例)
c-x
指令全名:recode-region
指令作用:将以错误编码解码的选区以指定编码重新解码
调用方法:(以目标编码gbk为例)
m-x recode-region
除了这些操作外,还有一种方法,那就是在文件开始的部分给出如下形式的指令,直接告诉编辑器在打开和保存时应采用的编码:
-*- coding: gbk -*-
接触过python的同学是不是觉得很眼熟?
[补充] 查看当前编码选择的命令为:
m-x describe-coding-system
也可在回车前输入具体的编码名称(如gbk)以查看详细说明。
为克服拖延症,在此列出2011年要做的事,给未来的自己监督:
1- 继续去年未完成的产品改造,换一种更稳妥的方式推进;
2- 积极参与社区交流活动,不论线上还是线下;
3- 深入学习scala,辅以clojure和haskell;
4- 系统学习postgresql;
5- 全面使用emacs;
6- 开始读linux源码;
7- 重读《红楼梦》;
8- 至少读两本英文原著;
9- 带儿子回一趟老家。
就这样。