<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Here comes the Sun</title><link href="https://sunng.info/blog/" rel="alternate"></link><link href="https://sunng.info/blog/feeds/all.atom.xml" rel="self"></link><id>https://sunng.info/blog/</id><updated>2023-09-13T21:33:00+08:00</updated><subtitle></subtitle><entry><title>The Jetty 12 Adapter for Clojure Ring, and A Decade of A Custom Ring Adapter</title><link href="https://sunng.info/blog/the-jetty-12-adapter-for-clojure-ring-and-a-decade-of-a-custom-ring-adapter.html" rel="alternate"></link><published>2023-09-13T21:33:00+08:00</published><updated>2023-09-13T21:33:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2023-09-13:/blog/the-jetty-12-adapter-for-clojure-ring-and-a-decade-of-a-custom-ring-adapter.html</id><summary type="html">&lt;h2&gt;The Jetty 12 Adapter for Clojure Ring&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://github.com/eclipse/jetty.project/releases/tag/jetty-12.0.0"&gt;12.0
release&lt;/a&gt; of
&lt;a href="https://eclipse.dev/jetty/"&gt;Jetty&lt;/a&gt; saw its most important architectural change:
the split of its Jetty-Core API and enterprise Java APIs. The Jetty-Core API is
fully functional for Java server and client development, and is the first
version of Jetty that being …&lt;/p&gt;</summary><content type="html">&lt;h2&gt;The Jetty 12 Adapter for Clojure Ring&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://github.com/eclipse/jetty.project/releases/tag/jetty-12.0.0"&gt;12.0
release&lt;/a&gt; of
&lt;a href="https://eclipse.dev/jetty/"&gt;Jetty&lt;/a&gt; saw its most important architectural change:
the split of its Jetty-Core API and enterprise Java APIs. The Jetty-Core API is
fully functional for Java server and client development, and is the first
version of Jetty that being &lt;em&gt;Servlet-free&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;My Clojure library
&lt;a href="https://github.com/sunng87/ring-jetty9-adapter"&gt;ring-jetty9-adapter&lt;/a&gt; has been
tracking Jetty's development since early release of 9.0 (also named after that
version). During the last few weeks I worked with
&lt;a href="https://github.com/jasonjckn"&gt;@jasonjckn&lt;/a&gt; to port this adapter to Jetty 12,
making its name even more confusing :). Thanks to previous
&lt;a href="https://github.com/ring-clojure/ring/pull/483"&gt;changes&lt;/a&gt;, the core library of
&lt;a href="https://github.com/ring-clojure/ring"&gt;Ring&lt;/a&gt; no longer depends on Servlet API.
So in this upgrade, we removed Servlet API completely in
ring-jetty9-adapter. The handler and request/response implementation has been
rewritten to match those from Jetty-Core, while keeping it compatible with Ring
spec.&lt;/p&gt;
&lt;p&gt;The new version has been released to
&lt;a href="https://clojars.org/info.sunng/ring-jetty9-adapter"&gt;clojars&lt;/a&gt; as &lt;code&gt;0.30.0&lt;/code&gt;. To
upgrade to this version, note that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Of course, access to Servlet API in your Clojure code is now invalid&lt;/li&gt;
&lt;li&gt;Java 17 is now required&lt;/li&gt;
&lt;li&gt;&lt;code&gt;:async-timeout&lt;/code&gt; option is removed, let me know if your application relies on
  it&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We saw at least 10% throughput improvement thanks to less layer of
abstraction. As early adopters of Jetty-Core API, I'm giving shout-out Jetty
maintainers &lt;a href="https://github.com/joakime"&gt;@joakime&lt;/a&gt;,
&lt;a href="https://github.com/sbordet"&gt;@sbordet&lt;/a&gt; and &lt;a href="https://github.com/lorban"&gt;@lorban&lt;/a&gt;
for their help during the upgrade.&lt;/p&gt;
&lt;h2&gt;A Decade of A Custom Ring Adapter&lt;/h2&gt;
&lt;p&gt;While working on this upgrade, I noticed that the library was started exact 10
years ago in 2013 when I was a full-time Clojure developer at
&lt;a href="https://www.crunchbase.com/organization/avos"&gt;AVOS&lt;/a&gt;. It was born because at
that time, the official Jetty adapter stuck on version 7. The WebSocket API in
Jetty 9 was what we needed for our Clojure/Ring based application.&lt;/p&gt;
&lt;p&gt;But I didn't stop there. During the last 10 years, with help from many
contributors, I managed to keep adding new features to this library, making it a
feature-rich alternative to the official one:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;2013/4, the initial release based on Jetty 9.0.3, with WebSocket API&lt;/li&gt;
&lt;li&gt;2015/7, HTTP/2 connector&lt;/li&gt;
&lt;li&gt;2017/5, Async Ring handler&lt;/li&gt;
&lt;li&gt;2018/9, TLS 1.3 as default SSL protocol&lt;/li&gt;
&lt;li&gt;2020/7, JDK 11 and JDK 8u252 upgrade, removal of Conscrypt&lt;/li&gt;
&lt;li&gt;2020/12, Jetty 10 upgrade&lt;/li&gt;
&lt;li&gt;2021/10, WebSocket protocol upgrade, making it possible to return websocket
  callbacks from a ring handler&lt;/li&gt;
&lt;li&gt;2022/2, HTTP/3 connector&lt;/li&gt;
&lt;li&gt;2022/10, Jetty 11 upgrade&lt;/li&gt;
&lt;li&gt;2023/9, Jetty 12 upgrade, virtual threads and removal of servlet&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A total downloads of 237,550 and 83 releases is how it benefits the community.&lt;/p&gt;
&lt;p&gt;As I'm no longer using Clojure &lt;a href="https://greptime.com/"&gt;at my work&lt;/a&gt;, this library
has been my last few direct connections to the programming language I always
enjoy a lot. Using it in my &lt;a href="https://obm.sunng.info/"&gt;side projects&lt;/a&gt; is still a
lot of fun. I'm looking forward to run this project in next decade.&lt;/p&gt;</content><category term="misc"></category><category term="clojure"></category></entry><entry><title>一个好团队</title><link href="https://sunng.info/blog/yi-ge-hao-tuan-dui.html" rel="alternate"></link><published>2022-03-30T23:36:02+08:00</published><updated>2022-03-30T23:36:02+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2022-03-30:/blog/yi-ge-hao-tuan-dui.html</id><summary type="html">&lt;p&gt;什么是好团队是个非常抽象的问题，尤其是身在其中遇到问题，更需要一个标尺
来度量，才能让理性战胜一时的冲动。结合 …&lt;/p&gt;</summary><content type="html">&lt;p&gt;什么是好团队是个非常抽象的问题，尤其是身在其中遇到问题，更需要一个标尺
来度量，才能让理性战胜一时的冲动。结合着过去一段时间的所见所闻所感，大
致对自己比较向往的“好团队”有了一个结构化的定义。&lt;strong&gt;能力&lt;/strong&gt;，&lt;strong&gt;流程&lt;/strong&gt;，
&lt;strong&gt;信任&lt;/strong&gt;和&lt;strong&gt;士气&lt;/strong&gt;，是我想到的四个关键词。&lt;/p&gt;
&lt;h3&gt;能力&lt;/h3&gt;
&lt;p&gt;要解释好团队，又提出一个更宽泛的概念，能力。能力本身分很多层面，体现出
来也有很多表征。不过在这个上下文里，我打算只选一个最终的体现，就是
&lt;strong&gt;应变&lt;/strong&gt;的能力，能随机而应变，能达到&lt;em&gt;车到山前必有路&lt;/em&gt;的境界，就是能力强
的团队。&lt;/p&gt;
&lt;p&gt;The Pragmatic Programmer 里说好的代码是 &lt;em&gt;ETC, Easy to Change&lt;/em&gt;。可见
change 本身是一个挑战，能产出 ETC 的方案，或者在不可预测的环境里，兵来
将挡，水来土掩，就是绝对意义上的能力强。&lt;/p&gt;
&lt;p&gt;有这样的应变能力，可以让团队在执行的过程中尝试更多的可能性，在相同时间
和相同资源的情况下，踩更多的坑，从概率上讲也是有更大的可能性走上正确的
路。&lt;/p&gt;
&lt;h3&gt;流程&lt;/h3&gt;
&lt;p&gt;然而能力可遇不可求，云淡风轻的化险为夷实际可能要经过长时间的积累，或者
出众的资质。那么要让一个平平无奇的团队也能达到可接受的标准，就需要流程
来保障。流程可以增加做事的确定性，减少执行过程中的变数，不用那样频繁地
考验团队“应变”的能力。&lt;/p&gt;
&lt;p&gt;流程说白了就是分工和协作的机制，KPI。用更严酷的说法，流程就是纪律。把
一批水准参差不齐，各有各的想法的人捏合在一起做事，需要定义好每个人每个
阶段要交付什么，然后用管理机制来确保流程的链条运转起来。&lt;/p&gt;
&lt;h3&gt;信任&lt;/h3&gt;
&lt;p&gt;然而流程定义的 KPI 是对事物的一种简化建模，是为了能够在一群人里快速达
成一致。实际工作中，单纯对着 KPI 做事，结果很可能把事情做得棱角分明，
甚至割手。&lt;/p&gt;
&lt;p&gt;在 KPI 拟合不到那部分灰色地带，如果也需要达到好的效果，就需要在协作中
跳出既有的流程框架，在这些边缘处达成一致。相互信任是这方面的前提。缺少
信任，出圈的行为都可能引来猜忌，圈内的人也不想被误解。&lt;/p&gt;
&lt;p&gt;在这种贬义的“各司其职”状态下，如果不能从信任的层面解决，那只有反向地把
流程向高维度高复杂度的方向去推动，形成冗余和损耗：文山会海，反复确认，
提前确认，都是表现形态。&lt;/p&gt;
&lt;h3&gt;士气&lt;/h3&gt;
&lt;p&gt;士气和信任，与能力、流程相比，都是偏精神层面的东西。如果说信任是人与人
之间关系的描述，那么士气是整个人群所体现的一个整体的精神度量。其实前面
这句描述复杂了，简单说就是在上面三个维度都确定的情况下，大家是否愿意全
速前进。&lt;/p&gt;
&lt;p&gt;一提到精神层面就比较玄学。我经历过很多士气低落的状态，大家觉得在做的事
情不靠谱没希望，这种情况太常见了。反倒是“士气高昂”，我总认为被误解了，
那种打鸡血的、运动式的、喊着口号的、卷着的状态并不一定，而且往往不是，
真正的最佳士气。更像是星际里枪兵打兴奋剂，先自己折掉20%的血条，这不是
士气，是毒药。&lt;/p&gt;
&lt;p&gt;我更愿意相信那种持续的心态轻松，能激发创造力的状态，能够让能力充分发挥、
让流程平稳运行、让信任逐步建立，这是我心中好的士气。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Recapping 2021</title><link href="https://sunng.info/blog/recapping-2021.html" rel="alternate"></link><published>2021-12-31T14:16:00+08:00</published><updated>2021-12-31T14:16:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2021-12-31:/blog/recapping-2021.html</id><summary type="html">&lt;p&gt;一转眼这个 Blog 已经有三年多没有更新了，要不是赶上域名续费，都有点忘记
这块自留地和这种表达方式了。好在 pelican 维护也 …&lt;/p&gt;</summary><content type="html">&lt;p&gt;一转眼这个 Blog 已经有三年多没有更新了，要不是赶上域名续费，都有点忘记
这块自留地和这种表达方式了。好在 pelican 维护也比较慢，前两天把 Blog
的源码在新电脑上 clone 下来，整个编译发布的流程还能用。&lt;/p&gt;
&lt;p&gt;这两年（废）话说得多了，写东西就相应变少了。19年坚持了一端时间手写日记，
今年尝试用 orgroam 记录了几次，也没有持续坚持下来。不过今年最后几天还
是很想总结一下过去这一年。&lt;/p&gt;
&lt;p&gt;今年最积极的三件事：代码，观鸟和开车。&lt;/p&gt;
&lt;h3&gt;902&lt;/h3&gt;
&lt;p&gt;今年在 Github 上记录的贡献数是 902，从 2018 年开始这个数字分别是 980,
762 和 745 。也就是说虽然还是没有恢复过去的 1000+ 的水平，但至少止住了
下降的颓势。&lt;/p&gt;
&lt;p&gt;项目方面，&lt;a href="https://github.com/sunng87/handlebars-rust"&gt;handlebars&lt;/a&gt; 今
年发布了 4.0 版本，进一步补齐了和 js 版本的功能差异，性能也有了一些提
升，与 tera 相比差距又有一点缩小（功能仍然更丰富）。这个库已经维护到第
7个年头，下载量预计在明年1月底会达到 600 万。不过随着社区的老人逐渐淡
出，新的项目使用 tera 的明显更多，尤其是 docs.rs 和 rustdoc 相继选型了
tera，实话说对我的积极性也有一点打击。&lt;/p&gt;
&lt;p&gt;今年另外新起了一个 rust 项目
&lt;a href="https://github.com/sunng87/metriki"&gt;metriki&lt;/a&gt;，把 Java 生态上非常喜欢
的 dropwizard metrics 库搬到了 rust 上来。之前 rust 生态的几个相关项目
一直用不顺手，实际上包括 influxdb_iox 这样的大项目都自己实现了一遍
metrics 库。明年也许会尝试宣传一下，也许只是给自己以后的 rust 项目留个
好工具。&lt;/p&gt;
&lt;p&gt;下半年学习了一点 clojurescript 的 react 框架 re-frame，基于 ebird 的数
据做了一个小应用，预计在春节前把它放到我的 linode 上。至今还是很惋惜
Clojure 生态目前的状态，现在只有少数的爱好者还在坚持维护和宣传，整个生
态虽然够用，但是完全可以更好。Clojure 是那种可以自己当作一门手艺的语言，
不用考虑什么招人\培训之类的事情，只是单纯一个人搞定最多事情的称手工具。&lt;/p&gt;
&lt;p&gt;另外今年给 redis 7.0 提供了一个功能，是 2015 年就提的 feature request。
这个等到发布之后再细说吧。&lt;/p&gt;
&lt;h3&gt;133&lt;/h3&gt;
&lt;p&gt;今年是有数据完整观鸟的第一年，在
&lt;a href="https://ebird.org/profile/MTk3NjY1Mg"&gt;ebird&lt;/a&gt; 总计记录了 133 种鸟类
（也许今天下午还会有增加？）。&lt;/p&gt;
&lt;p&gt;观鸟这个爱好是受家属引导，总结起来其中有三重乐趣：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;观察自然：首当其冲当然是观察鸟类的特点\习性&lt;/li&gt;
&lt;li&gt;数据统计：观察和记录是一个积累的过程，利用 ebird 这样的平台和工具可
  以发现其中数据层面的很多乐趣&lt;/li&gt;
&lt;li&gt;模拟狩猎：一定程度上是一个现实中的 LBS 游戏，每次出门观鸟都有一定的
  不确定性：可能加新满载而归，也可能大失所望看到的都是预期中的鸟。这种
  不确定性是很多游戏或者古代狩猎场景里特有的乐趣。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;马上进入 2022 年这个数字又要重置，1 月 1 日去十渡希望能开个好头，2022
年争取突破 133.&lt;/p&gt;
&lt;h3&gt;开车&lt;/h3&gt;
&lt;p&gt;今年工作上调动到造车部门，虽然还是后端，不是直接接触车本身，但是该懂的
名词术语黑话还是需要深入了解。机缘巧合下半年终于弄到一辆车。驾照已经拿
了 9 年了，终于真正开起来。&lt;/p&gt;
&lt;p&gt;国庆节前还出了一次刮蹭事故，该走的流程也被迫熟悉了一遍。&lt;/p&gt;
&lt;p&gt;开车的好处莫过于自由，比如在远郊的团队活动，仍然可以晚上赶回家里，不用
被迫喝酒和参加游戏。&lt;/p&gt;
&lt;h3&gt;Emacs&lt;/h3&gt;
&lt;p&gt;去年开始逐渐地更深入地配置了我的 Emacs，用
&lt;a href="https://github.com/SuperCuber/dotter"&gt;dotter&lt;/a&gt; 管理 dotfiles，过去拼拼
凑凑的 emacs 配置文件通过 &lt;code&gt;use-package&lt;/code&gt; 一组织也变得有调理一些。并且平
时开始用 orgroam 来管理笔记，通过自己的 nextcloud 在多台电脑间同步。&lt;/p&gt;
&lt;p&gt;今年听了一些 &lt;a href="https://emacstalk.github.io/"&gt;emacstalk&lt;/a&gt; 节目，平时偶尔刷一
刷 emacschina，还捐了款。和前面提到的 Clojure 一样，Emacs 作为一个编辑
器和开发平台，也是一个可以长时间探索、依赖和伴随的东西。在 2020 年之后
这样变数巨大的时代，这个&lt;em&gt;长期可靠&lt;/em&gt;显得弥足珍贵。&lt;/p&gt;
&lt;h3&gt;其他&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;今年告别了足球&lt;/li&gt;
&lt;li&gt;看了 15 本书（每年都是雄心壮志计划 30 本起步，实际…）&lt;/li&gt;
&lt;li&gt;去了 7 个城市&lt;/li&gt;
&lt;/ul&gt;</content><category term="misc"></category></entry><entry><title>Announcing Espresso: the start of a modern Java web ecosystem</title><link href="https://sunng.info/blog/announcing-espresso-the-start-of-a-modern-java-web-ecosystem.html" rel="alternate"></link><published>2018-11-17T16:07:19+08:00</published><updated>2018-11-17T16:07:19+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2018-11-17:/blog/announcing-espresso-the-start-of-a-modern-java-web-ecosystem.html</id><summary type="html">&lt;p&gt;We have seen rapid development of Java itself these days. The
introduction of functional programming elements made Java coding
experience quite sharper.&lt;/p&gt;
&lt;p&gt;However, the Java web ecosystem is still with its early days . The
Servlet API is dated, heavy and verbose. Even with Spring boot has
improved Java development, simpler …&lt;/p&gt;</summary><content type="html">&lt;p&gt;We have seen rapid development of Java itself these days. The
introduction of functional programming elements made Java coding
experience quite sharper.&lt;/p&gt;
&lt;p&gt;However, the Java web ecosystem is still with its early days . The
Servlet API is dated, heavy and verbose. Even with Spring boot has
improved Java development, simpler abstraction like Python's WSGI is
still my preference.&lt;/p&gt;
&lt;p&gt;So I started &lt;a href="https://github.com/espresso4j/"&gt;Espresso&lt;/a&gt;, a core web
abstraction and a family of micro libraries to support it. The idea of
Espresso is highly inspired by Clojure's
&lt;a href="https://github.com/ring-clojure/ring"&gt;Ring&lt;/a&gt; and Rust's
&lt;a href="https://github.com/iron/iron"&gt;Iron&lt;/a&gt;. And the name &lt;em&gt;Espresso&lt;/em&gt; is
specially chosen for &lt;em&gt;Java&lt;/em&gt;. Imaging the Java web application is a cup
of coffee, the Espresso is the soul of it.&lt;/p&gt;
&lt;p&gt;The core of Espresso is quite simple, it's a function takes a
&lt;code&gt;Request&lt;/code&gt; object and returns a &lt;code&gt;Response&lt;/code&gt; object. There is also an
asynchronous variant.&lt;/p&gt;
&lt;p&gt;A hello world web application with espresso can be simple as:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;io.github.espresso4j.espresso.*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;io.github.espresso4j.jettino.Jettino&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;App&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;throws&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Jettino&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;It works.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Basically the application starts a jetty server and hosts a web
application that return HTTP 200 and a body of &lt;code&gt;It works.&lt;/code&gt;;&lt;/p&gt;
&lt;p&gt;To make Espresso usable, the initial family of the system contains:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/espresso4j/jettino"&gt;Jettino&lt;/a&gt;: A web server
  adapter to run Espresso with &lt;a href="http://www.eclipse.org/jetty/"&gt;Jetty&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/espresso4j/latte"&gt;Latte&lt;/a&gt;: A URL router to route
  request to particular espresso function, based on URL pattern. And
  it generates an espresso application itself.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/espresso4j/sugar"&gt;Sugar&lt;/a&gt;: A parameter extractor
  for dealing with query string, form and Json body.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A simple demo is available
&lt;a href="https://github.com/espresso4j/espresso-examples"&gt;here&lt;/a&gt;. You can grab
and run it with &lt;code&gt;gradle run&lt;/code&gt;. The code is super easy to understand.&lt;/p&gt;
&lt;p&gt;The libraries have been published on &lt;a href="https://search.maven.org/search?q=g:io.github.espresso4j"&gt;Maven
central&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Call for collaboration&lt;/h2&gt;
&lt;p&gt;To grow a ecosystem, it requires quite a lot work on
upstream/downstream libraries, documents and communication. I have
some ideas to be implemented like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Extensible session middleware&lt;/li&gt;
&lt;li&gt;Cookie middleware&lt;/li&gt;
&lt;li&gt;Content negotiation&lt;/li&gt;
&lt;li&gt;Spring-boot integration&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you find the library and idea interesting and want to join me in
the development on code, doc, design and communication, feel free
to contact me via the email list on &lt;a href="https://github.com/sunng87/"&gt;my github
page&lt;/a&gt;.&lt;/p&gt;</content><category term="misc"></category><category term="java"></category><category term="web"></category></entry><entry><title>What to expect for Jiangsu Suning's summer break</title><link href="https://sunng.info/blog/what-to-expect-for-jiangsu-sunings-summer-break.html" rel="alternate"></link><published>2018-06-13T17:45:33+08:00</published><updated>2018-06-13T17:45:33+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2018-06-13:/blog/what-to-expect-for-jiangsu-sunings-summer-break.html</id><summary type="html">&lt;p&gt;This article is written for &lt;a href="https://www.jiangsuguoxinsainty.com/"&gt;Jiangsu Suning fan blog&lt;/a&gt;. This is the first time I write about football, in both English and Chinese. After confirmed from the site owner, I can repost it on my own blog.&lt;/p&gt;
&lt;p&gt;The 2018 World Cup is coming in next week. Chinese Super League will …&lt;/p&gt;</summary><content type="html">&lt;p&gt;This article is written for &lt;a href="https://www.jiangsuguoxinsainty.com/"&gt;Jiangsu Suning fan blog&lt;/a&gt;. This is the first time I write about football, in both English and Chinese. After confirmed from the site owner, I can repost it on my own blog.&lt;/p&gt;
&lt;p&gt;The 2018 World Cup is coming in next week. Chinese Super League will be paused until early July. Yesterday, Suning team was heading off to Italy for a special summer training camp. They will be using their sister club, Inter's Suning Training Center during this summer.&lt;/p&gt;
&lt;p&gt;11 weeks into 2018, Suning gained 19 points and 18 goals, ranked 4th with only 4 points away from top, which is a huge improvement if you compare it to 2017. But fans always expect more. With promising young star Huang Zichang, an extraordinary Ji Xiang and super reliable Teixeira, can we expect a return to Asia Champions League, or even a title for 2018? The summer break can really decide.&lt;/p&gt;
&lt;h2&gt;Olaroiu's secret new formation&lt;/h2&gt;
&lt;p&gt;After taking over the team from Capello, Olaroiu soon switched formation from 4-3-3 to 4-4-2. Using Wu Xi and Tian Yinong holding the midfield, and recovered Boakye &amp;amp; Teixeira on the front, Olaroiu managed to get 16 points in 8 matches. &lt;/p&gt;
&lt;p&gt;However, the Romanian still received questions about his never-changed lineup and substitutions: 89' for Zhang Lingfeng and 91' for Gao Tianyi. These two U23s gave impressive performance during Capello era, but now were only for satisfying CFA's U23 policy, with no tactical meaning.&lt;/p&gt;
&lt;p&gt;In an interview before victory over Dalian Yifang, Olaroiu told CCTV-5 journalist Wang Tao, picking up the team in mid-season, he had little time to experiment any new formation and it's what he will definitely do during the summer break. &lt;/p&gt;
&lt;p&gt;The current 4-4-2 relies heavily on our starting XI. But we don't have many good substitutions for the system. Meanwhile, talent players like Wang Song, Gao Tianyi, Abduhamit were wasting their time on the bench. So I really wish Olaroiu can find his new formation in this summer, and utilize our potential for a more flexible squad. For example, if we can start 2 U23 players, we will have two meaning substitutions.&lt;/p&gt;
&lt;h2&gt;Ramires&lt;/h2&gt;
&lt;p&gt;If I come from 2012, I will never believe we have super star like Ramires in team, and super star cannot find a position in starting XI. But that's the truth for 2018. The new policy allows only 3 foreign players in squad. It seems we have no choice but using Teixeira, Boakye and Paletta.&lt;/p&gt;
&lt;p&gt;Needless to say Teixeira is the soul of team on attack, Boakye provides Teixeira more room in the front. Without Boakye, the opponent can spend 3 defenders to deal with Teixeira. On the defense side, Paletta plays a fatal role in Jiangsu Suning's system. Not only for holding the back, but nowadays he is also responsible for ball controlling and passing through midfield. &lt;/p&gt;
&lt;p&gt;So the fact is, Wu Xi and Tian Yinong can do 70% that Ramires provides, but no one else can replace Teixeira, Boakye or Paletta. That's the reason Ramires just played 1 CFA Cup match. Even if he sent two assists, we were still not having him in the league.&lt;/p&gt;
&lt;p&gt;I won't be surprise if Ramires leave us in this Summer. He needs playing time and totally deserves it. Otherwise, we need new formation to unleash his power.&lt;/p&gt;
&lt;h2&gt;Newcomers&lt;/h2&gt;
&lt;p&gt;There are few good Chinese players to buy from the market, until Guangzhou Evergrand president Xu Jiayin stated that no one in his team is unavailable for sale. So can we take someone from Evergrand to enhance our squad? Rumor said Suning actually queried Zhang Linpeng, the Evergrand defender this winter. There are a few names in the Guangzhou team can benefit us, but I doubt if they really want to sell.&lt;/p&gt;
&lt;p&gt;R&amp;amp;F's Xiao Zhi is on Suning's hunting list too. This 32y old forward became the best Chinese target man in his thirties. Quanjian's Yang Xu is my own top choice if we can't have Xiao Zhi.&lt;/p&gt;
&lt;p&gt;If Ramires is going to leave, we will free a slot for new foreign player. After World Cup I think there will be some good players willing to come to China. A strong target forward is in top priority in my opinion. If we do it right, we can soon catch up in title race, like signing Roger Martinez in 2016. &lt;/p&gt;
&lt;h2&gt;Fitness&lt;/h2&gt;
&lt;p&gt;The fixtures after World Cup is really tough. From July 28th, we will play 6 matches in 3 weeks. For most cities in China, includes our home Nanjing, the weather at that time is simply, hell. Some of our key players includes Huang Zichang, Wu Xi, Abduhamit and Zhang Lingfeng, were called up in national team during the break. Huang Zichang, being called up by national team, U23 and U21 at the same time, must be tired. &lt;/p&gt;
&lt;p&gt;Tactically, our play relies on running very much, if we keeps using same squad for every match, it will be a huge challenge to the power of players.&lt;/p&gt;
&lt;p&gt;Capello did a good job this winter, on building up a strong team in both tactical and fitness. During the summer camp, we need to keep up with it. Good news is probably we can utilize the facility in Inter's training center, to do it efficiently.&lt;/p&gt;
&lt;p&gt;These are my cents on the summer expectation. Overall, I'm pretty optimistic to our second half of 2018.&lt;/p&gt;</content><category term="misc"></category><category term="football"></category></entry><entry><title>Custom JRE for Clojure app distribution</title><link href="https://sunng.info/blog/custom-jre-for-clojure-app-distribution.html" rel="alternate"></link><published>2018-01-28T23:18:40+08:00</published><updated>2018-01-28T23:18:40+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2018-01-28:/blog/custom-jre-for-clojure-app-distribution.html</id><summary type="html">&lt;p&gt;Modular Java was introduced in JDK 9, and considered to be the largest architectural change
in Java, ever. The core of Java libraries were split into several modules, sits in &lt;code&gt;$JAVA_HOME/jmods&lt;/code&gt;.
The bloated, monolithic &lt;code&gt;rt.jar&lt;/code&gt; is no more.&lt;/p&gt;
&lt;p&gt;Modular Java requires you to include a &lt;code&gt;module-info.java&lt;/code&gt; for …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Modular Java was introduced in JDK 9, and considered to be the largest architectural change
in Java, ever. The core of Java libraries were split into several modules, sits in &lt;code&gt;$JAVA_HOME/jmods&lt;/code&gt;.
The bloated, monolithic &lt;code&gt;rt.jar&lt;/code&gt; is no more.&lt;/p&gt;
&lt;p&gt;Modular Java requires you to include a &lt;code&gt;module-info.java&lt;/code&gt; for a module. As of
Clojure 1.9, there is no plan (AFAIK) for module support. However, we can still
benefit from the new architecture. By using the &lt;code&gt;jlink&lt;/code&gt; tool, we can create a
customized Java runtime, contains certain modules. The minimal one, which has
&lt;code&gt;java.base&lt;/code&gt; module, is only 29MB. And this JRE, is capable for running most of
Clojure application. You can still use its &lt;code&gt;java&lt;/code&gt; executable as before.&lt;/p&gt;
&lt;p&gt;I created &lt;a href="https://github.com/sunng87/lein-jlink"&gt;lein-jlink&lt;/a&gt; to manage these
kind of customized JRE for clojure development. Put &lt;code&gt;lein-jlink&lt;/code&gt; in &lt;code&gt;:plugins&lt;/code&gt;
of your &lt;code&gt;project.clj&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;defproject &lt;/span&gt;&lt;span class="nv"&gt;jlinktest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;0.1.0-SNAPSHOT&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;...&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="ss"&gt;:plugins&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;lein-jlink&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;0.2.0-SNAPSHOT&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;By running &lt;code&gt;lein jlink init&lt;/code&gt;, a default (minimal) JRE is created in &lt;code&gt;target/default/jlink&lt;/code&gt;.
(the target path contains your profile name)&lt;/p&gt;
&lt;p&gt;Now, adding a hello world ring app to our application:&lt;/p&gt;
&lt;p&gt;in &lt;code&gt;project.clj&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="ss"&gt;:dependencies&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="nv"&gt;org.clojure/clojure&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;1.9.0&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;info.sunng/ring-jetty9-adapter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;0.10.4&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;in &lt;code&gt;src/jlinktest/core.clj&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;ns &lt;/span&gt;&lt;span class="nv"&gt;jlinktest.core&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:gen-class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:require&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;ring.adapter.jetty9&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;:refer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;run-jetty&lt;/span&gt;&lt;span class="p"&gt;]]))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;defn &lt;/span&gt;&lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;req&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;lt;h1&amp;gt;It works&amp;lt;/h1&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;defn &lt;/span&gt;&lt;span class="nv"&gt;-main&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;I don&amp;#39;t do a whole lot ... yet.&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;args&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;run-jetty&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:port&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;}))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This app can be run with &lt;code&gt;lein jlink run&lt;/code&gt;, which uses the &lt;code&gt;java&lt;/code&gt; executable from
our customized JRE.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;lein&lt;span class="w"&gt; &lt;/span&gt;jlink&lt;span class="w"&gt; &lt;/span&gt;run
Defaulting&lt;span class="w"&gt; &lt;/span&gt;Uptime&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;NOIMPL&lt;span class="w"&gt; &lt;/span&gt;due&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;java.lang.UnsupportedOperationException&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Implementation&lt;span class="w"&gt; &lt;/span&gt;not&lt;span class="w"&gt; &lt;/span&gt;available&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;this&lt;span class="w"&gt; &lt;/span&gt;environment
&lt;span class="m"&gt;2018&lt;/span&gt;-01-28&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;23&lt;/span&gt;:51:12.571:INFO::main:&lt;span class="w"&gt; &lt;/span&gt;Logging&lt;span class="w"&gt; &lt;/span&gt;initialized&lt;span class="w"&gt; &lt;/span&gt;@-1ms&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;org.eclipse.jetty.util.log.StdErrLog
&lt;span class="m"&gt;2018&lt;/span&gt;-01-28&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;23&lt;/span&gt;:51:12.695:INFO:oejs.Server:main:&lt;span class="w"&gt; &lt;/span&gt;jetty-9.4.8.v20171121,&lt;span class="w"&gt; &lt;/span&gt;build&lt;span class="w"&gt; &lt;/span&gt;timestamp:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2017&lt;/span&gt;-11-22T05:27:37+08:00,&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;hash:&lt;span class="w"&gt; &lt;/span&gt;82b8fb23f757335bb3329d540ce37a2a2615f0a8
&lt;span class="m"&gt;2018&lt;/span&gt;-01-28&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;23&lt;/span&gt;:51:12.742:INFO:oejs.AbstractConnector:main:&lt;span class="w"&gt; &lt;/span&gt;Started&lt;span class="w"&gt; &lt;/span&gt;ServerConnector@2e645fbd&lt;span class="o"&gt;{&lt;/span&gt;HTTP/1.1,&lt;span class="o"&gt;[&lt;/span&gt;http/1.1&lt;span class="o"&gt;]}{&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.0.0.0:8080&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="m"&gt;2018&lt;/span&gt;-01-28&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;23&lt;/span&gt;:51:12.744:INFO:oejs.Server:main:&lt;span class="w"&gt; &lt;/span&gt;Started&lt;span class="w"&gt; &lt;/span&gt;@-1ms
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This environment has no &lt;code&gt;java.util.logging&lt;/code&gt; package so you can see Jetty is
fallback to stderr for logging. But it still fully functional on serving http
requests.&lt;/p&gt;
&lt;p&gt;To visualize the benefit on distributing our Clojure app, we can assemble a
distributable environment, using &lt;code&gt;lein jlink assemble&lt;/code&gt;. It runs &lt;code&gt;lein uberjar&lt;/code&gt;
and copy the jar into JRE directory, then put a shortcut script for running our
app.&lt;/p&gt;
&lt;p&gt;If you care about the size of your application, it's only 37MB totally for this
hello world ring web application, includes runtime.&lt;/p&gt;
&lt;p&gt;This path can be added into a docker base image, like &lt;code&gt;ubuntu&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;ubuntu:16.04&lt;/span&gt;

&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;target/default/jlink&lt;span class="w"&gt; &lt;/span&gt;/opt/jlinktest
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/opt/jlinktest/bin/jlinktest
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;No need for installing openjdk-8-jre. The result image size is 159MB, the ubuntu
base takes 121MB from it.&lt;/p&gt;
&lt;p&gt;Note that you may want to run it on a minimal base like &lt;code&gt;alpine&lt;/code&gt;. &lt;code&gt;alpine&lt;/code&gt; is
based on musl-libc. If your development environment is glibc based (in most this
  is true), it won't work on &lt;code&gt;alpine&lt;/code&gt;. We will need to use a glibc variant and
add required packages:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;frolvlad/alpine-glibc&lt;/span&gt;

&lt;span class="k"&gt;RUN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;apk&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;--no-cache&lt;span class="w"&gt; &lt;/span&gt;libstdc++

&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;target/default/jlink&lt;span class="w"&gt; &lt;/span&gt;/opt/jlinktest
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/opt/jlinktest/bin/jlinktest
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This runnable image size is, just 50.7MB!&lt;/p&gt;
&lt;p&gt;So with jlink and modular Java, you can ship your Clojure app with this minimal
runtime. This could change Java ecosystem and enable even more use cases for it.&lt;/p&gt;</content><category term="misc"></category><category term="clojure"></category></entry><entry><title>微服务设计套路</title><link href="https://sunng.info/blog/wei-fu-wu-she-ji-tao-lu.html" rel="alternate"></link><published>2017-12-26T15:52:00+08:00</published><updated>2017-12-26T15:52:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2017-12-26:/blog/wei-fu-wu-she-ji-tao-lu.html</id><summary type="html">&lt;p&gt;有些名词第一次听到以为是噱头，或者根本没有在意，然后一两个月内又听到一次，再然后一周内又听到一次。三人成虎，发 …&lt;/p&gt;</summary><content type="html">&lt;p&gt;有些名词第一次听到以为是噱头，或者根本没有在意，然后一两个月内又听到一次，再然后一周内又听到一次。三人成虎，发现真的是热门的概念了。比如 &lt;a href="https://buoyant.io/2017/04/25/whats-a-service-mesh-and-why-do-i-need-one/"&gt;Service mesh&lt;/a&gt;，比如 sidecar。
第一次看到 sidecar 这个词是在&lt;a href="http://shop.oreilly.com/product/0636920072768.do"&gt;这本书里&lt;/a&gt;，昨天又看到了这篇&lt;a href="https://azure.microsoft.com/en-us/blog/design-patterns-for-microservices/"&gt;总结性的文章 Design patterns for microservices&lt;/a&gt;，都是出自微软背景的技术人员。
也许是出自微软的惯用语？&lt;/p&gt;
&lt;p&gt;不过 Design patterns for microservices 倒是值得关注一下，介绍了 9 种微服务架构套路。&lt;/p&gt;
&lt;h3&gt;Ambassador&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/azure/architecture/patterns/ambassador"&gt;大使&lt;/a&gt;。大使进程专门负责对外调用，解决一些服务可靠性、监控统计等方面的问题。也叫 service proxy（？）
大使可以作为一个 sidecar 和服务一起部署，也可以作为一个基础设施部署在宿主机上供上面的所有服务共用。&lt;/p&gt;
&lt;p&gt;大使可以用在那些没有对微服务架构优化过的老应用上，将老应用的请求全部发到就近的大使上。大使提供一种可靠的，可控制的服务间调用。&lt;/p&gt;
&lt;h3&gt;Anti-Corruption Layer pattern&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/azure/architecture/patterns/anti-corruption-layer"&gt;防腐层&lt;/a&gt;。俗话说没有什么问题不是加一层可以解决的，如果有，就再加一层。
防腐层用来专门处理新旧应用的集成，比如调用协议的不兼容，而又不想让这些确认会被抛弃的代码 ruin 新的应用。&lt;/p&gt;
&lt;h3&gt;Backends for Frontends&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/azure/architecture/patterns/backends-for-frontends"&gt;不翻译了&lt;/a&gt;。这个估计会有些争议。
给每一种类型的客户端准备一个专门的后端服务，比如浏览器访问浏览器的后端，移动设备访问移动设备的后端，避免一套代码供全球导致的对前端特性的忽视。
当然和所有套路类似，也不需要教条地针对所有场景都使用这种套路，只是在确有痛点的时候找到“这么做有先例”的论据。&lt;/p&gt;
&lt;h3&gt;Bulkhead&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/azure/architecture/patterns/bulkhead"&gt;隔墙&lt;/a&gt;。给每个任务分配固定的资源上限，当某个任务出现异常开始消耗资源时，能够控制资源的消耗上限。
比如，调用外部服务的线程池，到服务A响应变慢时，肯定会占用更多的线程资源，影响那些只依赖服务B的业务。如果为不同的依赖设计不同的线程池，就可以达到隔离的效果，既然挂不可避免，那么也只挂 A，不要影响 B。&lt;/p&gt;
&lt;h3&gt;Gateway Aggregation&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/azure/architecture/patterns/gateway-aggregation"&gt;网关聚合&lt;/a&gt;。当应用依赖多个外部服务时，设计这么一种代理网关可以更好地并行请求后端依赖，并且把聚合的结果一次性返回给应用。
当应用访问多个服务时有明显的延迟时，一个统一的出入口可以节省调用的代价。对标准的调用协议，聚合网关也可以是一个标准的架构组件。&lt;/p&gt;
&lt;h3&gt;Gateway Offloading&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/azure/architecture/patterns/gateway-offloading"&gt;网关卸载&lt;/a&gt;。在网关层面把 SSL、安全、认证等辅助任务终结掉，在应用层面保持轻量。&lt;/p&gt;
&lt;h3&gt;Gateway Routing&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/azure/architecture/patterns/gateway-routing"&gt;网关路由&lt;/a&gt;。为所有微服务提供一个统一的入口网关，对外部服务隔离内部的部署、划分细节。部署时还可以通过路由规则施展一些小手段，比如蓝绿部署。
不过对网关的配置能力要求比较高，当网关后面有二十多个各色服务时，如果只是手动维护，代价和风险有点大。（说出了现状）&lt;/p&gt;
&lt;h3&gt;Sidecar&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/azure/architecture/patterns/sidecar"&gt;挂斗&lt;/a&gt;。对应用中一些通用的需求，比如日志、网络请求等，把他们实现为一组独立的进程，有几方面的好处：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;避免对核心业务的资源争抢&lt;/li&gt;
&lt;li&gt;可重用在任意系统，不依赖特定的语言&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;比如 &lt;a href="https://istio.io/"&gt;Istio&lt;/a&gt; 作为一个 sidecar 接管所有的远程调用，提供服务管控能力。&lt;/p&gt;
&lt;h3&gt;Strangler&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/azure/architecture/patterns/strangler"&gt;扼杀模式&lt;/a&gt;。当新旧服务在过渡期时，根据策略将请求路由到旧服务或新服务，目标是逐步废弃遗留系统，平缓迁移到新系统。
这种机制用于平滑迁移服务，保持兼容性，甚至可回退的能力。直到所有用户都迁移到新版时，将这套机制和旧版服务一同退役下线。&lt;/p&gt;</content><category term="misc"></category><category term="architecture"></category></entry><entry><title>Slacker 0.15 released</title><link href="https://sunng.info/blog/slacker-015-released.html" rel="alternate"></link><published>2017-07-22T15:41:00+08:00</published><updated>2017-07-22T15:41:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2017-07-22:/blog/slacker-015-released.html</id><summary type="html">&lt;p&gt;After another year of development, I'm proud to announce a new major release of
my Clojure RPC library, &lt;a href="https://github.com/sunng87/slacker"&gt;slacker&lt;/a&gt;. This
release, 0.15.0, includes an update on wire protocol, some performance
improvement and bugfix.&lt;/p&gt;
&lt;h2&gt;The request extension&lt;/h2&gt;
&lt;p&gt;This release of slacker client use v6 protocol by default. The server …&lt;/p&gt;</summary><content type="html">&lt;p&gt;After another year of development, I'm proud to announce a new major release of
my Clojure RPC library, &lt;a href="https://github.com/sunng87/slacker"&gt;slacker&lt;/a&gt;. This
release, 0.15.0, includes an update on wire protocol, some performance
improvement and bugfix.&lt;/p&gt;
&lt;h2&gt;The request extension&lt;/h2&gt;
&lt;p&gt;This release of slacker client use v6 protocol by default. The server can still
accept v5 protocol for backward compatibility.&lt;/p&gt;
&lt;p&gt;The v6 protocol added an extension field to request and response
packet. By using this field, you can exchange some metadata in RPC call. A
typical use case is for distributed tracing. Trace ID is passed as extension.&lt;/p&gt;
&lt;p&gt;In the API level, we kept the non-invasive principle. Extensions will be injected
as var bindings.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;slacker.common&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;:refer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;with-extension&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;slacker.client&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;:as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;sc&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;def &lt;/span&gt;&lt;span class="nv"&gt;conn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;sc/slackerc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;127.0.0.1:2104&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;sc/defn-remote&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;conn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;slacker.example.api/timestamp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;def &lt;/span&gt;&lt;span class="nv"&gt;extension-id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;with-extension&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;extension-id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;extension-value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A more typical usage of extension is in interceptors.
&lt;a href="https://github.com/sunng87/slacker-htrace"&gt;slacker-htrace&lt;/a&gt; use extension to
pass &lt;a href="http://htrace.apache.org"&gt;htrace&lt;/a&gt; trace id along with rpc call.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;defn &lt;/span&gt;&lt;span class="nv"&gt;client-interceptor&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="nv"&gt;Tracer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;tracer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;span-id-extension-id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:pre&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;req&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;scope-name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:fname&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;:outer&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nv"&gt;trace-scope&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;.newScope&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;tracer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;scope-name&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;-&amp;gt; &lt;/span&gt;&lt;span class="nv"&gt;req&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;assoc &lt;/span&gt;&lt;span class="ss"&gt;::trace-scope&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;trace-scope&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;:extensions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;assoc&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="nv"&gt;span-id-extension-id&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;from-span-id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;.getSpanId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="nv"&gt;TraceScope&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;trace-scope&lt;/span&gt;&lt;span class="p"&gt;))))))&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="ss"&gt;:post&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;when-let &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;scope&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;::trace-scope&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;.close&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="nv"&gt;TraceScope&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;dissoc &lt;/span&gt;&lt;span class="nv"&gt;resp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;::trace-scope&lt;/span&gt;&lt;span class="p"&gt;))})&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;defn &lt;/span&gt;&lt;span class="nv"&gt;server-interceptor&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="nv"&gt;Tracer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;tracer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;span-id-extension-id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:before&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;req&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;scope-name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:fname&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;:inner&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                   &lt;/span&gt;&lt;span class="nv"&gt;span-id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;-&amp;gt; &lt;/span&gt;&lt;span class="nv"&gt;req&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;:extensions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;get &lt;/span&gt;&lt;span class="nv"&gt;span-id-extension-id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;to-span-id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                   &lt;/span&gt;&lt;span class="nv"&gt;trace-scope&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;.newScope&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;tracer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;scope-name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;span-id&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="w"&gt;               &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;-&amp;gt; &lt;/span&gt;&lt;span class="nv"&gt;req&lt;/span&gt;
&lt;span class="w"&gt;                   &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;assoc &lt;/span&gt;&lt;span class="ss"&gt;::trace-scope&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;trace-scope&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="ss"&gt;:after&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;when-let &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;scope&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;::trace-scope&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;.close&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="nv"&gt;TraceScope&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;dissoc &lt;/span&gt;&lt;span class="nv"&gt;resp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;::trace-scope&lt;/span&gt;&lt;span class="p"&gt;))})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Flexible server thread pool&lt;/h2&gt;
&lt;p&gt;Previously, you can only configure one thread pool for all exposed namespace.
This adds risk that one backend issue may exhaust all your server threads. And
your server stops respond requests for any namespace.&lt;/p&gt;
&lt;p&gt;To isolate the execution for namespaces, slacker 0.15 added support for finer
thread pool configuration.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;start-slacker-server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="nf"&gt;the-ns&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;&amp;#39;slacker.example.api&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;the-ns&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;&amp;#39;slacker.example.api2&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="w"&gt;                      &lt;/span&gt;&lt;span class="mi"&gt;2104&lt;/span&gt;
&lt;span class="w"&gt;                      &lt;/span&gt;&lt;span class="ss"&gt;:executors&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;slacker.example.api&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;                                   &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;ThreadPoolExecutor.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="w"&gt;                                                        &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;TimeUnit/MINUTES&lt;/span&gt;
&lt;span class="w"&gt;                                                        &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;LinkedBlockingQueue.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                                                        &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;ThreadPoolExecutor$AbortPolicy.&lt;/span&gt;&lt;span class="p"&gt;))})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Once all your execution in &lt;code&gt;slacker.example.api&lt;/code&gt; blocked, it won't affect requests
to &lt;code&gt;slacker.example.api2&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Bugfix and performance improvement&lt;/h2&gt;
&lt;p&gt;There are some more fixes in release:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Fixed issue in &lt;code&gt;stop-slacker-server&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Use platform specific buffer allocator for (de)serialization&lt;/li&gt;
&lt;li&gt;Improved client side error report. Full context can be accessed via &lt;code&gt;ex-data&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Middleware&lt;/h2&gt;
&lt;p&gt;Also I created two example middleware for slacker:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://github.com/sunng87/slacker-metrics"&gt;metrics middleware&lt;/a&gt; integrates
&lt;a href="https://github.com/sjl/metrics-clojure"&gt;metrics-clojure&lt;/a&gt; into slacker server.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://github.com/sunng87/slacker-htrace"&gt;htrace middleware&lt;/a&gt; enables htrace
distributed tracing on both server and client side.&lt;/li&gt;
&lt;/ul&gt;</content><category term="misc"></category><category term="clojure"></category></entry><entry><title>从 HTTP 0.9 到 QUIC</title><link href="https://sunng.info/blog/cong-http-09-dao-quic.html" rel="alternate"></link><published>2016-10-22T10:39:00+08:00</published><updated>2016-10-22T10:39:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2016-10-22:/blog/cong-http-09-dao-quic.html</id><summary type="html">&lt;p&gt;1989 年 &lt;a href="https://en.wikipedia.org/wiki/Www"&gt;WWW&lt;/a&gt; 诞生之后，HTTP 和 HTML 迅速成为主导世界的应用层协议。在今天，几乎任何场景的应用都或多或少地使用 HTTP（就像 JavaScript 一样 …&lt;/p&gt;</summary><content type="html">&lt;p&gt;1989 年 &lt;a href="https://en.wikipedia.org/wiki/Www"&gt;WWW&lt;/a&gt; 诞生之后，HTTP 和 HTML 迅速成为主导世界的应用层协议。在今天，几乎任何场景的应用都或多或少地使用 HTTP（就像 JavaScript 一样）。HTTP 本身也不仅仅用于网页、浏览器，各式各样的 API，移动应用同样使用这个原本为 HTML 设计的协议。80 和 443 端口成了网络上最重要的端口。&lt;/p&gt;
&lt;p&gt;在近 30 年的历史中，HTTP 协议本身有比较大的发展，同时，还有一些重大的变动也在酝酿之中。这些演化使得这个协议的表现力更强，性能更好，更能满足日新月异的应用需求。这里就来回顾和展望一下 HTTP 的历史和未来。&lt;/p&gt;
&lt;h3&gt;HTTP 0.9&lt;/h3&gt;
&lt;p&gt;历史上第一个有记载的 HTTP 版本是 &lt;a href="https://www.w3.org/Protocols/HTTP/AsImplemented.html"&gt;0.9&lt;/a&gt;，它诞生在 1991 年。这个协议被设计用于从服务器获取 HTML 文档。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;telnet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;
&lt;span class="k"&gt;GET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;EOF&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;整个协议的请求只有1行：&lt;code&gt;GET&lt;/code&gt; 加文档路径。&lt;code&gt;GET&lt;/code&gt;无需多解释，这是 HTTP 至今都一直保留的 "method"，是 HTTP 的动词。1991 年的 HTTP 仅支持 &lt;code&gt;GET&lt;/code&gt; 这唯一的动词。之后的路径是文档在服务器的位置（逻辑位置），即实际要获取的内容。请求以换行结束。服务器收到请求之后，就会返回对应的 HTML 文档内容。输出完毕后，关闭连接。&lt;/p&gt;
&lt;p&gt;当时的 HTTP 协议是一个非常简单的文本协议。直到今天，我们熟悉的 memcached 和 redis 还是使用类似风格的协议。使用者甚至可以直接在 &lt;code&gt;telnet&lt;/code&gt; 中与服务器交互。&lt;/p&gt;
&lt;h3&gt;HTTP 1.0&lt;/h3&gt;
&lt;p&gt;1996 年，一个更加完整，更加接近我们目前对 HTTP 认知的版本，&lt;a href="https://www.w3.org/Protocols/HTTP/1.0/spec.html"&gt;HTTP 1.0&lt;/a&gt; 发布了。这个版本中已经包含了很多我们如今耳熟能详的概念：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HTTP 响应状态码：在响应的第一行，首先返回状态码和说明文本。相当于在 HTTP 0.9 基础上增加了返回类型的支持。&lt;/li&gt;
&lt;li&gt;HTTP 头：除了首行的动词和路径之外，请求和响应都支持一系列的“头”。这些“头”以键值对的形式出现，为当时和日后 HTTP 的各种周边设置提供了载体。&lt;/li&gt;
&lt;li&gt;HTTP 方法：增加了 HEAD 和 POST 等方法。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;HTTP 1.1&lt;/h3&gt;
&lt;p&gt;随着互联网的迅速发展，人们对 HTTP 协议有了更高的要求。1999 年，现在最常见的 &lt;a href="https://www.w3.org/Protocols/rfc2616/rfc2616.html"&gt;HTTP 1.1&lt;/a&gt; 版本诞生了。从此之后，这个 HTTP 协议一直服务至今。并且，在后来的十多年里，这个协议还不断更新和细化，最终在2014年形成了 5 个 RFC：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://tools.ietf.org/html/rfc7230"&gt;RFC7230 HTTP/1.1: Message Syntax and Routing&lt;/a&gt; 协议框架和连接管理&lt;/li&gt;
&lt;li&gt;&lt;a href="http://tools.ietf.org/html/rfc7231"&gt;RFC7231 HTTP/1.1: Semantics and Content&lt;/a&gt; 动词、状态码和头定义&lt;/li&gt;
&lt;li&gt;&lt;a href="http://tools.ietf.org/html/rfc7232"&gt;RFC7232 HTTP/1.1: Conditional Requests&lt;/a&gt; 条件请求&lt;/li&gt;
&lt;li&gt;&lt;a href="http://tools.ietf.org/html/rfc7233"&gt;RFC7233 HTTP/1.1: Range Requests&lt;/a&gt; 断点续传相关&lt;/li&gt;
&lt;li&gt;&lt;a href="http://tools.ietf.org/html/rfc7234"&gt;RFC7234 HTTP/1.1: Caching&lt;/a&gt; 缓存相关&lt;/li&gt;
&lt;li&gt;&lt;a href="http://tools.ietf.org/html/rfc7235"&gt;RFC7235 HTTP/1.1: Authentication&lt;/a&gt; 认证相关&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;HTTP 1.1 协议已经相对庞大，不过要选择其中对 HTTP 1.0 最大的改进，非连接管理莫属。 HTTP 1.0 仅仅介绍了该协议可以使用在 TCP/IP 之上，但是没有进一步地介绍。实际使用的方式仍然是请求结束后就将连接断开，这样我们需要为每一个 HTTP 请求都重新创建 TCP 连接。然而，每一个新的 TCP 连接在创建时需要经历&lt;a href="https://en.wikipedia.org/wiki/Transmission_Control_Protocol#Connection_establishment"&gt;握手&lt;/a&gt;和&lt;a href="https://en.wikipedia.org/wiki/TCP_congestion_control#Slow_start"&gt;慢启动&lt;/a&gt;的机制，客户端在使用新的连接发送 HTTP 请求时，都要经历可观的延迟。随着网页内容的丰富，交互增加，使用新连接的代价是相当大的。&lt;/p&gt;
&lt;p&gt;HTTP 1.1 针对这个问题，对连接管理&lt;a href="https://tools.ietf.org/html/rfc7230#section-6"&gt;有了明确的说明&lt;/a&gt;，默认即使用持久连接的机制。在 HTTP 头中，包含了一个 &lt;code&gt;Connection&lt;/code&gt; 字段，对是否保持、重用连接进行说明。当 &lt;code&gt;Connection: keep-alive&lt;/code&gt;时，连接将被保持，之后客户端可以继续在这个 TCP 连接上发送新的请求。当客户端确实需要关闭连接是，发送的请求要明确说明&lt;code&gt;Connection: close&lt;/code&gt;，服务器端在处理完成后就会关闭连接。&lt;/p&gt;
&lt;p&gt;通过持久连接的机制，同一个 TCP 连接可以传输多次的 HTTP 请求、响应，他的使用率已经得到了一定的提高。不过，由于 HTTP 协议采用请求-响应的模型，在一个 TCP 连接上，同一个时刻只能有一个请求，请求发送后，客户端必须等待返回。这种同步、阻塞的方式限制了连接的吞吐量。当页面上有大量元素时，这样的等待会造成页面内容的顺序加载。因此，为了提高吞吐量，客户端（浏览器）通常会打开多个连接，&lt;a href="http://stackoverflow.com/questions/985431/max-parallel-http-connections-in-a-browser/30064610#30064610"&gt;很多现代浏览器会对每个域名至多打开6个连接&lt;/a&gt;，并且重用它们。然而即使是6个连接，在加载包含大量外部资源的页面时仍然会捉襟见肘。为此，还出现了&lt;a href="http://www.stevesouders.com/blog/2009/05/12/sharding-dominant-domains/"&gt;切分域名&lt;/a&gt;等优化方法。但终归来说，各种方式仍然会面临创建 TCP 连接所带来的开销、延迟。&lt;/p&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/HTTP_pipelining"&gt;HTTP Pipelining&lt;/a&gt;  机制允许客户端在响应返回前直接发送下一个请求，以 Firefox 为例，一旦启用了 HTTP Pipelining，至多可以同时发送 32 个请求，之后只需要等待这些请求次第返回即可。然而这种特性绝非没有代价。Pipelining 机制对顺序有严格的要求。如果响应返回的顺序与请求的顺序不一致，就要求客户端在更高层面增加顺序判断的机制，否则就将引起混乱。而在日常的浏览器场景下，是无从增加这种机制的，这就对服务器的实现提出了要求：单一连接上的请求必须顺序处理，顺序返回。对顺序的要求就导致了机制上的出现排队的可能。如果一个请求的处理时间较慢，那么后续所有的请求都会被拖慢，即使后续的请求很“便宜”。&lt;/p&gt;
&lt;p&gt;对顺序的要求是 HTTP 1.1 无法回避的。因此，后续的改变就需要大刀阔斧了。&lt;/p&gt;
&lt;h3&gt;SPDY &amp;amp; HTTP/2&lt;/h3&gt;
&lt;p&gt;从 2009 年开始，Google 开始设计和开发一个能够解决上述问题的新协议：SPDY。在 SPDY 的基础上，2015 年，&lt;a href="https://tools.ietf.org/html/rfc7540"&gt;HTTP/2&lt;/a&gt;终于诞生了。针对 HTTP 1.1 已有的顺序问题，HTTP/2 给出了相应的回答：多路复用。&lt;/p&gt;
&lt;p&gt;HTTP/2 仍然使用持久连接，客户端与服务器需要长时间保持一个连接。但由于增加了多路复用的机制，因此这里也仅仅需要一个连接即可，这样就避免了 TCP 连接反复创建的开销。多路复用机制是在纯的 TCP 抽象在再抽象出一层 stream 的概念。请求-响应全部搭载在 stream 之上，在同一个 stream 上仍然保持原先的请求-响应模型。但是两端之间可以创建多组 stream，这样就避免了排队等待的问题。而且由于抽象的 stream 几乎没有创建代价，在用户体验和实际性能上，都比原先创建新的 TCP 连接要快很多。服务器在处理多个 stream 的请求时，也无需受限于顺序，这样就可以充分使用多个线程并行处理，且无需做任何等待。&lt;/p&gt;
&lt;p&gt;表面上 HTTP/2 解决了之前所有的问题，然而事实并不尽然。由于今天 HTTP/2 下层的传输协议仍然是 TCP，而 TCP 本身是要求顺序的，对顺序的要求就会带来排队的问题。HTTP/2 层面的多路复用可以说解决了应用层的顺序问题：比如服务器可以用多线程并行处理同一个 TCP 连接上的请求。但是在传输层，顺序问题仍然会限制吞吐量，一旦服务器将结果写到了连接上，这个顺序就将被保证。尽管上文说 stream 之间可以互不干扰，不过在传输层 TCP 仍然会“老老实实”地按照服务器端写出的顺序交付给客户端。可惜客户端并不完全在乎这个顺序，客户端只需要保证在同一个 stream 之内有序即可。如果传输中出现了丢包的情况，操作系统必须等到之前的数据重传完成后才会交付到上层的应用程序。在这部分等待的数据中，很可能就包含其他 stream 的完整数据包，而它们原本可以提前交付给应用层。&lt;/p&gt;
&lt;p&gt;这就是一个多路复用的应用层协议运行在一个非多路复用的传输层协议上产生的问题。&lt;/p&gt;
&lt;h3&gt;QUIC&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://tools.ietf.org/html/draft-tsvwg-quic-protocol"&gt;QUIC&lt;/a&gt; 并非是一个应用层协议。它诞生于 2012 年，目的就是在 SPDY 之后解决传输层上积累的问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;传输层的多路复用&lt;/li&gt;
&lt;li&gt;TCP+TLS 连接创建的代价过大&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;QUIC 的一大特点是基于 UDP，对于这个长期几乎被应用层遗忘的协议来说，是一件令人兴奋的事情。但是 QUIC 选用 UDP 并非意味着它对完整性、顺序传输等机制的放弃，而仅仅是因为 UDP 是 IP 的一个很薄的包装。现在的网络已经不允许人们随意发明一个新的传输层协议了，各种网络设备对数据包的传输层协议都有要求。QUIC 在 UDP 上实现了一个多路复用的 TCP，它几乎包含了 TCP 所有的主要功能。&lt;/p&gt;
&lt;p&gt;与 HTTP/2 + TLS + TCP 相比，QUIC 承担了 HTTP/2 中的整个 stream 管理部分、TLS 安全连接部分和 TCP 的重传、顺序、流控等机制。针对上一节描述的问题，QUIC 可以将 TCP 对顺序的要求进一步细化到 stream 上。在 QUIC 中，重传的粒度被提高到 stream 的层面，这样使得上层的抽象与下层的实现达成了一致。QUIC 重新将 HTTP/2 中较复杂的流管理（在应用层显得不伦不类）移到了本该存在传输协议中，也简化了 HTTP/2 协议，使这个应用层协议可以专注于 HTTP 的语义本身。&lt;/p&gt;
&lt;p&gt;另一方面，目前使用的 TCP + TLS 握手环节存在数据交互过多的问题。尽管 HTTP/2 已经可以实现对一个地址只创建一个连接的机制，但是创建连接仍然需要反复的握手，是一个亟待优化的环节。针对这个问题，Google 推动了一个 TCP 的改进： &lt;a href="https://en.wikipedia.org/wiki/TCP_Fast_Open"&gt;TCP Fast Open&lt;/a&gt;。Fast Open 允许 TCP 客户端在握手的第一个 SYN 包中携带应用层的第一个数据包，握手完成时服务器端可以直接处理这个数据包。与之思路类似的是 TLS 1.3 的 0-RTT 机制，允许客户端在 TLS 握手的 &lt;code&gt;Client HELLO&lt;/code&gt; 环节就带上应用层数据，服务器端回复 &lt;code&gt;Server HELLO&lt;/code&gt; 时就可以直接返回应用层的结果。这两个改进目前还没有完全推广到业界，不过 QUIC 已经吸收了 TCP Fast Open 的机制，并且将在未来直接支持 TLS 1.3&lt;/p&gt;
&lt;h3&gt;总结&lt;/h3&gt;
&lt;p&gt;看 HTTP 协议从过去到未来的发展历程，如果要总结一下的话：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;由于创建连接代价较大，尽可能提高连接使用率：持久连接，Pipelining 机制，多路复用机制&lt;/li&gt;
&lt;li&gt;减小创建连接的代价：减少客户端服务器端交互次数，持久连接，TCP FastOpen, TLS 1.3 0-RTT&lt;/li&gt;
&lt;li&gt;保持应用层与传输层的抽象、实现一致：持久连接，stream 管理&lt;/li&gt;
&lt;/ul&gt;</content><category term="misc"></category><category term="http"></category><category term="programming"></category><category term="quic"></category></entry><entry><title>衡量可扩展性的立方体模型</title><link href="https://sunng.info/blog/heng-liang-ke-kuo-zhan-xing-de-li-fang-ti-mo-xing.html" rel="alternate"></link><published>2016-09-25T14:37:41+08:00</published><updated>2016-09-25T14:37:41+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2016-09-25:/blog/heng-liang-ke-kuo-zhan-xing-de-li-fang-ti-mo-xing.html</id><summary type="html">&lt;p&gt;最近看&lt;a href="http://theartofscalability.com"&gt;《架构即未来》(The Art of Scalability)&lt;/a&gt;，结合自己目前的工作，发现比很多年前看第一版的
时候有更多切身的体会。很一般的博客、新 …&lt;/p&gt;</summary><content type="html">&lt;p&gt;最近看&lt;a href="http://theartofscalability.com"&gt;《架构即未来》(The Art of Scalability)&lt;/a&gt;，结合自己目前的工作，发现比很多年前看第一版的
时候有更多切身的体会。很一般的博客、新闻介绍扩展性的结构不同，这本书2/3的篇幅并非教读者如何切分、
如何拆分，水平还是垂直等等，而是再说组织架构，怎样设置团队，怎样规范架构的流程，各个部门的责任等。
在看第一版的那个时候，这部分我可能就直接忽略了，但是现在有了亲身体会之后，发现大部分的问题，即使
表现为技术问题，甚至是低级失误，其实根源都是管理方面的问题：责任分配不清晰，资源不足，决策草率缺
乏长远考虑，等等等等。这些问题，如果没有经验，在解决了我们熟悉的基本的扩展性问题之后就会慢慢遇到，
从宏观角度看这些恰恰是真正制约扩展性的关键因素。&lt;/p&gt;
&lt;p&gt;不过今天并不是想写这个。书中后半部分介绍了一个衡量扩展性的 AKF 立方体模型，把关于扩展性的问题抽象
得很精致。这个模型用 X/Y/Z 三个轴表示扩展的三个不同方向。其中&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;X 轴表示同等职责、同等分工下的拆分扩展，这种扩展可以通过单纯的拷贝实现业务能力的扩大。&lt;/li&gt;
&lt;li&gt;Y 轴表示通过对职责的细分，将不同的职责拆分到不同的单元，从而实现处理能力的扩大。&lt;/li&gt;
&lt;li&gt;Z 轴表示对客户、来源的切分，实现业务的隔离，从而扩展业务能力。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;上面我自己的概括可能比书上的还要抽象，没关系，举一个团队的例子：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在公司初创的阶段，所有的工程师除了日常的开发，还要担负技术支持和运维等工作职责。在这个阶段，增加
这样的工程师的数量，就是在 Y=0, Z=0 的方向上，进行 X 轴的扩展。&lt;/li&gt;
&lt;li&gt;当业务量增加时，引入了专门的技术支持工程师、销售工程师、运维工程师的专业角色，将原来人员的职责细化。
这是在 Y 轴上进行扩展。这时可以根据各个业务类型的需求不同，分别增加工程师的数量，就实现在 X/Y 轴两个
方向上的同时扩展。与单纯的 X 轴扩展相比，这时扩展的粒度更细，针对性更强（并不需要为了引入一个销售工程师，
而按照最初的标准招聘一个全能的工程师）。&lt;/li&gt;
&lt;li&gt;当业务进一步发展时，可以根据服务客户的地域、行业类型等，对团队进行进一步的划分，这就是在 Z 轴上的
扩展。例如，可以根据市场就近的原则，在广州、上海、北京分别设立办事处，支持附近市场的扩展。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;从这个角度说，这套模型并非仅仅描述软件架构的问题，实际也适用于其他方面。重新回到软件结构，用我们都熟悉的语言来说。&lt;/p&gt;
&lt;p&gt;X 轴的扩展性解决的是最基本的问题，即一个服务能否扩展。通常，通过一个负载均衡设备就可以将业务分别分配
到后端不同的实例上，就解决了 X 轴扩展的问题。如果在 X 轴都完全无法扩展的话，说明架构有严重的问题，
完全不能工作。单纯的 X 轴扩展，用现在的话说就是 monolithic 的系统，这种系统粒度大，内部耦合性强。&lt;/p&gt;
&lt;p&gt;针对这个问题，引入了 Y 轴的扩展，将业务细分。也就是前些年说的面向服务的架构 SOA，和这些年所说的
微服务的架构。这种架构在系统上、业务上、团队上都按照功能职责（动词）进行细分，解除各部分之间的耦合。
在这个基础上，每个细分的模块也必须具备 X 轴扩展的能力。&lt;/p&gt;
&lt;p&gt;Z 轴的细分是根据客户细分，将不同个客户隔离开，同时也达到了故障隔离的目的。这样系统的故障不会蔓延到
整个业务上。Z 轴的扩展成本较高，不同的分区负载可能差别很大。&lt;/p&gt;
&lt;p&gt;以我们目前的消息系统为例，开发初期我们主要关注的也是单纯 X 轴的扩展，这个阶段业务本身的规模有限，
开发者本身对业务的理解和分析能力也不足。所有的功能都集中在单一的系统上，这个系统不仅负责客户端连接
的管理，消息解包等，还要处理很多业务逻辑。&lt;/p&gt;
&lt;p&gt;很快在发展两年后就出现了 monolithic 的弊端：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;代码变大，还在很快变得更大&lt;/li&gt;
&lt;li&gt;耦合性强&lt;/li&gt;
&lt;li&gt;部署规模过大，不灵活&lt;/li&gt;
&lt;li&gt;扩展粒度太大，浪费资源&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这时我们开始未雨绸缪，考虑将业务细分：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;登录授权子系统，动词 login&lt;/li&gt;
&lt;li&gt;消息转发子系统，动词 forward&lt;/li&gt;
&lt;li&gt;消息存储子系统，动词 save&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些子系统的关注点、性能瓶颈都不同，在分别执行 X 轴扩展时，需要的资源也与原先的单一系统不同。同时拆分
后的子系统有了更灵活的部署周期，可以快速修复问题。而这些系统本身，通过我们内部的服务框架、服务发现机制和
资源调度机制，也可以轻松实现基于 X 轴的扩展。&lt;/p&gt;
&lt;p&gt;但是 Y 轴的扩展不是一蹴而就的，而是需要一个过程。对于可以归属给子系统的新功能，直接在子系统上开发。
对已有的功能需要逐步地、谨慎地将原先单一系统中剥离。这部分的功能还在进行中，但是主要的障碍都已经解决，
后续的进展是比较乐观的。&lt;/p&gt;
&lt;p&gt;而作为一个云服务，在 Z 轴的扩展也是十分必要的。但是目前来说，由于本身灵活没有状态，我们还没有完全
地实现 Z 轴方向的切分。一方面，Z 轴的扩展需要较高的成本，在资源有限的情况下，如果按照 Z 轴切分
会导致比较明显的不均衡和浪费。但是在 Z 轴方向，我们也积累了运维的经验，例如对重点用户的高峰时段进行
专门的隔离，尽可能避免多租户环境下 noisy neighbor 的问题。但是目前的机制仍然依赖手动的调度，
相比 X 轴和 Y 轴目前的进展，Z 轴算是刚刚迈出第一步。&lt;/p&gt;
&lt;p&gt;可以说这三个方向的描述，基本上阐明了扩展性这件事的核心。书中也提到，并非所有的业务所有的公司都需要在三个方向上同时扩展。
有的只需要在 X 轴上扩展就可以支撑业务。所以 X/Y/Z 三个轴表示三个潜在的方向，也并非强制需要达到。&lt;/p&gt;</content><category term="misc"></category><category term="programming"></category><category term="architecture"></category><category term="scalability"></category></entry><entry><title>路边野餐</title><link href="https://sunng.info/blog/lu-bian-ye-can.html" rel="alternate"></link><published>2016-07-19T20:53:37+08:00</published><updated>2016-07-19T20:53:37+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2016-07-19:/blog/lu-bian-ye-can.html</id><summary type="html">&lt;p&gt;非常喜欢最近上映的这部《路边野餐》，几乎是重新又喜欢看电影了那种喜欢。
第一回在电影院里看了两遍。第二遍看完基本 …&lt;/p&gt;</summary><content type="html">&lt;p&gt;非常喜欢最近上映的这部《路边野餐》，几乎是重新又喜欢看电影了那种喜欢。
第一回在电影院里看了两遍。第二遍看完基本上看明白剧情了，于是想写个类似
影评的东西。但是写完又不太满意，感觉没表达出什么东西来。最后还是保存下来，
因为好久没有写成段的东西了，慢慢重新找回这个感觉试试看。&lt;/p&gt;
&lt;p&gt;时光，回忆，人与人间的亲疏，是电影长久的主题。&lt;/p&gt;
&lt;h2&gt;一&lt;/h2&gt;
&lt;p&gt;陈升与妻子张夕是在舞厅相识的。陈升性格内向，不擅辞言，更不用说唱歌了。
不过两人是住在瀑布边的房子里，瀑布的声音盖过说话，两人就索性不用说话了。&lt;/p&gt;
&lt;p&gt;张夕患了重病，陈升找当地大哥花和尚借钱为妻子治病。花和尚儿子被仇家活埋，
被杀前还被剁了手。没能留具全尸让花和尚耿耿于怀，多年之后仍然是萦绕的梦
魇：儿子从地下伸出手来，想要个手表。花和尚烧了好几个手表仍然无法摆脱，
只能金盆洗手，在镇远乡下开起钟表店。&lt;/p&gt;
&lt;p&gt;身为小弟的老陈得知这事，带着弟兄替大哥报了断指之仇。然后一人顶了罪，坐
了九年牢，算是还清了大哥的人情。&lt;/p&gt;
&lt;p&gt;度过“没有心脏”的九年铁窗生涯，出狱后老陈得知母亲和张夕都已去世。他在
狱中学会的歌再也没机会唱给自己的爱人。&lt;/p&gt;
&lt;p&gt;母亲去世前，嘱咐将房子留给老陈，并且希望他照看好同母异父兄弟老歪的儿子
卫卫。花和尚也给老陈留下一笔钱，母亲希望老陈用这笔钱盘下老医生的诊所，
算是出狱后的营生。&lt;/p&gt;
&lt;h2&gt;二&lt;/h2&gt;
&lt;p&gt;老陈不断在梦中被芦笙环绕，捉摸不到的，关于母亲的梦让他不安。&lt;/p&gt;
&lt;p&gt;在老歪看来的，对卫卫过度的关心，家中遗产分配的不公，加重了兄弟二人的隔
阂。老歪重修了母亲的墓碑，直接去掉了老陈的名字，划清了界线。为了隔离儿
子和老陈，他让花和尚带卫卫回镇远乡下。&lt;/p&gt;
&lt;p&gt;老医生闲谈时讲起自己年轻时候的好友。冬天的夜晚，两人手握住手电，让光穿
过手指，仿佛可以取暖。老医生毕生清贫，积攒旧物的百宝箱里，除了死去儿子
未用完的婚礼鞭炮，就是与这位好友的约定：一盒磁带，一件衣服。&lt;/p&gt;
&lt;p&gt;老陈决定回一次镇远，寻找芦笙、卫卫和老医生的好友。&lt;/p&gt;
&lt;h2&gt;三&lt;/h2&gt;
&lt;p&gt;火车穿过山洞，明暗变化。车轮在铁轨接缝出撞击，咔咔作响。老陈进入了梦境。&lt;/p&gt;
&lt;p&gt;梦中老陈穿过涵洞到达了世外桃源般的荡麦。他梦境中吹芦笙的苗人而来。准备
离开时，却被梦境中的青年时代的卫卫带入到荡麦的中心。&lt;/p&gt;
&lt;p&gt;在那里，老陈偶遇张夕，梦境中的理发师，在和瀑布小屋神似的理发店里，向她
述说自己的回忆。最后在荡麦的演出上，唱出了自己一直没有机会给张夕唱的歌。
压抑在心中对妻子的情感终得释放。&lt;/p&gt;
&lt;h2&gt;四&lt;/h2&gt;
&lt;p&gt;路边野餐并不是一个专注讲故事的电影，与导演多次提到的几位大师的作品类似，
故事的结构让位于影片中情绪氛围的结构。导演接收采访时说由于电影的情绪已
经在荡麦唱歌时到达顶点，后面直接删除了二十分钟。这也导致电影在母亲这条
故事线索上的不完整，一部分片头铺垫的意向没有进一步延展。&lt;/p&gt;
&lt;p&gt;由于片名和荡麦的缘故，很多人把这部电影和老塔的《潜行者》关联。我倒是认
为其中关于梦境、思念，以及诗歌的串联，更像是老塔的《镜子》。&lt;/p&gt;
&lt;p&gt;与现在大多数通俗易懂的电影不同，路边野餐在即使关键部分没有跌宕的情节，
没有歇斯底里的宣泄，甚至看不出什么碰撞。整个电影保持着连续的一致的节奏，
最后仅仅主人公一首跑调的儿歌就将全片推上了高潮。相比那些节奏趋于离散的
电影，观众通过观察影像片段的值域变化就可以分辨出关键部分（比如一个大场
面，一段冲突）。路边野餐这种风格的电影，则是需要观众在内心对连续的影像
去做一阶二阶三阶导数才能感受到导演的表达。&lt;/p&gt;
&lt;p&gt;相比老塔的电影里晦涩的宗教意味、俄国的文化背景，毕赣的这部电影里传达的
是大多数人都能理解的情感。&lt;/p&gt;
&lt;h2&gt;五&lt;/h2&gt;
&lt;p&gt;电影里野人的线索用来穿插勾连老陈出狱和现今。&lt;/p&gt;
&lt;p&gt;防野人的木棍出现在酒鬼的闹报废汽车，荡麦里的青年卫卫给老陈也插上木棍。&lt;/p&gt;
&lt;p&gt;手电光照手最初是老医生提到的，在梦境里成了老陈的海豚。&lt;/p&gt;
&lt;p&gt;小时候的卫卫在面条和米粉中选择了米粉，于是梦境中他们随意地吃了一些米粉。&lt;/p&gt;
&lt;p&gt;导演本人在荡麦露了个面，演一个卖酒的老板。&lt;/p&gt;</content><category term="misc"></category><category term="movie"></category></entry><entry><title>Writing library for both Clojure and ClojureScript</title><link href="https://sunng.info/blog/writing-library-for-both-clojure-and-clojurescript.html" rel="alternate"></link><published>2016-05-24T22:40:00+08:00</published><updated>2016-05-24T22:40:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2016-05-24:/blog/writing-library-for-both-clojure-and-clojurescript.html</id><summary type="html">&lt;p&gt;Clojure 1.7 introduced reader conditionals and standardized way to write Clojure
code for various platforms. Last few months ago, when I started to write
&lt;a href="https://github.com/sunng87/rigui"&gt;Rigui&lt;/a&gt;, I decided to support both Clojure and
ClojureScript. And it turns out to be possible. However, during the development,
you need to be aware …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Clojure 1.7 introduced reader conditionals and standardized way to write Clojure
code for various platforms. Last few months ago, when I started to write
&lt;a href="https://github.com/sunng87/rigui"&gt;Rigui&lt;/a&gt;, I decided to support both Clojure and
ClojureScript. And it turns out to be possible. However, during the development,
you need to be aware of a few differences between these two hosts, from tooling,
syntax to host natures. That's just way more than reader conditionals.&lt;/p&gt;
&lt;h3&gt;Host differences&lt;/h3&gt;
&lt;p&gt;The most important difference between JVM and JavaScript runtime is multithread
support. At the moment, almost all JavaScript runtime are event-driven. For most
time there is only one thread you can run your code on. And when it comes to
multithread, like WebWorker, the platform offers a message-passing API and you
don't have any shared memory or data. So features like STM, Agent are not
necessary and completely unavailable in this runtime. In case you were using
&lt;code&gt;dosync&lt;/code&gt; or &lt;code&gt;send!&lt;/code&gt; in your Clojure code, you need to add &lt;code&gt;:cljs&lt;/code&gt; branch and
switch to &lt;code&gt;atom&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="nv"&gt;?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:clj&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;dosync&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;get &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;ensure &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;.-buckets&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;trigger-time&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;alter &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;.-buckets&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;dissoc &lt;/span&gt;&lt;span class="nv"&gt;trigger-time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;ensure &lt;/span&gt;&lt;span class="nv"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="ss"&gt;:cljs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;get &lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;.-buckets&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;trigger-time&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;swap!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;.-buckets&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;dissoc &lt;/span&gt;&lt;span class="nv"&gt;trigger-time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="nv"&gt;b&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Due to the same reason, &lt;code&gt;future&lt;/code&gt; and &lt;code&gt;promise&lt;/code&gt; (the Clojure promise) are not
available in ClojureScript. If you were using a promise as derefable result in
API, you need to find other solution in ClojureScript.&lt;/p&gt;
&lt;h3&gt;Reader conditionals&lt;/h3&gt;
&lt;p&gt;The Clojure wiki has &lt;a href="http://clojure.org/reference/reader#_reader_conditionals"&gt;a brief
intro&lt;/a&gt; for reader
conditionals.&lt;/p&gt;
&lt;p&gt;Just to add that if you have a namespace that has completely different
implementations, you can write them in different files. For example,
&lt;code&gt;platform.clj&lt;/code&gt; and &lt;code&gt;platform.cljs&lt;/code&gt;. When importing the namespace &lt;code&gt;(require
'[platform]')&lt;/code&gt;, the reader will look for right file for you.&lt;/p&gt;
&lt;h3&gt;Syntax differences&lt;/h3&gt;
&lt;p&gt;There are some minor syntax differences between clj and cljs. To access a field
of an object or record, you will need to use &lt;em&gt;(.-fieldName obj)&lt;/em&gt;. Otherwise cljs
will try to call it as a function. Actually this syntax is also supported in clj
currently, so I recommend you to switch to this completely in all your clojure
code.&lt;/p&gt;
&lt;p&gt;Cljs requires explicit macro import. You will need &lt;code&gt;(:require-macros)&lt;/code&gt; in your
&lt;code&gt;(ns)&lt;/code&gt; header, or using &lt;code&gt;:refer-macros&lt;/code&gt; in a require form.&lt;/p&gt;
&lt;h3&gt;Tooling&lt;/h3&gt;
&lt;p&gt;If you were to write a library for clojurescript, you can use leiningen without
plugins. Because cljs libraries are also distributed as jars so you don't need
to generate JavaScript files. And in case you need to,
&lt;a href="https://github.com/emezeske/lein-cljsbuild"&gt;cljsbuild&lt;/a&gt; is the de-facto plugin
for cljs development.&lt;/p&gt;
&lt;p&gt;Second, there is no perfect REPL workflow in ClojureScript as in Clojure, to be
honest. It's a pain to test your cljs code in Clojure way and browser/node
environment. If you have recommendation please tell me.&lt;/p&gt;
&lt;p&gt;I thought tests were as pain as REPL until I found
&lt;a href="https://github.com/bensu/doo/"&gt;doo&lt;/a&gt;. doo is a lein plugin made cljs testing as
easy as clojure. What you have to do is provide a runner namespace and generate
js for this runner. No copy/paste required, no duplicated code. Without doo, you
will need to generate JavaScript sources from you clojure test file using
cljsbuild, ns by ns, and execute these files one by one. You can find my test
runner
&lt;a href="https://github.com/sunng87/rigui/blob/master/doo-test/rigui/runner.cljs"&gt;here&lt;/a&gt;
and its &lt;a href="https://github.com/sunng87/rigui/blob/master/project.clj#L21"&gt;cljsbuild
configuration&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;These are my cents about developing library for both clj and cljs. The
conclusion is simply &lt;strong&gt;it works&lt;/strong&gt;. In 2016 you can, and you should be encouraged
to write Clojure library with cross-platform in mind. &lt;/p&gt;</content><category term="misc"></category><category term="clojure"></category><category term="clojurescript"></category><category term="programming"></category></entry><entry><title>将网站迁移到 Gitlab Pages</title><link href="https://sunng.info/blog/jiang-wang-zhan-qian-yi-dao-gitlab-pages.html" rel="alternate"></link><published>2016-04-21T22:40:00+08:00</published><updated>2016-04-21T22:40:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2016-04-21:/blog/jiang-wang-zhan-qian-yi-dao-gitlab-pages.html</id><summary type="html">&lt;p&gt;前段时间 Github 连续出现负面新闻后，Gitlab 似乎感觉嗅到了机会，作为追
赶者发布了新版本和新功能。&lt;a href="http://pages.gitlab.io/"&gt;Gitlab Pages&lt;/a&gt; 就是
他们对应 Github Pages 的 …&lt;/p&gt;</summary><content type="html">&lt;p&gt;前段时间 Github 连续出现负面新闻后，Gitlab 似乎感觉嗅到了机会，作为追
赶者发布了新版本和新功能。&lt;a href="http://pages.gitlab.io/"&gt;Gitlab Pages&lt;/a&gt; 就是
他们对应 Github Pages 的产品。&lt;/p&gt;
&lt;p&gt;经过一番对比之后，我现在把整个网站从 Github 迁移过来，体验好了不少。&lt;/p&gt;
&lt;h4&gt;HTTPS&lt;/h4&gt;
&lt;p&gt;首先，Gitlab Pages 原生支持 HTTPS。如果使用 &lt;code&gt;yourname.gitlab.io&lt;/code&gt; 你会
使用 Gitlab 的 HTTPS 证书。而如果绑定自己的域名你可以使用自己的证书，
比如从 &lt;a href="http://letsencrypt.org/"&gt;LetsEncrypt&lt;/a&gt; 获得的证书。而过去在
Github 原生并不支持 HTTPS，过去是通过
&lt;a href="https://cloudflare.com/"&gt;CloudFlare&lt;/a&gt; 这样的服务做一个代理。但是
CloudFlare 免费的版本不支持自定义的证书，并且从 CloudFlare 到Github 仍
然是 Plain HTTP。&lt;/p&gt;
&lt;h4&gt;Page Builder&lt;/h4&gt;
&lt;p&gt;Github Pages 背后是一个默认的 jekyll。在 LetsEncrypt 验证时，要求建立
的 &lt;code&gt;.well-known&lt;/code&gt; 目录会由于以&lt;code&gt;.&lt;/code&gt;开头，默认被 jekyll 忽略，需要加上一个
&lt;code&gt;.nojekyll&lt;/code&gt; 文件告诉 Github 不要用 jekyll 来构建。&lt;/p&gt;
&lt;p&gt;相比之下 Gitlab 的构建方式就比较透明，官方的例子包含了市面上几乎所有的
静态网站生成器的例子。而且 Gitlab 是通过他的 Runner 来做构建（和
Github 已经具有完整生态不通，Gitlab 作为 catchup 自己做了一整套周边，
连 badge 都是自己实现的）。Gitlab 共享版本的 Runner 运行在 docker 环境
里，用户几乎可以安装任何工具、指定任意的命令来生成网站，只要保证页面生
成在 &lt;code&gt;public/&lt;/code&gt; 目录下即可。之后会触发 Gitlab 内部的 builder 将
&lt;code&gt;public&lt;/code&gt; 目录打包上传到某些神秘的地方，网站就上线了。&lt;/p&gt;
&lt;p&gt;Gitlab 的缺点是文档相对差劲一些，比如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Runner 的文档比较分散，找 registration token 找了很久。另外比较奇怪
  的是 Runner 的注册必须通过一个本地的命令行工具
  &lt;code&gt;gitlab-ci-multi-runner&lt;/code&gt; 来注册，而注册之后这个工具基本上就没什么用
  了。&lt;/li&gt;
&lt;li&gt;设置域名时 Gitlab 对所有域名都提示设置为 CNAME。但是类似 sunng.info
  这样的域名是无法设置为 CNAME 的，这时必须设置为 A 记录，文档上没有说
  明。方法是通过 &lt;code&gt;dig yourname.gitlab.io&lt;/code&gt; 看一下网站实际的 IP，然后把
  这个 IP 设置为 A 记录的地址。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;总而言之瑕不掩瑜，Gitlab 作为后来者一定分析了 Github Pages 使用时的痛
点，想必开发者本身也曾经是 Github 的用户吧。&lt;/p&gt;</content><category term="misc"></category><category term="gitlab"></category><category term="git"></category></entry><entry><title>Using external drive as rootfs for raspberry pi</title><link href="https://sunng.info/blog/using-external-drive-as-rootfs-for-raspberry-pi.html" rel="alternate"></link><published>2016-03-27T21:25:06+08:00</published><updated>2016-03-27T21:25:06+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2016-03-27:/blog/using-external-drive-as-rootfs-for-raspberry-pi.html</id><summary type="html">&lt;p&gt;Raspberry pi uses SD/TF card as storage. However, sometimes it becomes
a bottleneck of performance because of the I/O speed on SD card is
quite limited.&lt;/p&gt;
&lt;p&gt;A simple &lt;code&gt;fio&lt;/code&gt; read/write test on my setup shows even with USB2.0, an
external SSD is way faster than SD …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Raspberry pi uses SD/TF card as storage. However, sometimes it becomes
a bottleneck of performance because of the I/O speed on SD card is
quite limited.&lt;/p&gt;
&lt;p&gt;A simple &lt;code&gt;fio&lt;/code&gt; read/write test on my setup shows even with USB2.0, an
external SSD is way faster than SD card.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sdcard: (groupid=0, jobs=1): err= 0: pid=582: Sun Mar 27 13:36:30 2016
  read : io=65392KB, bw=4442.4KB/s, iops=1110, runt= 14720msec
    clat (usec): min=7, max=18574, avg=175.16, stdev=1657.17
     lat (usec): min=8, max=18575, avg=176.57, stdev=1657.16
    clat percentiles (usec):
     |  1.00th=[    7],  5.00th=[    8], 10.00th=[    8], 20.00th=[    9],
     | 30.00th=[    9], 40.00th=[   10], 50.00th=[   10], 60.00th=[   10],
     | 70.00th=[   11], 80.00th=[   11], 90.00th=[   12], 95.00th=[   15],
     | 99.00th=[  322], 99.50th=[17280], 99.90th=[18048], 99.95th=[18048],
     | 99.99th=[18560]
    bw (KB  /s): min=   93, max=12760, per=95.99%, avg=8527.36, stdev=5281.16
  write: io=65680KB, bw=4461.1KB/s, iops=1115, runt= 14720msec
    clat (usec): min=23, max=3373.8K, avg=699.49, stdev=38712.68
     lat (usec): min=24, max=3373.8K, avg=701.24, stdev=38712.68
    clat percentiles (usec):
     |  1.00th=[   23],  5.00th=[   23], 10.00th=[   24], 20.00th=[   24],
     | 30.00th=[   24], 40.00th=[   24], 50.00th=[   25], 60.00th=[   25],
     | 70.00th=[   26], 80.00th=[   30], 90.00th=[   32], 95.00th=[   45],
     | 99.00th=[   82], 99.50th=[18048], 99.90th=[19840], 99.95th=[20608],
     | 99.99th=[2572288]
    bw (KB  /s): min=   99, max=12832, per=96.08%, avg=8573.43, stdev=5318.31
    lat (usec) : 10=18.57%, 20=29.59%, 50=49.06%, 100=1.59%, 250=0.10%
    lat (usec) : 500=0.30%
    lat (msec) : 4=0.01%, 10=0.01%, 20=0.74%, 50=0.04%, 1000=0.01%
    lat (msec) : &amp;gt;=2000=0.01%
  cpu          : usr=2.38%, sys=4.69%, ctx=1521, majf=0, minf=22
  IO depths    : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, &amp;gt;=64=0.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, &amp;gt;=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, &amp;gt;=64=0.0%
     issued    : total=r=16348/w=16420/d=0, short=r=0/w=0/d=0, drop=r=0/w=0/d=0
     latency   : target=0, window=0, percentile=100.00%, depth=1
ssd: (groupid=0, jobs=1): err= 0: pid=583: Sun Mar 27 13:36:30 2016
  read : io=65392KB, bw=28543KB/s, iops=7135, runt=  2291msec
    clat (usec): min=7, max=196418, avg=63.48, stdev=1925.74
     lat (usec): min=8, max=196419, avg=64.85, stdev=1925.74
    clat percentiles (usec):
     |  1.00th=[    7],  5.00th=[    8], 10.00th=[    8], 20.00th=[    8],
     | 30.00th=[    9], 40.00th=[    9], 50.00th=[   10], 60.00th=[   10],
     | 70.00th=[   10], 80.00th=[   11], 90.00th=[   12], 95.00th=[   15],
     | 99.00th=[ 1608], 99.50th=[ 1832], 99.90th=[ 2128], 99.95th=[ 2256],
     | 99.99th=[146432]
    bw (KB  /s): min= 6144, max=39320, per=100.00%, avg=27607.50, stdev=15537.13
  write: io=65680KB, bw=28669KB/s, iops=7167, runt=  2291msec
    clat (usec): min=15, max=280132, avg=54.86, stdev=2193.37
     lat (usec): min=17, max=280134, avg=56.55, stdev=2193.37
    clat percentiles (usec):
     |  1.00th=[   16],  5.00th=[   16], 10.00th=[   16], 20.00th=[   17],
     | 30.00th=[   17], 40.00th=[   17], 50.00th=[   17], 60.00th=[   17],
     | 70.00th=[   18], 80.00th=[   18], 90.00th=[   22], 95.00th=[   26],
     | 99.00th=[ 1256], 99.50th=[ 1720], 99.90th=[ 2096], 99.95th=[ 2288],
     | 99.99th=[ 3024]
    bw (KB  /s): min= 6176, max=39392, per=100.00%, avg=27584.75, stdev=15473.86
    lat (usec) : 10=21.33%, 20=70.89%, 50=5.47%, 100=0.41%, 250=0.18%
    lat (usec) : 500=0.14%, 750=0.01%, 1000=0.03%
    lat (msec) : 2=1.33%, 4=0.20%, 250=0.01%, 500=0.01%
  cpu          : usr=15.28%, sys=23.14%, ctx=686, majf=0, minf=22
  IO depths    : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, &amp;gt;=64=0.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, &amp;gt;=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, &amp;gt;=64=0.0%
     issued    : total=r=16348/w=16420/d=0, short=r=0/w=0/d=0, drop=r=0/w=0/d=0
     latency   : target=0, window=0, percentile=100.00%, depth=1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So if you want a better pi, it's highly recommended to use an external
disk as rootfs.&lt;/p&gt;
&lt;p&gt;Last year, I backed
&lt;a href="http://gungho.io/product/pidrive/"&gt;piDrive&lt;/a&gt; on Kickstart. The
project was aimed to provide a SSD adapter for pi with low power
requirement. This board made it possible to use an SSD with Pi. And we
can put our rootfs on this disk to run the Pi smoothly.&lt;/p&gt;
&lt;p&gt;Before we get started, I urge you to ensure your power adapter could
provide 5V/2A for Pi2 and 5V/2.5A for Pi3. Otherwise, you may be
resulted in file system corruption. Even worse, the SSD could be
destroyed completely, like my first one.&lt;/p&gt;
&lt;p&gt;You will still need a SD card as &lt;code&gt;/boot&lt;/code&gt; your pi because that's what
pi requires. A ~128MB card will be just ok for &lt;code&gt;/boot&lt;/code&gt; but I know it's
already difficult to find on the market. Perhaps you can create two
partitions on your card so you can utilize the remaining spaces.&lt;/p&gt;
&lt;p&gt;Create partition on your SSD and format them as you want. You can use
ext4 for your rootfs. But for sdcard, only vfat is allowed for the
&lt;code&gt;/boot&lt;/code&gt; partition. Mount the SSD and sdcard to your current system,
for example, as &lt;code&gt;/mnt/root/&lt;/code&gt; and &lt;code&gt;/mnt/boot/&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I'm using &lt;a href="http://archlinuxarm.org"&gt;archlinuxarm&lt;/a&gt; as my os, it's clean
and compact as archlinux. The base tarball is just 270MB, makes
raspbian's 1G+ seems a little bloated.&lt;/p&gt;
&lt;p&gt;Download the tarball from archlinuxarm, at the time of writing, both
Pi2 and Pi3 uses Pi2 file system.&lt;/p&gt;
&lt;p&gt;Extract the tarball to &lt;code&gt;/mnt/root&lt;/code&gt; with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;bsdtar&lt;span class="w"&gt; &lt;/span&gt;-xpf&lt;span class="w"&gt; &lt;/span&gt;ArchLinuxARM-rpi-2-latest.tar.gz&lt;span class="w"&gt; &lt;/span&gt;-C&lt;span class="w"&gt; &lt;/span&gt;/mnt/root
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Copy &lt;code&gt;/boot&lt;/code&gt; directory to SD card:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;cp&lt;span class="w"&gt; &lt;/span&gt;-r&lt;span class="w"&gt; &lt;/span&gt;/mnt/root/boot/*&lt;span class="w"&gt; &lt;/span&gt;/mnt/boot/
mv&lt;span class="w"&gt; &lt;/span&gt;/mnt/root/boot&lt;span class="w"&gt; &lt;/span&gt;/mnt/root/boot_bak
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Edit &lt;code&gt;/mnt/boot/cmdline.txt&lt;/code&gt; to tell Pi where your rootfs is:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;root=/dev/sda1 rootdelay=5 rw rootwait console=ttyAMA0,115200 console=tty1 selinux=0 plymouth.enable=0 smsc95xx.turbo_mode=N dwc_otg.lpm_enable=0 kgdboc=ttyAMA0,115200 elevator=noop
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Actually you just need to change two key/value pairs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Change &lt;code&gt;root=&lt;/code&gt; to your SSD partition that contains root
  fs. Typically your disk will be &lt;code&gt;sda1&lt;/code&gt; here. From my experiments,
  the bootloader doesn't recognize &lt;code&gt;PARTUUID=...&lt;/code&gt; as told by
  &lt;a href="https://learn.adafruit.com/external-drive-as-raspberry-pi-root?view=all"&gt;adafruit&lt;/a&gt;
  and &lt;code&gt;/dev/disk/by-uuid/...&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;rootdelay=5&lt;/code&gt; to let bootloader to wait for your partition to be
  loaded.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then you will need to edit &lt;code&gt;/mnt/root/etc/fstab&lt;/code&gt; to tell linux (not pi) about
the file system layout:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt;
&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;fstab&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;system&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;information&lt;/span&gt;
&lt;span class="err"&gt;#&lt;/span&gt;
&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;system&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;dump&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;pass&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;mmcblk0p1&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;boot&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;vfat&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;defaults&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;sda1&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="nx"&gt;ext4&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;defaults&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So that's simply all you need to get prepared. Run &lt;code&gt;sync&lt;/code&gt; on your
system to make sure everything is on mounted disks. Umount sdcard and
ssd. Set them up on your Pi. Profit!&lt;/p&gt;</content><category term="misc"></category><category term="raspberrypi"></category><category term="linux"></category><category term="archlinux"></category></entry><entry><title>"Smoothing Your Rust Crates Release With Cargo-release"</title><link href="https://sunng.info/blog/smoothing-your-rust-crates-release-with-cargo-release.html" rel="alternate"></link><published>2016-03-21T22:28:06+08:00</published><updated>2016-03-21T22:28:06+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2016-03-21:/blog/smoothing-your-rust-crates-release-with-cargo-release.html</id><summary type="html">&lt;p&gt;Cargo-release is a cargo subcommand that automates your libraries
release process. The idea was inspired by Clojure's leiningen, which
has a &lt;code&gt;release&lt;/code&gt; command does exact same thing.&lt;/p&gt;
&lt;p&gt;Typically, when you decide to release a library to crates.io, you need
to go through following steps at least:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;bump version in …&lt;/li&gt;&lt;/ul&gt;</summary><content type="html">&lt;p&gt;Cargo-release is a cargo subcommand that automates your libraries
release process. The idea was inspired by Clojure's leiningen, which
has a &lt;code&gt;release&lt;/code&gt; command does exact same thing.&lt;/p&gt;
&lt;p&gt;Typically, when you decide to release a library to crates.io, you need
to go through following steps at least:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;bump version in Cargo.toml&lt;/li&gt;
&lt;li&gt;create a git tag for the release&lt;/li&gt;
&lt;li&gt;run &lt;code&gt;cargo publish&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;push tags/commits to github&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;cargo-release command deals these tasks for you. In addition, it
increases your patch version and add a &lt;code&gt;pre&lt;/code&gt; extension after &lt;code&gt;cargo
publish&lt;/code&gt;. The Java/Maven world uses a &lt;code&gt;SNAPSHOT&lt;/code&gt; version for
pre-release. What's sad, no other package manager follows this great
idea. With this pre-release extension, users are able to tell if your
master branch is on a released version or not.&lt;/p&gt;
&lt;p&gt;By default, cargo-release removes pre-release extension as the version
to release. You can override this behavior by a &lt;code&gt;--level&lt;/code&gt;. The value
for &lt;code&gt;--level&lt;/code&gt; can be &lt;code&gt;major&lt;/code&gt;, &lt;code&gt;minor&lt;/code&gt; or &lt;code&gt;patch&lt;/code&gt;. As the name
suggests, it increases different part of a semver version.&lt;/p&gt;
&lt;p&gt;For multi-crates repository, &lt;code&gt;cargo-release&lt;/code&gt; reads you sub-directory
name and use it as prefix of git tag to avoid conflicts.&lt;/p&gt;
&lt;p&gt;Some more features are planned for this tool, for instance:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Custom tag prefix&lt;/li&gt;
&lt;li&gt;Build docs and push to gh-pages&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I hope this tool can be used as a standard of rust library release
process. You can install this tool by &lt;code&gt;cargo install
cargo-release&lt;/code&gt;. And the github repo is
&lt;a href="https://github.com/sunng87/cargo-release"&gt;here&lt;/a&gt;.&lt;/p&gt;</content><category term="misc"></category><category term="rust"></category><category term="programming"></category></entry><entry><title>"Summarizing Changes in Slacker 0.13"</title><link href="https://sunng.info/blog/summarizing-changes-in-slacker-013.html" rel="alternate"></link><published>2015-10-18T13:45:37+08:00</published><updated>2015-10-18T13:45:37+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2015-10-18:/blog/summarizing-changes-in-slacker-013.html</id><summary type="html">&lt;p&gt;After a year of feature development and minor fixes, &lt;a href="https://github.com/sunng87/slacker"&gt;Slacker&lt;/a&gt; and &lt;a href="https://github.com/sunng87/slacker-cluster"&gt;Slacker Cluster&lt;/a&gt; version 0.13 is now available. In this article, I will summarize changes in this release and give a you short introduction of new features and improvements.&lt;/p&gt;
&lt;p&gt;Slacker is an RPC framework features non-invasive design. It exposes …&lt;/p&gt;</summary><content type="html">&lt;p&gt;After a year of feature development and minor fixes, &lt;a href="https://github.com/sunng87/slacker"&gt;Slacker&lt;/a&gt; and &lt;a href="https://github.com/sunng87/slacker-cluster"&gt;Slacker Cluster&lt;/a&gt; version 0.13 is now available. In this article, I will summarize changes in this release and give a you short introduction of new features and improvements.&lt;/p&gt;
&lt;p&gt;Slacker is an RPC framework features non-invasive design. It exposes clojure namespace as remote service, and keeps your remote invocation as simple as local version. Slacker cluster uses Zookeeper for service discovery, helps you to build micro-service based architecture. The grouping function gives you full control over request routing.&lt;/p&gt;
&lt;h3&gt;Application managed thread pool&lt;/h3&gt;
&lt;p&gt;During 0.12 series, Slacker server uses Netty managed thread pool for task execution. Netty assign a single thread from its pool to a connection. The thread will be used for all requests from the connection. And these requests will be processed in a serial manner. This works perfect for non-blocking tasks. However, if your tasks are data-intensive, this causes head-of-line blocking issue.&lt;/p&gt;
&lt;p&gt;The Netty design is to keep request/esponse ordered for a connection. Slacker uses multiplex on its connection, so ordering is not an issue. In 0.13, we now use an application managed thread pool for task execution. You can still configure the pool size by &lt;code&gt;:threads&lt;/code&gt; option. If your tasks are non-blocking ones, just set the threads equals your cores. Otherwise, you can customize the size based on blocking time of your tasks.&lt;/p&gt;
&lt;h3&gt;Interrupt&lt;/h3&gt;
&lt;p&gt;0.13 introduces a new low-level API called &lt;code&gt;interrupt&lt;/code&gt; and a new option &lt;code&gt;interrupt-on-timeout&lt;/code&gt;. This is backend by a new protocol level command, &lt;code&gt;interrupt&lt;/code&gt;. The new command allows the client to interrupt server execution for a particular task. The server thread will be released once &lt;code&gt;interrupt&lt;/code&gt; received.&lt;/p&gt;
&lt;p&gt;Typically you don't have to call &lt;code&gt;interrupt&lt;/code&gt; on slacker client. The &lt;code&gt;interrupt-on-timeout&lt;/code&gt; option allows you to cancel a tasks on both client and server when it's timeout. Following the design principle of transparency, the cancellation is synchronized to server-side, just like a local invocation.&lt;/p&gt;
&lt;h3&gt;Plug-able Serializers&lt;/h3&gt;
&lt;p&gt;To keep our dependency-tree clean, we detects cheshire/nippy/carbonite at runtime, and makes these dependencies totally optional to slacker.&lt;/p&gt;
&lt;p&gt;The new default serializer is Clojure EDN because it requires no additional packages. Slacker provides built-in support for cheshire(&lt;code&gt;:json&lt;/code&gt;) and nippy(&lt;code&gt;:nippy&lt;/code&gt;). &lt;a href="https://github.com/ptaoussanis/nippy"&gt;Nippy&lt;/a&gt; is high recommended for Slacker. It's a clojure-native binary format, compact and fast. We have been using nippy with Slacker in our production for a long time without any issue.&lt;/p&gt;
&lt;p&gt;You can also extend our serializer system by create new implementations for serializer multi-method.&lt;/p&gt;
&lt;h3&gt;Server data for Slacker Cluster&lt;/h3&gt;
&lt;p&gt;The new Slacker Cluster &lt;code&gt;start-slacker-server&lt;/code&gt; offers a new option &lt;code&gt;:server-data&lt;/code&gt;. It allows you to assign some data for this server, for example, the environment (production or stage?). The data will be stored to Zookeeper and synchronized to client side.&lt;/p&gt;
&lt;p&gt;In the client grouping function, you can use the data to filter servers:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;slacker.client.cluster&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;:refer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;:all&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;def &lt;/span&gt;&lt;span class="nv"&gt;sc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;clustered-slackerc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;...&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;defn-remote&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;sc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;some-function&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="ss"&gt;:grouping&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kd"&gt;ns &lt;/span&gt;&lt;span class="nv"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;params&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;servers&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;               &lt;/span&gt;&lt;span class="c1"&gt;;; test :prod? property of server data&lt;/span&gt;
&lt;span class="w"&gt;               &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;rand-nth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;filter &lt;/span&gt;&lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:prod?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;server-data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="nv"&gt;sc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;servers&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The grouping function in this snippet filters production servers, and choose one from them to call.&lt;/p&gt;
&lt;p&gt;The server side looks pretty simple:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;slacker.server.cluster&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;:refer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;start-slacker-server&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;start-slacker-server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;some-port&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;some-ns&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="ss"&gt;:server-data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:prod?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;true&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Besides of these features, we also fixed issues Zookeeper timeout issue on startup, ephemeral node lost and etc.&lt;/p&gt;
&lt;p&gt;After almost 4 years of development, we are stepping near to a 1.0 release. Hopefully we will reach the 1.0 milestone in 2016.&lt;/p&gt;</content><category term="misc"></category><category term="'programming'"></category><category term="'clojure'"></category></entry><entry><title>Running Ring web application on HTTP2 with rj9a</title><link href="https://sunng.info/blog/running-ring-web-application-on-http2-with-rj9a.html" rel="alternate"></link><published>2015-07-25T14:54:54+08:00</published><updated>2015-07-25T14:54:54+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2015-07-25:/blog/running-ring-web-application-on-http2-with-rj9a.html</id><summary type="html">&lt;p&gt;&lt;a href="https://github.com/sunng87/ring-jetty9-adapter"&gt;Ring-jetty9-adapter(rj9a)&lt;/a&gt; just received an update, the &lt;a href="https://clojars.org/info.sunng/ring-jetty9-adapter"&gt;0.9&lt;/a&gt;, with Jetty 9.3 adoption. The most important feature in this release is support for HTTP2. That means, you can run your Ring application on the new HTTP2 protocol.&lt;/p&gt;
&lt;p&gt;In case you still have no idea about HTTP2, it's the biggest …&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;a href="https://github.com/sunng87/ring-jetty9-adapter"&gt;Ring-jetty9-adapter(rj9a)&lt;/a&gt; just received an update, the &lt;a href="https://clojars.org/info.sunng/ring-jetty9-adapter"&gt;0.9&lt;/a&gt;, with Jetty 9.3 adoption. The most important feature in this release is support for HTTP2. That means, you can run your Ring application on the new HTTP2 protocol.&lt;/p&gt;
&lt;p&gt;In case you still have no idea about HTTP2, it's the biggest update to HTTP, the protocol we use everyday and everywhere. In short, &lt;a href="https://en.wikipedia.org/wiki/HTTP/2"&gt;HTTP2&lt;/a&gt; introduces connection multiplex to reuse connection for several request/response simultaneously. Also the persisted connection makes server push possible, and that's part of HTTP2. HTTP2 uses TLS by default. In order to keep most servers backward compatible, we will run HTTP2 and HTTP1.1 on the same server and port. Modern client will detect server configuration on SSL handshake, via a TLS extension called ALPN. The server will list supported application layer protocols in SERVER HELLO and let client to choose what it understands.&lt;/p&gt;
&lt;p&gt;The basic part of HTTP2 is fully compatible for 1.1, so you won't have to modify your application code to use it. In rj9a, just add option &lt;code&gt;:h2? true&lt;/code&gt; to enable HTTP2. And &lt;code&gt;:h2c? true&lt;/code&gt; to enable its variance on plain socket.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;defn &lt;/span&gt;&lt;span class="nv"&gt;dummy-app&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;req&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:body&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;It works&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;:status&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;jetty/run-jetty&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;dummy-app&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:port&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;
&lt;span class="w"&gt;                            &lt;/span&gt;&lt;span class="ss"&gt;:h2c?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;true&lt;/span&gt;
&lt;span class="w"&gt;                            &lt;/span&gt;&lt;span class="ss"&gt;:h2?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;true&lt;/span&gt;
&lt;span class="w"&gt;                            &lt;/span&gt;&lt;span class="ss"&gt;:ssl?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;true&lt;/span&gt;
&lt;span class="w"&gt;                            &lt;/span&gt;&lt;span class="ss"&gt;:ssl-port&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5443&lt;/span&gt;
&lt;span class="w"&gt;                            &lt;/span&gt;&lt;span class="ss"&gt;:keystore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;...&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;                            &lt;/span&gt;&lt;span class="ss"&gt;:key-password&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;...&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To test HTTP2 interface, you will need to install &lt;a href="https://nghttp2.org"&gt;nghttp&lt;/a&gt;. It's pretty similar to curl:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;nghttp&lt;span class="w"&gt; &lt;/span&gt;-v&lt;span class="w"&gt; &lt;/span&gt;https://localhost:5443
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.000&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Connected
The&lt;span class="w"&gt; &lt;/span&gt;negotiated&lt;span class="w"&gt; &lt;/span&gt;protocol:&lt;span class="w"&gt; &lt;/span&gt;h2-14
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.031&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;send&lt;span class="w"&gt; &lt;/span&gt;SETTINGS&lt;span class="w"&gt; &lt;/span&gt;frame&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;&lt;span class="nv"&gt;length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;12&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;flags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0x00,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;stream_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&amp;gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;niv&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;SETTINGS_MAX_CONCURRENT_STREAMS&lt;span class="o"&gt;(&lt;/span&gt;0x03&lt;span class="o"&gt;)&lt;/span&gt;:100&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;SETTINGS_INITIAL_WINDOW_SIZE&lt;span class="o"&gt;(&lt;/span&gt;0x04&lt;span class="o"&gt;)&lt;/span&gt;:65535&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.031&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;send&lt;span class="w"&gt; &lt;/span&gt;PRIORITY&lt;span class="w"&gt; &lt;/span&gt;frame&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;&lt;span class="nv"&gt;length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;flags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0x00,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;stream_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&amp;gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;dep_stream_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;weight&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;201&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;exclusive&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.031&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;send&lt;span class="w"&gt; &lt;/span&gt;PRIORITY&lt;span class="w"&gt; &lt;/span&gt;frame&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;&lt;span class="nv"&gt;length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;flags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0x00,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;stream_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&amp;gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;dep_stream_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;weight&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;101&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;exclusive&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.031&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;send&lt;span class="w"&gt; &lt;/span&gt;PRIORITY&lt;span class="w"&gt; &lt;/span&gt;frame&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;&lt;span class="nv"&gt;length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;flags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0x00,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;stream_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt;&amp;gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;dep_stream_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;weight&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;exclusive&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.031&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;send&lt;span class="w"&gt; &lt;/span&gt;PRIORITY&lt;span class="w"&gt; &lt;/span&gt;frame&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;&lt;span class="nv"&gt;length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;flags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0x00,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;stream_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;9&lt;/span&gt;&amp;gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;dep_stream_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;weight&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;exclusive&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.031&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;send&lt;span class="w"&gt; &lt;/span&gt;PRIORITY&lt;span class="w"&gt; &lt;/span&gt;frame&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;&lt;span class="nv"&gt;length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;flags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0x00,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;stream_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;11&lt;/span&gt;&amp;gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;dep_stream_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;weight&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;exclusive&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.031&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;send&lt;span class="w"&gt; &lt;/span&gt;HEADERS&lt;span class="w"&gt; &lt;/span&gt;frame&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;&lt;span class="nv"&gt;length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;37&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;flags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0x25,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;stream_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;13&lt;/span&gt;&amp;gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;END_STREAM&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;END_HEADERS&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;PRIORITY
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;padlen&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;dep_stream_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;11&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;weight&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;16&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;exclusive&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Open&lt;span class="w"&gt; &lt;/span&gt;new&lt;span class="w"&gt; &lt;/span&gt;stream
&lt;span class="w"&gt;         &lt;/span&gt;:method:&lt;span class="w"&gt; &lt;/span&gt;GET
&lt;span class="w"&gt;         &lt;/span&gt;:path:&lt;span class="w"&gt; &lt;/span&gt;/
&lt;span class="w"&gt;         &lt;/span&gt;:scheme:&lt;span class="w"&gt; &lt;/span&gt;https
&lt;span class="w"&gt;         &lt;/span&gt;:authority:&lt;span class="w"&gt; &lt;/span&gt;localhost:5443
&lt;span class="w"&gt;         &lt;/span&gt;accept:&lt;span class="w"&gt; &lt;/span&gt;*/*
&lt;span class="w"&gt;         &lt;/span&gt;accept-encoding:&lt;span class="w"&gt; &lt;/span&gt;gzip,&lt;span class="w"&gt; &lt;/span&gt;deflate
&lt;span class="w"&gt;         &lt;/span&gt;user-agent:&lt;span class="w"&gt; &lt;/span&gt;nghttp2/1.0.1
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.032&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;recv&lt;span class="w"&gt; &lt;/span&gt;SETTINGS&lt;span class="w"&gt; &lt;/span&gt;frame&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;&lt;span class="nv"&gt;length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;12&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;flags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0x00,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;stream_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&amp;gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;niv&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;SETTINGS_HEADER_TABLE_SIZE&lt;span class="o"&gt;(&lt;/span&gt;0x01&lt;span class="o"&gt;)&lt;/span&gt;:4096&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;SETTINGS_INITIAL_WINDOW_SIZE&lt;span class="o"&gt;(&lt;/span&gt;0x04&lt;span class="o"&gt;)&lt;/span&gt;:65535&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.032&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;send&lt;span class="w"&gt; &lt;/span&gt;SETTINGS&lt;span class="w"&gt; &lt;/span&gt;frame&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;&lt;span class="nv"&gt;length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;flags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0x01,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;stream_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&amp;gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ACK
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;niv&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.032&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;recv&lt;span class="w"&gt; &lt;/span&gt;SETTINGS&lt;span class="w"&gt; &lt;/span&gt;frame&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;&lt;span class="nv"&gt;length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;flags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0x01,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;stream_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&amp;gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ACK
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;niv&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.033&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;recv&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;stream_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;13&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;:status:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;200&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.033&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;recv&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;stream_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;13&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;server:&lt;span class="w"&gt; &lt;/span&gt;Jetty&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;9&lt;/span&gt;.3.1.v20150714&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.033&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;recv&lt;span class="w"&gt; &lt;/span&gt;HEADERS&lt;span class="w"&gt; &lt;/span&gt;frame&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;&lt;span class="nv"&gt;length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;flags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0x04,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;stream_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;13&lt;/span&gt;&amp;gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;END_HEADERS
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;padlen&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;First&lt;span class="w"&gt; &lt;/span&gt;response&lt;span class="w"&gt; &lt;/span&gt;header
It&lt;span class="w"&gt; &lt;/span&gt;works&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.033&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;recv&lt;span class="w"&gt; &lt;/span&gt;DATA&lt;span class="w"&gt; &lt;/span&gt;frame&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;&lt;span class="nv"&gt;length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;8&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;flags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0x01,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;stream_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;13&lt;/span&gt;&amp;gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;END_STREAM
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.033&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;send&lt;span class="w"&gt; &lt;/span&gt;GOAWAY&lt;span class="w"&gt; &lt;/span&gt;frame&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;&lt;span class="nv"&gt;length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;8&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;flags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0x00,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;stream_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&amp;gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;last_stream_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;error_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;NO_ERROR&lt;span class="o"&gt;(&lt;/span&gt;0x00&lt;span class="o"&gt;)&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;opaque_data&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)=[])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The verbose output shows us every detail about request and response in HTTP2.&lt;/p&gt;
&lt;p&gt;Note that in order to run HTTP2, you will need JDK 8 / OpenJDK 1.8 and &lt;a href="http://www.eclipse.org/jetty/documentation/current/alpn-chapter.html#alpn-starting"&gt;put alpn-boot jar in your bootclasspath&lt;/a&gt;.  I have created &lt;a href="https://github.com/sunng87/lein-bootclasspath-deps"&gt;a leiningen plugin&lt;/a&gt; to manage bootclasspath in clojure project.&lt;/p&gt;
&lt;p&gt;The complete example is available in &lt;a href="https://github.com/sunng87/ring-jetty9-adapter/blob/master/examples/rj9a/http2.clj"&gt;github repository&lt;/a&gt;.&lt;/p&gt;</content><category term="misc"></category><category term="'programming'"></category><category term="'clojure'"></category></entry><entry><title>"Handlebars 的 rust 实现"</title><link href="https://sunng.info/blog/handlebars-de-rust-shi-xian.html" rel="alternate"></link><published>2015-01-25T16:22:50+08:00</published><updated>2015-01-25T16:22:50+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2015-01-25:/blog/handlebars-de-rust-shi-xian.html</id><summary type="html">&lt;p&gt;本来一度感觉到用过 Clojure 之后很难对新语言产生兴趣了，还好遇到了 Rust 再次激活了这方面的生命力。今年的重点之一是学 …&lt;/p&gt;</summary><content type="html">&lt;p&gt;本来一度感觉到用过 Clojure 之后很难对新语言产生兴趣了，还好遇到了 Rust 再次激活了这方面的生命力。今年的重点之一是学习 Rust 语言，方便自己能真正 touch bare metal。1月17号的 Rust 聚会上发现很多人都持有类似的想法。 C++ 之后鲜有这种语言，以至于之后成长起来的一代人都是在一个 VM 里编程，无论是 Java 还是 Python，最终都没有办法自己去管理内存，Rust 的出现给了大家一个机会。一个具备现代特性的系统编程语言，Zero runtime，可以运行在各种设备上。去年还给&lt;a href="http://sunng.info/blog/blog/2014/04/20/rust-concurrent-made-safely/"&gt;程序员杂志写了一篇 Rust 的文章&lt;/a&gt;，结果导致现在程序员杂志停刊了。&lt;/p&gt;
&lt;p&gt;扯远了，和当时学 Clojure 一样，这次的计划还是写一个正经的项目来促进学习。关于时机的选择，主要是 crates.io 仓库的发布基本上标志生态圈开始建立了，这个时候写东西就方便很多了。&lt;/p&gt;
&lt;p&gt;这次选的就是实现 Handlebars，主要原因是 rust 已经逐渐有一点 web 开发的生态圈了，但是缺少一个模版引擎，于是我就来趟这潭浑水吧。为什么是 Handlebars 呢：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不要把 rust 代码写进 html 模版里，反例： jsp, ejs&lt;/li&gt;
&lt;li&gt;不要把 html 代码写进 rust 里，反例： hiccup&lt;/li&gt;
&lt;li&gt;能够复用，基于“继承”而不是 include&lt;/li&gt;
&lt;li&gt;能够简单地自定义标签，反例：mustach&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;基于以上的原则，&lt;a href="https://github.com/sunng87/handlebars-rust"&gt;handlebars-rust&lt;/a&gt; 实现了基本的模版解析、渲染，重用机制（partial/include）和自定义 helper。除了不支持一些 mustach 风格的语法以外（可以用 #each / #if 这样的 helper 替代，更清晰），基本上所有的 handlebars 功能全部支持了。如果有遗漏的话欢迎 PR。另外还写了一个 &lt;a href="https://github.com/sunng87/handlebars-iron"&gt;handlebars-iron&lt;/a&gt; 项目，作为一个 &lt;a href="http://ironframework.io"&gt;Iron 框架&lt;/a&gt;的 middlaware。&lt;/p&gt;
&lt;p&gt;简单总结几点收获：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Rust 中要实现类似OO的多态需要用枚举类型，trait可以用来做范型&lt;/li&gt;
&lt;li&gt;静态类型语言和一个基于 javascript 视角的模版引擎对接很困难，比如 js 里有 falsy 的概念，if 的判断里 &lt;code&gt;false&lt;/code&gt;/&lt;code&gt;0&lt;/code&gt;/&lt;code&gt;""&lt;/code&gt;/&lt;code&gt;[]&lt;/code&gt; 这些值都是 false，但是在rust里需要根据不同类型作判断，直接使用简直不可能。所以在 handlebars-rust 里利用了 rustc-serialize 里的 &lt;code&gt;Json&lt;/code&gt; 枚举类型（没有真正序列化），要求所有渲染的数据都必须实现 &lt;code&gt;ToJson&lt;/code&gt;，算是设计上的一个取舍。&lt;/li&gt;
&lt;li&gt;Rust 的 derive 是一个神奇的功能，后来发现确实是一个 magic，因为可以 derive 的 trait 都是写死在编译器里的&lt;/li&gt;
&lt;li&gt;关于 Rust 的 ownership 看&lt;a href="http://nercury.github.io/rust/guide/2015/01/19/ownership.html"&gt;这篇文章&lt;/a&gt;，作者承诺再写一篇关于 borrow 和 lifetime 的，相信也不错&lt;/li&gt;
&lt;li&gt;有任何问题都可以在 stackoverflow 上问，有几个人会很快回复&lt;/li&gt;
&lt;/ul&gt;</content><category term="misc"></category><category term="'programming'"></category><category term="'rust'"></category></entry><entry><title>"AVOS Cloud 实时通信服务架构：微服务和服务发现"</title><link href="https://sunng.info/blog/avos-cloud-shi-shi-tong-xin-fu-wu-jia-gou-wei-fu-wu-he-fu-wu-fa-xian.html" rel="alternate"></link><published>2014-08-19T16:46:20+08:00</published><updated>2014-08-19T16:46:20+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2014-08-19:/blog/avos-cloud-shi-shi-tong-xin-fu-wu-jia-gou-wei-fu-wu-he-fu-wu-fa-xian.html</id><summary type="html">&lt;p&gt;“微服务” (Microservice) 是今年特别热的一个概念，&lt;a href="http://martinfowler.com/articles/microservices.html"&gt;Martin Fowler 的文章&lt;/a&gt;对微服务作了详细的介绍。简而言之，微服务鼓励用户把功能拆分以细粒 …&lt;/p&gt;</summary><content type="html">&lt;p&gt;“微服务” (Microservice) 是今年特别热的一个概念，&lt;a href="http://martinfowler.com/articles/microservices.html"&gt;Martin Fowler 的文章&lt;/a&gt;对微服务作了详细的介绍。简而言之，微服务鼓励用户把功能拆分以细粒度的服务接口暴露出来，并通过REST 服务或轻量级消息队列集成。在微服务架构里，一个业务的实现，可能由不同的功能单元组合而实现。&lt;/p&gt;
&lt;p&gt;在 AVOS Cloud，我们提供数据存储、统计、实时通信等不同功能的服务，在实现上，这些功能需要共用基础设施，有的服务本身也根据业务性质的不同拆分功能模块，我们目前就是以这种“微服务”架构思路来实现拆分。有句话说，if you cannot split, you cannot scale.&lt;/p&gt;
&lt;p&gt;以实时通信服务为例，根据功能角色的不同，我们有这样一些模块：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Push 服务：处理推送的订阅关系，触发推送&lt;/li&gt;
&lt;li&gt;长连接服务器：维持设备与服务的长连接&lt;/li&gt;
&lt;li&gt;Router：应用层的 lookup，负责分配合适的长连接服务器给新设备&lt;/li&gt;
&lt;li&gt;WatchDog：从多台长连接服务器收集运行和统计数据，对异常情况发起报警&lt;/li&gt;
&lt;li&gt;数据存储：群组数据，用户可以通过API访问&lt;/li&gt;
&lt;li&gt;HBase：消息记录存储&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;模块间的集成，根据业务的特性分别使用 &lt;a href="https://github.com/sunng87/slacker"&gt;Slacker&lt;/a&gt; 远程调用框架和 &lt;a href="http://twitter.github.io/kestrel/"&gt;Kestrel&lt;/a&gt; 消息队列。
对于可能耗时较大的任务，我们通过 Kestrel 队列放到后台执行，避免阻塞前台服务，影响吞吐量。而另一些需要实时的集成，则使用 Slacker 远程调用实现多个进程间的通信。&lt;/p&gt;
&lt;p&gt;Slacker 是一个专门为 Clojure 语言设计的远程调用框架，利用 Slacker 你可以暴露一个 或多个 Clojure 的 namespace 供远程调用。在客户端，Slacker 利用 &lt;a href="http://clojure.org/macros"&gt;Clojure 宏&lt;/a&gt;的特性，保持远程调用和本地调用的代码完全一致，这样本地和远程调用的切换只要更改一个 &lt;code&gt;(require)&lt;/code&gt; 即可实现，把框架对业务代码的侵入降到最低。此外，Slacker 使用二进制序列化 &lt;a href="https://github.com/ptaoussanis/nippy"&gt;nippy&lt;/a&gt;，在网络连接层面使用异步复用，同时在超时方面也做了良好的控制。&lt;/p&gt;
&lt;p&gt;以上的基础设施帮助我们良好地拆分模块，为下一步的扩展提供了可能。&lt;/p&gt;
&lt;h3&gt;服务发现&lt;/h3&gt;
&lt;p&gt;长连接服务器是实时通信的功能核心，它的瓶颈在内存和 CPU，可以通过增加部署来达到线性扩展。随着业务量的增加和硬件资源的整合，它可能会面临较频繁的部署变化，另外它也需要有能通过新增部署来快速平滑高峰压力的能力。基于 &lt;a href="https://en.wikipedia.org/wiki/Publish/subscribe"&gt;Pub/Sub 抽象&lt;/a&gt;的消息队列对此有良好的支持，但这对我们以 RPC 为核心的集成方式提出了新的要求，依赖模块也能快速响应服务部署的变化：我们不可能在新增某个服务部署后修改每个依赖的配置再逐一重启。&lt;/p&gt;
&lt;p&gt;在这方面，我们利用了 &lt;a href="https://github.com/sunng87/slacker-cluster"&gt;Slacker Cluster 框架&lt;/a&gt;。他的核心思想是在部署和服务间增加一层抽象：对于服务的消费者而言，只需声明自己所依赖的服务，而无需静态地了解进程的地址。&lt;/p&gt;
&lt;p&gt;所有的服务提供者将自己能够提供的服务注册在 &lt;a href="https://zookeeper.apache.org/"&gt;Zookeeper&lt;/a&gt; 集群里，并将部署地址注册为 Ephemeral 节点。Ephemeral 节点在创建它的连接断后会自动删除，这样当一个部署下线后，它相应的节点也会自动删除。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Zookeeper 目录结构&lt;/span&gt;
ls&lt;span class="w"&gt; &lt;/span&gt;/slacker/example-cluster/namespaces/
&lt;span class="o"&gt;[&lt;/span&gt;my.serviceA,&lt;span class="w"&gt; &lt;/span&gt;my.serviceB&lt;span class="o"&gt;]&lt;/span&gt;

ls&lt;span class="w"&gt; &lt;/span&gt;/slacker/example-cluster/namespaces/my.serviceA
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;192&lt;/span&gt;.168.1.100:2104,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;192&lt;/span&gt;.168.1.101:2014...&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;所有服务的客户端会 watch 自己感兴趣的 Zookeeper 节点，而部署变化时，所有的客户端都会得到通知，进而刷新服务列表，将流量引向新的节点。&lt;/p&gt;
&lt;p&gt;在实时通信服务中，Router 服务会通过这个 RPC 机制轮询所有在线的长连接服务器，记录他们实时的运行状态。所有的用户设备并非直接连接到固定的长连接服务器，而是先询问 Router，由后者分配一台压力较轻的实例。当有新的长连接服务器部署后，Router收到通知，新的连接将优先连接这个新进程。此外，监控和数据收集的服务也会自动地把新实例加入管理范围。&lt;/p&gt;
&lt;p&gt;有了这样一套服务发现机制，我们就可以对整个架构中的任意模块随时增减部署，保证服务可以以健康的状态运行。未来，我们还会集成云主机的提供商的API，来实现基础设施的自动化：当系统压力达到阀值时，云主机自动分配新的资源自动开机，jenkins 自动部署，加上现有的服务发现机制，实现0手工操作。这将是云服务运维的新篇。&lt;/p&gt;
&lt;p&gt;原载 &lt;a href="https://blog.avoscloud.com/1927/"&gt;AVOSCloud Blog&lt;/a&gt;&lt;/p&gt;</content><category term="misc"></category><category term="programming"></category></entry><entry><title>"在 Docker 中安装和使用 Rust nightly 版本"</title><link href="https://sunng.info/blog/zai-docker-zhong-an-zhuang-he-shi-yong-rust-nightly-ban-ben.html" rel="alternate"></link><published>2014-08-02T18:02:12+08:00</published><updated>2014-08-02T18:02:12+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2014-08-02:/blog/zai-docker-zhong-an-zhuang-he-shi-yong-rust-nightly-ban-ben.html</id><summary type="html">&lt;p&gt;一直关注 Rust 语言，最近一下发现了两个 web 框架，&lt;a href="http://ironframework.org"&gt;Iron&lt;/a&gt; 和 &lt;a href="http://nickel.rs"&gt;Nickel.rs&lt;/a&gt;。先不说这两个框架成熟度如何，一般情况下，一个语言有了 web 框架 …&lt;/p&gt;</summary><content type="html">&lt;p&gt;一直关注 Rust 语言，最近一下发现了两个 web 框架，&lt;a href="http://ironframework.org"&gt;Iron&lt;/a&gt; 和 &lt;a href="http://nickel.rs"&gt;Nickel.rs&lt;/a&gt;。先不说这两个框架成熟度如何，一般情况下，一个语言有了 web 框架，算是一个里程碑，说明他离靠谱也不远了。这样我决定跟一下 nightly 版本（新框架都是跟 nightly），另外也能感受一下 &lt;a href="https://mail.mozilla.org/pipermail/rust-dev/2014-March/009090.html"&gt;Yehuda Katz 的构建工具 Cargo&lt;/a&gt;。ArchLinux 的仓库里已经有 0.11 版本，再用脚本安装必然会有冲突。于是想到了最近半年&lt;a href="https://twitter.com/jessenoller/status/495037475421954048"&gt;最火的 Docker&lt;/a&gt;，可以轻松的创建多个环境，正是一个非常好的场景。&lt;/p&gt;
&lt;h2&gt;安装&lt;/h2&gt;
&lt;p&gt;安装 Docker, Arch Linux 仓库里很早就有，非常方便：&lt;code&gt;sudo pacman -S docker&lt;/code&gt;。完成之后启动他：&lt;code&gt;sudo systemctl start docker&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;之后我们拉一个 ubuntu 的镜像下来：&lt;code&gt;docker pull ubuntu&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;完成之后，我们启动一个 container，做一些基本的 setup：&lt;code&gt;docker run -i -t ubuntu:14.04 /bin/bash&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;这相当与运行在 ubuntu:14.04 这个镜像上运行一个 shell，接下来就进入了这个 shell 环境，和 ubuntu 安装版本完全一致，我们做一些基础的准备，安装一些必要的工具：&lt;code&gt;apt-get install build-essentials git curl libssl-dev&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;之后，就可以下载 Rust 提供的脚本来安装 nightly 版本了：&lt;code&gt;curl -s http://www.rust-lang.org/rustup.sh &amp;gt; rustup&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;这里有个问题，rustup 脚本判断64位系统时会出错导致安装失败：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Detect 64 bit linux systems with 32 bit userland and force 32 bit compilation&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$CFG_OSTYPE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;unknown-linux-gnu&lt;span class="w"&gt; &lt;/span&gt;-a&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$CFG_CPUTYPE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;x86_64&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;then&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;file&lt;span class="w"&gt; &lt;/span&gt;-L&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$SHELL&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;grep&lt;span class="w"&gt; &lt;/span&gt;-q&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;x86[_-]64&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;!&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;CFG_CPUTYPE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;i686
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;因为在我的机器上已知系统是64位，就强行绕过了他的判断。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$CFG_OSTYPE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;unknown-linux-gnu&lt;span class="w"&gt; &lt;/span&gt;-a&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$CFG_CPUTYPE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;x86_64&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;then&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;file&lt;span class="w"&gt; &lt;/span&gt;-L&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$SHELL&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;grep&lt;span class="w"&gt; &lt;/span&gt;-q&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;x86[_-]64&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;CFG_CPUTYPE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;i686
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;之后执行 rustup 就可以直接安装最近的 rustc 和 cargo 了。安装完成执行 &lt;code&gt;rustc -v&lt;/code&gt; 和 &lt;code&gt;cargo --version&lt;/code&gt; （两个工具还不统一！）可以了解安装情况。&lt;/p&gt;
&lt;p&gt;exit 退出 shell，commit 你的镜像，这样一个干净的镜像要好好保存：&lt;code&gt;docker commit IMAGE_ID sunng/rust-nightly&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;Hello World&lt;/h2&gt;
&lt;p&gt;之后可以写点代码了，我们不在 docker 里写，我们在 host 机器上写，然后挂载到 docker 上，因此 emacs 什么的也不用配置了。&lt;/p&gt;
&lt;p&gt;创建一个目录，比如在 &lt;code&gt;$HOME/var/docker/helloworld&lt;/code&gt;下，最简单的 rust 项目只要两个文件： &lt;code&gt;Cargo.toml&lt;/code&gt; 和 &lt;code&gt;src/main.rs&lt;/code&gt;。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;#Cargo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toml&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;package&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;hello-world&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;0.1.0&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;authors&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt; &amp;quot;sunng@about.me&amp;quot; &lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;//main.rs&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hello world&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;构建项目不需要手动 rustc 了，那是上个世纪的东西，我们直接 &lt;code&gt;cargo build&lt;/code&gt; 就可以：&lt;code&gt;docker run -i -t -v $HOME/var/docker:/mnt/data -w /mnt/data/helloworld sunng/nightly cargo build&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;其中 &lt;code&gt;-v&lt;/code&gt; 参数用于挂载目录，&lt;code&gt;-w&lt;/code&gt; 参数指定执行的 pwd。&lt;/p&gt;
&lt;p&gt;如果构建成功，就可以执行了，在 docker 中执行：&lt;code&gt;docker run -i -t -v $HOME/var/docker:/mnt/data -w /mnt/data/helloworld sunng/nightly target/hello-world&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;其实可以直接在 host 系统里执行也是完全可以的：&lt;code&gt;$HOME/var/docker/helloworld/target/hello-world&lt;/code&gt;。&lt;/p&gt;
&lt;h2&gt;Web Hello World&lt;/h2&gt;
&lt;p&gt;前面说了 Rust 都有 web 框架了，我们就写一个 Web 版本的 Hello World 吧。这次用 Iron 框架，首先添加依赖到 Cargo 文件：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;[package]&lt;/span&gt;

&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hello-world&amp;quot;&lt;/span&gt;
&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;0.1.0&amp;quot;&lt;/span&gt;
&lt;span class="na"&gt;authors&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;[ &amp;quot;sunng@about.me&amp;quot; ]&lt;/span&gt;

&lt;span class="k"&gt;[dependencies.iron]&lt;/span&gt;

&lt;span class="na"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;https://github.com/iron/iron.git&amp;quot;&lt;/span&gt;

&lt;span class="k"&gt;[dependencies.core]&lt;/span&gt;

&lt;span class="na"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;https://github.com/iron/core.git&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Cargo 目前还没有中央仓库，但是&lt;a href="http://crates.io/faq.html#github"&gt;据说将来会有&lt;/a&gt;。目前还都是用 git 仓库来直接添加，所以构建环境里必须要有 git。&lt;/p&gt;
&lt;p&gt;照着 &lt;a href="https://github.com/iron/iron/blob/master/examples/hello.rs"&gt;Iron 的例子&lt;/a&gt;写一个最简单的 hello world 程序。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;extern&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;crate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;iron&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;extern&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;crate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;::&lt;span class="n"&gt;io&lt;/span&gt;::&lt;span class="n"&gt;net&lt;/span&gt;::&lt;span class="n"&gt;ip&lt;/span&gt;::&lt;span class="n"&gt;Ipv4Addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;iron&lt;/span&gt;::&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Iron&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Chain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Alloy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Unwind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FromFn&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;::&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;hello_world&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nc"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Alloy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="nc"&gt;Status&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;serve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;::&lt;span class="nb"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Hello, world!&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Unwind&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;: &lt;span class="nc"&gt;Server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Iron&lt;/span&gt;::&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FromFn&lt;/span&gt;::&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hello_world&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Ipv4Addr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;127&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;编译 &lt;code&gt;docker run -i -t -v $HOME/var/docker:/mnt/data -w /mnt/data/helloworld sunng/nightly cargo build&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;运行 &lt;code&gt;docker run -i -t -v $HOME/var/docker:/mnt/data -w /mnt/data/helloworld -p 3000:3000 sunng/nightly target/hello-world&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;新增的参数&lt;code&gt;-p&lt;/code&gt;是把 docker 环境里的端口3000映射到 host 上的3000，这样我们才能在外面访问。&lt;/p&gt;
&lt;p&gt;最后还有一个问题，因为程序听的是127.0.0.1，所以在 host 上是无法访问这个端口的，修改代码：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Ipv4Addr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;就可以正常工作了。&lt;/p&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;总结一下上面用 docker 比虚拟机的好处：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;占用资源少，启动快&lt;/li&gt;
&lt;li&gt;与 host 共享网络、硬盘都非常方便，满足开发需要不成问题&lt;/li&gt;
&lt;li&gt;所有都是命令，与 host 系统上的进程集成也非常方便&lt;/li&gt;
&lt;li&gt;支持镜像的版本控制和仓库&lt;/li&gt;
&lt;/ul&gt;</content><category term="misc"></category><category term="programming"></category><category term="rust"></category><category term="docker"></category></entry><entry><title>"Clojure Microservice architecture with Slacker Cluster"</title><link href="https://sunng.info/blog/clojure-microservice-architecture-with-slacker-cluster.html" rel="alternate"></link><published>2014-07-08T22:12:52+08:00</published><updated>2014-07-08T22:12:52+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2014-07-08:/blog/clojure-microservice-architecture-with-slacker-cluster.html</id><summary type="html">&lt;p&gt;&lt;a href="http://www.infoq.com/presentations/Micro-Services"&gt;Microservice&lt;/a&gt; has been a hot new concept in these days. Martin Fowler explained microservice &lt;a href="http://martinfowler.com/articles/microservices.html"&gt;in this article&lt;/a&gt;. From me, microservice is a set of fine-grained function units running on independent process, each of them are connected with light-weighted transports: RESTful API or light messaging queue.&lt;/p&gt;
&lt;p&gt;It's a new concept in …&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;a href="http://www.infoq.com/presentations/Micro-Services"&gt;Microservice&lt;/a&gt; has been a hot new concept in these days. Martin Fowler explained microservice &lt;a href="http://martinfowler.com/articles/microservices.html"&gt;in this article&lt;/a&gt;. From me, microservice is a set of fine-grained function units running on independent process, each of them are connected with light-weighted transports: RESTful API or light messaging queue.&lt;/p&gt;
&lt;p&gt;It's a new concept in enterprise architecture, since the last movement in the field promotes SOA architecture. SOA encourages architects to componentize their business logic in service, and deploy service bus(ESB) for integration. Microservice can be more concrete and light-weighted. The service units in Microservice can be any standalone function, or just a tier in traditional tier based development. These units can be deployed on dedicate process or grouped into a process.&lt;/p&gt;
&lt;p&gt;In clojure development at &lt;a href="https://avoscloud.com"&gt;avoscloud&lt;/a&gt;, we are using the &lt;a href="https://github.com/sunng87/slacker-cluster"&gt;slacker cluster framework&lt;/a&gt; for our microsrvice architecture.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/sunng87/slacker"&gt;Slacker RPC&lt;/a&gt; exposes services as  clojure namespace (pretty light-weighted) All functions in the namespace can be called from remote. A slacker server can expose any number of namespaces:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;start-slacker-server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2014&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;my.serviceA&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;my.serviceB&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;...&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Slacker uses a binary protocol on TCP and configurable serialization (json/edn/&lt;a href="https://github.com/ptaoussanis/nippy"&gt;nippy&lt;/a&gt;) for communication, which is fast and compact.&lt;/p&gt;
&lt;p&gt;And in slacker cluster, exposed namespaces are registered on zookeeper as ephemeral nodes. The client doesn't have to know which service is deployed on which process. Instead, it connects to zookeeper and look up all process address for service it interests in. &lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;def &lt;/span&gt;&lt;span class="nv"&gt;sc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;clustered-slacker-client&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;zk-addr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;...&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;defn-remote&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;&amp;#39;sc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;my.serviceA/fn-abc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;;;when calling remote function for the first time, the client will look up zookeeper for remote processes and cache the results&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fn-abc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If there are more than one process available, the client library will balance the load on each process. And for stateful service, slacker cluster also elects master node to ensure all requests go to single process. (&lt;a href="http://sunng.info/blog/blog/2014/06/09/grouping-in-slacker-0-dot-12/"&gt;Slacker cluster grouping&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;Zookeeper directory structure:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ls /slacker/example-cluster/namespaces/
[my.serviceA, my.serviceB]

ls /slacker/example-cluster/namespaces/my.serviceA
[192.168.1.100:2104, 192.168.1.101:2014...]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Decoupling processes and services made microservice deployment quite flexible. Functional namespaces can be deployed on any process, standalone or grouped together, like Martin Fowler's chart &lt;a href="http://martinfowler.com/articles/microservices/images/sketch.png"&gt;shows&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;All these nodes are also watched by clients. If a process crashed or put offline, the clients will get notified by zookeeper, then no requests will be made on that process. Also, when you exhausted service capacity, just simply put on another process, the client will soon balance load to the new node. Scaling services is easy like that.&lt;/p&gt;
&lt;p&gt;Thanks to zookeeper's watch mechanism, there's no need to configure service static and update while you add/remove nodes. This is especially important in large-scale deployment. (Since microservices are often find-grained, you will always have a lot of process to update/restart.)&lt;/p&gt;
&lt;p&gt;For more about Slacker Cluster, &lt;a href="https://github.com/sunng87/slacker-cluster"&gt;check my code repository&lt;/a&gt;.&lt;/p&gt;</content><category term="misc"></category><category term="programming"></category><category term="clojure"></category></entry><entry><title>"Mapzei 和 观察者"</title><link href="https://sunng.info/blog/mapzei-he-guan-cha-zhe.html" rel="alternate"></link><published>2014-07-07T21:17:47+08:00</published><updated>2014-07-07T21:17:47+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2014-07-07:/blog/mapzei-he-guan-cha-zhe.html</id><summary type="html">&lt;p&gt;写了两个 Android 应用一致没有在博客上提，那么就一起提一下吧。&lt;/p&gt;
&lt;p&gt;其一是3月份写的 &lt;a href="https://play.google.com/store/apps/details?id=info.sunng.muzei.maps"&gt;Mapzei&lt;/a&gt;，是&lt;a href="https://plus.google.com/+RomanNurik/posts"&gt;Roman Nurik&lt;/a&gt;的live wallpaper &lt;a href="https://play.google.com/store/apps/details?id=net.nurik.roman.muzei"&gt;Muzei&lt;/a&gt;的扩展。 Roman Nurik 之前做过 Dash Clock，本身 …&lt;/p&gt;</summary><content type="html">&lt;p&gt;写了两个 Android 应用一致没有在博客上提，那么就一起提一下吧。&lt;/p&gt;
&lt;p&gt;其一是3月份写的 &lt;a href="https://play.google.com/store/apps/details?id=info.sunng.muzei.maps"&gt;Mapzei&lt;/a&gt;，是&lt;a href="https://plus.google.com/+RomanNurik/posts"&gt;Roman Nurik&lt;/a&gt;的live wallpaper &lt;a href="https://play.google.com/store/apps/details?id=net.nurik.roman.muzei"&gt;Muzei&lt;/a&gt;的扩展。 Roman Nurik 之前做过 Dash Clock，本身也是个开放架构，允许第三方增加扩展。 Mapzei是Muzei的扩展，每天取一张随机城市的地图作为桌面。用户可以配置地图的数据源：Google,OpenStreetMap或者Mapbox。&lt;/p&gt;
&lt;p&gt;另一个是&lt;a href="http://guancha.cn"&gt;观察者网&lt;/a&gt;的&lt;a href="https://play.google.com/store/apps/details?id=info.sunng.guanchazhe"&gt;客户端&lt;/a&gt;，看新闻用。应用本身倒没有什么，用了一些典型的开源库：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;UniversalImageLoader&lt;/li&gt;
&lt;li&gt;SuperListView&lt;/li&gt;
&lt;li&gt;ButterKnife&lt;/li&gt;
&lt;li&gt;Eventbus&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;另外这个应用还发布在了&lt;a href="http://app.mi.com/detail/65794"&gt;小米商店&lt;/a&gt;.&lt;/p&gt;</content><category term="misc"></category><category term="android"></category></entry><entry><title>"扩展 Linux ephemeral 端口限制"</title><link href="https://sunng.info/blog/kuo-zhan-linux-ephemeral-duan-kou-xian-zhi.html" rel="alternate"></link><published>2014-07-01T17:11:58+08:00</published><updated>2014-07-01T17:11:58+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2014-07-01:/blog/kuo-zhan-linux-ephemeral-duan-kou-xian-zhi.html</id><summary type="html">&lt;p&gt;默认情况下，单一Linux能发起的客户端连接数是十分有限的，为此，我们要测试大规模连接程序时不得不启动很多客户端机器模拟连接。下面介绍一些增加单台Linux发起连接数的方法 …&lt;/p&gt;</summary><content type="html">&lt;p&gt;默认情况下，单一Linux能发起的客户端连接数是十分有限的，为此，我们要测试大规模连接程序时不得不启动很多客户端机器模拟连接。下面介绍一些增加单台Linux发起连接数的方法。&lt;/p&gt;
&lt;h3&gt;增加文件打开数&lt;/h3&gt;
&lt;p&gt;第一步最为基础的，提高打开文件描述符的数量。默认的情况下，这个配置为1024，是不能满足我们的使用的。增加到999999个：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ulimit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-n&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;999999&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;持久化这个配置，可以在&lt;code&gt;/etc/security/&lt;/code&gt;（或&lt;code&gt;/etc/security.d/&lt;/code&gt;，取决于你的发行版）下建立文件，增加&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;*&lt;/span&gt;       hard    nofile      999999
&lt;span class="k"&gt;*&lt;/span&gt;       soft    nofile      999999
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;这将对所有用户起效。&lt;/p&gt;
&lt;h3&gt;增加客户端端口数&lt;/h3&gt;
&lt;p&gt;当Linux发起客户端连接时，如果没有显式指定，会给客户端socket绑定一个 ephemeral 端口。这个端口的范围是从这个区间选取的：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt; $ cat /proc/sys/net/ipv4/ip_local_port_range
32768   61000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;如果这个区间的端口耗尽，socket就会产生&lt;code&gt;cannot assign requested address&lt;/code&gt;的错误。要增加端口范围，我们需要把他设置得更大：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;1025 65535&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;/proc/sys/net/ipv4/ip_local_port_range
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;这样，单台机器就可以发出六万多个连接。&lt;/p&gt;
&lt;h3&gt;增加虚拟网卡&lt;/h3&gt;
&lt;p&gt;对于内存大一点的客户端机器，六万多个连接远不是其性能极限。由于IP消息中，一条消息是由 &lt;code&gt;src_addr&lt;/code&gt;, &lt;code&gt;src_port&lt;/code&gt;, &lt;code&gt;dst_addr&lt;/code&gt;, &lt;code&gt;dst_port&lt;/code&gt; 四元组标识，所以要增加连接，我们需要更多IP。在Linux上，我们可以启动虚拟网卡绑定额外的IP。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;ifconfig&lt;span class="w"&gt; &lt;/span&gt;eth0:0&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;192&lt;/span&gt;.168.1.100
$&lt;span class="w"&gt; &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;ifconfig&lt;span class="w"&gt; &lt;/span&gt;eth0:1&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;192&lt;/span&gt;.168.1.101
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;要关闭这些虚拟网卡&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;ifconfig&lt;span class="w"&gt; &lt;/span&gt;eth0:0&lt;span class="w"&gt; &lt;/span&gt;down
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;使用虚拟网卡连接&lt;/h3&gt;
&lt;p&gt;拥有多个IP之后，客户端socket需要显示绑定这些IP才行，以python为例，在connect前调用：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;local_addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;local_port&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;可以指定连接的源地址和端口。在普通的Linux编程里，当你指定&lt;code&gt;local_port&lt;/code&gt;为&lt;code&gt;0&lt;/code&gt;时，Linux会分配一个之前提到的 ephemeral 端口。但是当使用虚拟IP时，如果仍然指定0，系统并不会因为IP不同而重用端口号，达到六万多的限制后，仍然会抛出不能获得地址的异常。&lt;/p&gt;
&lt;p&gt;实际上是可以获得的，这里需要用户显式地指定端口好。如果需要大规模的连接，那就一个一个绑定好了。&lt;/p&gt;
&lt;h3&gt;启用time_wait reuse和recycle&lt;/h3&gt;
&lt;p&gt;Linux的socket进入&lt;code&gt;time_wait&lt;/code&gt;后需要有一定的时间回收，之后端口才能重新使用。这在大规模测试的时候就比较麻烦，为了免去等待，可以打开&lt;code&gt;tw_reuse&lt;/code&gt;和&lt;code&gt;tw_recycle&lt;/code&gt;这两个选项。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;/proc/sys/net/ipv4/tcp_tw_recycle
$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;/proc/sys/net/ipv4/tcp_tw_reuse
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;注意这两个选项都比较激进，最好仅在测试机上使用。&lt;/p&gt;</content><category term="misc"></category><category term="linux"></category><category term="programming"></category></entry><entry><title>"Slacker Cluster 0.12: Grouping"</title><link href="https://sunng.info/blog/slacker-cluster-012-grouping.html" rel="alternate"></link><published>2014-06-09T21:06:39+08:00</published><updated>2014-06-09T21:06:39+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2014-06-09:/blog/slacker-cluster-012-grouping.html</id><summary type="html">&lt;h2&gt;What are Slacker and Slacker Cluster&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/sunng87/slacker"&gt;Slacker&lt;/a&gt; is my side project started in late 2011. The goal of Slacker project is to provide a high performance RPC system for clojure, with elegant API. Slacker doesn't ruin your code. Your remote invocation looks exactly same as local, from code. That means …&lt;/p&gt;</summary><content type="html">&lt;h2&gt;What are Slacker and Slacker Cluster&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/sunng87/slacker"&gt;Slacker&lt;/a&gt; is my side project started in late 2011. The goal of Slacker project is to provide a high performance RPC system for clojure, with elegant API. Slacker doesn't ruin your code. Your remote invocation looks exactly same as local, from code. That means you can switch back and forth at same time.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/sunng87/slacker-cluster"&gt;Slacker Cluster&lt;/a&gt; is a support module for running Slacker servers with multiple instances. Cluster enabled slacker server will publish all its served namespaces to Zookeeper cluster. The Cluster enabled client reads and watches these meta data. The most important feature of Slacker Cluster is you can add or remove servers without changing client configuration.&lt;/p&gt;
&lt;h2&gt;Grouping in Slacker Cluster&lt;/h2&gt;
&lt;p&gt;Started in 0.11, then enhanced in 0.12, Slacker Cluster now has flexible &lt;strong&gt;grouping&lt;/strong&gt; choices for your scenario. In Slacker Cluster, &lt;strong&gt;grouping&lt;/strong&gt; means which server(s) to call on a particular invocation.&lt;/p&gt;
&lt;p&gt;There and four kinds of grouping for you: &lt;code&gt;:random&lt;/code&gt;, &lt;code&gt;:leader&lt;/code&gt;, &lt;code&gt;:all&lt;/code&gt; and custom.&lt;/p&gt;
&lt;h3&gt;:random&lt;/h3&gt;
&lt;p&gt;By default, Slacker cluster clients use &lt;code&gt;:random&lt;/code&gt; grouping: select a random server from server list. Random grouping works great for stateless services. It automatically balances load of each server.&lt;/p&gt;
&lt;h3&gt;:leader&lt;/h3&gt;
&lt;p&gt;Slacker servers selects leader for each namespace they expose. So at any time there will be one and only one leader node for every namespaces. The &lt;code&gt;:leader&lt;/code&gt; grouping routes all invocations onto the leader node. This is required when your server has state, and you have to ensure the consistency and availability.&lt;/p&gt;
&lt;h3&gt;:all&lt;/h3&gt;
&lt;p&gt;As the name suggests, &lt;code&gt;:all&lt;/code&gt; grouping routes invocations on every node at same time. In other words, it's broadcast. Note that this grouping might change your function return values. In &lt;code&gt;:random&lt;/code&gt; and &lt;code&gt;:leader&lt;/code&gt; mode, there's only one server called, just like local invocation. In &lt;code&gt;:all&lt;/code&gt;, there's chances several servers are called and several values returned. I will talk about how to deal with these return values later.&lt;/p&gt;
&lt;h3&gt;Custom&lt;/h3&gt;
&lt;p&gt;You can also provide a function for dynamic grouping. For requested namespace, function and arguments, you can specify any server(s) or grouping option.&lt;/p&gt;
&lt;h2&gt;Grouping results&lt;/h2&gt;
&lt;p&gt;Grouping may break original behavior of you code by returning multiple values from multiple servers. But you still have full control over it. There are four types of value you can specify for results aggregation: &lt;code&gt;:single&lt;/code&gt;, &lt;code&gt;:vector&lt;/code&gt;, &lt;code&gt;:map&lt;/code&gt; and custom function.&lt;/p&gt;
&lt;p&gt;In short words:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;:single&lt;/code&gt; returns the first valid result, and behavior same as calling single server or local invocation. This is the default value.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;:vector&lt;/code&gt; returns  a vector of all results.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;:map&lt;/code&gt; returns a map of all results, indexed by server addresses.&lt;/li&gt;
&lt;li&gt;Custom aggregation function accepts the results and allows you to merge the values.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Grouping exceptions&lt;/h2&gt;
&lt;p&gt;What happens when remote function threw exceptions? The grouping exception option defines that. When set to &lt;code&gt;:all&lt;/code&gt;, the client will raise an error only if all remote nodes broken. Otherwise, the broken result will be ignored and only valid results will apply &lt;code&gt;grouping-results&lt;/code&gt; rules. The opposite option is &lt;code&gt;:any&lt;/code&gt;, which mean client will raise error when any of calls is broken.&lt;/p&gt;
&lt;h2&gt;Granularity&lt;/h2&gt;
&lt;p&gt;The grouping options can be set to client level:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;clustered-slackerc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;cluster-name&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;127.0.0.1:2181&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;:grouping&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;:leader&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;or function level:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;defn-remote&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;sc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;slacker.example.api/timestamp&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="ss"&gt;:grouping&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;:all&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="ss"&gt;:grouping-results&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;:single&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/sunng87/slacker-cluster"&gt;Slacker Cluster&lt;/a&gt; has been used in our Avos Cloud backend for service integration. Feel free to let me know if you have interests or questions with this library.&lt;/p&gt;</content><category term="misc"></category><category term="clojure"></category><category term="programming"></category></entry><entry><title>"Fork-Join in Papaline"</title><link href="https://sunng.info/blog/fork-join-in-papaline.html" rel="alternate"></link><published>2014-05-27T21:47:45+08:00</published><updated>2014-05-27T21:47:45+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2014-05-27:/blog/fork-join-in-papaline.html</id><summary type="html">&lt;p&gt;&lt;a href="http://github.com/sunng87/papaline"&gt;Papaline&lt;/a&gt; 0.3 introduced a new model "fork-join" for task execution. It allows you to split a task into smaller units, and execute them in parallel.&lt;/p&gt;
&lt;p&gt;Before that, a task is processed as a single unit from the first stage to the second, the third and the last. Within a …&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;a href="http://github.com/sunng87/papaline"&gt;Papaline&lt;/a&gt; 0.3 introduced a new model "fork-join" for task execution. It allows you to split a task into smaller units, and execute them in parallel.&lt;/p&gt;
&lt;p&gt;Before that, a task is processed as a single unit from the first stage to the second, the third and the last. Within a stage, all computing is done in a single thread.&lt;/p&gt;
&lt;p&gt;&lt;img alt="linear execution" src="http://i.imgur.com/w6RlNZo.png"&gt;&lt;/p&gt;
&lt;p&gt;This model has limitation that you are required to execute any of your stage in serial. If your task has a few split-able units, it's always better to run them in parallel. Here we have &lt;code&gt;(fork)&lt;/code&gt; command for the situation.&lt;/p&gt;
&lt;p&gt;For example, you are using the &lt;em&gt;fanout-on-write&lt;/em&gt; model to build an activity stream. Once a user posted a new status, you need to find all followers(stage 1) of that user and append the status to their timeline(stage 2).&lt;/p&gt;
&lt;p&gt;In previous version of papaline, these two stages are:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;defn &lt;/span&gt;&lt;span class="nv"&gt;find-followers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;followers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;query-db-for-followers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;followers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;defn &lt;/span&gt;&lt;span class="nv"&gt;fanout-to-user-timeline&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;user-ids&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;doseq &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;user-id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;user-ids&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;write-redis-list&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;user-id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In the second task, the msg is appended to user's timeline one by one.&lt;/p&gt;
&lt;p&gt;Using &lt;code&gt;(fork)&lt;/code&gt;, the &lt;code&gt;fanout-to-user-timeline&lt;/code&gt; can be executed in parallel.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;defn &lt;/span&gt;&lt;span class="nv"&gt;find-followers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;followers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;query-db-for-followers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fork&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;map &lt;/span&gt;&lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;vector &lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;followers&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;defn &lt;/span&gt;&lt;span class="nv"&gt;fanout-to-user-timeline&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;user-ids&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;write-redis-list&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;user-id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After the &lt;code&gt;find-followers&lt;/code&gt; function, the result will be splitted into &lt;code&gt;(count followers)&lt;/code&gt; parts and sent into input channel of stage 2. So the tasks execution will be like:&lt;/p&gt;
&lt;p&gt;&lt;img alt="forked execution" src="http://i.imgur.com/MLhZ0Pm.png"&gt;&lt;/p&gt;
&lt;p&gt;To collect the results of all forked sub-tasks, you can use &lt;code&gt;(join)&lt;/code&gt;. If the return value is wrapped with join, it won't trigger next stage immediately but to wait all forked tasks to finish.&lt;/p&gt;
&lt;p&gt;&lt;img alt="join" src="http://i.imgur.com/BVDEH9Q.png"&gt;&lt;/p&gt;
&lt;p&gt;So with &lt;code&gt;(fork)&lt;/code&gt; and &lt;code&gt;(join)&lt;/code&gt;, it's very flexible to change execution model in Papaline.  Internally, I use clojure's &lt;a href="http://clojure.org/metadata"&gt;metadata&lt;/a&gt; to add flags for the return value, without ruining the non-invasive design of &lt;a href="http://github.com/sunng87/papaline"&gt;Papaline&lt;/a&gt;.&lt;/p&gt;</content><category term="misc"></category><category term="clojure"></category><category term="programming"></category></entry><entry><title>"Papaline: Concurrent pipeline with core.async"</title><link href="https://sunng.info/blog/papaline-concurrent-pipeline-with-coreasync.html" rel="alternate"></link><published>2014-04-20T17:21:44+08:00</published><updated>2014-04-20T17:21:44+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2014-04-20:/blog/papaline-concurrent-pipeline-with-coreasync.html</id><summary type="html">&lt;p&gt;According to &lt;a href="http://en.wikipedia.org/wiki/Staged_event-driven_architecture"&gt;wikipedia&lt;/a&gt;, Staged Event-driven Architecture is an approach to software architecture that decomposes a complex, event-driven application into a set of stages connected by queues. We were using Java framework, &lt;a href="https://github.com/sunng87/stages"&gt;stages&lt;/a&gt;, to implement queue based SEDA. As we are using more and more Clojure nowadays, I decide to re-implement …&lt;/p&gt;</summary><content type="html">&lt;p&gt;According to &lt;a href="http://en.wikipedia.org/wiki/Staged_event-driven_architecture"&gt;wikipedia&lt;/a&gt;, Staged Event-driven Architecture is an approach to software architecture that decomposes a complex, event-driven application into a set of stages connected by queues. We were using Java framework, &lt;a href="https://github.com/sunng87/stages"&gt;stages&lt;/a&gt;, to implement queue based SEDA. As we are using more and more Clojure nowadays, I decide to re-implement it in Clojure language, and in Clojure way. It's &lt;a href="https://github.com/sunng87/papaline"&gt;papaline&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The most important difference between papaline and stages is the usage of IoC threads. Core.async introduces IoC threads for Clojure, which is a popular concurrent mechanism recently. In traditional queue based thread pool, threads are blocked on queue to wait for tasks. While for IoC threads, channels act similar to queues but no actual thread is blocked on channel. Once there is a task available in channel, an underlying thread will be picked to execute it. So for core.async, you don't have to assign a static thread pool to each channel. The channel will pick thread from a shared system thread pool on demand. In current core.async release, it's a fixed thread pool with &lt;em&gt;(processors * 4) + 42&lt;/em&gt; threads. That's much flexible and efficient.&lt;/p&gt;
&lt;p&gt;Papaline takes advantages of this feature. The base concept in papaline is &lt;strong&gt;stage&lt;/strong&gt; and &lt;strong&gt;pipeline&lt;/strong&gt;. A pipeline is created with a ordered sequence of stages. Stages configured in a pipeline are connected with channels, instead of queues. Threads are automatically managed by core.async, and scheduled based on load of channels. &lt;/p&gt;
&lt;p&gt;When you run a pipeline, the input data is sent to the inbound channel of the first stage. The stage will received the data and pick a thread to execute the function. Then the result is put into the second stage's inbound channel. The user-visible behavior is much like &lt;code&gt;comp&lt;/code&gt;, but in concurrent.&lt;/p&gt;
&lt;p&gt;Also core.async offers different type of channel buffers: fixed &lt;code&gt;buffer&lt;/code&gt;, &lt;code&gt;sliding-buffer&lt;/code&gt; and &lt;code&gt;dropping-buffer&lt;/code&gt;. They are channels equivalent to j.u.c thread pool's &lt;code&gt;RejectedExecutionHandler&lt;/code&gt;. &lt;/p&gt;
&lt;p&gt;We have already deployed papaline in our asynchronous system and it works great by far. Find the project on &lt;a href="https://github.com/sunng87/papaline"&gt;github&lt;/a&gt; if you are interested in.&lt;/p&gt;</content><category term="misc"></category><category term="clojure"></category><category term="programming"></category></entry><entry><title>"Rust语言：安全的并发"</title><link href="https://sunng.info/blog/rustyu-yan-an-quan-de-bing-fa.html" rel="alternate"></link><published>2014-04-20T16:31:54+08:00</published><updated>2014-04-20T16:31:54+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2014-04-20:/blog/rustyu-yan-an-quan-de-bing-fa.html</id><summary type="html">&lt;p&gt;本文原载于2014年3月《程序员》杂志编程语言专题。&lt;/p&gt;
&lt;p&gt;Rust 语言是近两年来 Mozilla 正在开发的一种新编程语言，它以并发，安全和实用为口号 …&lt;/p&gt;</summary><content type="html">&lt;p&gt;本文原载于2014年3月《程序员》杂志编程语言专题。&lt;/p&gt;
&lt;p&gt;Rust 语言是近两年来 Mozilla 正在开发的一种新编程语言，它以并发，安全和实用为口号，主要使用场景是系统编程，用来取代C++语言的角色。有人戏称 Mozilla 由于大量使用 C++，深知其弊端，所以诞生了 Rust。目前，Rust 的两个主要产品是 Mozilla 下一代的浏览器布局引擎 Servo 和 Rust 编译器。由于语言在快速的开发和演进中，每个版本都会产生一些不兼容的变更，所以现在并非是学习这门语言细节并真正使用它的最佳时机。不过，这并不影响我们了解 Rust 语言：作为多年来鲜有的新系统编程语言，他本身有很多新颖之处，也可以帮助我们了解一些编程语言设计的流行趋势。&lt;/p&gt;
&lt;h2&gt;并发&lt;/h2&gt;
&lt;p&gt;和现在流行的很多新语言一样，Rust 在语言层面支持了绿色线程（Green threads）：Task。Task 作为并发执行的单元，是用户空间的“线程”，创建和调度成本较低，可以大量共存。Task之间通过消息传递通信，没有直接的共享数据。从最近的流行趋势来看，绿色线程几乎已经成为并发方案大战最终的赢家。除了Rust，之前流行的Go语言，Erlang，Python的Gevent，以及最近Clojure世界里正在发展的core.async，采用的都是这种绿色线程的模式。&lt;/p&gt;
&lt;p&gt;绿色线程的程序与传统多线程程序的写法几乎一致。在编写服务器程序时，与事件驱动的回调机制相比，编写更简单，表义更清晰。当并发任务增多时，传统的多线程程序由于启动线程和调度线程的成本高而使系统整体性能降低。而绿色线程可以基本不受限制，随意创建。Rust的文档指出在32位系统上可以支持数十万个Task同时存在。&lt;/p&gt;
&lt;p&gt;Task也是Rust程序的基础单元，一个Rust进程又多个并行的task组成，main函数本身也是一个Task。Task之间通过一个 (Port, Chan) 元组传递数据。Port和Chan相当于管道的两端，Port用于取数据，Chan用于发送数据。下面的例子里，我们通过do spawn语法（类似Ruby的block语法），启动一个新Task，并打印收到的数据。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hello world&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;chan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Chan&lt;/span&gt;::&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;chan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kr"&gt;do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;spawn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;received_msg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recv&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;received_msg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;引用系统&lt;/h2&gt;
&lt;p&gt;Rust 语言设计的核心是安全性（这里安全性指safety，而非 security）。Rust 希望通过语言的机制和编译器的功能，把程序员易于犯错，不易检查的问题解决在编译期，避免运行时的Segmentation Fault。Rust 的设计可以说是处处小心。Clojure语言强调可变性给编程带来的复杂性，在 Rust 语言中，设计者对这点也有格外的重视。除非特别声明为 mut，所有 Rust 的局部变量默认都是不可变的，对不可变变量值的修改会导致 编译器直接报错。&lt;/p&gt;
&lt;p&gt;Rust 的安全性还通过独有的引用类型系统来实现。&lt;/p&gt;
&lt;p&gt;Rust 语言中堆内存块的引用类型叫做box。最新版本的Rust 在语言层面只保留了一种owned box，它在使用时具有一种所有权（ownership）的概念，只有具有所有权的变量才可以访问这段内存。Owned box在同一时刻只允许一个变量作为所有者，它的变量赋值称为move。一旦owned pointer被赋值，用户就无法通过原先的引用访问这块数据，这种错误会在编译时检查。
一个简单的例子：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;: &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;: &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;{:?}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;~ 代表owned box，这里我们把一个包含值为50的owned box赋给owned pointer a。然后把a的所有权通过赋值的形式move给b。最后我们试图通过 &lt;code&gt;*a&lt;/code&gt; 访问这个值。在C语言里，这时a和b同时指向统一块内存，可以通过&lt;code&gt;*a&lt;/code&gt;访问到这里的值。但Rust的所有权机制给予了这段内存额外的保护。编译这段程序将失败：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;own&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rs&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;error&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;moved&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n n-Quoted"&gt;`a`&lt;/span&gt;
&lt;span class="n"&gt;own&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rs&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;{:?}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;                               &lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;own&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rs&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;note&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n n-Quoted"&gt;`a`&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;moved&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;here&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;because&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;has&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n n-Quoted"&gt;`~int`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;which&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;moved&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;by&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n n-Quoted"&gt;`ref`&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;override&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;own&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rs&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;编译器会明确地指出错误的引用在何处被move。事实上所有owned box的生命周期管理都是直接在编译时完成的，编译器通过静态检查跟踪使用情况，完成内存开辟和回收。这是Rust 确保编程正确、安全的重要手段。&lt;/p&gt;
&lt;p&gt;新版本的Rust在标准库中提供 &lt;code&gt;std::rc::Rc&lt;/code&gt;（引用计数） 和 &lt;code&gt;std::gc::Gc&lt;/code&gt; （垃圾回收）类型，取代了原先的managed box，用来提供可以有限共享的引用类型。&lt;/p&gt;
&lt;p&gt;在 Task 间传递数据，如果要避免数据拷贝，也有专门的引用类型：用于不可变数据的&lt;code&gt;extra::arc::Arc&lt;/code&gt; (atomically reference counted ，原子的引用计数类型) , 以及用于可变数据的 RWArc（带读写锁的原子引用计数类型） 。RWArc在操作可变数据时，通过内在的读写锁控制对共享数据的访问，从而在API层面实现安全性。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;extern&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mod&lt;/span&gt; &lt;span class="nn"&gt;extra&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;extra&lt;/span&gt;::&lt;span class="n"&gt;arc&lt;/span&gt;::&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;RWArc&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hello world&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;arc_ref&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;RWArc&lt;/span&gt;::&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;chan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Chan&lt;/span&gt;::&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;chan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arc_ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kr"&gt;do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;spawn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;arc_local_ref&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recv&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;arc_local_ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push_char&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;&amp;#39;!&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;arc_local_ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Rust谨慎地定义如此繁多，各具功能的引用类型，就是希望用户在编程过程中，根据应用场景、引用的功能职责，选择合适的类型，进而在引用类型系统和编译器的保护下，减少在运行时出错的机会。这一点也和Clojure的4种引用类型的设计初衷类似，不过Clojure并不能提供太多编译时的安全保护。&lt;/p&gt;
&lt;h2&gt;更多&lt;/h2&gt;
&lt;p&gt;篇幅所限，我只选择了Rust最具特点的两个部分介绍。Rust是一门具备自身显著特点，精心设计的语言，而绝非普通的“又一门编程语言”。在语法层面，它包含了模式匹配，闭包，泛型等流行功能，作为系统编程语言，使用的舒适度不亚于脚本语言。另外还可以通过FFI（Foreign Function Interface）调用已有的C语言库，满足了实用性的需要。&lt;/p&gt;
&lt;p&gt;如果你也开始对这门新语言感兴趣，可以：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;通过&lt;a href="http://static.rust-lang.org/doc/master/tutorial.html"&gt;它的文档&lt;/a&gt;学习最新版本的语法和细节 &lt;/li&gt;
&lt;li&gt;加入&lt;a href="https://mail.mozilla.org/listinfo/rust-dev"&gt;rust-dev邮件列表&lt;/a&gt;了解开发者的讨论&lt;/li&gt;
&lt;li&gt;关注&lt;a href="https://github.com/mozilla/rust"&gt;Rust项目代码仓库&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;关注&lt;a href="https://delicious.com/tag/rust"&gt;Delicious&lt;/a&gt; 和 &lt;a href="http://www.reddit.com/r/rust"&gt;Reddit&lt;/a&gt; 上流行的 rust 链接&lt;/li&gt;
&lt;li&gt;关注&lt;a href="http://cmr.github.io/"&gt;The week in Rust&lt;/a&gt;，介绍每周 Rust 语言正在发生的变化&lt;/li&gt;
&lt;/ul&gt;</content><category term="misc"></category><category term="rust"></category><category term="programming"></category></entry><entry><title>"slacker library updated"</title><link href="https://sunng.info/blog/slacker-library-updated.html" rel="alternate"></link><published>2014-03-04T23:40:00+08:00</published><updated>2014-03-04T23:40:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2014-03-04:/blog/slacker-library-updated.html</id><summary type="html">&lt;p&gt;After almost two years idle in commit log, I restarted development of my slacker frameworks recently. It will be used in our AVOS Cloud production as integration solution between clojure systems.&lt;/p&gt;
&lt;p&gt;The recent update in slacker, slacker-cluster and link are:&lt;/p&gt;
&lt;h3&gt;link&lt;/h3&gt;
&lt;p&gt;There are two releases in the &lt;a href="https://github.com/sunng87/link"&gt;link&lt;/a&gt; library, features …&lt;/p&gt;</summary><content type="html">&lt;p&gt;After almost two years idle in commit log, I restarted development of my slacker frameworks recently. It will be used in our AVOS Cloud production as integration solution between clojure systems.&lt;/p&gt;
&lt;p&gt;The recent update in slacker, slacker-cluster and link are:&lt;/p&gt;
&lt;h3&gt;link&lt;/h3&gt;
&lt;p&gt;There are two releases in the &lt;a href="https://github.com/sunng87/link"&gt;link&lt;/a&gt; library, features include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ported to Netty 4&lt;/li&gt;
&lt;li&gt;WebSocket server handler&lt;/li&gt;
&lt;li&gt;Ability to shutdown server&lt;/li&gt;
&lt;li&gt;Ability to use core.async with link ring adapter&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;slacker&lt;/h3&gt;
&lt;p&gt;The most recent release of &lt;a href="https://github.com/sunng87/slacker"&gt;slacker&lt;/a&gt; is 0.11.0:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;New ping-interval option for clients&lt;/li&gt;
&lt;li&gt;Async callback will accept two argument, and can handle exceptions&lt;/li&gt;
&lt;li&gt;Slingshot removed, we are now using clojure built-in ex-info&lt;/li&gt;
&lt;li&gt;All client creation functions and defn-remote are delayed&lt;/li&gt;
&lt;li&gt;Bugfix for issue when several clients started in a process&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;slacker-cluster&lt;/h3&gt;
&lt;p&gt;There's major enhancement in &lt;a href="https://github.com/sunng87/slacker-cluster"&gt;slacker cluster&lt;/a&gt; 0.11.0&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Server exception no longer blocks client&lt;/li&gt;
&lt;li&gt;Added options to call functions on multiple servers, control return type and exception handling&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All these libraries are available on github. I will write more about how we use them in AVOS Cloud in future.&lt;/p&gt;</content><category term="misc"></category><category term="clojure"></category><category term="programming"></category></entry><entry><title>"Archlinux 安全更换硬盘"</title><link href="https://sunng.info/blog/archlinux-an-quan-geng-huan-ying-pan.html" rel="alternate"></link><published>2013-10-22T22:26:00+08:00</published><updated>2013-10-22T22:26:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2013-10-22:/blog/archlinux-an-quan-geng-huan-ying-pan.html</id><summary type="html">&lt;p&gt;公司升级所有笔记本到SSD，本来是个好事，不过迁移是个难事。除了我，所有的同事都是OS X，他们有自动工具，毫无个性，不理 …&lt;/p&gt;</summary><content type="html">&lt;p&gt;公司升级所有笔记本到SSD，本来是个好事，不过迁移是个难事。除了我，所有的同事都是OS X，他们有自动工具，毫无个性，不理他们。我们Linux要做这个事其实想想比较简单：首先，我们的分区和目录是抽象为两层的；其次，整个文件系统是透明的，只要拷贝就好了。&lt;/p&gt;
&lt;p&gt;首先，拿到新硬盘要按需分区和格式化。用gpartd就可以，这一步千万不要忘记把/boot的目标分区设置为bootable，否则会导致无法引导。&lt;/p&gt;
&lt;p&gt;分区完成之后，挂载各个分区，把当前硬盘里的所有文件用&lt;code&gt;cp -ar&lt;/code&gt;拷贝到新硬盘上，这一步非常花时间。&lt;/p&gt;
&lt;p&gt;搞定之后要再手动mkdir几个目录&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;/run&lt;/li&gt;
&lt;li&gt;/sys&lt;/li&gt;
&lt;li&gt;/proc&lt;/li&gt;
&lt;li&gt;/dev&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;使系统启动的时候可以mount上特殊分区。&lt;/p&gt;
&lt;p&gt;此外，要修改&lt;code&gt;/etc/fstab&lt;/code&gt;，根据新的硬盘UUID来配置。UUID可以在&lt;code&gt;ll /dev/disks/by-uuid&lt;/code&gt;里看到。&lt;/p&gt;
&lt;p&gt;挂载一些特殊分区到新硬盘目录：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;mount -o bind /dev /mnt/new/dev&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mount -t proc none /mnt/new/proc&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mount -t sys none /mnt/new/sys&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;然后就可以chroot到新硬盘。&lt;/p&gt;
&lt;p&gt;最后一步，在新硬盘上安装grub：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;grub-install /dev/sdb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;之后可以检查一下生成的/boot/grub/grub.cfg，观察一下硬盘的uuid是否正确，如果不对可以手动改掉。&lt;/p&gt;
&lt;p&gt;重启，见证奇迹。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>"Delicious 发布"</title><link href="https://sunng.info/blog/delicious-fa-bu.html" rel="alternate"></link><published>2013-10-08T21:56:00+08:00</published><updated>2013-10-08T21:56:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2013-10-08:/blog/delicious-fa-bu.html</id><summary type="html">&lt;p&gt;很久不写了，我们最近忙于 Delicious 的发布，终于上个月24号，新版本正式和所有用户见面了。&lt;/p&gt;
&lt;p&gt;作为 Web 2.0 的旗帜性产品，Delicious 已经存在 …&lt;/p&gt;</summary><content type="html">&lt;p&gt;很久不写了，我们最近忙于 Delicious 的发布，终于上个月24号，新版本正式和所有用户见面了。&lt;/p&gt;
&lt;p&gt;作为 Web 2.0 的旗帜性产品，Delicious 已经存在了整整十年。我们这次改版，给这个经典的网站换了新的设计，另外增加了一些新的功能，帮助你在收藏之外，从 Delicious 上获取更多的信息。比如可以从&lt;a href="https://delicious.com/tag/programming"&gt;#programming&lt;/a&gt;里看到最近流行的关于programming被收藏最多的链接。新的Discover会根据用户的社交网络，推荐热门的链接。&lt;/p&gt;
&lt;p&gt;所有这些才仅仅是开始，接下来的一段时间内，Delicious 还有更多的功能和产品会推出。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>"工作四周年"</title><link href="https://sunng.info/blog/gong-zuo-si-zhou-nian.html" rel="alternate"></link><published>2013-07-16T21:33:00+08:00</published><updated>2013-07-16T21:33:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2013-07-16:/blog/gong-zuo-si-zhou-nian.html</id><summary type="html">&lt;p&gt;一不小心工作四年了，四年意味着，工作的时间都超过了上大学的时间。当然这也不能说明什么，接下来还会更快地超过上 …&lt;/p&gt;</summary><content type="html">&lt;p&gt;一不小心工作四年了，四年意味着，工作的时间都超过了上大学的时间。当然这也不能说明什么，接下来还会更快地超过上高中的时间。这些年来，我越来越喜欢把22岁大学毕业作为一个中点，对称着看。往后多少年是什么样，往前多少年又是什么样。这两个样子往往存在这因果关系，离中点近的直接，离中点远的深刻。&lt;/p&gt;
&lt;p&gt;我很发愁给自己列计划，所以也不知道当时毕业的时候想的现在，应该是个什么摸样。反正就我照镜子的感受来说，应该没变老。这并不是说保养得好，主要是因为我第一天上班就有人问我是不是来领实习生。而最近都已经有些不开眼的妈妈跟自己的孩子说，还不快谢谢哥哥让座。&lt;/p&gt;
&lt;p&gt;工作了四年，四家公司，三个城市，这段说起来太沧桑坎坷，就不提了。前段时间看到某个微薄名人说刚毕业几年，应该多闯几个行业，有好处。我虽然是一直敲键盘写代码，但是也辗转了不同的软件行业。以前以为没有区别，实际上一圈下来最后回到互联网行业里，才知道这大不同，不同到甚至同是程序员你们站在一起一样可能找不到哪怕一个共同话题。互联网行业的节奏快，创新多，年轻人在其中学习的空间最大。&lt;/p&gt;
&lt;p&gt;另外我特别认可自己当初下了决定来AVOS。工作的第四年，是完完整整在这里，有机会有舞台面对挑战。我们上线了&lt;a href="http://readwise.net/"&gt;美味爱读&lt;/a&gt;，现在团队紧张地为&lt;a href="https://delicious.com"&gt;Delicious&lt;/a&gt;添砖加瓦。6年前我注册这个书签网站的时候，是无论如何想不到今天在上面做修改的人是自己。也就是这样的机会，最为吸引人。&lt;/p&gt;
&lt;p&gt;无论是公司，还是我们团队，还是我自己，现在还没有到盘点成绩的时候。希望明年夏天再回顾的时候，能有更多的收获。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Grunt for requirejs projects</title><link href="https://sunng.info/blog/grunt-for-requirejs-projects.html" rel="alternate"></link><published>2013-07-05T00:00:00+08:00</published><updated>2013-07-05T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2013-07-05:/blog/grunt-for-requirejs-projects.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;Grunt&lt;/li&gt;
&lt;li&gt;javascript
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;h3&gt;the Problem&lt;/h3&gt;&lt;/p&gt;

&lt;p&gt;You have modularize your JavaScript project with requirejs. And you need a build tool for both development and deployment phases, helping you to organize, optimize the source code.&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;the Solution&lt;/h3&gt;&lt;/p&gt;

&lt;p&gt;Grunt has been the standard tool chain for web …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;Grunt&lt;/li&gt;
&lt;li&gt;javascript
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;h3&gt;the Problem&lt;/h3&gt;&lt;/p&gt;

&lt;p&gt;You have modularize your JavaScript project with requirejs. And you need a build tool for both development and deployment phases, helping you to organize, optimize the source code.&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;the Solution&lt;/h3&gt;&lt;/p&gt;

&lt;p&gt;Grunt has been the standard tool chain for web development. I used to think the grunt configuration file could be verbose and complicated, because the JavaScript world has never come up with a standard, even common, archetype (until yeoman, but it's after Grunt). The projects are organized manually, or by some custom shell scripts. So grunt cannot take the advantage of 'convention over configuration', which Maven does well in the Java world.&lt;/p&gt;

&lt;p&gt;This idea was changed until I use Grunt seriously. Grunt configuration parser has excellent support for expressions of 'path'. You can use wildcards like 'src/*.js', 'src/**/*.js', ['src/**/*.js', 'vendor/lib/*.js'] and even more 'path' to find and match your files. You don't have to write your script name one-by-one. I'm sure Grunt developers have lots of experience on JavaScript development so they know the problem.&lt;/p&gt;

&lt;p&gt;So I strongly recommend you to use Grunt to manage your JavaScript project, especially when it's a large project.&lt;/p&gt;

&lt;p&gt;Assume you have your JavaScript sources in 'src', and the 'src/main.js' is the entry point of your requirejs modules.&lt;/p&gt;

&lt;p&gt;The first step is to create a work directory for publishing your sources, say 'public', and also put your vendor scripts in 'src/libs' Setup your first Grunt task, copy:dev. The &lt;a href="https://github.com/gruntjs/grunt-contrib-copy"&gt;copy plugin&lt;/a&gt; is one of the most used plugin for copying files. This will copy your source code from src/ to public/, as well as your vendor scripts.&lt;br /&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;copy&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;files&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;expand&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;src&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;src/**/*.js&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;dest&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;public/js&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;isFile&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then move your requirejs.config from html to your main.js. Require.js is able to read configuration after it loaded your entry script. This is of great helpful for optimized code.&lt;/p&gt;

&lt;p&gt;In development phase, you can have your static server over 'public'. For deployment, you need to optimize the JavaScript code: concat and uglify. This can be done with r.js, which is developed by requirejs project. Grunt also has a plugin to integrate r.js, &lt;a href="https://github.com/gruntjs/grunt-contrib-requirejs"&gt;grunt-contrib-requirejs&lt;/a&gt;. r.js could concat all requirejs modules by analyzing their dependency tree. You need a simple config for that:&lt;br /&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;requirejs&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;compile&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;src/&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;mainConfigFile&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;src/main.js&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;public/js/main.js&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;/p&gt;

&lt;p&gt;The mainConfigFile is the JavaScript file contains requirejs.config. r.js can also parse it from a requirejs module. r.js will concat and uglify all your scripts, include vendor scripts in one file, output as 'public/js/main.js'. With the same name of your entry point.&lt;/p&gt;

&lt;p&gt;With that, you don't have to change any single line of js/html for switching between deployment and development. During development, requirejs loads JavaScript files on demand. While on the production, it loads main.js with all dependencies combined in.
&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>给 Raspberry Pi 添加一个显示 IP 的液晶屏</title><link href="https://sunng.info/blog/gei-raspberry-pi-tian-jia-yi-ge-xian-shi-ip-de-ye-jing-ping.html" rel="alternate"></link><published>2013-06-29T00:00:00+08:00</published><updated>2013-06-29T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2013-06-29:/blog/gei-raspberry-pi-tian-jia-yi-ge-xian-shi-ip-de-ye-jing-ping.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;raspberry_pi
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;我的 Raspberry Pi 在家里一直是通过 DHCP 联网的，每次要登录上去都要先进路由管理界面看看他的 IP 到底是什 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;raspberry_pi
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;我的 Raspberry Pi 在家里一直是通过 DHCP 联网的，每次要登录上去都要先进路由管理界面看看他的 IP 到底是什么，很不方便。于是就买了这块液晶屏幕，准备显示一下 IP，另外以后也可以作为一个输出设备。毕竟用这个比外接一个显示器方便多了（现在显示器最小都是21寸）。&lt;/p&gt;

&lt;p&gt;液晶屏就是最普通的1602屏幕，淘宝上有很多，基本上都一样。要注意的是需要自备引脚，要自己焊到板上，否则无法连接。我也是第一回焊，感觉还比较简单。(&lt;a href="http://instagram.com/p/bIPSEcBM6R/" target="_blank"&gt;如图。&lt;/a&gt;)另外为了控制液晶屏的对比度需要有一个电位器，为此我中途还专门跑了一趟中关村。&lt;/p&gt;

&lt;p&gt;之后就可以按照 &lt;a href="http://learn.adafruit.com/drive-a-16x2-lcd-directly-with-a-raspberry-pi/overview" target="_blank"&gt;Adafruit 上的教程&lt;/a&gt;一步一步做了。&lt;/p&gt;

&lt;p&gt;最后我简化了一下显示 IP 的脚本，时间就不去刷新了。
&lt;script src="https://gist.github.com/sunng87/5890563.js"&gt;&lt;/script&gt;&lt;/p&gt;

&lt;p&gt;把这个脚本作为 systemd 的服务，可以参考&lt;a href="http://blog.sdbarker.com/adding-custom-units-services-to-systemd-on-arch-linux/" target="_blank"&gt;这里&lt;/a&gt;有关如何创建自定义服务的说明。然后通过systemctl enable ip-display.service设置自启动。&lt;/p&gt;

&lt;p&gt;最终效果如下：
&lt;img src="http://i.imgur.com/ZqAc2dh.jpg" alt="my ip display setup" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>纸上得来终觉浅</title><link href="https://sunng.info/blog/zhi-shang-de-lai-zhong-jue-qian.html" rel="alternate"></link><published>2013-06-07T00:00:00+08:00</published><updated>2013-06-07T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2013-06-07:/blog/zhi-shang-de-lai-zhong-jue-qian.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;之前看过无数的书无数的文章，讲可扩展性讲大型架构，不过真到了自己上场，毕竟还是没有从头到尾 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;之前看过无数的书无数的文章，讲可扩展性讲大型架构，不过真到了自己上场，毕竟还是没有从头到尾实践过，心里还有些没底。每做一个决定，每选定一个方案，心里都要反复打鼓，这是不是在给自己挖坑，是不是导致今后要浪费很多可乐咖啡的一个决定。第一份工作在盛大的时候，参与过这种规模比较大的项目，但是核心的部分，容量规划，性能评估，优化，都是那时的老大操心，我在一旁旁观。操心和旁观的区别大了去了，晚上睡不着的那几个小时，旁观的人享受不到，等来临的时候，就要如履薄冰地担着。&lt;/p&gt;

&lt;p&gt;公司的团队构成跟硅谷类似，不存在专门的“产品经理”，负责人要从后端到前端，从开发到部署，从运维到监控，从代码到UI设计，产品甚至到运营，都需要有所操心。好在团队经过一年多的锻炼，技术成熟配合也默契，各司其职又都是三头六臂，能在这样的过程里成长，书里读不到。&lt;/p&gt;

&lt;p&gt;一直是做互联网信息类的产品，现在自己制造的信息却越来越少，有些于心不忍，于是寥寥记录一下。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Checkout Ring Adapter for Jetty 9</title><link href="https://sunng.info/blog/checkout-ring-adapter-for-jetty-9.html" rel="alternate"></link><published>2013-04-24T00:00:00+08:00</published><updated>2013-04-24T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2013-04-24:/blog/checkout-ring-adapter-for-jetty-9.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;jetty&lt;/li&gt;
&lt;li&gt;web&lt;/li&gt;
&lt;li&gt;websocket
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;The Clojure world has been using Jetty 7 for quite a long time because it's supported by the Ring development team. However, Jetty 9 brings us exciting features like WebSocket and SPDY. In order to use websocket in my Clojure …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;jetty&lt;/li&gt;
&lt;li&gt;web&lt;/li&gt;
&lt;li&gt;websocket
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;The Clojure world has been using Jetty 7 for quite a long time because it's supported by the Ring development team. However, Jetty 9 brings us exciting features like WebSocket and SPDY. In order to use websocket in my Clojure web application, I built this adapter last weekend.&lt;/p&gt;

&lt;p&gt;[info.sunng/ring-jetty9-adapter "0.1.0"]&lt;/p&gt;

&lt;p&gt;The API for Clojure is still consistent with the one for jetty 7.&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(use 'ring.adapter.jetty9)&lt;br /&gt;
(run-jetty app {})&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;Options supported in jetty 9 are almost same as jetty 7 except the configurator is dropped. And a new "WebSockets" option is added. Accepting a map of context path and websocket class, it enables websocket protocol in your web application.&lt;/p&gt;

&lt;p&gt;[cc lang="clojure"]&lt;br /&gt;
(use 'ring.adapter.jetty9)&lt;br /&gt;
(run-jetty app {:websockets {"/loc" LocationTracker}})&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;Due to the lack of WebSocket API standards, I don't spend time on the WebSocket abstraction. Just use Jetty's internal API for websocket. Here is a typical implementation of websocket listener.&lt;br /&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;;; sample code&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;ns &lt;/span&gt;&lt;span class="nv"&gt;xxx.ws.location&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:gen-class&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;xxx.LocationTracker&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="ss"&gt;:init&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;init&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="ss"&gt;:state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;state&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="ss"&gt;:extends&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;org.eclipse.jetty.websocket.api.WebSocketAdapter&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="ss"&gt;:prefix&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ws-&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="ss"&gt;:exposes-methods&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;onWebSocketConnect&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;superOnWebSocketConnect&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:require&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;clojure.data.json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;:as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;json&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;clojure.tools.logging&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;:as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;monger.collection&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;:as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;mc&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;org.eclipse.jetty.websocket.api&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;WebSocketAdapter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;java.util&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;defn &lt;/span&gt;&lt;span class="nv"&gt;ws-init&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;[[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:client-id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;UUID/randomUUID&lt;/span&gt;&lt;span class="p"&gt;))}])&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;defn &lt;/span&gt;&lt;span class="nv"&gt;ws-onWebSocketConnect&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;session&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;.superOnWebSocketConnect&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;logging/warn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;new connection: &amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;get-client-id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;this&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;defn &lt;/span&gt;&lt;span class="nv"&gt;ws-onWebSocketText&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;message&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;msg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;json/read-json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nv"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;defn &lt;/span&gt;&lt;span class="nv"&gt;ws-onWebSocketClose&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;logging/debug&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;close socket&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Since Jetty will create new instance of adapter for each connection, it requires heavy usage of "gen-class" . Remember to add the namespace to AOT compilation. Detailed Jetty API spec can be found &lt;a href="http://download.eclipse.org/jetty/stable-9/apidocs/org/eclipse/jetty/websocket/api/WebSocketAdapter.html" target="_blank"&gt;here&lt;/a&gt;.&lt;br /&gt;

And also find the project is &lt;a href="https://github.com/sunng87/ring-jetty9-adapter" target="_blank"&gt;here&lt;/a&gt;.&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>'[ANN] Handlebars Clojure API'</title><link href="https://sunng.info/blog/ann-handlebars-clojure-api.html" rel="alternate"></link><published>2013-04-12T00:00:00+08:00</published><updated>2013-04-12T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2013-04-12:/blog/ann-handlebars-clojure-api.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;The ONLY real-world modern clojure templating system. &lt;br /&gt;
I just can't believe that the clojure world doesn't build web application with server-side template.&lt;br /&gt;
hiccup and enlive are neither **real-world**. So people tends to use single page architecture for a clojure backend. That's PAINFUL.&lt;/p&gt;

&lt;p&gt;Available …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;The ONLY real-world modern clojure templating system. &lt;br /&gt;
I just can't believe that the clojure world doesn't build web application with server-side template.&lt;br /&gt;
hiccup and enlive are neither **real-world**. So people tends to use single page architecture for a clojure backend. That's PAINFUL.&lt;/p&gt;

&lt;p&gt;Available on clojars [hbs "0.4.1"], code and docs on &lt;a href="https://github.com/readwise/hbs" target="_blank"&gt;github&lt;/a&gt;. We have been using hbs on &lt;a href="http://readwise.net" target="_blank"&gt;readwise.net&lt;/a&gt; for a long time.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>第一个开关</title><link href="https://sunng.info/blog/di-yi-ge-kai-guan.html" rel="alternate"></link><published>2013-02-23T00:00:00+08:00</published><updated>2013-02-23T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2013-02-23:/blog/di-yi-ge-kai-guan.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;Arduino&lt;/li&gt;
&lt;li&gt;Electronic
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;img src="http://i.imgur.com/3S4WPUG.jpg" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;过年回家路上无聊看了一些关于电子制作的书，知道了 Android 上的开发板 IOIO。另外认识了 Arduino 的程序，感觉 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;Arduino&lt;/li&gt;
&lt;li&gt;Electronic
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;img src="http://i.imgur.com/3S4WPUG.jpg" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;过年回家路上无聊看了一些关于电子制作的书，知道了 Android 上的开发板 IOIO。另外认识了 Arduino 的程序，感觉要比想象中还要简单。本来担心还要学一门新的语言，看了之后发现完全是多虑。&lt;/p&gt;

&lt;p&gt;在淘宝上找 IOIO 的时候正好发现了&lt;a href="http://item.taobao.com/item.htm?id=13115078082"&gt;一整套元件&lt;/a&gt;。买回来以后看看可能价格是贵了，但是对我这样的新手来说，省去了自己找元件的麻烦。因为找元件实在是很容易让人退缩。&lt;/p&gt;

&lt;p&gt;点亮 Arduino 基本上没什么问题。但是在 Archlinux 上访问 serial port 需要一些配置。首先要把用户加入 uucp 组确保可以访问 /dev/tty* 的设备。其次，需要给当前用户访问 /run/lock 的权限，否则 Arduino IDE 还是无法访问到 Arduino，这点在 Archlinux 的 Wiki 上有提及。&lt;/p&gt;

&lt;p&gt;我这个开关电路太简单了就不详细说了，参考书是一本 Getting started with Arduino。据说这本书出第二版了，第一版上 Arduino 的版本不是 Uno，所以图片有一些出入，不过接口都没有变化。这第一个二极管开关的电路还是挺有成就感的，晚上我就可以让台灯什么的都退了休吧。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>'R/W Update#1: 更好的时间线'</title><link href="https://sunng.info/blog/rw-update1-geng-hao-de-shi-jian-xian.html" rel="alternate"></link><published>2013-01-31T00:00:00+08:00</published><updated>2013-01-31T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2013-01-31:/blog/rw-update1-geng-hao-de-shi-jian-xian.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;readwise
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;img src="http://i.imgur.com/wReNhae.jpg" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;这周的工作，界面上能展示的不多。我们对时间线做了比较大的手术。&lt;/p&gt;

&lt;p&gt;原先的时间线的工作机制，是以☐☐☐☐☐☐☐☐☐☐☐☐☐（此 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;readwise
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;img src="http://i.imgur.com/wReNhae.jpg" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;这周的工作，界面上能展示的不多。我们对时间线做了比较大的手术。&lt;/p&gt;

&lt;p&gt;原先的时间线的工作机制，是以☐☐☐☐☐☐☐☐☐☐☐☐☐（此处省略200字）。这样的机制有两个明显的问题：
&lt;ol /&gt;&lt;/p&gt;

&lt;p&gt; &lt;li&gt;    看过和没有看过的文章常常交错在一起，让人迷惘&lt;/li&gt;&lt;/p&gt;

&lt;p&gt;     &lt;li&gt;   一夜过后，时间线上出现很神奇的情况，文章的热度排列为「冷…热…冷…热…」&lt;/li&gt;
&lt;/p&gt;

&lt;p&gt;不仅是很多用户，我自己使用的过程中也感到很不方便。于是我们痛下决心，抄起键盘对时间线大刀阔斧一番。 一小时前上线的版本已经基本解决了上面提到的问题。如若问题依旧，请参考题图。&lt;/p&gt;

&lt;p&gt;还有可以提一句，用户名旁边显示的数字是你的 Karma。目前 Karma 可以通过评论和评论被投票获得。&lt;/p&gt;

&lt;p&gt;&lt;img src="http://i.imgur.com/tqjVRN6.png" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;还有什么比工程师写得产品博客更平淡的，而且他还要一边 test on production。&lt;/p&gt;

&lt;p&gt;对对对，我们的工程师 &lt;a href="https://github.com/debugger87/"&gt;debugger87&lt;/a&gt; 昨天回家结婚，顺便过年了，我们祝他新婚愉快。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>'OpenStreetMap Nanjing: A Year of Edits, 2012'</title><link href="https://sunng.info/blog/openstreetmap-nanjing-a-year-of-edits-2012.html" rel="alternate"></link><published>2013-01-09T00:00:00+08:00</published><updated>2013-01-09T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2013-01-09:/blog/openstreetmap-nanjing-a-year-of-edits-2012.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;Nanjing&lt;/li&gt;
&lt;li&gt;OpenStreetMap
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;又到了A Year of Edits节目时间了，去年的场景还&lt;a href="http://sunng.info/blog/2012/01/openstreetmap-nanjing-a-year-of-edits/" target="_blank"&gt;历历在目&lt;/a&gt;。2012年OSM上的南京地图，变化更加可观。&lt;/p&gt;

&lt;p&gt;&lt;img src="http://i.imgur.com/S4OSv.png" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;脚本和mapnik依然在&lt;a href="https://gist.github.com/1639915" target="_blank"&gt;原处&lt;/a&gt;，mapnik升级到2.1.x，配置文件有了一些 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;Nanjing&lt;/li&gt;
&lt;li&gt;OpenStreetMap
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;又到了A Year of Edits节目时间了，去年的场景还&lt;a href="http://sunng.info/blog/2012/01/openstreetmap-nanjing-a-year-of-edits/" target="_blank"&gt;历历在目&lt;/a&gt;。2012年OSM上的南京地图，变化更加可观。&lt;/p&gt;

&lt;p&gt;&lt;img src="http://i.imgur.com/S4OSv.png" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;脚本和mapnik依然在&lt;a href="https://gist.github.com/1639915" target="_blank"&gt;原处&lt;/a&gt;，mapnik升级到2.1.x，配置文件有了一些变化。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>OSM reporter</title><link href="https://sunng.info/blog/osm-reporter.html" rel="alternate"></link><published>2012-12-24T00:00:00+08:00</published><updated>2012-12-24T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2012-12-24:/blog/osm-reporter.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;leaflet&lt;/li&gt;
&lt;li&gt;OpenStreetMap
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;好久没有Weekend project了，礼拜天给一个叫作&lt;a href="https://github.com/timlinux/osm-reporter"&gt;osm reporter&lt;/a&gt;的小项目写了一点代码。这个小程序的功能很简单，显示指定 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;leaflet&lt;/li&gt;
&lt;li&gt;OpenStreetMap
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;好久没有Weekend project了，礼拜天给一个叫作&lt;a href="https://github.com/timlinux/osm-reporter"&gt;osm reporter&lt;/a&gt;的小项目写了一点代码。这个小程序的功能很简单，显示指定区域里建筑物的贡献者情况。&lt;/p&gt;

&lt;p&gt;我做了一些修改，增加了道路贡献情况。还有，利用自己的heatcanvas库显示用户个人编辑的分布情况。&lt;/p&gt;

&lt;p&gt;&lt;a href=" http://reporter.fluv.io/?bbox=118.51638793945312,31.92943755974919,119.16183471679688,32.133175697091374&amp;obj=highway"&gt;这里&lt;/a&gt;有一个live demo，显示南京的编辑情况，不幸而又庆幸的是，离开南京大半年，我已经远远落后于sinopitt了。&lt;/p&gt;

&lt;p&gt;这个项目的缘起：&lt;a href="http://linfiniti.com/2012/12/holiday-openstreetmap-project-for-swellendam/"&gt;http://linfiniti.com/2012/12/holiday-openstreetmap-project-for-swellendam/&lt;/a&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>螺旋发展的技术</title><link href="https://sunng.info/blog/luo-xuan-fa-zhan-de-ji-zhu.html" rel="alternate"></link><published>2012-12-21T00:00:00+08:00</published><updated>2012-12-21T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2012-12-21:/blog/luo-xuan-fa-zhan-de-ji-zhu.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;css
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;那个谁说世界是螺旋发展的，我说也真是。&lt;/p&gt;

&lt;p&gt;现在的云计算潮流，经常能听到哪个地方哪个企业又推广云 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;css
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;那个谁说世界是螺旋发展的，我说也真是。&lt;/p&gt;

&lt;p&gt;现在的云计算潮流，经常能听到哪个地方哪个企业又推广云办公了，大家只要有一个显示器一样的瘦终端就可以连到云中，走到哪办公到哪。可是一想，这不就是黑客时代的主机/终端架构吗，一个用作输入输出的终端，连接到用于运算的主机上。当然，这里又可以说出千差万别（要不然这30年也白活了），但是至少大方面上他们是相同的。&lt;/p&gt;

&lt;p&gt;再说这两年流行的移动应用开发，让我想到快十年前那阵的很多共享软件。那会大家还学个Windows的桌面开发，MFC什么的。后来一阵所谓B/S架构的风刮起来，人们一夜之间都不屑于做这种桌面开发了。当时几个论据，跨平台的问题，软件分发的问题，确实得到了很好的解决。但是iphone流行起来以后，仿佛一夜之间又回到了桌面开发的时代。跨平台和软件分发的事也没人提了，辛辛苦苦地为了各种分辨率做设计，做测试，似乎看起来都不是个事。因为人们发现，还是本地应用的体验好。可是谁又能保证也许有那么一天，当本地应用的优势不再那么明显的时候，大家又会谈起跨平台，软件分发的事。&lt;/p&gt;

&lt;p&gt;说到跨平台了，Java的WORA（write once, run anywhere）当年是很大的卖点。可后来人们发现，好像也没这个必要，WOCA可以，退一步各个平台开发也不是事。不过又过了这么多年，平台越来越多了，光手机就三四个，这样的事恐怕又要被提起了。&lt;/p&gt;

&lt;p&gt;最后再说个小的。当年的网页用表格布局，后来被我们俗称div+css的组合横扫了，以至于用表格布局最后都不好意思跟人打招呼。这么些年过去了，人们发现自己其实真真切切地需要用表格布局，但是为了体现时代的进步，现在叫做grid layout。看看微软提得CSS 3的grid属性：&lt;br /&gt;
[cc lang="css"]&lt;br /&gt;
grid-column: 1; grid-column-span: 3; grid-row: 1;&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;活脱一个table里的colspan。&lt;/p&gt;

&lt;p&gt;当然，其实还是发展了，原先写在html里的东西，现在用css就可以解决。和前面的例子一样，看似一样，但是都是绕了回来。不好说是从弯路绕回直路，还是相反。在一个时期是正确的，待一些客观条件成熟了，也许换回原来的方式也是正确的。&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>ANN 美味爱读 readwise.net</title><link href="https://sunng.info/blog/ann-mei-wei-ai-du-readwisenet.html" rel="alternate"></link><published>2012-12-05T00:00:00+08:00</published><updated>2012-12-05T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2012-12-05:/blog/ann-mei-wei-ai-du-readwisenet.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;readwise&lt;/li&gt;
&lt;li&gt;work
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;诚惶诚恐地，但是还是得把这盘菜捧出来给大家尝尝。&lt;/p&gt;

&lt;p&gt;我们的新产品，&lt;a href="http://readwise.net/" target="_blank"&gt;美味爱读(ReadWise)&lt;/a&gt;现在正式开放帐号申 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;readwise&lt;/li&gt;
&lt;li&gt;work
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;诚惶诚恐地，但是还是得把这盘菜捧出来给大家尝尝。&lt;/p&gt;

&lt;p&gt;我们的新产品，&lt;a href="http://readwise.net/" target="_blank"&gt;美味爱读(ReadWise)&lt;/a&gt;现在正式开放帐号申请了。&lt;/p&gt;

&lt;p&gt;从哪里说起呢，美味书签，包括整个AVOS都在做信息提取相关的互联网产品，美味爱读依然延续的这个路线。美味爱读是一个阅读产品，不过与市面上其他的类似产品相比，我们有点不同。&lt;/p&gt;

&lt;p&gt;首先，我们通过技术手段，给每天抓取到的RSS文章打上粒度极细的标签（就像Delicious那样的tag），初到网站时，您可以选择自己感兴趣的几个标签，从而获得相关的新文章。
&lt;img src="http://i.imgur.com/UsPfE.png" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;其二，我们会跟踪文章在互联网上的传播情况，在微博和twitter上的引用次数，借此来判断它的价值，影响它在阅读器里的位置。我们还会从这些网站抓取用户的评论，看看别人如何说。
&lt;img src="http://i.imgur.com/Qxncb.png" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;由于时间和资源有限，初期我们还只有技术和科技相关的内容。其中有很多英文，有的用户可能会对此存在疑问。考虑这个领域有的特殊性，为了保证信息的质量，我们最后还是决定不回避这些英文内容。这恐怕也是开了先河，既然要不同，那就要不同地彻底。当然很快随着领域的扩大，内容的增加，高质量的中文文章肯定会越来越多。
&lt;img src="http://i.imgur.com/A3t71.png" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;我们采用的responsive UI可以在各种分辨率的屏幕上工作，所以Android的Chrome和iPhone的浏览器也可以直接访问，不用担心。
&lt;img src="http://i.imgur.com/WexJU.png" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;最后，在阅读的UI上，可以用键盘J/K查看文章，O打开链接，M标记收藏。之所以强调一下，是因为这是一个工程师驱动的产品，这样的小功能是必备的。&lt;/p&gt;

&lt;p&gt;这是我来北京后的第一个完整的项目，参与了从原型到初期开发，到现在的开放邀请，经历了宝贵的从无到有的过程。现在终于推开门，迈出第一步。&lt;/p&gt;

&lt;p&gt;产品的详细介绍，请移步我们的&lt;a href="http://readwise.net/blog/12-17-2012/this-is-our-ann.html" target="_blank"&gt;产品博客&lt;/a&gt;。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>heatcanvas is available via bower</title><link href="https://sunng.info/blog/heatcanvas-is-available-via-bower.html" rel="alternate"></link><published>2012-11-19T00:00:00+08:00</published><updated>2012-11-19T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2012-11-19:/blog/heatcanvas-is-available-via-bower.html</id><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;heatcanvas&lt;/li&gt;
&lt;li&gt;javascript
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;The heat map toolkit, heatcanvas, is now available via twitter's package management tool, bower.&lt;/p&gt;

&lt;p&gt;You can now install heatcanvas with a single command:&lt;/p&gt;

&lt;p&gt;bower install heatcanvas&lt;/p&gt;

&lt;p&gt;Enjoy this library.&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Exaile 豆瓣电台插件 0.0.13</title><link href="https://sunng.info/blog/exaile-dou-ban-dian-tai-cha-jian-0013.html" rel="alternate"></link><published>2012-11-12T00:00:00+08:00</published><updated>2012-11-12T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2012-11-12:/blog/exaile-dou-ban-dian-tai-cha-jian-0013.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;Douban&lt;/li&gt;
&lt;li&gt;exaile&lt;/li&gt;
&lt;li&gt;github&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;近来工作甚忙，一直没有精力来更新这个插件。加之exaile被archlinux清到了aur仓库，项目的前景亦是堪忧。恰好有两位热心网友 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;Douban&lt;/li&gt;
&lt;li&gt;exaile&lt;/li&gt;
&lt;li&gt;github&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;近来工作甚忙，一直没有精力来更新这个插件。加之exaile被archlinux清到了aur仓库，项目的前景亦是堪忧。恰好有两位热心网友@JoveYu和@watermelonlh在github上提出愿意帮助维护这个项目。两位都是很好的贡献者，用了一周左右的时间，居然看遍了我乱糟糟的代码，更看遍了Exaile更乱糟糟的代码。说干就干，还真让这个插件在Exaile 0.3.3健健康康地运行起来。而这段时间我没能给两位什么具体的帮助，着实惭愧。&lt;/p&gt;

&lt;p&gt;不管怎样，现在我们又拥有了和原先一模一样的功能：
&lt;img src="http://i.imgur.com/L3oqp.png" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;你可以在github找到这个项目并下载最新的版本：&lt;br /&gt;
https://github.com/sunng87/exaile-doubanfm-plugin&lt;/p&gt;

&lt;p&gt;同时，适用于GNOME 3.6的扩展也已经在审核中了，一旦成功，根据gnome-shell据说最新的自动更新功能，已经安装的用户可以直接更新的最新版本。
&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Adding -var-missing to Clojure Namespace</title><link href="https://sunng.info/blog/adding-var-missing-to-clojure-namespace.html" rel="alternate"></link><published>2012-10-27T00:00:00+08:00</published><updated>2012-10-27T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2012-10-27:/blog/adding-var-missing-to-clojure-namespace.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;clojure
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;h3&gt;Motivation&lt;/h3&gt;&lt;/p&gt;

&lt;p&gt;I Just need some mechanism like "methondMissing" in Ruby. When a nonexistent var is called, the lookup system will try to call a "-var-missing" function in the namespace. This function should return a var and clojure compiler assumes this var as …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;clojure
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;h3&gt;Motivation&lt;/h3&gt;&lt;/p&gt;

&lt;p&gt;I Just need some mechanism like "methondMissing" in Ruby. When a nonexistent var is called, the lookup system will try to call a "-var-missing" function in the namespace. This function should return a var and clojure compiler assumes this var as the one it was looking for.&lt;/p&gt;

&lt;p&gt;For example, in the shake library:&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(ns shake.core)&lt;/p&gt;

&lt;p&gt;(defn -var-missing [sym]&lt;br /&gt;
  (create-executable-var sym))&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;To support lazy loading, shake 0.3.0 won't read your path. It will now create vars on demand. So on calling sh/uname, a var named `uname` will be created. And to create a var, just use intern or eval.&lt;/p&gt;

&lt;p&gt;[cc lang="clojure"]&lt;br /&gt;
(require '[shake.core :as sh])&lt;br /&gt;
(sh/uname -a)&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;How to&lt;/h3&gt;&lt;/p&gt;

&lt;p&gt;I'm sorry there is no way to implement this except hacking into Clojure's compiler. Fortunately, it's not too difficult to find out the injection point. &lt;/p&gt;

&lt;p&gt;As you may know, there are two phases in Clojure compiler: expanding macros and evaluating forms. Both phases will look up vars to find macros or values. So we should take care both of them.&lt;/p&gt;

&lt;p&gt;All code diff is here:
&lt;script src="https://gist.github.com/3962750.js?file=Compiler.java"&gt;&lt;/script&gt;&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;Conclusion&lt;/h3&gt;
Adding -var-missing is just an attempt to implement lazy loading of vars. And it provides another smooth syntax for writing DSLs. But actually, Clojure's macro system provides a great metaprogramming mechanism. So in most case, you don't have to hack into the Compiler like this. Just put your DSL into a top level macro, and you can get them done all in clojure scripts. &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>New shake syntax</title><link href="https://sunng.info/blog/new-shake-syntax.html" rel="alternate"></link><published>2012-09-23T00:00:00+08:00</published><updated>2012-09-23T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2012-09-23:/blog/new-shake-syntax.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;project
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;As shake goes public, I received a lot of feedback. The top issue is about using clojure variables in shake macros. Now it has been fixed in 0.2.2. Let me show you the new syntax. &lt;/p&gt;

&lt;p&gt;Using vars, local bindings in …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;project
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;As shake goes public, I received a lot of feedback. The top issue is about using clojure variables in shake macros. Now it has been fixed in 0.2.2. Let me show you the new syntax. &lt;/p&gt;

&lt;p&gt;Using vars, local bindings in shake macros:&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(require '[shake.core :as sh])&lt;/p&gt;

&lt;p&gt;(let [x "/home/nsun"]&lt;br /&gt;
  (sh/ls -l $x))&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;So you have to prefix the clojure variable with a dollar sign. This is quite similar to what we did in shell programming.&lt;/p&gt;

&lt;p&gt;And more interesting, you can also use a $ prefixed clojure form in shake:&lt;/p&gt;

&lt;p&gt;[cc lang="clojure"]&lt;br /&gt;
(sh/curl $(format "https://github.com/%s" "sunng87"))&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;Thanks to Clojure macro system, it has great flexibility to manipulate symbols and code lists, making inventing new syntax much easier than other languages. Shake can be a great example in describing macro system.
&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>'Shake: Every Program Can Be a Clojure Function'</title><link href="https://sunng.info/blog/shake-every-program-can-be-a-clojure-function.html" rel="alternate"></link><published>2012-09-21T00:00:00+08:00</published><updated>2012-09-21T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2012-09-21:/blog/shake-every-program-can-be-a-clojure-function.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;github&lt;/li&gt;
&lt;li&gt;linux
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;You might have heard of &lt;a href="http://amoffat.github.com/sh/index.html"&gt;sh&lt;/a&gt;, which brings python an interface to call subprocesses. The API of sh is pretty cool: Every command can be treated as a python function, and imported from a namespace. Options and arguments are passed in …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;github&lt;/li&gt;
&lt;li&gt;linux
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;You might have heard of &lt;a href="http://amoffat.github.com/sh/index.html"&gt;sh&lt;/a&gt;, which brings python an interface to call subprocesses. The API of sh is pretty cool: Every command can be treated as a python function, and imported from a namespace. Options and arguments are passed in as python string.&lt;/p&gt;

&lt;p&gt;But I think in Clojure, things can be even cooler. We dynamically create symbols for every program. We will have a beautiful DSL so you don't have to quote arguments as string. So when you are using this library, it may look like:&lt;/p&gt;

&lt;p&gt;[cc lang="clojure"]&lt;br /&gt;
(ls)&lt;br /&gt;
(uname -a)&lt;br /&gt;
(ip -4 addr)&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;And actually it's just like that! I create this library called &lt;strong&gt;&lt;a href="https://github.com/sunng87/shake/" target="_blank"&gt;shake&lt;/a&gt;&lt;/strong&gt;. When you load `shake.core`, it indexes all the executables in your path. Then all programs are available to you in a clojure native way.&lt;/p&gt;

&lt;p&gt;[cc lang="clojure"]&lt;/p&gt;

&lt;p&gt;(use 'shake.core)&lt;br /&gt;
(uname -a) ;; returns a java.lang.Process, that you can send data, read data and wait for termination.&lt;/p&gt;

&lt;p&gt;;; for those just need output&lt;br /&gt;
(alter-var-root *print-outpt* (fn [_] true))&lt;br /&gt;
(uname -a)&lt;br /&gt;
;; it prints ...&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;There's a lot of fun in implementing this library. First, to be able to use custom symbol in the DSL, you have to make these executables as macros. Second, find a way to programmably create vars which are named by string. The power of Clojure enables all the ideas and makes it possible. Check out the source code if you are interested in: &lt;a href="https://github.com/sunng87/shake/"&gt;https://github.com/sunng87/shake/&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>'cljts: Java Topology Suite for Clojure'</title><link href="https://sunng.info/blog/cljts-java-topology-suite-for-clojure.html" rel="alternate"></link><published>2012-09-15T00:00:00+08:00</published><updated>2012-09-15T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2012-09-15:/blog/cljts-java-topology-suite-for-clojure.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;GIS
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;I almost forgot to announce this library I made half of a year ago. This library is aiming to bring Clojure to GIS. So you can manipulate geometry objects with a set of clojure functions.&lt;/p&gt;

&lt;p&gt;The library covers :
&lt;ul&gt;
    &lt;li&gt;Geometries defined in Simple …&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;GIS
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;I almost forgot to announce this library I made half of a year ago. This library is aiming to bring Clojure to GIS. So you can manipulate geometry objects with a set of clojure functions.&lt;/p&gt;

&lt;p&gt;The library covers :
&lt;ul&gt;
    &lt;li&gt;Geometries defined in Simple Feature Spec&lt;/li&gt;
    &lt;li&gt;Spatial relationship test, based on DE-9IM.&lt;/li&gt;
    &lt;li&gt;IO functions, WKT and WKB support&lt;/li&gt;
    &lt;li&gt;some spatial analysis functions such as buffer, convex-hull&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;Also, this week Alexey Pushkin sent pull request and added support for Perpared Geometry and Affine transformations.&lt;/p&gt;

&lt;p&gt;The current release of cljts is 0.2.0-SNAPSHOT. You can find API document at &lt;a href="http://sunng87.github.com/cljts/"&gt;here&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;If you are also interested in bringing clojure to GIS, feel free to get connected and hope I could help you.
&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>丰宁草原</title><link href="https://sunng.info/blog/feng-zhu-cao-yuan.html" rel="alternate"></link><published>2012-09-09T00:00:00+08:00</published><updated>2012-09-09T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2012-09-09:/blog/feng-zhu-cao-yuan.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;D60&lt;/li&gt;
&lt;li&gt;Travel
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;来了北京以后一直没有停止到处游走的脚步，这周末去了丰宁的坝上草原。第一回去草原，第一回骑 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;D60&lt;/li&gt;
&lt;li&gt;Travel
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;来了北京以后一直没有停止到处游走的脚步，这周末去了丰宁的坝上草原。第一回去草原，第一回骑马，第一回看日出。&lt;/p&gt;

&lt;p&gt;丰宁在承德市，我们去的地方叫大滩镇，在丰宁县的北部。晚上九点从北京出发，达到大滩的农家院已经是半夜两点了。农家院的住宿条件还不错。当天天气晴朗，晚上抬头能看到多年未见漫天星辰。本来约好当天去看日出，谁知睁开眼的时候屋里都满是阳光了。上午就直接骑马去草原：村里的旅游经济搞得红火，有开旅店的，有养马领路的。&lt;/p&gt;

&lt;p&gt;第一回骑马，或者说第一回和个动物挨这么近。前半程紧张的够呛，坐在马上浑身紧着，都不知道哪用力（或者说全身都在使劲）。骑着骑着渐渐放松，知道用腿加紧马，手拽着缰绳。我把马的速度戏分为三档：一档起步，踱步子；二档小跑，颠着；三档叫奔，后腿蹬起来，骑着有种要飞的感觉（或者说要摔下来的感觉）。这里二档是最难受的，颠得屁股生疼，五脏六腑都快倒出来。开始的时候没觉得累，反倒是休息了一下，开始背疼腰疼腿疼，等到第二天早晨居然就真的弯不下腰了。不过侧面倒是说明，别看这好像是坐在马上，全身肌肉到是没闲着。腰背这部分常年没练，疼点也正常。再说马，马是个随大流的动物，跑不跑全看他认识的这一伙马跑不跑。他要是不想动，再怎么蹬也没有用。当然这马也能被理解，就像那句“一定有一些马，想回到古代”。&lt;/p&gt;

&lt;p&gt;第二天早上终于坚定地看到了日出。在满天月亮金星木星的时辰披上长袖出门，看着背后的天色，一路走走跑跑生怕阳光不等看日出的人就位就露出来。坐到山坡上，等天边的第一缕阳光的心情，是美好的。不过更美好的是等到阳光普照后，你踱着步子从山上下来，穿过牛群马群羊群庄稼蔬菜，再被马群穿过。拖上长长的影子，一路鸡鸣犬吠马啸牛叫，往粉雾里的村庄走。&lt;/p&gt;

&lt;p&gt;上午去的闪电湖，是草原上一个水坝成湖。有水的地方能有灵性，至少在干燥的地方，一片湖泊让人精神放松，有工夫想点有灵性的事。远处的大坝分隔，这水天相接，像一面镜子一样，互相照着。我在这打破了自己上个月的北纬的纪录，41.656度。&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/7962669566/" title="DSC_0077 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm9.staticflickr.com/8319/7962669566_26b6dcf278.jpg" width="500" height="336" alt="DSC_0077" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/7962670216/" title="DSC_0134 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm9.staticflickr.com/8458/7962670216_6a0676ee75.jpg" width="500" height="336" alt="DSC_0134" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/7962670708/" title="DSC_0146 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm9.staticflickr.com/8171/7962670708_664b68b9e8.jpg" width="500" height="336" alt="DSC_0146" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/7962671048/" title="DSC_0161 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm9.staticflickr.com/8299/7962671048_6944da4427.jpg" width="500" height="336" alt="DSC_0161" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/7962671382/" title="DSC_0165 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm9.staticflickr.com/8300/7962671382_ed75b437cc.jpg" width="500" height="336" alt="DSC_0165" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/7962671604/" title="DSC_0170 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm9.staticflickr.com/8442/7962671604_4994affce5.jpg" width="500" height="336" alt="DSC_0170" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/7962672440/" title="DSC_0176 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm9.staticflickr.com/8171/7962672440_1ebbb21ca0.jpg" width="500" height="336" alt="DSC_0176" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/7962672702/" title="DSC_0185 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm9.staticflickr.com/8173/7962672702_be01baf8a8.jpg" width="500" height="336" alt="DSC_0185" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/7962673572/" title="DSC_0209 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm9.staticflickr.com/8311/7962673572_efcc4fc722.jpg" width="500" height="336" alt="DSC_0209" /&gt;&lt;/a&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>京张铁路</title><link href="https://sunng.info/blog/jing-zhang-tie-lu.html" rel="alternate"></link><published>2012-08-19T00:00:00+08:00</published><updated>2012-08-19T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2012-08-19:/blog/jing-zhang-tie-lu.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;Beijing&lt;/li&gt;
&lt;li&gt;Railway
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;这两周体验了一下北京周边的铁路。上周从北京西到张家口，这周从北京北到延庆。&lt;/p&gt;

&lt;p&gt;用倒叙的方式说 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;Beijing&lt;/li&gt;
&lt;li&gt;Railway
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;这两周体验了一下北京周边的铁路。上周从北京西到张家口，这周从北京北到延庆。&lt;/p&gt;

&lt;p&gt;用倒叙的方式说，我们常说的京张铁路是詹天佑修的那条百年历史的路线。这条路从现在的眼光看，南起西直门的北京北站，经过八达岭，康庄跨过官厅水库到河北沙城，进而宣化，张家口。不过这个路线现在已经少有车走了（查到一条L295次，但不确定具体路线）。从北京北开到延庆的S2动车是现在这条路线上主角。这是一趟郊区线，准确地说是旅游列车，为了解决从北京市区到八达岭交通压力大的问题。所有的车厢一律是动车一等座，从北京北到八达岭只要5元，可以说是物美价廉。一直到八达岭之前，路线都是老的京张铁路，在青龙桥依然还有著名的”人字形“铁路。作为旅游列车的话还真是个保留节目。&lt;/p&gt;

&lt;p&gt;这次车在康庄附近离开京张线，进入康延线，一直开到延庆站，铁路就到了尽头。目前延庆车站也只有往返这个区间的S2线。另外据说延庆还有一个火车北站，是大秦线的一座货运车站，没有客运业务。&lt;/p&gt;

&lt;p&gt;再说上周去张家口。现在北京去张家口的主要列车都从丰沙线走，经过丰台，石景山南，三家店进入京西的山区，最后在沙城和老的京张铁路汇合。在车上听人介绍，这段铁路1954年修成。出京经过62个隧道，进京65个。这些隧道全部分布在丰沙线上，据说是这条线是詹天佑最初选定路线，但是因为成本较高只能选了八达岭方向。我乘4447次从北京西出发，进入这段之后风景秀丽，有山有水（官厅水库），打开车窗清风拂面，实在是坐火车的最佳体验。&lt;/p&gt;

&lt;p&gt;最后，张家口现在有两个车站。南站位于京包线上，几乎所有经过到达张家口的车都在这里停，是目前主要的车站。它门前是詹天佑的铜像。另一个张家口站是真正的京张铁路终点，也是百年历史，深入张家口市区，现在只有终到张家口的少数几次车。他的样式和我印象里的南京西站几乎一样，地位似乎也和西站类似。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Top Github users in China</title><link href="https://sunng.info/blog/top-github-users-in-china.html" rel="alternate"></link><published>2012-07-19T00:00:00+08:00</published><updated>2012-07-19T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2012-07-19:/blog/top-github-users-in-china.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;github
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;有感于&lt;a href="http://sofish.de/file/demo/github/" target="_blank"&gt;这个版本&lt;/a&gt;的github中国用户排名，我觉得单纯根据用户的followers数量不能完全说明问题。因此，需要改进一下排名的分数，加入项目Watchers的数量，这样可以 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;github
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;有感于&lt;a href="http://sofish.de/file/demo/github/" target="_blank"&gt;这个版本&lt;/a&gt;的github中国用户排名，我觉得单纯根据用户的followers数量不能完全说明问题。因此，需要改进一下排名的分数，加入项目Watchers的数量，这样可以让优质项目多的用户排名到前面。&lt;/p&gt;

&lt;p&gt;这个策略是：followers + 1.5 * watchers + 2 * forks&lt;/p&gt;

&lt;p&gt;得到的排名如下：
&lt;script src="https://gist.github.com/3141146.js?file=results"&gt;&lt;/script&gt;&lt;/p&gt;

&lt;p&gt;另外，这前100名用户里，语言使用的情况如下：&lt;br /&gt;
'JavaScript': 80&lt;br /&gt;
'Ruby': 52&lt;br /&gt;
'Python': 46&lt;br /&gt;
'C': 34&lt;br /&gt;
'C++': 30&lt;br /&gt;
'Java': 28&lt;br /&gt;
'VimL': 28&lt;br /&gt;
'Shell': 24&lt;br /&gt;
'Objective-C': 24&lt;br /&gt;
'PHP': 21&lt;br /&gt;
'CoffeeScript': 13&lt;br /&gt;
'Perl': 10&lt;br /&gt;
'Erlang': 9&lt;br /&gt;
'Emacs Lisp': 9&lt;br /&gt;
'Lua': 6&lt;br /&gt;
'Haskell': 5&lt;br /&gt;
'Clojure': 5&lt;br /&gt;
'Go': 5&lt;br /&gt;
'ActionScript': 4&lt;br /&gt;
'C#': 3&lt;br /&gt;
'Scheme': 2&lt;br /&gt;
'Common Lisp': 2&lt;br /&gt;
'Elixir': 2&lt;br /&gt;
'Scala': 1&lt;br /&gt;
'Objective-J': 1&lt;br /&gt;
'Vala': 1&lt;br /&gt;
'Nemerle': 1&lt;/p&gt;

&lt;p&gt;明天我会补充一下城市的排名。&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>更新了stages</title><link href="https://sunng.info/blog/geng-xin-liao-stages.html" rel="alternate"></link><published>2012-07-13T00:00:00+08:00</published><updated>2012-07-13T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2012-07-13:/blog/geng-xin-liao-stages.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;github&lt;/li&gt;
&lt;li&gt;java
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="https://github.com/sunng87/stages"&gt;Stages&lt;/a&gt;原先是业余时间开发的一个简单的框架，用来实现SEDA。不过现在这个库经过同事们的加强，已经用 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;github&lt;/li&gt;
&lt;li&gt;java
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="https://github.com/sunng87/stages"&gt;Stages&lt;/a&gt;原先是业余时间开发的一个简单的框架，用来实现SEDA。不过现在这个库经过同事们的加强，已经用在了美味和zeen的生产环境。最近我也对开源的这个分支做了一些修改。&lt;/p&gt;

&lt;p&gt;首先是使用更安全的线程池，现在必须指定任务积压后的策略，即RejectionHandler。用户也可以设置自己的Queue，并且指定允许的大小。这是之前应该要做但是一直没做的事情。&lt;/p&gt;

&lt;p&gt;其次在Task上增加了一个priority属性，如果你使用的是PriorityBlockingQueue，那么任务会根据这个优先级排序。但是由于PriorityBlockingQueue是unbounded，所以请慎用这个功能。&lt;/p&gt;

&lt;p&gt;另外，现在所有的Task都通过perf4j计时，你可以用过配置log4j打印出性能报表日志。也可以通过JMX将耗时情况发布出去，我们在生产环境里通过ganglia监控了这些数据，还是比较有参考价值的。&lt;/p&gt;

&lt;p&gt;最后，现在jmx监控stage积压队列的功能终于实现了。我会把每个队列的pending tasks数量通过mbean暴露出来，同样可以配置监控这个数据。有了这个数据后，我们可以很明确地了解各个stage的压力情况，调整线程池的策略。&lt;/p&gt;

&lt;p&gt;&lt;img src="http://i.imgur.com/ashLh.png" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Timing brings perf4j to clojure</title><link href="https://sunng.info/blog/timing-brings-perf4j-to-clojure.html" rel="alternate"></link><published>2012-07-02T00:00:00+08:00</published><updated>2012-07-02T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2012-07-02:/blog/timing-brings-perf4j-to-clojure.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;github&lt;/li&gt;
&lt;li&gt;java
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="https://github.com/sunng87/timing" target="_blank"&gt;Timing&lt;/a&gt; is a dead simple clojure library wraps perf4j, while perf4j is an advanced library to log call time. It's like log4j to System.out.println, that to System.currentTimeMillies. &lt;/p&gt;

&lt;p&gt;The core part of Timing library is a `timed` macro. You …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;github&lt;/li&gt;
&lt;li&gt;java
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="https://github.com/sunng87/timing" target="_blank"&gt;Timing&lt;/a&gt; is a dead simple clojure library wraps perf4j, while perf4j is an advanced library to log call time. It's like log4j to System.out.println, that to System.currentTimeMillies. &lt;/p&gt;

&lt;p&gt;The core part of Timing library is a `timed` macro. You can put any forms in it and it will log the call time.&lt;/p&gt;

&lt;p&gt;[cc lang="clojure"]&lt;br /&gt;
(timed "demo"&lt;br /&gt;
  (look-for-something-from-the-moon)&lt;br /&gt;
  ...)&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;And it logs:&lt;br /&gt;
start[1341215254682] time[1000] tag[demo]&lt;/p&gt;

&lt;p&gt;Timing looks up your classpath at startup for a logging backend: slf4j or log4j. If neither of them found, it fails back to log to stderr. By the way, log4j is the recommended logging backend. There are some predefined appender in perf4j to generate semi-realtime summary and charts. Anyway, Timing, as a library, doesn't depend on any library other than perf4j.&lt;/p&gt;

&lt;p&gt;The project is hosted at &lt;a href="https://github.com/sunng87/timing" target="_blank"&gt;github&lt;/a&gt; and available on clojars of version "0.1.0".&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>登Py山</title><link href="https://sunng.info/blog/deng-pyshan.html" rel="alternate"></link><published>2012-06-17T00:00:00+08:00</published><updated>2012-06-17T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2012-06-17:/blog/deng-pyshan.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;Beijing&lt;/li&gt;
&lt;li&gt;outdoor
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;我的个天总算是有可写的东西了。&lt;/p&gt;

&lt;p&gt;这次来北京有个重要的主题叫做锻炼身体，为此不惜代价抓住有 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;Beijing&lt;/li&gt;
&lt;li&gt;outdoor
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;我的个天总算是有可写的东西了。&lt;/p&gt;

&lt;p&gt;这次来北京有个重要的主题叫做锻炼身体，为此不惜代价抓住有效的机会来参加这些活动。昨天跑去爬了昌平十三陵水库旁边的蟒山。这个蟒山的，我就叫他python山。据说是本地爬山的入门路线之一。这次活动正好副领队又是三年多没见过的老同学，于是就毫不犹豫地参加了。爬山前的周四是上线的日子，晚上折腾到两点多，第二天又早早去上班。我要说的是周六显然不在最佳状态。&lt;/p&gt;

&lt;p&gt;上次有这样的活动还是07年去庐山，那次就几乎中暑倒在山谷里了，为了避免悲剧发生这次背了3升水上山。这山是前半段比较陡，后半段稍微好一些。还是和上次庐山一样，跟着体力好走得快的人，很快就把力气用完了，坐在路边上一歇就是十五分钟，眼看着从队伍前头落到了队尾。然后也没有然后了，登顶的时候确实是在队尾。一开始还有心思照照相，后来赶紧把相机收起来，你不知道那个相机在肚子上一弹一弹的多耗费体力。&lt;/p&gt;

&lt;p&gt;登顶之后吃过午饭感觉就好多了，下到天池看了一样大水库，然后再从景区的台阶返回。据说下山的路是1299级台阶。还是一样，刚出发的时候还在前面，走着走着就看见收队的同学了。下台阶下得两腿发颤，最累的时候感觉随时都可能腿一软就坐下了，幸好这种情况没有发生。&lt;/p&gt;

&lt;p&gt;这次爬山最有成就感的，除了没有掉队以外，就是没出什么洋相。比庐山下山时坐倒n次要强多了。另外就是参加这个活动，可以跟各个年龄层次的人一起，也是感受一下这些有活力有生活的人。平时接触的年轻人、网民什么的太多了，总是无时不刻地抱怨却少有行动，够腻歪。跟大家一起爬山，是有一种完全不同的空气。&lt;/p&gt;

&lt;p&gt;最后就是风景真好，不虚此行。遗憾是只带了35/1.8的镜头，拍风景有点力不从心。这样壮阔的场面还是应该用18段来拍更精彩。
&lt;a href="http://www.flickr.com/photos/40741608@N08/7384408990/" title="DSC_0014 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.staticflickr.com/5452/7384408990_31ca11fb21.jpg" width="500" height="336" alt="DSC_0014" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/7384410222/" title="DSC_0018 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm9.staticflickr.com/8019/7384410222_d5e0cd81cc.jpg" width="500" height="336" alt="DSC_0018" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/7384411678/" title="DSC_0040 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.staticflickr.com/5114/7384411678_d14f81bab4.jpg" width="500" height="336" alt="DSC_0040" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/7384413274/" title="DSC_0042 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm8.staticflickr.com/7220/7384413274_b65eec8ab8.jpg" width="336" height="500" alt="DSC_0042" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/7384414130/" title="DSC_0046 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm8.staticflickr.com/7100/7384414130_44a25f2442.jpg" width="500" height="336" alt="DSC_0046" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/7384415136/" title="DSC_0059 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm8.staticflickr.com/7233/7384415136_ec1532ce2d.jpg" width="500" height="336" alt="DSC_0059" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/7384416602/" title="DSC_0069 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm8.staticflickr.com/7214/7384416602_f40226b59c.jpg" width="500" height="336" alt="DSC_0069" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/7384417628/" title="DSC_0071 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm8.staticflickr.com/7227/7384417628_0f5f81982c.jpg" width="500" height="336" alt="DSC_0071" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/7384419526/" title="DSC_0076 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm8.staticflickr.com/7097/7384419526_b725db3e50.jpg" width="336" height="500" alt="DSC_0076" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/7384421628/" title="DSC_0078 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.staticflickr.com/5321/7384421628_62d1735dcc.jpg" width="500" height="336" alt="DSC_0078" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/7384422998/" title="DSC_0084 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.staticflickr.com/5159/7384422998_176421ec81.jpg" width="500" height="336" alt="DSC_0084" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/7384424660/" title="DSC_0086 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.staticflickr.com/5464/7384424660_2e5601fc4c.jpg" width="500" height="336" alt="DSC_0086" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;以后只要有时间我还去，哪怕还是38度。
&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Keep-Alive</title><link href="https://sunng.info/blog/keep-alive.html" rel="alternate"></link><published>2012-06-02T00:00:00+08:00</published><updated>2012-06-02T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2012-06-02:/blog/keep-alive.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;史无前例，上个月只写了一篇blog，再加上网站遭遇认证很多地方都访问不了，故而有必要keep alive一下，该reconnect的请自觉重连。&lt;/p&gt;

&lt;p&gt;从家 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;史无前例，上个月只写了一篇blog，再加上网站遭遇认证很多地方都访问不了，故而有必要keep alive一下，该reconnect的请自觉重连。&lt;/p&gt;

&lt;p&gt;从家里出来，再次跑到北京来，就做好了艰苦卓绝的思想准备。现在的工作节奏，和2010年春天在盛大在线的时候差不多。那时一腔热血，也没什么经验，做什么事都心里没底。当然也是那段时间学到的东西最多，人生就是这样，熬过一个最困难的时候，总会有巨大的收获（如果没有收获说明还没熬过去，下同）。时过境迁，现在的情况有了巨大的不同，比如看&lt;a href="http://tech.sina.com.cn/i/2012-05-26/03317171784.shtml" target="_blank"&gt;这里&lt;/a&gt;。曾经没日没夜的奋斗的项目，最后烟消云散仿佛都不存在过。&lt;/p&gt;

&lt;p&gt;人最大的瓶颈是时间，时间不可逆，挥霍掉就没有了。为了能有口饭吃，大部分时间还得拿自己的时间去换别人的金钱。如果这个时间能有合理的回报或是各取所需最好，如果这个工作自己也认同倒也不错。如果是纯粹的时间换金钱（所谓工作毕竟是工作），那就难免悲伤了。所以看到&lt;a href="http://www.gsb.stanford.edu/news/research/aaker_happiness_2011.html" target="_blank"&gt;这样&lt;/a&gt;一篇文章，还是觉得你很有必要快速浏览一下：
&lt;ul&gt;
    &lt;li&gt;Spend time with the right people. &lt;/li&gt;
    &lt;li&gt;Spend time on the right activities. &lt;/li&gt;
    &lt;li&gt;Enjoy experiences without spending time actually doing them. &lt;/li&gt;
    &lt;li&gt;Expand your time. &lt;/li&gt;
    &lt;li&gt;Be aware that happiness changes over time.&lt;/li&gt;
&lt;/ul&gt;
时间自由是人生最高自由。为了合理的利用时间，请关掉你正在折腾配置文件的emacs。&lt;/p&gt;

&lt;p&gt;做了一个brogrammer的&lt;a href="http://www.aqee.net/docs/Quiz-Are-You-a-Brogrammer/" target="_blank"&gt;测验&lt;/a&gt;，和公司同事一起做的。结果得分-105，算是标准nerd了，在公司也是仅次于老板的-120。这个分数基本让人满意，对于那些戴墨镜编程的brogrammer，我想说的是，我的emacs一直是深色背景。&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>加入了美味书签</title><link href="https://sunng.info/blog/jia-ru-liao-mei-wei-shu-qian.html" rel="alternate"></link><published>2012-05-11T00:00:00+08:00</published><updated>2012-05-11T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2012-05-11:/blog/jia-ru-liao-mei-wei-shu-qian.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;Beijing&lt;/li&gt;
&lt;li&gt;meifm
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;距离上次更新已经有大半个月的时间了，最近一直忙着搬家和适应新环境。现在我又跑到北京来工 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;Beijing&lt;/li&gt;
&lt;li&gt;meifm
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;距离上次更新已经有大半个月的时间了，最近一直忙着搬家和适应新环境。现在我又跑到北京来工作，好在这次终于不是在上地。我记得在写工作第三年的愿望的时候，有一条就是能加入一个优秀的小团队。那么目前看来一切顺利，美味书签这样的团队就是我一直希望能找到的。&lt;/p&gt;

&lt;p&gt;我们的团队是Youtube创始人陈士骏的&lt;a href="http://avos.com" target="_blank"&gt;AVOS公司&lt;/a&gt;中国分支，中国团队由江宏带领，目前有20人，包括技术（前端、后端、移动），编辑等等。我们的团队网站上一一介绍了每个成员，我就不重复了。&lt;/p&gt;

&lt;p&gt;在工作上，眼下最好的事情，是我终于可以利用自己喜爱和擅长的技术，来主导一些系统的开发。我们会尽可能地用热爱的语言（毫无疑问是clojure）开发，这想起来就让人兴奋。我这个人对技术还是有一些洁癖和偏执的。这样虽然路漫漫坑无数，但是收获一定不小，过程也在把握中，青春岁月不可再蹉跎了。&lt;/p&gt;

&lt;p&gt;最后欢迎大家都来用&lt;a href="http://mei.fm" target="_blank"&gt;我们的产品&lt;/a&gt;。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Slacker 0.8.0</title><link href="https://sunng.info/blog/slacker-080.html" rel="alternate"></link><published>2012-04-22T00:00:00+08:00</published><updated>2012-04-22T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2012-04-22:/blog/slacker-080.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;github&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;slacker
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;A new release 0.8.0 of &lt;a href="https://github.com/sunng87/slacker"&gt;slacker&lt;/a&gt; has been pushed to clojars. Let's go through the changes in this version.&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;Clojure 1.3 compatible&lt;/h3&gt;
Slacker finally landed on clojure 1.3. It takes advantages of performance in 1.3 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;github&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;slacker
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;A new release 0.8.0 of &lt;a href="https://github.com/sunng87/slacker"&gt;slacker&lt;/a&gt; has been pushed to clojars. Let's go through the changes in this version.&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;Clojure 1.3 compatible&lt;/h3&gt;
Slacker finally landed on clojure 1.3. It takes advantages of performance in 1.3. Also, you can use 1.3 API in slacker. For example, a timeout argument is supported in deref, which is useful when dealing with promise returned by slacker's asynchronous call.&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;Performance Boost&lt;/h3&gt;&lt;/p&gt;

&lt;p&gt;The performance enhancement is on the highest priority in this release.  I have migrated the NIO infrastructure to a new library called &lt;a href="https://github.com/sunng87/link"&gt;link&lt;/a&gt;. Now slacker 0.8.0 is at least &lt;strong&gt;8x&lt;/strong&gt; faster than previous release. There is significant improvement both on per-request latency and overall throughput. And the server thread model is optimized for data-intensive tasks. Heavy IO tasks in hosted functions won't block the whole server. &lt;/p&gt;

&lt;p&gt;&lt;h3&gt;slacker as a ring app&lt;/h3&gt;&lt;/p&gt;

&lt;p&gt;Instead of running default transportation, slacker now can be configured as a ring app and deployed on any ring adapter. &lt;/p&gt;

&lt;p&gt;[cc lang="clojure"]&lt;br /&gt;
(use 'slacker.server)&lt;br /&gt;
(use 'ring.adapter.jetty)&lt;/p&gt;

&lt;p&gt;(run-jetty (slacker-ring-app (the-ns 'slacker.example.api)) {:port 8080})&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;This will expose the name space &lt;em&gt;slacker.example.api&lt;/em&gt; with HTTP. Functions could be called with following URL pattern:&lt;/p&gt;

&lt;p&gt;http://localhost:8080/&amp;lt;namespace&amp;gt;/&amp;lt;function&amp;gt;.&amp;lt;content-type&amp;gt;&lt;/p&gt;

&lt;p&gt;For instance: http://localhost:8080/slacker.example.api/timestamp.json&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;defn-remote&lt;/h3&gt;&lt;/p&gt;

&lt;p&gt;There is a minor update for the defn-remote macro. &lt;/p&gt;

&lt;p&gt;In 0.7.0, you have to specify remote namespace with an option:&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(defn-remote sc timestamp :remote-ns "slacker.example.api")&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;In 0.8.0, it's more convenience: &lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(defn-remote sc slacker.example.api/timestamp)&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;To keep the core library compact, in 0.8.0, the cluster support has been moved to a standalone project &lt;a href="https://github.com/sunng87/slacker-cluster"&gt;slacker-cluster&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;All above summarized my recent work in the slacker project. If you have any question with this library, feel free to drop me an email sunng@about.me .
&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Visualizing OpenStreetMap Nanjing Contribution</title><link href="https://sunng.info/blog/visualizing-openstreetmap-nanjing-contribution.html" rel="alternate"></link><published>2012-04-20T00:00:00+08:00</published><updated>2012-04-20T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2012-04-20:/blog/visualizing-openstreetmap-nanjing-contribution.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;Nanjing&lt;/li&gt;
&lt;li&gt;OpenStreetMap&lt;/li&gt;
&lt;li&gt;visualization
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;早上在prismatic上看到mapbox的一篇&lt;a href="http://mapbox.com/blog/how-to-map-contributions-openstreetmap/" target="_blank"&gt;博客&lt;/a&gt;，介绍通过TileMill可视化OSM的贡献者，非常酷。于是我在南京的地图上也做了一个这样的可视化。&lt;/p&gt;

&lt;p&gt;&lt;img src="http://i.imgur.com/U2yXK.png" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;一个详细的 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;Nanjing&lt;/li&gt;
&lt;li&gt;OpenStreetMap&lt;/li&gt;
&lt;li&gt;visualization
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;早上在prismatic上看到mapbox的一篇&lt;a href="http://mapbox.com/blog/how-to-map-contributions-openstreetmap/" target="_blank"&gt;博客&lt;/a&gt;，介绍通过TileMill可视化OSM的贡献者，非常酷。于是我在南京的地图上也做了一个这样的可视化。&lt;/p&gt;

&lt;p&gt;&lt;img src="http://i.imgur.com/U2yXK.png" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;一个详细的大图在&lt;a href="http://i.imgur.com/YnULm.png" target="_blank"&gt;这里&lt;/a&gt;。虽然只做了南京的五个主要贡献者，基本上涵盖了大部分数据。&lt;/p&gt;

&lt;p&gt;图例就不专门输出了&lt;br /&gt;
[user = 'Sunng'] { marker-fill: @magenta;} &lt;br /&gt;
[user = 'fuwuyuan'] { marker-fill: @blue;}&lt;br /&gt;
[user = 'sinopitt'] {marker-fill: @yellow;}&lt;br /&gt;
[user = 'larryy'] {marker-fill: @green;}&lt;br /&gt;
[user = 'zhengz'] {marker-fill: @red;}&lt;/p&gt;

&lt;p&gt;MapBox家的东西真的非常酷，这家的技术以nodejs为主，围绕osm开发了不少产品。最近比较大的新闻，比如4sq转到osm上，其实就是转到这家的osm服务上。有兴趣你可以关注一下！
&lt;img src="http://i.imgur.com/Hum2r.png" alt="" /&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>多线程服务器</title><link href="https://sunng.info/blog/duo-xian-cheng-fu-wu-qi.html" rel="alternate"></link><published>2012-04-12T00:00:00+08:00</published><updated>2012-04-12T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2012-04-12:/blog/duo-xian-cheng-fu-wu-qi.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;NIO&lt;/li&gt;
&lt;li&gt;programming
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;写了挺长时间网络程序了，有些事到最近才弄明白，记录一下。&lt;/p&gt;

&lt;p&gt;写一个高性能的服务器，传统的BIO方式基本上已经被淘汰了，很难 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;NIO&lt;/li&gt;
&lt;li&gt;programming
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;写了挺长时间网络程序了，有些事到最近才弄明白，记录一下。&lt;/p&gt;

&lt;p&gt;写一个高性能的服务器，传统的BIO方式基本上已经被淘汰了，很难获得理想的性能。所以现在都是以事件驱动的方式来写，在Linux上用epoll,Java平台上就是NIO。再向上，有一些包装的库，比如twisted比如libevent比如Java上的Netty。&lt;/p&gt;

&lt;p&gt;Netty的server需要至少两组线程，BossPool和WorkerPool,。前者负责accept，后者负责r/w。通常的例子里，这就是仅有的两组线程。用户在框架之上的业务逻辑，写在handler里，以单线程的方式运行在worker线程上。&lt;/p&gt;

&lt;p&gt;这样的方式在很多例子里很普遍。但是如果handler里的业务逻辑比较复杂，尤其是IO的等待过长(比如查询数据库)，就会由于在handler上阻塞的时间过长，导致服务器读写的效率下降。所以通常是不能在这样环境下的handler里写IO阻塞的代码。&lt;/p&gt;

&lt;p&gt;解决这个问题，一种方式是多线程，一种是全异步化。后者最典型的例子就是nodejs，坦白说编程模型非常复杂，对开发者要求较高。一种相对简单的办法，就是以多线程的方式，增加CPU的利用效率，来平衡IO阻塞带来的影响。IO等待时间的比重越大，线程数就可以陪得越高。牺牲一些sy的CPU时间，换取更高的利用率。&lt;/p&gt;

&lt;p&gt;但是随之而来了另一个问题，因为在handler中使用了多线程的模型。对顺序收到的包，交由不同的线程并行处理，就没有办法保证返回时的顺序。客户端就无法了解刚刚返回的包是对应哪个请求的。&lt;/p&gt;

&lt;p&gt;这个情况也有两种办法处理。其一是在线程管理上做文章，采用一种折中的办法。对于每一个客户端连接来说，仍然是占用同一个线程来处理。这样首先任务不会占用worker线程，其次在整体上仍然提高了CPU的利用率。但是这样做的缺点是，在单一客户端看来，任务仍然是串行执行: 三个需要耗时500ms的请求同时顺序发出，第三个至少要在1500ms之后才能收到响应，客户端的latency比较高。&lt;/p&gt;

&lt;p&gt;另一种，就是在应用协议层面做一个冗余字段，通常叫做serial id或者transaction id。客户端为每一个请求生成一个这样的id，并存储这个id对应的回调。服务器端不需要对包的顺序作任何关注，只需要把这个id原封不动地拷贝回去即可。这样，服务器就可以自由调度线程来处理请求。以上面的例子，在不繁忙的情况下，三个响应在500ms左右就都可以到达了。&lt;/p&gt;

&lt;p&gt;这种方式对应用协议有特殊的要求，但是比较常见的协议都预留了这个字段，Diameter协议甚至留了两个这样的字段来便于代理的实现。&lt;/p&gt;

&lt;p&gt;slacker 0.7.x基于aleph，由于aleph / lamina无法侵入协议层面，所以采用的都是顺序的客户端和服务器。这种方式编程非常简洁，协议设计简单，实现起来很快。但是作为RPC框架，一旦客户的函数阻塞较长，就会影响整体性能。0.8.0开始，通过新的协议和link库支持，slacker采用了transaction id的方式，服务器端默认使用并行处理，客户端不再依赖顺序指定响应和回调。尽管在一些CPU为局限的测试里性能有少许下降(与单线程相比，增加了调度的成本)，但是针对实际应用里IO等待较多的情况，新版本应该会表现出更好的综合性能。&lt;/p&gt;

&lt;p&gt;以上这些，就是最近的心得，希望能解释清楚事件驱动服务器里的这些事情。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Slacker performance enhanced</title><link href="https://sunng.info/blog/slacker-performance-enhanced.html" rel="alternate"></link><published>2012-04-02T00:00:00+08:00</published><updated>2012-04-02T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2012-04-02:/blog/slacker-performance-enhanced.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;github&lt;/li&gt;
&lt;li&gt;netty&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;slacker
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;In the slacker framework, performance issue becomes more and more critical as the basic features are almost completed. As mentioned in cnclojure meetup, I will focus on the performance enhancement in next release. &lt;/p&gt;

&lt;p&gt;Now I have worked out …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;github&lt;/li&gt;
&lt;li&gt;netty&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;slacker
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;In the slacker framework, performance issue becomes more and more critical as the basic features are almost completed. As mentioned in cnclojure meetup, I will focus on the performance enhancement in next release. &lt;/p&gt;

&lt;p&gt;Now I have worked out a testable version. The new slacker core has been moved to a new NIO library, &lt;a href="https://github.com/sunng87/link" target="_blank"&gt;link&lt;/a&gt;. Compared with aleph, link is a thin wrapper of Netty. It takes some nice features from aleph (gloss codec dsl, elegant API), and drops the heavy abstraction, lamina. The new slacker client runs on a real nonblocking connection. Connection pooling is no longer needed.&lt;/p&gt;

&lt;p&gt;I have some performance benchmark to visualize the improvement. The test was made on my laptop (Intel(R) Core(TM)2 Duo CPU     T5870  @ 2.00GHz). It ran 400,000 calls with 40 threads on a local slacker server.&lt;/p&gt;

&lt;p&gt;slacker 0.7.0 (clojure 1.2, aleph 0.2.0): &lt;strong&gt;614005.059259msecs&lt;/strong&gt;
slacker 0.7.1-SNAPSHOT (clojure 1.3, aleph 0.2.1-beta2): &lt;strong&gt;409110.909142msecs&lt;/strong&gt;
slacker 0.8.0-SNAPSHOT (clojure 1.3, link 0.2.0-SNAPSHOT): &lt;strong&gt;42468.401522msecs&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="http://i.imgur.com/gMtdo.jpg" alt="tps chart" /&gt;&lt;/p&gt;

&lt;p&gt;Check out the new slacker on the &lt;a href="https://github.com/sunng87/slacker/tree/0.8-dev" target="_blank"&gt;0.8-dev&lt;/a&gt; branch. &lt;/p&gt;

&lt;p&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>为WebWorker设置正确的路径</title><link href="https://sunng.info/blog/wei-webworkershe-zhi-zheng-que-de-lu-jing.html" rel="alternate"></link><published>2012-03-22T00:00:00+08:00</published><updated>2012-03-22T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2012-03-22:/blog/wei-webworkershe-zhi-zheng-que-de-lu-jing.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;heatcanvas&lt;/li&gt;
&lt;li&gt;javascript
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;WebWorker的路径通常是写在代码源文件中，而且这个路径并非其相对父js文件的相对路径，而似乎是相对页面的路径。所以指定一个正确的可随处部署的路 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;heatcanvas&lt;/li&gt;
&lt;li&gt;javascript
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;WebWorker的路径通常是写在代码源文件中，而且这个路径并非其相对父js文件的相对路径，而似乎是相对页面的路径。所以指定一个正确的可随处部署的路径变得有些麻烦。昨天有人给HeatCanvas提了这个问题我才想到上网搜索了一下，有一个还算挺不错的办法。&lt;/p&gt;

&lt;p&gt;写一个getPath函数，从document里找到父js的路径，拼到Worker的名字上。对heatcanvas.js这个文件来说就是：&lt;/p&gt;

&lt;p&gt;[cc lang="javascript"]&lt;br /&gt;
HeatCanvas.getPath = function() {&lt;br /&gt;
    var scriptTags = document.getElementsByTagName("script");&lt;br /&gt;
    for (var i=0; i&lt;scriptTags.length; i++) {&lt;br /&gt;
        var src = scriptTags[i].src;&lt;br /&gt;
        var pos = src.indexOf("heatcanvas.js");&lt;br /&gt;
        if (pos &gt; 0) {&lt;br /&gt;
            return src.substring(0, pos);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return "";&lt;br /&gt;
};&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;因此现在HeatCanvas已经解决了这个路径问题，现在这个库应该更好用了。当然如果你改了我的文件名我就无话可说了。
&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>My favorite feature in leiningen 2</title><link href="https://sunng.info/blog/my-favorite-feature-in-leiningen-2.html" rel="alternate"></link><published>2012-03-21T00:00:00+08:00</published><updated>2012-03-21T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2012-03-21:/blog/my-favorite-feature-in-leiningen-2.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;leiningen&lt;/li&gt;
&lt;li&gt;maven
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Recently, the leiningen team has released a preview version for leiningen 2. It brings &lt;a href="https://github.com/technomancy/leiningen/blob/master/NEWS.md" target="_blank"&gt;new features&lt;/a&gt; to the clojure build tool. But my favorite one is not listed in any document. So I would like to share with you here …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;leiningen&lt;/li&gt;
&lt;li&gt;maven
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Recently, the leiningen team has released a preview version for leiningen 2. It brings &lt;a href="https://github.com/technomancy/leiningen/blob/master/NEWS.md" target="_blank"&gt;new features&lt;/a&gt; to the clojure build tool. But my favorite one is not listed in any document. So I would like to share with you here.&lt;/p&gt;

&lt;p&gt;As you know, leiningen is a project oriented tool. It manages dependencies for a particular project. In Java world, Maven and Gradle are also working in this manner. &lt;/p&gt;

&lt;p&gt;In contrast, there are tools which manages dependencies in a system scope. For instance, pip(python), npm(nodejs) and gems(ruby). One advantage of these tools is easy for evaluating a library. Concretely, when you want to test pyclj, just run "pip install pyclj" to install it. Then open a REPL and type "import pyclj". That's pretty easy.&lt;/p&gt;

&lt;p&gt;But in clojure/leiningen, to take a tutorial of "core.logic", we have following steps: 
&lt;ol&gt;
&lt;li&gt;Find a right directory and type "lein new logic-abc" to create a project. &lt;/li&gt;
&lt;li&gt;cd into it, edit project.clj, add core.logic as a dependency&lt;/li&gt;
&lt;li&gt;Run `lein deps`&lt;/li&gt;
&lt;li&gt;Start a REPL and follow the tutorial&lt;/li&gt;
&lt;/ol&gt;&lt;/p&gt;

&lt;p&gt;So I guest you must have a lot of empty projects created for such purpose. &lt;/p&gt;

&lt;p&gt;Now we could say goodbye to this situation. Leiningen 2 has move its dependency management core to a new library, called &lt;a href="https://github.com/cemerick/pomegranate" target="_blank"&gt;pomegranate&lt;/a&gt;. Pomegranate wraps aether, which is a maven library created by sonatype. With pomegranate, we can add a maven artifact from repository to REPL classpath. Still on the core.logic example, it becomes much easier:&lt;/p&gt;

&lt;p&gt;[cc lang="clojure"]&lt;br /&gt;
$ lein2 repl&lt;br /&gt;
Welcome to REPL-y!&lt;br /&gt;
Clojure 1.3.0&lt;br /&gt;
    Exit: Control+D or (exit) or (quit)&lt;br /&gt;
Commands: (help)&lt;br /&gt;
    Docs: (doc function-name-here)&lt;br /&gt;
          (find-doc "part-of-name-here")&lt;br /&gt;
  Source: (source function-name-here)&lt;br /&gt;
          (sourcery function-name-here)&lt;br /&gt;
 Javadoc: (javadoc java-object-or-class-here)&lt;br /&gt;
Examples from clojuredocs.org:&lt;br /&gt;
          (clojuredocs name-here)&lt;br /&gt;
          (clojuredocs "ns-here" "name-here")&lt;br /&gt;
nil&lt;br /&gt;
user=&gt; (use '[cemerick.pomegranate :only (add-dependencies)])&lt;br /&gt;
nil&lt;br /&gt;
user=&gt; (add-dependencies :coordinates '[[org.clojure/core.logic "0.6.8"]])&lt;br /&gt;
{[org.clojure/clojure "1.3.0"] nil, [org.clojure/core.logic "0.6.8"] #{[org.clojure/clojure "1.3.0"]}}&lt;br /&gt;
user=&gt; (use  '[clojure.core.logic])&lt;br /&gt;
nilWARNING: == already refers to: #'clojure.core/== in namespace: user, being replaced by: #'clojure.core.logic/==&lt;br /&gt;
user=&gt; (run* [q] (== q 1))&lt;br /&gt;
(1)&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;Now core.logic is right on you classpath and you are ready to use any functions under the namespace. There's no need to create project, no need to care about where the jars stored. Just start a REPL at anywhere you want. When you finished, send EOF to the REPL. Nothing to clean up.&lt;/p&gt;

&lt;p&gt;My jython dependency manager &lt;a href="https://github.com/sunng87/jip" target="_blank"&gt;jip&lt;/a&gt; has &lt;a href="http://sunng87.github.com/jip/#section-6" target="_blank"&gt;similar feature&lt;/a&gt; as I described above. It does great help to me. So I have been waiting for this feature in leiningen for a long time. Thanks to leiningen guys, it finally comes.&lt;/p&gt;

&lt;p&gt;Edit 20120323 21:39&lt;/p&gt;

&lt;p&gt;If you want to load libraries from clojars, you should explicitly add clojars in add-dependencies:&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(add-dependencies :coordinates '[[incanter "1.2.3"]]&lt;br /&gt;
                  :repositories (merge cemerick.pomegranate.aether/maven-central&lt;br /&gt;
                                       {"clojars" "http://clojars.org/repo"}))&lt;br /&gt;
[/cc]&lt;br /&gt;
(The example is copied from Pomegranate document.)&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>刷HTC EVO 3D GSM</title><link href="https://sunng.info/blog/shua-htc-evo-3d-gsm.html" rel="alternate"></link><published>2012-03-19T00:00:00+08:00</published><updated>2012-03-19T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2012-03-19:/blog/shua-htc-evo-3d-gsm.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;android
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;去年买的水货Desire Z在服役了刚刚一年之后就坏了，看来肯定是上了奸商的当。鉴于最近的新手机也没有什么像当时Desire Z那样一见倾心的，这次保守起 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;android
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;去年买的水货Desire Z在服役了刚刚一年之后就坏了，看来肯定是上了奸商的当。鉴于最近的新手机也没有什么像当时Desire Z那样一见倾心的，这次保守起见买了个行货EVO 3D。买行货的问题不仅是贵（贵很多），而且默认的ROM实在是没法用。几大国产流氓软件堂而皇之地强制安装后台运行，系统连google账户，官方market都没有。&lt;/p&gt;

&lt;p&gt;那么只能刷一下了。所有的步骤开始之前都是解锁，按照官方的解锁方式(http://www.htcdev.com)基本上没有什么难度。在我的archlinux上，不需要安装HTC Sync（也没得装），只需要从aur安装android-sdk和android-sdk-platform-tools就有adb和fastboot在PATH里。唯一值得一提的是，在我的系统上fastboot oem get_identifier_token需要sudo，否则会一直wait device。除了这个小插曲以外，按照官方的步骤就可以解锁HBOOT。&lt;/p&gt;

&lt;p&gt;接下来就可以刷recovery了，HTC EVO 3D GSM版的codename叫做shootru，比较可靠的一个版本是4.0.1.4-shooteru，可以在网上搜索 cwm-4.0.1.4-shooteru.img 这个文件，比较好找。继续通过 sudo fastboot flash recovery cwm-4.0.1.4-shooteru.img把recovery刷进手机。&lt;/p&gt;

&lt;p&gt;接下来就是ROM的选择了。最好的选择是cyanogenmod，EVO 3D分为GSM和CDMA版，cmod 7只支持CDMA版，似乎还没有稳定的正式版。对GSM似乎有正在开发7.2，不过按照他们的说法，自从4.0发布之后，所有人的注意力都转移到基于4.0的cmod9上，所以7.2这个版本希望也比较渺茫。9.0已经有开发版本，但是都有一些还未解决的严重bug。说了这么多就是说我暂时放弃cmod了。&lt;/p&gt;

&lt;p&gt;除了cmod，EVO 3D GSM上一个比较被认可的ROM叫做&lt;a href="http://leedroid.com/evo-3d/?c=roms"&gt;LeeDrOiD&lt;/a&gt;.从网站上下载5.3.0的发布版，拷贝到sd卡上，通过recovery就可以安装。安装之前先要清除旧的数据。剩下这一步也没有什么悬念。但是安装之后5.3.0上，WIFI无法启动。必须继续更新kernel，从&lt;a href="http://leedroid.com/evo-3d/?c=kernels"&gt;网站上下&lt;/a&gt;载。安装kernel的方法和之前不太一样，不能通过recovery安装。需要用一个叫做FlashImageGUI的工具，可以在网上直接搜索这个名字找到下载。剩下就很简单了。&lt;/p&gt;

&lt;p&gt;折腾这么一圈，手机基本上能用了。不过我还是非常期待早日能用上cmod 9.
&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Kliment Voroshilov tank 2</title><link href="https://sunng.info/blog/kliment-voroshilov-tank-2.html" rel="alternate"></link><published>2012-03-11T00:00:00+08:00</published><updated>2012-03-11T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2012-03-11:/blog/kliment-voroshilov-tank-2.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;D60&lt;/li&gt;
&lt;li&gt;tank
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;业余时间，我除了写括号以外，也会做点模型，不过是入门水平。用淘宝店家的话说，你随便涂涂就行了 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;D60&lt;/li&gt;
&lt;li&gt;tank
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;业余时间，我除了写括号以外，也会做点模型，不过是入门水平。用淘宝店家的话说，你随便涂涂就行了。这个KV-2坦克是去年秋天买的，后来周末学车加上天冷坐不住就搁置下来。现在稍微暖和一点，就赶紧把开工。&lt;/p&gt;

&lt;p&gt;KV-2坦克，是二战时期苏联的重型坦克。最早参加了苏芬战争，后来投入到早期的苏德战争。但是绝大多数被德军在巴巴罗萨行动中摧毁、俘虏。被俘虏的KV-2坦克被刷上德军的标志也为德军服役，这在二战早期的东线战场倒是很常见。我的这个KV-2附带的水贴纸也分两个版本，分别是苏军和德军的涂装。&lt;/p&gt;

&lt;p&gt;花了一天时间素组。
&lt;a href="http://www.flickr.com/photos/40741608@N08/6825567316/" title="DSC_0016 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm8.staticflickr.com/7043/6825567316_29e7e13830.jpg" width="500" height="335" alt="DSC_0016" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6825568984/" title="DSC_0013 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm8.staticflickr.com/7041/6825568984_8e897f9513.jpg" width="335" height="500" alt="DSC_0013" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6971689967/" title="DSC_0023 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm8.staticflickr.com/7180/6971689967_171ab05c49.jpg" width="335" height="500" alt="DSC_0023" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6825572612/" title="DSC_0019 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm8.staticflickr.com/7176/6825572612_a4bdb17248.jpg" width="500" height="335" alt="DSC_0019" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6971692767/" title="DSC_0029 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm8.staticflickr.com/7182/6971692767_c84b5aba61.jpg" width="500" height="335" alt="DSC_0029" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;今天又花了一上午时间上漆。第一次上漆，也没看什么教程，就是这个结果了。&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6971694113/" title="DSC_0003 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm8.staticflickr.com/7057/6971694113_73c6939dc8.jpg" width="335" height="500" alt="DSC_0003" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6825576398/" title="DSC_0009 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm8.staticflickr.com/7059/6825576398_e57bf106a2.jpg" width="500" height="335" alt="DSC_0009" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6971697399/" title="DSC_0015 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm8.staticflickr.com/7045/6971697399_fd01ca6c69.jpg" width="335" height="500" alt="DSC_0015" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;下一个目标就是T34。&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>What's new in slacker 0.7.0 ?</title><link href="https://sunng.info/blog/whats-new-in-slacker-070.html" rel="alternate"></link><published>2012-03-09T00:00:00+08:00</published><updated>2012-03-09T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2012-03-09:/blog/whats-new-in-slacker-070.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;slacker
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;I just released [slacker "0.7.0"] to clojars. This is the first release after my presentation on the Clojure China Meetup. &lt;a href="https://github.com/lbt05" target="_blank"&gt;lbt05&lt;/a&gt; contributed an ACL module to slacker, which is the most significant feature in this release. &lt;/p&gt;

&lt;p&gt;The ACL module provides …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;slacker
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;I just released [slacker "0.7.0"] to clojars. This is the first release after my presentation on the Clojure China Meetup. &lt;a href="https://github.com/lbt05" target="_blank"&gt;lbt05&lt;/a&gt; contributed an ACL module to slacker, which is the most significant feature in this release. &lt;/p&gt;

&lt;p&gt;The ACL module provides a simple DSL to define access rules.&lt;/p&gt;

&lt;p&gt;[cc lang="clojure"]&lt;br /&gt;
(use 'slacker.acl)&lt;br /&gt;
(use 'slacker.server)&lt;/p&gt;

&lt;p&gt;(defrules myrule&lt;br /&gt;
  (allow ["10.60.15.*"]))&lt;/p&gt;

&lt;p&gt;(start-slacker-server ...&lt;br /&gt;
                      :acl myrule)&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;"myrule" defines a limited access control list. Only clients from IP segment 10.60.15.* could access the slacker service.&lt;/p&gt;

&lt;p&gt;And there are also minor enhancements in this release:&lt;/p&gt;

&lt;p&gt;&lt;ul&gt;
&lt;li&gt;Content compression, new content type :deflate-carb :deflate-json and :deflate-clj&lt;/li&gt;
&lt;li&gt;In debug mode, server side stacktraces are printed on client&lt;/li&gt;
&lt;li&gt;Zookeeper node path refined&lt;/li&gt;
&lt;li&gt;New options in use-remote, :only and :exclude&lt;/li&gt;
&lt;li&gt;Cheshire used as json library&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;slacker 0.7.0 will be the last version on clojure 1.2 . As aleph 0.2.1 is coming near, we will migrate to clojure 1.3 as soon as possible. If you like to taste slacker on your 1.3 application now, there is a 0.7.1-SNAPSHOT available.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Slacker slides on cnclojure meetup</title><link href="https://sunng.info/blog/slacker-slides-on-cnclojure-meetup.html" rel="alternate"></link><published>2012-03-06T00:00:00+08:00</published><updated>2012-03-06T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2012-03-06:/blog/slacker-slides-on-cnclojure-meetup.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;slacker
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;从北京回来两天了，稍微有点累，还没来得及总结一下，先把slides上传分享一下： &lt;a href="http://www.box.com/s/k0alcj1p115jq40bdkik" target="_blank"&gt;http://www.box.com/s/k0alcj1p115jq40bdkik&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;解压之后 lein deps &amp;&amp; lein run 一下即 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;slacker
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;从北京回来两天了，稍微有点累，还没来得及总结一下，先把slides上传分享一下： &lt;a href="http://www.box.com/s/k0alcj1p115jq40bdkik" target="_blank"&gt;http://www.box.com/s/k0alcj1p115jq40bdkik&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;解压之后 lein deps &amp;&amp; lein run 一下即可。&lt;/p&gt;

&lt;p&gt;这是一个借助impress.js制作的幻灯片。之所以让它跑在webbit里，是因为我简单hack了impress.js让你可以通过websocket远程控制幻灯片播放。这样，就可以通过手机上的firefox浏览器（目前android上仅有firefox支持websocket）控制播放，把手机变成一款遥控器。&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>'dosync+: dosync with event listener'</title><link href="https://sunng.info/blog/dosync-dosync-with-event-listener.html" rel="alternate"></link><published>2012-02-24T00:00:00+08:00</published><updated>2012-02-24T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2012-02-24:/blog/dosync-dosync-with-event-listener.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;clojure
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Discussed in clojure-cn mailing list, we come up with an extensible dosync block with event listener: on-start, on-retry and on-committed.&lt;/p&gt;

&lt;p&gt;&lt;script src="https://gist.github.com/1898383.js?file=dosync-plus.clj"&gt;&lt;/script&gt;&lt;/p&gt;

&lt;p&gt;With these extension points, you can bind STM monitor to a transaction. And I believe there will more advanced use cases on …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;clojure
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Discussed in clojure-cn mailing list, we come up with an extensible dosync block with event listener: on-start, on-retry and on-committed.&lt;/p&gt;

&lt;p&gt;&lt;script src="https://gist.github.com/1898383.js?file=dosync-plus.clj"&gt;&lt;/script&gt;&lt;/p&gt;

&lt;p&gt;With these extension points, you can bind STM monitor to a transaction. And I believe there will more advanced use cases on this.&lt;/p&gt;

&lt;p&gt;&lt;script src="https://gist.github.com/1898383.js?file=main.clj"&gt;&lt;/script&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>燕子矶</title><link href="https://sunng.info/blog/yan-zi-ji.html" rel="alternate"></link><published>2012-02-18T00:00:00+08:00</published><updated>2012-02-18T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2012-02-18:/blog/yan-zi-ji.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;D60&lt;/li&gt;
&lt;li&gt;Nanjing&lt;/li&gt;
&lt;li&gt;photo
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;作为几乎是很多人脑海里南京最北端的地方，燕子矶平时算是人迹罕至。我上一次去这个公园是 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;D60&lt;/li&gt;
&lt;li&gt;Nanjing&lt;/li&gt;
&lt;li&gt;photo
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;作为几乎是很多人脑海里南京最北端的地方，燕子矶平时算是人迹罕至。我上一次去这个公园是99年小学毕业的夏天，一晃都已经13年（13年！）过去了。&lt;/p&gt;

&lt;p&gt;当年通往燕子矶的小街已经被新修的永济大道取代，不过当时的和平街、临江街依然存在。燕子矶公园的门原本开在临江街上，现在这个门已经几乎倒掉，公园在小院的另一侧开了一个面向永济大道的新门。以前这门口是各式拖挂卡车来来往往，环境整治以后现在到公园门口这一段没有卡车，所有的卡车从前面一条小街“新燕街”开到江边的码头。所以万变不离其宗，江边的主题依然是各种大型运输工具。&lt;/p&gt;

&lt;p&gt;[mapsmarker marker="3"]&lt;/p&gt;

&lt;p&gt;刚到公园门口，就听见有人感叹自己也十年没来这个公园。看来这个公园已经小到、偏到不隔个十多年都想不起他来。然而可贵就可贵在，这么长时间小公园几乎就没有变化。规模小，又有一点特殊的历史背景，再加上有政府的补贴，也不必因为自负盈亏去折腾去影响气氛。说难也难，说简单也简单，就这么无欲无求地保存下来。&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6895709289/" title="DSC_0004 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm8.staticflickr.com/7186/6895709289_7017db7469.jpg" width="500" height="335" alt="DSC_0004" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6895702169/" title="DSC_0010 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm8.staticflickr.com/7198/6895702169_f23616b13e.jpg" width="335" height="500" alt="DSC_0010" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6895706809/" title="DSC_0013 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm8.staticflickr.com/7194/6895706809_bb2a000726.jpg" width="335" height="500" alt="DSC_0013" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6895704293/" title="DSC_0041 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm8.staticflickr.com/7052/6895704293_0aae20c9a5.jpg" width="335" height="500" alt="DSC_0041" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6895713699/" title="DSC_0037 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm8.staticflickr.com/7184/6895713699_33a202485e.jpg" width="335" height="500" alt="DSC_0037" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6895717673/" title="DSC_0028 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm8.staticflickr.com/7187/6895717673_35ff2ea296.jpg" width="500" height="332" alt="DSC_0028" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6895710599/" title="DSC_0020 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm8.staticflickr.com/7052/6895710599_32bf33d76f.jpg" width="309" height="500" alt="DSC_0020" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6895715441/" title="DSC_0044 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm8.staticflickr.com/7049/6895715441_68bedb5c77.jpg" width="500" height="335" alt="DSC_0044" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Python's Valentines Day Gift to Clojure</title><link href="https://sunng.info/blog/pythons-valentines-day-gift-to-clojure.html" rel="alternate"></link><published>2012-02-14T00:00:00+08:00</published><updated>2012-02-14T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2012-02-14:/blog/pythons-valentines-day-gift-to-clojure.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Inspired by meh's &lt;a href="https://github.com/meh/ruby-clj" target="_blank"&gt;Ruby-Clj&lt;/a&gt; module, I created the python equivalent "pyclj" last weekend. Pyclj is a clojure literal reader/writer for python. It enables data exchange between python and clojure, in a clojure-native way. It's Valentines Day today, I'd like to …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Inspired by meh's &lt;a href="https://github.com/meh/ruby-clj" target="_blank"&gt;Ruby-Clj&lt;/a&gt; module, I created the python equivalent "pyclj" last weekend. Pyclj is a clojure literal reader/writer for python. It enables data exchange between python and clojure, in a clojure-native way. It's Valentines Day today, I'd like to release it as the gift of python to clojure :)&lt;/p&gt;

&lt;p&gt;The API is very simple. It's all like python's data modules (json, pickle)&lt;/p&gt;

&lt;p&gt;[cc lang="python"]&lt;br /&gt;
import clj&lt;/p&gt;

&lt;p&gt;clj.loads("[1 2 3]")&lt;br /&gt;
clj.dumps({"a":1, "b":2})&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;Clojure types are mapping to python data structures :&lt;/p&gt;

&lt;p&gt;&lt;table border="1"&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;th&gt;Clojure&lt;/th&gt;
&lt;th&gt;Python&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;list&lt;/td&gt;
&lt;td&gt;list&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;vector&lt;/td&gt;
&lt;td&gt;list&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;set&lt;/td&gt;
&lt;td&gt;set&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;map&lt;/td&gt;
&lt;td&gt;dict&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;nil&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;float&lt;/td&gt;
&lt;td&gt;float&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;boolean&lt;/td&gt;
&lt;td&gt;boolean&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;char&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;keyword&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/p&gt;

&lt;p&gt;But how we win clojure's heart from ruby? &lt;/p&gt;

&lt;p&gt;We are faster.&lt;/p&gt;

&lt;p&gt;Considering clojure literal below:&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
[1 2 3 true false nil {:a 21.3 :b 43.2} "Hello"]&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;Comparing ruby-clj(0.0.4.5, ruby 1.9.3p0) and pyclj(0.1.3 python 2.7.2):&lt;br /&gt;
[cc lang="ruby"]&lt;br /&gt;
require 'clj'&lt;/p&gt;

&lt;p&gt;s = "[1 2 3 true false nil {:a 21.3 :b 43.2} \"Hello\"]"&lt;/p&gt;

&lt;p&gt;t1 = Time.now()&lt;br /&gt;
for i in 0...10000&lt;br /&gt;
  Clojure.parse(s)&lt;br /&gt;
end&lt;br /&gt;
puts Time.now()-t1&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;[cc lang="python"]&lt;br /&gt;
import clj&lt;br /&gt;
import time&lt;/p&gt;

&lt;p&gt;s = "[1 2 3 true false nil {:a 21.3 :b 43.2} \"Hello\"]"&lt;/p&gt;

&lt;p&gt;t1 = time.time()&lt;br /&gt;
for i in range(10000):&lt;br /&gt;
  clj.loads(s)&lt;br /&gt;
print time.time()-t1&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;The result:&lt;br /&gt;
ruby: &lt;del datetime="2012-02-16T05:30:18+00:00"&gt;&lt;strong&gt;13.451157809&lt;/strong&gt;&lt;/del&gt;
python: &lt;del datetime="2012-02-16T05:30:18+00:00"&gt;&lt;strong&gt;0.712423086166&lt;/strong&gt;&lt;/del&gt;
Edit 20120216 13:30&lt;br /&gt;
ruby-clj 0.0.5.3 has resolved the performance issue :) &lt;br /&gt;
The new result ruby-clj/0.0.5.4 Vs pyclj0.1.4 (on my laptop):&lt;br /&gt;
ruby-clj: 2.044872364&lt;br /&gt;
pyclj: 1.19659209251&lt;/p&gt;

&lt;p&gt;The project is hosted on &lt;a href="https://github.com/sunng87/pyclj" target="_blank"&gt;github&lt;/a&gt;. Feel free to join the development and enhance it.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>HeatCanvas performance enhanced</title><link href="https://sunng.info/blog/heatcanvas-performance-enhanced.html" rel="alternate"></link><published>2012-02-11T00:00:00+08:00</published><updated>2012-02-11T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2012-02-11:/blog/heatcanvas-performance-enhanced.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;heatcanvas&lt;/li&gt;
&lt;li&gt;html5&lt;/li&gt;
&lt;li&gt;javascript&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;img src="http://i.imgur.com/6vkpo.png" alt="heatcanvas" /&gt;&lt;/p&gt;

&lt;p&gt;时隔半年日日沉浸在clojure世界里的时候，多亏了github上&lt;a href="https://github.com/dazuma"&gt;Daniel Azuma&lt;/a&gt;的提示，现在HeatCanvas通过Image Data数组来绘制图像。过去由于不太熟悉Canvas API，我用的是fillRect来填充1像素大小的区域，模 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;heatcanvas&lt;/li&gt;
&lt;li&gt;html5&lt;/li&gt;
&lt;li&gt;javascript&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;img src="http://i.imgur.com/6vkpo.png" alt="heatcanvas" /&gt;&lt;/p&gt;

&lt;p&gt;时隔半年日日沉浸在clojure世界里的时候，多亏了github上&lt;a href="https://github.com/dazuma"&gt;Daniel Azuma&lt;/a&gt;的提示，现在HeatCanvas通过Image Data数组来绘制图像。过去由于不太熟悉Canvas API，我用的是fillRect来填充1像素大小的区域，模拟像素的渲染。但是这种方式导致浏览器渲染的效率非常低。&lt;/p&gt;

&lt;p&gt;ImageDataArray允许用户开辟一个固定大小的buffer，并设置每一像素的像素值，然后一次性地渲染到canvas上。详情可以参考这里：&lt;a href="https://developer.mozilla.org/En/HTML/Canvas/Pixel_manipulation_with_canvas" target="_blank"&gt;Pixel manipulation with canvas&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;这次性能的提升基本没有影响API，唯一的区别是如果原先自定义了value-color的映射函数的话，现在不再接受hsl的css字符串了，新的API需要你返回一个四个元素的数组，分别代表h, s, l, a，值域[0-1]。&lt;/p&gt;

&lt;p&gt;感谢关注HeatCanvas的朋友。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>'China Clojurians Meetup #2 is calling you'</title><link href="https://sunng.info/blog/china-clojurians-meetup-2-is-calling-you.html" rel="alternate"></link><published>2012-02-09T00:00:00+08:00</published><updated>2012-02-09T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2012-02-09:/blog/china-clojurians-meetup-2-is-calling-you.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;Beijing&lt;/li&gt;
&lt;li&gt;clojure
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;大家期待已久的第二次Clojure中国用户聚会来了，这次聚会将在三月初的北京举行。感兴趣的朋友请猛击&lt;a href="http://www.diaochapai.com/survey/312738f0-cab0-49b0-9f48-18803f79ddf4" target="_blank"&gt;这里&lt;/a&gt;报名。如果你 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;Beijing&lt;/li&gt;
&lt;li&gt;clojure
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;大家期待已久的第二次Clojure中国用户聚会来了，这次聚会将在三月初的北京举行。感兴趣的朋友请猛击&lt;a href="http://www.diaochapai.com/survey/312738f0-cab0-49b0-9f48-18803f79ddf4" target="_blank"&gt;这里&lt;/a&gt;报名。如果你有任何关于Clojure的心得，都欢迎加入分享，不要害羞～和上次一样本人会继续抛砖继续不害羞，介绍我的RPC框架Slacker。&lt;/p&gt;

&lt;p&gt;For English readers:&lt;br /&gt;
If you are interested in Clojure and also living in Beijing, feel free to join our meetup. Please register &lt;a href="http://www.diaochapai.com/survey/312738f0-cab0-49b0-9f48-18803f79ddf4" target="_blank"&gt;here&lt;/a&gt;. We will see you there!
&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Slacker Cluster</title><link href="https://sunng.info/blog/slacker-cluster.html" rel="alternate"></link><published>2012-02-04T00:00:00+08:00</published><updated>2012-02-04T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2012-02-04:/blog/slacker-cluster.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;slacker
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Cluster support is one of the big thing in slacker 0.6.x. Cluster enables high-availability and load balancing on slacker client and server. &lt;/p&gt;

&lt;p&gt;Slacker cluster has a centralized registry, a zookeeper node, stores information of all the namespaces and servers …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;slacker
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Cluster support is one of the big thing in slacker 0.6.x. Cluster enables high-availability and load balancing on slacker client and server. &lt;/p&gt;

&lt;p&gt;Slacker cluster has a centralized registry, a zookeeper node, stores information of all the namespaces and servers instances in the cluster. Once a client declared remote functions, by calling `defn-remote` or `use-remote`, it reads all available servers offering that namespace from the registry and create connection to each of them. We the user issues a request, the client randomly pick up a connection from them. So the load is eventually distributed to every instance of slacker servers. And thanks to zookeeper's notification feature, the client watches certain znode. It will be notified when 1. a connected server goes offline 2. a new server serving required namespace added into the cluster. Thus you don't have to change client code or restart client when server changes. &lt;/p&gt;

&lt;p&gt;To start a slacker server and add it to a cluster, you have to provide cluster information using the new &lt;strong&gt;:cluster&lt;/strong&gt; option:&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(start-slacker-server (the-ns 'slacker.example.api)&lt;br /&gt;
                      2104&lt;br /&gt;
                      :cluster {:zk "127.0.0.1:2181"&lt;br /&gt;
                                :name "example-cluster"})&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;&lt;ul&gt;
    &lt;li&gt;:zk is the address of zookeeper node&lt;/li&gt;
    &lt;li&gt;:name is a znode qualified string, to identify the cluster&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;On the client side, it's important to use APIs from `slacker.client.cluster` instead of `slacker.client`:&lt;/p&gt;

&lt;p&gt;[cc lang="clojure"]&lt;br /&gt;
(use 'slacker.client.cluster)&lt;br /&gt;
;; arguments: cluster-name, zookeeper address&lt;br /&gt;
(def sc (clustered-slackerc "example-cluster" "127.0.0.1:2181"))&lt;br /&gt;
(use-remote 'sc 'slacker.example.api)&lt;/p&gt;

&lt;p&gt;;; call the function from a random server&lt;br /&gt;
(timestamp)&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;If all servers provide 'slacker.example.api go offline, slacker client will raise a "not-found" exception.&lt;/p&gt;

&lt;p&gt;Slacker cluster is also designed with simple and clean in mind. You don't have to change you business code to make it remote or cluster. Everything is transparent and non-invasive. Enjoy it.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>'Slacker 0.6: Exposing multiple namespaces'</title><link href="https://sunng.info/blog/slacker-06-exposing-multiple-namespaces.html" rel="alternate"></link><published>2012-02-03T00:00:00+08:00</published><updated>2012-02-03T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2012-02-03:/blog/slacker-06-exposing-multiple-namespaces.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;slacker
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;After &lt;a href="https://github.com/sunng87/slacker/compare/8b4abdb62a...3e41e035b9" title="Changes"&gt;98 commits&lt;/a&gt; in about one month, I'm glad to announce [slacker "0.6.1"].&lt;/p&gt;

&lt;p&gt;One thing in slacker 0.6.x is you can expose multiple namespaces from a single server.&lt;/p&gt;

&lt;p&gt;Suppose you have two namespaces `redday.stats` and `redday …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;slacker
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;After &lt;a href="https://github.com/sunng87/slacker/compare/8b4abdb62a...3e41e035b9" title="Changes"&gt;98 commits&lt;/a&gt; in about one month, I'm glad to announce [slacker "0.6.1"].&lt;/p&gt;

&lt;p&gt;One thing in slacker 0.6.x is you can expose multiple namespaces from a single server.&lt;/p&gt;

&lt;p&gt;Suppose you have two namespaces `redday.stats` and `redday.api`, both contains functions you want to expose.&lt;/p&gt;

&lt;p&gt;[cc lang="clojure"]&lt;br /&gt;
  (start-slacker-server [(the-ns 'redday.stats)&lt;br /&gt;
                         (the-ns 'redday.api)]&lt;br /&gt;
                        6565)&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;This will expose `redday.stats` and `redday.api` on port 6565.&lt;/p&gt;

&lt;p&gt;On the client side, we have a new `use-remote` behaviors like clojure's use. Instead of local one, it imports functions from a remote namespace to your current namespace. &lt;/p&gt;

&lt;p&gt;[cc lang="clojure"]&lt;br /&gt;
(use 'slacker.client)&lt;br /&gt;
;; create a slacker client&lt;br /&gt;
(def scp (slackerc "127.0.0.1:6565")) &lt;/p&gt;

&lt;p&gt;(use-remote 'scp 'redday.api) ;; caution, use the symbol of 'scp here &lt;br /&gt;
(use-remote 'scp 'redday.stats)&lt;/p&gt;

&lt;p&gt;;;top-titles is a function in redday.api&lt;br /&gt;
;;now you can use the remote function transparently&lt;br /&gt;
(top-titles "programming") &lt;/p&gt;

&lt;p&gt;;;check function metadata you can find more slacker properties&lt;br /&gt;
(meta top-titles)&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;If you need to configure callback to a particular function, you can still use `defn-remote` to specify the callback function. In slacker 0.6.0, a `:remote-ns` is required when you define such a remote function.&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(defn-remote top-titles :remote-ns "redday.api" :callback #(println %))&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;The complete code example (both server and client) can be found &lt;a href="https://bitbucket.org/sunng/slacker-demo/overview"&gt;here&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;In next post, I will explain another big new feature of 0.6.x, cluster support.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>爬山！</title><link href="https://sunng.info/blog/pa-shan.html" rel="alternate"></link><published>2012-02-01T00:00:00+08:00</published><updated>2012-02-01T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2012-02-01:/blog/pa-shan.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;今天是2月1号，本人决定从今天开始到月底的每个工作日中午去爬山锻炼身体，以摆脱目前体重极速增长 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;今天是2月1号，本人决定从今天开始到月底的每个工作日中午去爬山锻炼身体，以摆脱目前体重极速增长的困局。口说无凭，立此为据。&lt;/p&gt;

&lt;p&gt;[mapsmarker marker="2"]&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>ClojureDocs Android App</title><link href="https://sunng.info/blog/clojuredocs-android-app.html" rel="alternate"></link><published>2012-01-24T00:00:00+08:00</published><updated>2012-01-24T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2012-01-24:/blog/clojuredocs-android-app.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;android&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;github&lt;/li&gt;
&lt;li&gt;project
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;利用春节的假期写了一个Android应用，可以在&lt;a href="http://clojuredocs.org" target="_blank"&gt;ClojureDocs.org&lt;/a&gt;上搜索clojure API，浏览文档、源代码和社区贡献的代码实例。ClojureDocs在我学习Clojure的过程中起了很大的作用 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;android&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;github&lt;/li&gt;
&lt;li&gt;project
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;利用春节的假期写了一个Android应用，可以在&lt;a href="http://clojuredocs.org" target="_blank"&gt;ClojureDocs.org&lt;/a&gt;上搜索clojure API，浏览文档、源代码和社区贡献的代码实例。ClojureDocs在我学习Clojure的过程中起了很大的作用，所以我想这个网站应该对很多人有用。&lt;/p&gt;

&lt;p&gt;无暇去学习Android平台上繁琐的知识，不过好在有Phonegap这样的框架，可以把网页应用转化为本地应用，并且提供访问本地设备的API。通过Phonegap开发的程序还可以直接移植到iphone平台上。ClojureDocs Android就是运行在Phonegap中。&lt;/p&gt;

&lt;p&gt;首页：
&lt;img src="http://i.imgur.com/zdMCt.png" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;搜索界面
&lt;img src="http://i.imgur.com/MVCz4.png" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;API函数界面
&lt;img src="http://i.imgur.com/M0fEI.png" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;你可以从github获得代码和签名过的apk：&lt;a href="https://github.com/sunng87/clojuredocs-android" target="_blank"&gt;https://github.com/sunng87/clojuredocs-android&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Known Issue，phonegap程序在屏幕旋转时会崩溃，已经在2.3和3.2上重现，目前还不清楚具体的原因。(Edit 20120127: Fixed in 1.0.4)&lt;/p&gt;

&lt;p&gt;欢迎任何的pull request。
&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>'OpenStreetMap Nanjing: A Year of Edits'</title><link href="https://sunng.info/blog/openstreetmap-nanjing-a-year-of-edits.html" rel="alternate"></link><published>2012-01-19T00:00:00+08:00</published><updated>2012-01-19T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2012-01-19:/blog/openstreetmap-nanjing-a-year-of-edits.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;OpenStreetMap
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;img src="http://i.imgur.com/0mVFk.png" alt="osm nanjing" /&gt;&lt;/p&gt;

&lt;p&gt;这是2011年OpenStreetMap上，南京的编辑情况。高亮的部分是2011年创建或更新的要素。从这张图上可以看出最近的这一年，南京的数据从无到有到 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;OpenStreetMap
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;img src="http://i.imgur.com/0mVFk.png" alt="osm nanjing" /&gt;&lt;/p&gt;

&lt;p&gt;这是2011年OpenStreetMap上，南京的编辑情况。高亮的部分是2011年创建或更新的要素。从这张图上可以看出最近的这一年，南京的数据从无到有到逐渐的完善，这里面倾注了本地几位贡献者结结实实的心血。&lt;/p&gt;

&lt;p&gt;对这个可视化感兴趣，可以参考&lt;a href="https://gist.github.com/1639915" target="_blank"&gt;这里的代码和样式表&lt;/a&gt;。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Clojure on CloudFoundry</title><link href="https://sunng.info/blog/clojure-on-cloudfoundry.html" rel="alternate"></link><published>2012-01-13T00:00:00+08:00</published><updated>2012-01-13T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2012-01-13:/blog/clojure-on-cloudfoundry.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;cloudfoundry&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;In this article, I will show you how to develop and deploy clojure web application on CloudFoundry. As you may know, CloudFoundry is an opensource PaaS backed by VMWare. Java, Ruby and Nodejs are officially supported. As a JVM language …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;cloudfoundry&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;In this article, I will show you how to develop and deploy clojure web application on CloudFoundry. As you may know, CloudFoundry is an opensource PaaS backed by VMWare. Java, Ruby and Nodejs are officially supported. As a JVM language, clojure is born to be also available on this platform, although it's not listed. &lt;/p&gt;

&lt;p&gt;CloudFoundry accepts a .war file for Java web application deployment. So you don't need the ring-jetty-adaptor and a procfile as heroku requires. To help your development and deployment, I strongly recommend the lein-ring plugin:&lt;/p&gt;

&lt;p&gt;[cc lang="clojure"]&lt;br /&gt;
  :dev-dependencies [[lein-ring "0.5.4"]]&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;CloudFoundry provides backend services like mysql, redis, mongodb and more. The connection information are stored as environment variables. &lt;a href="http://env.cloudfoundry.com/env" target="_blank"&gt;Here&lt;/a&gt; you can find a subset of them. &lt;/p&gt;

&lt;p&gt;Take mongodb as example, connection information (host, port, username and password) are encoded as JSON, stored in environment variables. You can get them with this function:&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(defn mongo-config [key]&lt;br /&gt;
  (if-let [services (System/getenv "VCAP_SERVICES")]&lt;br /&gt;
    (let [services-dict (json/read-json services false)]&lt;br /&gt;
      (-&gt; services-dict&lt;br /&gt;
        (get "mongodb-1.8")&lt;br /&gt;
        first&lt;br /&gt;
        (get "credentials")&lt;br /&gt;
        (get key)))))&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;In the server bootstrap function, create the mongodb connection:&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(defn app-init []&lt;br /&gt;
  (def db-conn (make-connection&lt;br /&gt;
                 (or (mongo-config "db") "lazypress")&lt;br /&gt;
                   :host (or (mongo-config "hostname") "localhost")&lt;br /&gt;
                   :port (or (mongo-config "port") 27017)))&lt;br /&gt;
  (when-not (nil? (mongo-config "username"))&lt;br /&gt;
    (authenticate db-conn&lt;br /&gt;
      (mongo-config "username")&lt;br /&gt;
      (mongo-config "password")))&lt;br /&gt;
[/cc]&lt;br /&gt;
By adding check for nil, local databased is also supported. This is pretty convenience for local development. These environment variables are consistent on all cloudfoundry application, so it's possible to deploy the application on multiple accounts without any changes.&lt;/p&gt;

&lt;p&gt;Then you can add your web stuff just like standard clojure web development. (If you are using compojure, place your static files under resource/public.)&lt;/p&gt;

&lt;p&gt;Finally, package it. (Suppose your application is named as "lazypress")&lt;br /&gt;
[cc lang="bash"]&lt;br /&gt;
lein ring uberwar lazypress.war&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;Use the vmc tool to deploy it:&lt;br /&gt;
[cc lang="bash"]&lt;br /&gt;
vmc update lazypress&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;For more usage about the vmc tool, you can read &lt;a href="http://blog.cloudfoundry.com/post/13481010498/simplified-application-deployment-with-cloud-foundry-manifest" target="_blank"&gt;this article&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So you have finished deploying your clojure web application to cloudfoundry.&lt;/p&gt;

&lt;p&gt;Backed by spring and vmware, cloudfoundry is more Java-friendly than other PaaS like heroku. You don't have to start a Java process by yourself ("lein run" isn't a graceful way to start your app in product environment). And you don't have to worry about your web container settings (configure jetty with limited options via ring-jetty-adaptor). All you have to do is package the application as a portable war file, which you can deploy to tomcat, glassfish, and also cloudfoundry. The vmc tool could detect you war file and handle it correctly.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>'Weekend Project: LazyPress'</title><link href="https://sunng.info/blog/weekend-project-lazypress.html" rel="alternate"></link><published>2012-01-08T00:00:00+08:00</published><updated>2012-01-08T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2012-01-08:/blog/weekend-project-lazypress.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;cloudfoundry&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;发布一个典型的weekend project, 名字叫做&lt;a href="http://lazypress.cloundfoundry.com"&gt;LazyPress&lt;/a&gt;.顾名思义,这是一个在线的写作系统. 取名Lazy, 除了因为它用Clojure写成,更因 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;cloudfoundry&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;发布一个典型的weekend project, 名字叫做&lt;a href="http://lazypress.cloundfoundry.com"&gt;LazyPress&lt;/a&gt;.顾名思义,这是一个在线的写作系统. 取名Lazy, 除了因为它用Clojure写成,更因为他的简单: 没有繁琐的注册,没有繁琐的分类tag,没有繁琐的格式化,无论是使用还是开发都力求做到最简单.&lt;br /&gt;
LazyPress采用Mozilla刚刚发布的&lt;a href="http://browserid.org/"&gt;BrowserID&lt;/a&gt;. 技术作为账号系统, 用户只需要在首次登录后提供一个ID即可(原本这一步也可以省略, 但是为了保护您的邮箱隐私, 现在需要一个LazyPress专用的ID). 这样LazyPress本身不存储用户的密码,也简化了用户账号管理的代价. BrowserID的登录流程非常简单, 速度也比传统的OpenID和OAuth要快, 用户体验要比多次跳转好很多.&lt;br /&gt;
LazyPress使用Markdown进行文本格式化, 简单的编辑器可以支持绝大多数格式的要求. 另外,在浏览器后台LazyPress使用HTML5 LocalStorage技术自动保存用户的文本草稿, 如果用户没有成功发布, 可以在下次浏览器打开时进行恢复.&lt;/p&gt;

&lt;p&gt;LazyPress后台存储采用mongodb. 正是mongodb的schema free特性降低了项目功能重构的成本, 促进了人们更快更频繁地优化产品的模型. 这应该是文档型数据库之于传统关系型数据库最大的优势. (犹如git之于svn, 开分支的成本要低得多, 看似是一个普通的功能改进, 实则鼓励促进了开发人员通过开分支实现自己的修改)&lt;br /&gt;
LazyPress运行在最近发布的compojure 1.0.0和ring 1.0.0上, 打包为标准的Java web应用直接部署在cloudfoundry上. 前端继续使用的是我偏爱的Mootools库, 因为使用了很多新的浏览器技术, 所以目前只能保证在最新的Firefox和Chromium/Chrome上正常使用.&lt;/p&gt;

&lt;p&gt;Fork me on github: &lt;a href="http://github.com/sunng87/lazypress"&gt;http://github.com/sunng87/lazypress&lt;/a&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>slacker 0.4.0 released</title><link href="https://sunng.info/blog/slacker-040-released.html" rel="alternate"></link><published>2012-01-04T00:00:00+08:00</published><updated>2012-01-04T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2012-01-04:/blog/slacker-040-released.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;slacker
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Slacker 0.4.0 has been released to clojars.org . There are new features and breaking changes in this release.&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;Breaking Changes&lt;/h3&gt;&lt;/p&gt;

&lt;p&gt;&lt;ul&gt;
    &lt;li&gt;New maven coordinator: [slacker "0.4.0"] (groupId renamed to slakcer)&lt;/li&gt;
    &lt;li&gt;defremote renamed to &lt;strong&gt;defn-remote&lt;/strong&gt;&lt;/li&gt;
    &lt;li&gt;SlackerException removed. slacker now …&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;slacker
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Slacker 0.4.0 has been released to clojars.org . There are new features and breaking changes in this release.&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;Breaking Changes&lt;/h3&gt;&lt;/p&gt;

&lt;p&gt;&lt;ul&gt;
    &lt;li&gt;New maven coordinator: [slacker "0.4.0"] (groupId renamed to slakcer)&lt;/li&gt;
    &lt;li&gt;defremote renamed to &lt;strong&gt;defn-remote&lt;/strong&gt;&lt;/li&gt;
    &lt;li&gt;SlackerException removed. slacker now uses slingshot for exception handling&lt;/li&gt;
    &lt;li&gt;Rename :async option of defn-remote to &lt;strong&gt;:async?&lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;What's new in 0.4.0&lt;/h3&gt;
&lt;ul&gt;
    &lt;li&gt;Add new serialization type &lt;strong&gt;:clj&lt;/strong&gt;&lt;/li&gt;
    &lt;li&gt;New interceptors: execution time stats, args logger, slow watch dog&lt;/li&gt;
    &lt;li&gt;New HTTP interface&lt;/li&gt;
    &lt;li&gt;Server inspect commands &lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;&lt;li&gt; utility functions/macros &lt;strong&gt;defn-remote-all&lt;/strong&gt;, &lt;strong&gt;defn-remote-batch&lt;/strong&gt; and &lt;strong&gt;meta-remote&lt;/strong&gt;&lt;/li&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Get more information on &lt;a href="https://github.com/sunng87/slacker" target="_blank"&gt;github&lt;/a&gt;.
&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Code 2011</title><link href="https://sunng.info/blog/code-2011.html" rel="alternate"></link><published>2011-12-31T00:00:00+08:00</published><updated>2011-12-31T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-12-31:/blog/code-2011.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;Life&lt;/li&gt;
&lt;li&gt;programming
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;成功人士是不写总结的，所以我来写个总结。&lt;/p&gt;

&lt;p&gt;今年的coding从ABAP开始，这个很多人现在不知道以后也不用知道最好 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;Life&lt;/li&gt;
&lt;li&gt;programming
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;成功人士是不写总结的，所以我来写个总结。&lt;/p&gt;

&lt;p&gt;今年的coding从ABAP开始，这个很多人现在不知道以后也不用知道最好永远也不要知道的语言和他的平台，是一个脱胎于Pascal，在发展过程里又杂糅了SQL和C++的怪物，最后几乎变成了满屏幕关键字。再加上缺乏文档，实在是一段不堪回首的记忆。&lt;/p&gt;

&lt;p&gt;3月份开始，我又重新开始了一个python项目叫做&lt;a href="https://github.com/sunng87/jip" target="_blank"&gt;jip&lt;/a&gt;，他兼容maven，可以与virtualenv和setuptools集成，帮助你解决jython项目的java依赖，简化jython项目的发布，提高工作效率。这个小工具倾注了我不少时间，如果你某一天要和jython打交道的话，可以试试看。&lt;/p&gt;

&lt;p&gt;今年还尝试了coffeescript，它恰到好处地消除了javascript里一些ugly的部分，大大提高了编码速度。我用它改写了网站&lt;a href="http://sunng.info/scripts/action.coffee" target="_blank"&gt;首页的js&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;6月份我又重拾起1年前的javascript库heatcanvas，和&lt;a href="http://nihuajie.com/" target="_blank"&gt;lbt05&lt;/a&gt;协作完善了程序。通过HTML5的Web worker API改善了渲染canvas时的用户体验，增加了对Google Maps，OpenLayers,Baidu Map以及Leaflet的支持。这个项目的介绍被顶到hackernews的首页，这是一种前所未有的感觉。&lt;/p&gt;

&lt;p&gt;7月份开始迎来了一个重大的转折。断断续续学习了一年半的clojure，终于开始写第一个库，&lt;a href="https://github.com/sunng87/reddit.clj" target="_blank"&gt;reddit.clj&lt;/a&gt;，用clojure封装reddit网站的API。通过这个入口算是真正走进了clojure的世界，七月底reddit.clj基本完成之后我开始写他的第一个应用，&lt;a href="https://github.com/sunng87/rageviewer" target="_blank"&gt;rageviewer&lt;/a&gt;。这是个clojure的web项目，借着这个契机又接触了compojure和ring。而且写rageviewer的时候恰逢clojurescript公开，一不做二不休，于是这个项目就成了一个full stack的clojure项目：前后端都是clojure。最后rageviewer部署在当时刚刚开始邀请测试的cloudfoundry上。&lt;/p&gt;

&lt;p&gt;8月参加了在上海的cn-clojure列表第一次聚会后，我开始用clojure克隆一个已有的并不复杂的程序，当时选择了beanstalkd，一个轻量级的task queue。这个项目取名为&lt;a href="https://github.com/sunng87/clojalk" target="_blank"&gt;clojalk&lt;/a&gt;。最后它成功地支持了beanstalkd协议的所有命令，支持了通过Write-ahead log做持久化和恢复。这个项目用到了aleph，见识了一把clojure思维下的网络编程。&lt;/p&gt;

&lt;p&gt;另外我还帮clojure-control写了一个leiningen的插件，现在这个插件已经合并到clojure-control项目里了。clojure能有这么快的发展，leiningen作为构建工具也有很大的帮助。就好比上半年做jip时，感受到jython的小世界里就没有一个好的方案来同时解决java和python的项目管理问题，而且人们也不重视这个问题。&lt;/p&gt;

&lt;p&gt;年底还有一个clojure RPC框架的诞生，这个项目叫做&lt;a href="https://github.com/sunng87/slacker" target="_blank"&gt;slacker&lt;/a&gt;。项目还没有到总结的时候，我的org file列表上还有一长串的TODO。&lt;/p&gt;

&lt;p&gt;总得来说，我觉得今年学习clojure的这个过程很有借鉴价值。对于一个新语言新平台新生态系统，如何入门并且getting real。你可以从一个功能简单的库开始，比如包装一个网站的API，或者（对于clojure来说），包装一个已有的java的库。在完成之后，利用这个库，写一个web应用，进而去了解这个平台上的web开发。再下一步，可以去克隆一个其他平台上的项目，规模不要太大。如此循序渐进，学习的效果很不错。另外，无论做了什么，只要是有用的，就应该说出来，这不仅是自我鼓励，有时候也能找到志同道合的朋友一起参与。&lt;/p&gt;

&lt;p&gt;最后除了clojure之外，今年还接着gnome-shell的发布和更新，接触了gnome-shell的gjs扩展开发。又是一个不堪回首的平台，也许是还没有finalize吧，没有任何文档，而且一个平台上的库连变量拼写的风格都不一样！我是不会再浪费时间了，当然，以后的这个&lt;a href="https://extensions.gnome.org/extension/24/exaile-doubanfm-control/" target="_blank"&gt;豆瓣电台的control&lt;/a&gt;还是会继续跟着gnome-shell的发布一直维护的。&lt;/p&gt;

&lt;p&gt;除了上面提到的，今年还尝了一些groovy，common-lisp，甚至octave，不管怎么说都算是一个big year了。但愿明年能把这种状态保持下去，享受这种愉悦。&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>使用Enlive作为模板引擎</title><link href="https://sunng.info/blog/shi-yong-enlivezuo-wei-mo-ban-yin-qing.html" rel="alternate"></link><published>2011-12-29T00:00:00+08:00</published><updated>2011-12-29T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-12-29:/blog/shi-yong-enlivezuo-wei-mo-ban-yin-qing.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;在所有的clojure web开发例子里，对模板的介绍都很少。很多的简单例子都是以hiccup作为页面生成的手段。hiccup是个clojure的html DSL，例子里用这样的DSL生成页面确实很酷，可是他是real …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;在所有的clojure web开发例子里，对模板的介绍都很少。很多的简单例子都是以hiccup作为页面生成的手段。hiccup是个clojure的html DSL，例子里用这样的DSL生成页面确实很酷，可是他是real world吗，当然不是。&lt;/p&gt;

&lt;p&gt;好在clojure世界里早就有了enlive，它不仅是一个通过css selector解析html的库，本身也可以作为模板引擎应用在web开发中。我不知道这种通过css selector的方式是否是enlive首创，不过他实在是非常新颖独特，而且平滑了页面设计和程序的集成。&lt;/p&gt;

&lt;p&gt;例如这样一个模板 index.html：&lt;br /&gt;
[cc lang="html"]
&lt;div id="cc"&gt;Sample Text&lt;/div&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;在clojure程序中，使用enlive的deftemplate&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(deftemplate index "index.html"&lt;br /&gt;
  [ctx]&lt;br /&gt;
  [:div#cc] (content (:data ctx)))&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;在控制器里，可以很MVC地渲染页面&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(index {:data "rendered text"})&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;除了content用于渲染文本，还有html-content可以渲染含html标签的内容，以及set-attr用来修改页面元素的属性。&lt;/p&gt;

&lt;p&gt;和传统的模板引擎相比，最大的不同是enlive里没有嵌入模板的直观的控制流，没有循环和条件判断，但是并非不可实现。&lt;/p&gt;

&lt;p&gt;循环输出一组list&lt;/p&gt;

&lt;p&gt;页面 list.html&lt;br /&gt;
[cc lang="html"]
&lt;ul id="the-list"&gt;
&lt;li class="list-item"&gt;&lt;/li&gt;
&lt;/ul&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;定义一个enlive的snippet&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(defsnippet item-model "page.html" [:.list-item]&lt;br /&gt;
  [ctx]&lt;br /&gt;
  [:.list-item] (content (:data ctx)))&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;在页面模板里&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(deftemplate list-page "list.html"&lt;br /&gt;
  [ctx]&lt;br /&gt;
  [:ul#the-list] (content (map item-model (:some-list ctx))))&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;这样在页面里列表项会被循环输出，而在页面设计时这里可以放任意个li，并且直接交给后台作为模板。&lt;/p&gt;

&lt;p&gt;条件判断&lt;/p&gt;

&lt;p&gt;页面，设计时显示所有的内容 msg.html&lt;br /&gt;
[cc lang="html"]
&lt;span id="msg"&gt;只在一定条件下显示&lt;/span&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;在模板中通过clojure的if进行判断&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(deftemplate msg "msg.html"&lt;br /&gt;
  [ctx]&lt;br /&gt;
  [:span#msg] (if (:show ctx) identity (html-content "")))&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;解决了这两个问题，基本上用enlive作为模板引擎就没有障碍了。不过enlive也有一点小问题，其一可能是性能的问题，方便的selector显然要比传统的模板语言消耗更多的CPU。另外，在开发过程里，页面文件在服务器启动后不能热加载，修改页面必须重启ring才能看到。也许有时间的话，可以给它加一个reload选项。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Roar for mootools 1.4</title><link href="https://sunng.info/blog/roar-for-mootools-14.html" rel="alternate"></link><published>2011-12-24T00:00:00+08:00</published><updated>2011-12-24T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-12-24:/blog/roar-for-mootools-14.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;github&lt;/li&gt;
&lt;li&gt;javascript&lt;/li&gt;
&lt;li&gt;mootools
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;早在天下大势还处在分久必合的时候，那时候mootools还有不少简单实用的小库，比如我今天搜索"mootools notification"就找到这个08年的库叫做&lt;a href="http://digitarald.de/project/roar/" target="_blank"&gt;Roar&lt;/a&gt;。不过遗憾 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;github&lt;/li&gt;
&lt;li&gt;javascript&lt;/li&gt;
&lt;li&gt;mootools
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;早在天下大势还处在分久必合的时候，那时候mootools还有不少简单实用的小库，比如我今天搜索"mootools notification"就找到这个08年的库叫做&lt;a href="http://digitarald.de/project/roar/" target="_blank"&gt;Roar&lt;/a&gt;。不过遗憾的是从那以后，这个库就再也没有更新过了。&lt;/p&gt;

&lt;p&gt;Mootools本身也沉寂了很久，这个项目恐怕也要思考自己未来的发展方向了。今年9月Mootools迈进了1.4，API上有一些变化。现在的下载页也能看到with/without backward compatibility的版本分开下载。为了用上Roar，我尝试了这两个版本发现都不能使用。最后downgrade到1.2可以确定Roar本身在当时是没有什么问题。&lt;/p&gt;

&lt;p&gt;这么多年对mootools痴心不改，所以顺手维护了一下Roar，现在可以在1.4 without compatibility的发布下运行了。主要是几个小修改，大多是一些多年deprecated函数被正式删除：
&lt;ul&gt;
    &lt;li&gt;Type常量，原先的String.type，Object.type现在统一到一个Type对象下，变成Type.isString和Type.isObject&lt;/li&gt;
    &lt;li&gt;$empty 常量被删除了，现在直接用function()或Function.from()代替&lt;/li&gt;
    &lt;li&gt;$pick 方法被Array.pick取代，参数现在也必须接受数组类型了&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt; &lt;li&gt;$merge 方法被Object.merge取代&lt;/li&gt;
    &lt;li&gt;$type 被typeOf取代&lt;/li&gt;
    &lt;li&gt;函数对象的create方法被删除了，现在可以用函数对象的bind方法替代&lt;/li&gt;
    &lt;li&gt;Browser.Engine 被删除了，需要用其他Browser的API替代&lt;/li&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;修改后的Roar，放在&lt;a href="https://gist.github.com/1516568" target="_blank"&gt;这个gist&lt;/a&gt;里，测试过可以在firefox和chromium上健康使用。IE没有做测试。这个08年的库，眼看四年过去了，用起来依然不错。&lt;/p&gt;

&lt;p&gt;作为mootools的铁杆，我还是会一直专一地坚守下去的。（于是，我也已经变成了多年前那些我眼中为旧事物顽抗到底的老家伙了）&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>TF101 101</title><link href="https://sunng.info/blog/tf101-101.html" rel="alternate"></link><published>2011-12-22T00:00:00+08:00</published><updated>2011-12-22T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-12-22:/blog/tf101-101.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;pad
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;上周末决定不再忍耐，又入了一个大件：华硕的平板，变形金刚。TF101上市已经半年了，而且现在TF102号称四核的版本已经开始接受预订了，所以差不多也到了应该出 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;pad
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;上周末决定不再忍耐，又入了一个大件：华硕的平板，变形金刚。TF101上市已经半年了，而且现在TF102号称四核的版本已经开始接受预订了，所以差不多也到了应该出手的时候了。因为我知道如果去等102的话，我还会像现在这样地去和103做比较。&lt;/p&gt;

&lt;p&gt;选择这款的主要原因即他的键盘配置，平板加键盘的组合彻底把上网本推进深渊。对于我这种还算是制造内容比率比较高的人来说，有个强有力的输入设备是必要的。目前市面上有这种搭配的只有transformer和think的，而且think似乎又只有配图和说明，没见真正卖那款键盘的。更严重的是，当然，think太贵了。&lt;/p&gt;

&lt;p&gt;接下来开始说问题：&lt;br /&gt;
第一关叫做充电。TF101在充电方面有严重的缺陷。当电池电量极低时，会出现无法充电的情况。明明接着电源，可是电量提示一直是0%。如此情况下我整整充了一天拔下电源依然无法开机。最后看了网上的说法，在充电自动开机后关机了半个小时终于起死回生。&lt;/p&gt;

&lt;p&gt;第二个小问题，键盘底座的平板本身电源是分离的，二者各自充电。所以在前面〝整整充了一天〞之前还有整整充了一晚上键盘。早晨起来键盘电满了，平板没充进去。&lt;/p&gt;

&lt;p&gt;系统出厂是Android 3.0，这个系统的伟大之处在于从他一启动开始，就开始不断有程序报出错退出，不断有应用停止响应。他简直都对不起这个版本号。直到后来充上电升级到3.2之后才可以用。但是，还是有浏览器突然僵死然后突然从眼前消失的场面。再有就是机捆绑的什么人人网，电子书，开心网什么的，让你想不root都不行。&lt;/p&gt;

&lt;p&gt;再有andrid 3.x上应用可能本来就不多，随机捆的又是一个流氓市场，除了满眼的流氓软件和山寨以外，就是版已经过时的软件。可怜这挺好一机器都不知道该装点什么。要说TF101硬件已经很可以了，只是配上这么个系统，用范伟的话说，白瞎你这个人了。手放键盘上，真恨不得能打开个终端来挡住这个浅薄的外观。等有时间我一定要尝试在这台机器上装个正经系统。也算是对得起这硬件了。&lt;/p&gt;

&lt;p&gt;一句话概括一下的话，硬件还好，软件拉倒。&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Finished my machine learning courses</title><link href="https://sunng.info/blog/finished-my-machine-learning-courses.html" rel="alternate"></link><published>2011-12-19T00:00:00+08:00</published><updated>2011-12-19T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-12-19:/blog/finished-my-machine-learning-courses.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;course&lt;/li&gt;
&lt;li&gt;ml
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;经过三个月的时间，终于看完了ml-class的所有视频课程，完成了所有review questions，提交了所有programming exercises.感觉不错，之前一直对数据挖掘 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;course&lt;/li&gt;
&lt;li&gt;ml
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;经过三个月的时间，终于看完了ml-class的所有视频课程，完成了所有review questions，提交了所有programming exercises.感觉不错，之前一直对数据挖掘相关的方面感兴趣，回想一下大学时候一些地统计分析甚至遥感图像数据处理的课都跟机器学习相关，但是毕竟不是这方面的课程，所以介绍的不是很系统。今年秋天斯坦福推出这个在线课程，机器学习作为其中之一真算是弥补了我们民间科学爱好者的遗憾了。&lt;/p&gt;

&lt;p&gt;这个课程在有限的篇幅里涵盖了linear regression, logistic regression, ANN, SVM, PCA, K-Means, Anomaly Detection等等知识，基本上算是一个完整实用的导论。Andrew Ng教授的讲解也算是通俗易懂深入浅出，完全感觉不到什么门槛。&lt;/p&gt;

&lt;p&gt;对于online course这种形式，今年秋天斯坦福的人工智能、数据库、机器学习也算是首开先河，目前这三门课程都已经结束，网上的反响非常强烈。好消息是明年Q1斯坦福还有更多数量更多方向的课程。今天MIT也宣布了明年的online course计划，他们也将加入提供在线课程的行列。而且，MIT的在线课程还会颁发一个名叫MITx的certification。开放式课程已经成为大势所趋，信息本应自由传播。&lt;/p&gt;

&lt;p&gt;对于对机器学习感兴趣的朋友，除了ml-class.org上的资源，你还可以在academic earth上找到ANG教授的授课视频。这套视频涵盖的内容比ml-class上的更详细完整：
&lt;a href="http://academicearth.org/courses/machine-learning" target="_blank"&gt;http://academicearth.org/courses/machine-learning&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;课程结束，我在ml-class上所有的编程作业都已经放在bitbucket上，如果有兴趣可以参考这些octave程序：
&lt;a href="https://bitbucket.org/sunng/ml-class" target="_blank"&gt;https://bitbucket.org/sunng/ml-class&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;明年一月斯坦福还会开放更多跟机器学习相关的课程，包括：
&lt;ul&gt;
    &lt;li&gt;Probabilistic Graphical Models &lt;a href="http://www.pgm-class.org/" target="_blank"&gt;pgm-class.org&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;Natural Language Processing &lt;a href="http://www.nlp-class.org" target="_blank"&gt;nlp-class.org&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Thank you, Professor Ng and your team for this well-prepared, high-quality online course.&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Extend slacker server with interceptors</title><link href="https://sunng.info/blog/extend-slacker-server-with-interceptors.html" rel="alternate"></link><published>2011-12-18T00:00:00+08:00</published><updated>2011-12-18T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-12-18:/blog/extend-slacker-server-with-interceptors.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;github&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;slacker
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;An interceptor framework was introduced in slacker 0.3.0. It's designed to allow user to add custom functionality without hacking into the internal of slacker.&lt;/p&gt;

&lt;p&gt;Like many server frameworks, slacker abstracts the request processing as a pipeline. The …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;github&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;slacker
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;An interceptor framework was introduced in slacker 0.3.0. It's designed to allow user to add custom functionality without hacking into the internal of slacker.&lt;/p&gt;

&lt;p&gt;Like many server frameworks, slacker abstracts the request processing as a pipeline. The request object is modified by adding or updating attributes through each node of the pipeline. So it's easy to add your interceptor into the pipeline, with which you can get the data before and after function executed.&lt;/p&gt;

&lt;p&gt;To create such an interceptor, you should use the &lt;em&gt;slacker.interceptor/definterceptor&lt;/em&gt; macro and &lt;em&gt;slacker.interceptor/definterceptor+&lt;/em&gt; macro:&lt;/p&gt;

&lt;p&gt;&lt;blockquote&gt;(definterceptor name&lt;br /&gt;
  :before interceptor-function&lt;br /&gt;
  :after interceptor-function)&lt;/blockquote&gt;&lt;/p&gt;

&lt;p&gt;&lt;blockquote&gt;(definterceptor+ name [arguments]&lt;br /&gt;
  :before interceptor-function&lt;br /&gt;
  :after interceptor-function)&lt;/blockquote&gt;&lt;/p&gt;

&lt;p&gt;definterceptor+ can accept arguments so you can configure the interceptor when you use it.&lt;/p&gt;

&lt;p&gt;See a simple example:&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(definterceptor log-function-call&lt;br /&gt;
  :before (fn [req] (println (str "calling " (:fname req))) req))&lt;/p&gt;

&lt;p&gt;(definterceptor+ log-function-call-prefixed [prefix]&lt;br /&gt;
  :before (fn [req] (println (str &lt;br /&gt;
                               (if (fn? prefix) (prefix) prefix) &lt;br /&gt;
                               " calling " &lt;br /&gt;
                               (:fname req))) &lt;br /&gt;
                    req))&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;Then, add it to your slacker server by&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(use '[slacker.interceptor])&lt;br /&gt;
(import '[java.util Date])&lt;br /&gt;
(start-slacker (the-ns 'slapi) 2104&lt;br /&gt;
  :interceptors (interceptors log-function-call&lt;br /&gt;
                              (log-function-call-prefixed &lt;br /&gt;
                                (fn [] (.toString (Date.)))))&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;Now you can log every function call of your slacker server.&lt;/p&gt;

&lt;p&gt;For more detail about the interceptor framework, especially the request data, please check the &lt;a href="https://github.com/sunng87/slacker/wiki/Interceptors" target="_blank"&gt;wiki page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In slacker 0.3.0, there is a built-in interceptor to stats function calls. You can find it at &lt;em&gt;slacker.interceptors.stats&lt;/em&gt;. The stats data is expose via JMX. You can also write monitoring application to retrieve the data. 
&lt;a href="http://imgur.com/vtOoL"&gt;&lt;img src="http://i.imgur.com/vtOoL.png" alt="" title="Hosted by imgur.com" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And there will be more built-in interceptors in 0.4.0, includes function call time stats and logging.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>使用Clojure Thread Macro的心得</title><link href="https://sunng.info/blog/shi-yong-clojure-thread-macrode-xin-de.html" rel="alternate"></link><published>2011-12-16T00:00:00+08:00</published><updated>2011-12-16T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-12-16:/blog/shi-yong-clojure-thread-macrode-xin-de.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;macro
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Thread Macro是clojure里一个很强大的宏，他帮助你简化嵌套函数的调用，比如&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(str (inc (count [:a :b])))&lt;br /&gt;
[/cc]&lt;br /&gt;
就可以利用thread macro简写成&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(-&gt; [:a :b] count inc str …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;macro
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Thread Macro是clojure里一个很强大的宏，他帮助你简化嵌套函数的调用，比如&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(str (inc (count [:a :b])))&lt;br /&gt;
[/cc]&lt;br /&gt;
就可以利用thread macro简写成&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(-&gt; [:a :b] count inc str)&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;-&amp;gt;&amp;gt;和-&amp;gt;类似，区别在于-&amp;gt;&amp;gt;把值作为函数的最后一个参数传入。&lt;/p&gt;

&lt;p&gt;简单的功能介绍完了，接下来就遇到问题了。我需要功能，能够接受一个或多个函数，然后把这些函数组成一个pipeline。这时很自然想到-&amp;gt;是一个好帮手。也许只需要一个类似这样的form就可以了： #(apply -&amp;gt; % [funcs])。结果失败了，因为-&amp;gt;是个宏，所以根本不能用apply。于是想到有apply-macro吗？有，或者说曾经有过。在contrib中曾经有一个apply-macro，不过被强烈不推荐使用。到这里，这条路堵死了，惟一的办法就是把-&amp;gt;放到API之外，放到用户代码里去。&lt;/p&gt;

&lt;p&gt;放到用户代码里，你需要写一个详细的说明文档并且告诉用户他必须这么做。然而在clojure世界里有一个更好的办法就是再写一个宏把-&amp;gt;包装起来。这么做看似多此一举，其实是保持了API的一致性。通过宏，我们可以把自己的API延伸到用户代码中去。或者说，通过一个类似DSL的宏，给一些并不优雅的API一个缓冲，也为API日后的演化留下空间。&lt;/p&gt;

&lt;p&gt;这里还要扯开一句关于宏的开发。clojure中所谓code is data，主要就是体现在宏里。原本在多数其他语言里，宏是不能求值的。但是在clojure里，由于code is data的缘故，宏是可以求值的。所有的输入数据都是list，你可以做first/reverse这样的操作。但是有一点要注意的是，宏中求得的值和代码里的值是不一样的。例如{:a inc}这样一个字面量，在宏里是可以通过:a做求值的，然后这里得到的并非一个函数（function），而是一个符号（symbol）。再者，调试宏的时候你可能会被这样的结果困惑：&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(defmacro a [f] (println (:a f)))&lt;br /&gt;
(a {:a 1}) ; ==&gt; prints 1&lt;br /&gt;
(def b {:a 1})&lt;br /&gt;
(a b) ; ==&gt; prints nil&lt;br /&gt;
[/cc]&lt;br /&gt;
字面量可以，同值的变量就不行了。原因还是那句，宏里不能求值。&lt;/p&gt;

&lt;p&gt;继续谈-&amp;gt;。这个宏其实远没有你想象的那么驯服。遇到复杂一点的情况：&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(def m {:a inc})&lt;br /&gt;
(-&gt; 2 (get m :a) str)&lt;br /&gt;
[/cc]&lt;br /&gt;
这个写法对吗？str是个函数，(get m :a)返回的是inc也是个函数，貌似正确。运行之后却报错get的参数数量错误。所以千万不要忘了-&amp;gt;是个宏，(get m :a)这里是不会求值到inc的，直接作为一个list被宏吞下去。在宏里只能通过符号的组合变化来生成代码，那么一不小心，就没有inc什么事了。&lt;/p&gt;

&lt;p&gt;于是，你可能想到这里需要一个确切的函数，就好比这样：&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(def m {:a inc})&lt;br /&gt;
(-&gt; 2 (fn [x] ((get m :a) x)))&lt;br /&gt;
[/cc]&lt;br /&gt;
也许这样就好多了，我们放了一个匿名函数，并不要求宏去求值，因为这个匿名函数会被宏生成到新的代码里。里面的get也会在运行时求值。看似没什么问题，可是一运行还是没有期待的结果，居然返回了一个匿名函数！而对这个匿名函数求值得到的也是一个错误的结果！简直有点无厘头了。&lt;/p&gt;

&lt;p&gt;呵呵，不故弄玄虚了。我们用macroexpand看看发生了什么。&lt;/p&gt;

&lt;p&gt;这是用匿名函数包装以前&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(macroexpand-1 '(-&gt; 2 (get m :a)))&lt;br /&gt;
(get 2 m :a)&lt;br /&gt;
[/cc]&lt;br /&gt;
-&amp;gt;居然只是简单地把2放到了get这个form里面！&lt;/p&gt;

&lt;p&gt;再看看用匿名函数包装后的结果&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(macroexpand-1 '(-&gt; 2 (fn [x] ((get m :a) x))))&lt;br /&gt;
(fn 2 [x] ((get m :a) x))&lt;br /&gt;
[/cc]&lt;br /&gt;
和刚才一样，2被放到了第一个form的第一个参数位置！得到的是一个非法的form。&lt;/p&gt;

&lt;p&gt;那么既然-&amp;gt;只是简单地把第一个参数放到后面form的首个参数的位置，那么这个宏正确的使用方法其实是&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(def m {:a inc})&lt;br /&gt;
(-&gt; 2 ((fn [x] ((get m :a) x))))&lt;br /&gt;
[/cc]&lt;br /&gt;
再加一层括号！&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(macroexpand-1 '(-&gt; 2 ((fn [x] ((get m :a) x)))))&lt;br /&gt;
((fn [x] ((get m :a) x)) 2)&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;可见，-&amp;gt;虽然是个功能强大的宏，但宏终归只是宏，和函数的区别是明显的。在使用的时候，不能完全按照函数的习惯。&lt;/p&gt;

&lt;p&gt;如果你想了解实际的代码，可以参考slacker 0.3.0里的这个interceptor框架：
&lt;a href="https://github.com/sunng87/slacker/blob/master/src/slacker/interceptor.clj" target="_blank"&gt;https://github.com/sunng87/slacker/blob/master/src/slacker/interceptor.clj&lt;/a&gt;
上面提到的难处，多半也都是在开发这个框架时亲身经历的。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>slacker 0.2.0 is out</title><link href="https://sunng.info/blog/slacker-020-is-out.html" rel="alternate"></link><published>2011-12-10T00:00:00+08:00</published><updated>2011-12-10T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-12-10:/blog/slacker-020-is-out.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;slacker
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="https://github.com/sunng87/slacker"&gt;Slacker 0.2.0&lt;/a&gt; has been pushed to clojars today. Connection pooling and json serialization are available in this release.&lt;/p&gt;

&lt;p&gt;&lt;h4&gt;Connection Pool&lt;/h4&gt;
Generally, pooling connection is a good idea in high concurrence application. To make slacker for real world, connection …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;slacker
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="https://github.com/sunng87/slacker"&gt;Slacker 0.2.0&lt;/a&gt; has been pushed to clojars today. Connection pooling and json serialization are available in this release.&lt;/p&gt;

&lt;p&gt;&lt;h4&gt;Connection Pool&lt;/h4&gt;
Generally, pooling connection is a good idea in high concurrence application. To make slacker for real world, connection pool support is a high-prioritized feature in its development. The new connection pool is backended by commons-pool which you might familiar with. To use connection pool, just create slacker client with a new function `slackerc-pool`&lt;/p&gt;

&lt;p&gt;[cc lang="clojure"]&lt;br /&gt;
(def scp (slackerc-pool "localhost" 2104))&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;Then you can use this pool just like a single client.&lt;/p&gt;

&lt;p&gt;Some options are available to configure the pool by your wish:
&lt;ul&gt;
    &lt;li&gt;&lt;em&gt;:max-active&lt;/em&gt;, max connections opened by the pool&lt;/li&gt;
    &lt;li&gt;&lt;em&gt;:exhausted-action&lt;/em&gt; &lt;br /&gt;
            &lt;ul&gt;
               &lt;li&gt;&lt;em&gt;:fail&lt;/em&gt; throw an exception when pool exhausted.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;:block&lt;/em&gt; block current thread and wait until max-wait exceed (throw an exception)&lt;/li&gt;
    &lt;li&gt;&lt;em&gt;:grow&lt;/em&gt; automatically create new connection and add it to pool&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;            
&lt;li&gt;&lt;em&gt;:max-wait&lt;/em&gt; max wait time before throwing an exception&lt;/li&gt;
    &lt;li&gt;&lt;em&gt;:min-idle&lt;/em&gt; minimal number of pool hold idle connections &lt;/li&gt;&lt;/p&gt;

&lt;p&gt;
The options are inherited from GenericObjectPool, you can find detailed information from their &lt;a href="http://commons.apache.org/pool/apidocs/org/apache/commons/pool/impl/GenericObjectPool.html"&gt;javadoc&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;h4&gt;JSON Serialization&lt;/h4&gt;
slacker just added json serialization provided by clj-json. According to my test, clj-json is 1x faster than carbonite in serialization. &lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(def sc (slackerc "localhost" 2104 :content-type :json))&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;However, with json serialization, you may lost some clojure types like keyword and set in type conversion. You should be caution when using json as serialization method. &lt;/p&gt;

&lt;p&gt;In next release, I am planning to use &lt;a href="https://github.com/AlibabaTech/fastjson/" target="_blank"&gt;fastjson&lt;/a&gt; as json lib which provides option to write type name into json so it could be a full featured serialization for clojure. And fastjson is claimed even faster than jackson.&lt;/p&gt;

&lt;p&gt;&lt;h4&gt;Performance&lt;/h4&gt;
slacker gains high performance with its non-blocking server, serialization and direct function call. As tested on a dual 6 core server,  it reaches 10000+ TPS for a single client (50 connections, 50 threads). The server just use 35% CPU so I consider it could have even more TPS if there is two or more client machines.&lt;/p&gt;

&lt;p&gt;So if you are interested in some benchmarks, you can test it with client like &lt;a href="https://gist.github.com/1449860" target="_blank"&gt;this&lt;/a&gt;. All the requests are using synchronous call because I believe it's the most common case you use slacker.&lt;/p&gt;

&lt;p&gt;&lt;h4&gt;Next steps&lt;/h4&gt;
Inspired by discussion in &lt;a href="http://groups.google.com/group/cn-clojure" target="_blank"&gt;cn-clojure&lt;/a&gt; mailing list, I'm going to add HTTP transport for slacker. With HTTP transport, it's easier to debug and evaluate your clojure functions, it also makes slacker available to ClojureScript. &lt;/p&gt;

&lt;p&gt;At lst, thanks Zach Tellman for reviewing my client code. 
&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>从GNOME网站安装exaile-doubanfm-gnome-shell-extension</title><link href="https://sunng.info/blog/cong-gnomewang-zhan-an-zhuang-exaile-doubanfm-gnome-shell-extension.html" rel="alternate"></link><published>2011-12-03T00:00:00+08:00</published><updated>2011-12-03T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-12-03:/blog/cong-gnomewang-zhan-an-zhuang-exaile-doubanfm-gnome-shell-extension.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;project
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;最近GNOME发布了期待已久的extension.gnome.org，这个网站允许你直接通过浏览器安装和管理gnome-shell扩展，有点类似app store的感觉，混乱的~/.local/share/gnome-shell/extensions/终于 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;project
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;最近GNOME发布了期待已久的extension.gnome.org，这个网站允许你直接通过浏览器安装和管理gnome-shell扩展，有点类似app store的感觉，混乱的~/.local/share/gnome-shell/extensions/终于有了一个官方的界面。&lt;/p&gt;

&lt;p&gt;网站开通的第一时间，我提交了exaile-doubanfm-gnome-shell-extension，经过review和修改，这个扩展也得到了进一步的完善，适配了gnome-shell 3.2的风格。&lt;/p&gt;

&lt;p&gt;你可以从这个地址直接安装启用
&lt;a href="https://extensions.gnome.org/extension/24/exaile-doubanfm-control/" target="_blank"&gt;https://extensions.gnome.org/extension/24/exaile-doubanfm-control/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;它会在exaile douban.fm启动后显示一个菜单在gnome-shell上，你可以通过这个菜单进行基本的操作。&lt;/p&gt;

&lt;p&gt;如果喜欢，别忘了在extension.gnome.org上vote一下 ：）&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Slacker 0.1.0 is out</title><link href="https://sunng.info/blog/slacker-010-is-out.html" rel="alternate"></link><published>2011-12-02T00:00:00+08:00</published><updated>2011-12-02T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-12-02:/blog/slacker-010-is-out.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;project
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Glad to roll out the first release of the &lt;strong&gt;slacker&lt;/strong&gt; framework. Slacker is a clojure RPC framework on top of a TCP binary protocol. It provides a set of non-invasive APIs for both server and client. The remote invocation is completely transparent …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;project
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Glad to roll out the first release of the &lt;strong&gt;slacker&lt;/strong&gt; framework. Slacker is a clojure RPC framework on top of a TCP binary protocol. It provides a set of non-invasive APIs for both server and client. The remote invocation is completely transparent to user.&lt;/p&gt;

&lt;p&gt;In addition to APIs introduced in &lt;a href="http://sunng.info/blog/2011/11/clojure-rpc-prototyping-and-early-thoughts/"&gt;last post&lt;/a&gt;, asynchronous approach is supported in client API :&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(defremote remote-func :async true)&lt;br /&gt;
@(remote-func)&lt;br /&gt;
[/cc]&lt;br /&gt;
If you add option `:async` to defremote, then the function facade will return a promise. You have to deref it by yourself. Also you can use the `:callback` option in defremote to specify a callback function.&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(defremote remote-func :callback #(println %))&lt;br /&gt;
(remote-func)&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;This gives you much more flexibility of using remote function. But be careful it will break consistency between local and remote mode. &lt;/p&gt;

&lt;p&gt;To use slacker, add it to your project.clj&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
:dependencies [[info.sunng/slacker "0.1.0"]]&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;You can find examples on the &lt;a href="https://github.com/sunng87/slacker" target="_blank"&gt;github page&lt;/a&gt;. &lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Exaile豆瓣电台插件0.0.11发布</title><link href="https://sunng.info/blog/exailedou-ban-dian-tai-cha-jian-0011fa-bu.html" rel="alternate"></link><published>2011-12-01T00:00:00+08:00</published><updated>2011-12-01T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-12-01:/blog/exailedou-ban-dian-tai-cha-jian-0011fa-bu.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;Douban&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;很高兴时隔半年后我继续发布了Exaile豆瓣电台插件的更新，从第一个版本发布到现在已经有一年半的时间，这期间豆瓣电 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;Douban&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;很高兴时隔半年后我继续发布了Exaile豆瓣电台插件的更新，从第一个版本发布到现在已经有一年半的时间，这期间豆瓣电台插件已经陆续出现在Rhythmbox、Banshee等播放器上。作为第一个视图把豆瓣电台移植到本地的尝试，我感到甚是欣慰：）&lt;/p&gt;

&lt;p&gt;这次的更新修正了长久依赖困绕用户登录问题，现在我们有一个专门的界面来输入验证码。这个功能要感谢&lt;a href="https://github.com/sunng87/exaile-doubanfm-plugin/issues/8"&gt;DigitalPig&lt;/a&gt;用户在github的报告（鞭策作用），此外，我参考了&lt;a href="http://code.google.com/p/banshee-doubanfm/"&gt;豆瓣电台banshee插件&lt;/a&gt;的实现，节省了研究含验证码登录的时间，感谢。总而言之，没有用户的推动，这个项目也不会坚持这么久。
&lt;img src="http://i.imgur.com/cYNrM.png" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;除此之外，插件还有一些支持了新的豆瓣说的推荐，优化了播放列表载入的策略。&lt;/p&gt;

&lt;p&gt;另外值得highlight的是，对应的gnome-shell扩展发布了0.0.2版本，唯一的更新是专辑封面现在会显示在gnome-shell的菜单中。
&lt;img src="http://i.imgur.com/7Q1CP.jpg" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;你可以从github获得最新的插件和gnome-shell扩展：
&lt;ul&gt;
&lt;li&gt;exaile-doubanfm-plugin 0.0.11-captcha: &lt;a href="https://github.com/sunng87/exaile-doubanfm-plugin/downloads"&gt;https://github.com/sunng87/exaile-doubanfm-plugin/downloads&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;exaile-doubanfm-gnome-shell-extension 0.0.2: &lt;a href="https://github.com/sunng87/exaile-doubanfm-gnome-shell-extension/tarball/0.0.2"&gt;https://github.com/sunng87/exaile-doubanfm-gnome-shell-extension/tarball/0.0.2&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;安装豆瓣电台插件请参考&lt;a href="https://github.com/sunng87/exaile-doubanfm-plugin/wiki/Installation"&gt;这里&lt;/a&gt;。gnome-shell扩展直接解压至~/.local/share/gnome-shell/extensions/即可
&lt;/p&gt;

&lt;p&gt;有任何问题都可以在github或这里留言。
&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Clojure RPC, prototyping and early thoughts</title><link href="https://sunng.info/blog/clojure-rpc-prototyping-and-early-thoughts.html" rel="alternate"></link><published>2011-11-27T00:00:00+08:00</published><updated>2011-11-27T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-11-27:/blog/clojure-rpc-prototyping-and-early-thoughts.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;opensource
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Last week, I prototyped an RPC framework, &lt;a href="https://github.com/sunng87/slacker" target="_blank"&gt;slacker&lt;/a&gt;, by clojure and for clojure. &lt;/p&gt;

&lt;p&gt;&lt;h3&gt;What I did ?&lt;/h3&gt;
Suppose you have a sets of clojure functions to expose. Define them under a namespace:&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(ns slapi)&lt;br /&gt;
(defn timestamp []&lt;br /&gt;
  (System/currentTimeMillis))&lt;/p&gt;

&lt;p&gt;;; ...more …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;opensource
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Last week, I prototyped an RPC framework, &lt;a href="https://github.com/sunng87/slacker" target="_blank"&gt;slacker&lt;/a&gt;, by clojure and for clojure. &lt;/p&gt;

&lt;p&gt;&lt;h3&gt;What I did ?&lt;/h3&gt;
Suppose you have a sets of clojure functions to expose. Define them under a namespace:&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(ns slapi)&lt;br /&gt;
(defn timestamp []&lt;br /&gt;
  (System/currentTimeMillis))&lt;/p&gt;

&lt;p&gt;;; ...more functions&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;Expose the namespace with slacker server, on port 2104:&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(use 'slacker.server)&lt;br /&gt;
(start-slacker-server (the-ns 'slapi) 2104)[/cc]&lt;/p&gt;

&lt;p&gt;On the client side, we use the `defremote` macro to create a facade for `timestamp` function. This API will keep the client code consistent with local mode.&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(use 'slacker.client)&lt;br /&gt;
(def sc (slackerc "localhost" 2104))&lt;br /&gt;
(defremote sc timestamp)&lt;br /&gt;
(timestamp)&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;Internally, slacker uses &lt;a href="https://github.com/ztellman/aleph" target="_blank"&gt;aleph&lt;/a&gt; for transport and &lt;a href="https://github.com/revelytix/carbonite" target="_blank"&gt;carbonite&lt;/a&gt; for serialization. I forked carbonite and made it compatible with clojure 1.2 because the aleph mainline is still running on 1.2. &lt;/p&gt;

&lt;p&gt;&lt;h3&gt;Going further&lt;/h3&gt;
&lt;h4&gt;High-Order Functions&lt;/h4&gt;
In clojure, functions are treated as first class value. Within memory, you can pass function as parameter to another function. However, this is not supported by serialization framework. So is it possible to add support for that in future?&lt;/p&gt;

&lt;p&gt;&lt;h4&gt;Lazy sequence as parameter&lt;/h4&gt;
This is another interesting feature in clojure function call. You can pass a lazy-sequence to clojure function. In RPC, it requires parameters to be evaluated on the server side.&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(defn get-first [&amp; args] (first args))&lt;br /&gt;
(apply get-first (range))&lt;br /&gt;
[/cc]&lt;br /&gt;
Example copied from &lt;a href="http://stackoverflow.com/q/8205446/371141" target="_blank"&gt;StackOverflow&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;h4&gt;Coordinated states between several remote servers&lt;/h4&gt;
With RPC, we can update states on several servers. So do we need something like distributed dosync:&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(defremote a1 update-a1-state)&lt;br /&gt;
(defremote a2 update-a2-state)&lt;br /&gt;
(dosync-distributed&lt;br /&gt;
  (update-a1-state some-value)&lt;br /&gt;
  (update-a2-state some-value))&lt;br /&gt;
[/cc]&lt;br /&gt;
I'm not sure if this is a valid scenario in real world but I think it's an interesting topic.(distributed STM?)&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;Conclusion&lt;/h3&gt;
RPC is the first step to distributed clojure world. I will keep you updated with my prototype. 
&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Spark in common lisp</title><link href="https://sunng.info/blog/spark-in-common-lisp.html" rel="alternate"></link><published>2011-11-19T00:00:00+08:00</published><updated>2011-11-19T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-11-19:/blog/spark-in-common-lisp.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;lisp
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;还是关于spark的，一石激起千层浪，每个人心中都有一个spark。其实spark脚本刚出来的时候问题很多，但是就是因为产生了共鸣，众人拾柴pull request多 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;lisp
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;还是关于spark的，一石激起千层浪，每个人心中都有一个spark。其实spark脚本刚出来的时候问题很多，但是就是因为产生了共鸣，众人拾柴pull request多。像redis的作者antirez也忍不住自己用c写了一个&lt;a href="https://github.com/antirez/aspark" target="_blank"&gt;aspark&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;说完了别人的，那么来看看我的：clspark，common-lisp的spark。原本是打算用clojure写，但是想到jvm的启动速度，把这个机会留给我的第一个common lisp程序吧。&lt;/p&gt;

&lt;p&gt;&lt;img src="http://i.imgur.com/oD7m4.png" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;其实很简单。
&lt;script src="https://gist.github.com/1375401.js"&gt; &lt;/script&gt;&lt;/p&gt;

&lt;p&gt;common lisp的核心库里没有split，所以这里从cl-cookbook拷贝了一个split的实现，坦白说我还看不太懂这个loop的写法。loop是common lisp中最尴尬的form，因为他的形式太多。这点在clojure中是不存在的。比较一下就能发现，在语言层面，clojure是相对现代得多的lisp方言。
&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>'My response to Spark: Visualize your mercurial commit history from commandline'</title><link href="https://sunng.info/blog/my-response-to-spark-visualize-your-mercurial-commit-history-from-commandline.html" rel="alternate"></link><published>2011-11-16T00:00:00+08:00</published><updated>2011-11-16T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-11-16:/blog/my-response-to-spark-visualize-your-mercurial-commit-history-from-commandline.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;github&lt;/li&gt;
&lt;li&gt;hg&lt;/li&gt;
&lt;li&gt;opensource&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;visualization
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;标题长了些。还是用母语吧。&lt;/p&gt;

&lt;p&gt;昨天HackerNews上一个&lt;a href="http://news.ycombinator.com/item?id=3237478" target="_blank"&gt;小脚本&lt;/a&gt;轰动了，所谓山不在高程序不在小。为了响应这个小脚 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;github&lt;/li&gt;
&lt;li&gt;hg&lt;/li&gt;
&lt;li&gt;opensource&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;visualization
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;标题长了些。还是用母语吧。&lt;/p&gt;

&lt;p&gt;昨天HackerNews上一个&lt;a href="http://news.ycombinator.com/item?id=3237478" target="_blank"&gt;小脚本&lt;/a&gt;轰动了，所谓山不在高程序不在小。为了响应这个小脚本，我写了一个更加简单的Mercurial(hg)扩展，帮助你输出hg仓库的提交历史。这个feature和github的直方图有点像，在我看来github的直方图是他们最重要的feature之一，它鞭策着你不断地commit。&lt;/p&gt;

&lt;p&gt;&lt;img src="http://i.imgur.com/ZcEfH.png" alt="hg summary" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;script src="https://gist.github.com/1366606.js"&gt; &lt;/script&gt;&lt;/p&gt;

&lt;p&gt;安装:将这个脚本放在任意一处，在你的hgrc中添加：&lt;br /&gt;
[cc lang="text"]&lt;br /&gt;
[extensions]&lt;br /&gt;
summary = /path/to/your/script&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;由于很简单，就不按照mercurial的规范发布了。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>再见2011赛季</title><link href="https://sunng.info/blog/zai-jian-2011sai-ji.html" rel="alternate"></link><published>2011-11-04T00:00:00+08:00</published><updated>2011-11-04T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-11-04:/blog/zai-jian-2011sai-ji.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;D60&lt;/li&gt;
&lt;li&gt;football&lt;/li&gt;
&lt;li&gt;photo
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;虽然还有两个月才能结束这个2011年，但是舜天队的赛季已经结束了。经过大半年的鏖战，大起大落之 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;D60&lt;/li&gt;
&lt;li&gt;football&lt;/li&gt;
&lt;li&gt;photo
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;虽然还有两个月才能结束这个2011年，但是舜天队的赛季已经结束了。经过大半年的鏖战，大起大落之后，最终球队取得了史无前例的第四名，这让我们看了十多年次级联赛的球迷一时间几乎无法接受。今年赶在联赛开始前我换工作回到南京，去主场的机会也比以前上学或是在外地时多了很多，最终今年一共看了6个主场，是看球16年以来最多的一年。当然不出意外明年还要刷新这个纪录！&lt;/p&gt;

&lt;p&gt;周三下午请假去球场目送了一下球队。距离明年开赛还有5个月，有一些球员可能转会，有的退役，所以每到最后一轮都是伤离别的时候。只是往年，这个时候都是夕阳西下，三三两两的观众看完无关紧要的比赛鸟兽散去；而今年的最后一轮，我们还为了理论上的一个亚冠机会争取。&lt;/p&gt;

&lt;p&gt;2009年加入球队，2011年学习回来后，杨晨是舜天队的领队兼助理教练。在比赛开始前，作为助理教练要带领队员作热身活动。
&lt;a href="http://www.flickr.com/photos/40741608@N08/6305940310/" title="resized-DSC_0017 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6059/6305940310_4497b80191.jpg" width="500" height="335" alt="resized-DSC_0017" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;队员训练。
&lt;a href="http://www.flickr.com/photos/40741608@N08/6305939050/" title="resized-DSC_0011 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6102/6305939050_01fa72bf36.jpg" width="500" height="335" alt="resized-DSC_0011" /&gt;&lt;/a&gt;
前面两位是去年从降级的长沙金德转投而来的刘建业和任航，刘建业现在也是队里唯一的国脚。两人转会过来以后一直是球队的主力。刘建业负责中场的拦截防守；任航是边后卫，可以胜任左右两边，攻防俱佳。&lt;br /&gt;
跟在后面的时候杜文辉和李炽。杜文辉上赛季结束后被国安队清洗，自由转会来到舜天本来希望通过更多的出场机会能获得国家队的席位，结果不料第四轮就重伤缺席了后面的比赛。复出后球队已经更换了教练，不过杜文辉还是舜天队里技术顶尖的球员，经历了一番周折到了赛季末他还是占据了主力的位置。后面李炽是队龄最大的队员之一，2005年从广东来到江苏，伴随球队度过了最艰难的05、06、07年。升级之后，李炽一直在主力和替补之间徘徊，不过最后总能通过努力获得首发的机会。今年李炽合同到期，据说一条微博还让很多球迷以为下赛季他会回到广东，为此，有了这么一条横幅：
&lt;a href="http://www.flickr.com/photos/40741608@N08/6305965870/" title="resized-DSC_0057 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6112/6305965870_54acc94e94.jpg" width="500" height="335" alt="resized-DSC_0057" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6305417351/" title="resized-DSC_0035 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6119/6305417351_604711e9d3.jpg" width="500" height="335" alt="resized-DSC_0035" /&gt;&lt;/a&gt;
中间的9号陆博飞是球队的队长，在球场上的角色有点类似皮尔洛。今年陆队已经32岁了，不过据他昨天在广播里说他的状态还可以再踢两年。20号孙可是队里唯一一个能打上主力的由舜天梯队培养出来的队员，今年孙可也经历了最重要的一个赛季，基本上完成了从替补到主力的转换，在比赛中的作用也越来越重要。&lt;/p&gt;

&lt;p&gt;罗马尼亚人达纳拉赫，这位仁兄上半赛季经历了漫长的进球荒，如果不是在场上特别积极，恐怕早就收拾东西走人了。好在从对陕西开和之后，状态越来越好，最终打进13球，在射手榜上排到了第三。
&lt;a href="http://www.flickr.com/photos/40741608@N08/6305413781/" title="resized-DSC_0002 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6221/6305413781_52b509b2ff.jpg" width="335" height="500" alt="resized-DSC_0002" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;达纳拉赫的诡异爆发得益于身边空降了一名优秀的同伴，塞尔维亚红星队的耶夫迪奇夏季转会来到球队，是球队历史上身价最高的球员（140万美元）。身价在总体上当然还是和能力成正比的，耶夫迪奇半个赛季就打进11个球，是球队在场上最重要的球员。
&lt;a href="http://www.flickr.com/photos/40741608@N08/6305414451/" title="resized-DSC_0005 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6048/6305414451_091646b612.jpg" width="335" height="500" alt="resized-DSC_0005" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;后防线上，两个外援塔基耶夫（左）和埃雷尔森（右）。前者是乌兹别克国脚，他的哥哥和弟弟都曾经在天津泰达踢球。后者是球队的老面孔，埃雷尔森从09年就加入球队，一直是后防线上的定海神针，今年也是联赛中的最佳后卫。
&lt;a href="http://www.flickr.com/photos/40741608@N08/6305445957/" title="resized-DSC_0097 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6056/6305445957_78408799d4.jpg" width="335" height="500" alt="resized-DSC_0097" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;守门员邓小飞也是09年加入球队，前半赛季一直是替补，后来成为主力之后发挥一直很出色，也是球队后半赛季辉煌的功臣。
&lt;a href="http://www.flickr.com/photos/40741608@N08/6305939562/" title="resized-DSC_0013 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6120/6305939562_29166f6ca4.jpg" width="500" height="335" alt="resized-DSC_0013" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6305943262/" title="resized-DSC_0050 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6056/6305943262_28b08f9c4e.jpg" width="500" height="335" alt="resized-DSC_0050" /&gt;&lt;/a&gt;
替补席上的秘鲁外援略显落寞，因为在球队没有位置明年他将是唯一一名离队的外援，所以也有球迷专门给他感谢
&lt;a href="http://www.flickr.com/photos/40741608@N08/6305440759/" title="resized-DSC_0054 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6045/6305440759_dbc9cdef90.jpg" width="500" height="335" alt="resized-DSC_0054" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6305441949/" title="resized-DSC_0066 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6220/6305441949_1b27db51ee.jpg" width="500" height="335" alt="resized-DSC_0066" /&gt;&lt;/a&gt;
&lt;a href="http://www.flickr.com/photos/40741608@N08/6305967488/" title="resized-DSC_0071 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6038/6305967488_acaf5953e0.jpg" width="500" height="335" alt="resized-DSC_0071" /&gt;&lt;/a&gt;
&lt;a href="http://www.flickr.com/photos/40741608@N08/6305966894/" title="resized-DSC_0067 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6054/6305966894_b9c252503e.jpg" width="500" height="335" alt="resized-DSC_0067" /&gt;&lt;/a&gt;
&lt;a href="http://www.flickr.com/photos/40741608@N08/6305444653/" title="resized-DSC_0086 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6223/6305444653_dcae78f290.jpg" width="500" height="335" alt="resized-DSC_0086" /&gt;&lt;/a&gt;
&lt;a href="http://www.flickr.com/photos/40741608@N08/6305446603/" title="resized-DSC_0099 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6220/6305446603_d947877f41.jpg" width="335" height="500" alt="resized-DSC_0099" /&gt;&lt;/a&gt;
&lt;a href="http://www.flickr.com/photos/40741608@N08/6305444149/" title="resized-DSC_0079 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6099/6305444149_d4f47a20f9.jpg" width="500" height="335" alt="resized-DSC_0079" /&gt;&lt;/a&gt;
比赛最终没有什么悬念地结束，虽然球队没有获得亚冠的名额，不过今年的成绩已经是18年历史上最佳排名。
&lt;a href="http://www.flickr.com/photos/40741608@N08/6305971668/" title="resized-DSC_0105 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6049/6305971668_046a4e5e6b.jpg" width="500" height="335" alt="resized-DSC_0105" /&gt;&lt;/a&gt;
球迷聚在场外放完烟火，约定明年再战！
&lt;a href="http://www.flickr.com/photos/40741608@N08/6305447755/" title="resized-DSC_0127 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6091/6305447755_90a5a74ebb.jpg" width="500" height="335" alt="resized-DSC_0127" /&gt;&lt;/a&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>lein-control 0.2.1 is out</title><link href="https://sunng.info/blog/lein-control-021-is-out.html" rel="alternate"></link><published>2011-11-01T00:00:00+08:00</published><updated>2011-11-01T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-11-01:/blog/lein-control-021-is-out.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;project
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;I have updated the leiningen plugin for &lt;a href="https://github.com/killme2008/clojure-control" target="_blank"&gt;clojure-control&lt;/a&gt; 0.2.1. There is no new feature for it as a plugin. But some new features from upstream are supported and should be highlighted:&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;SSH command DSL&lt;/h3&gt;
This feature is committed by me …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;project
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;I have updated the leiningen plugin for &lt;a href="https://github.com/killme2008/clojure-control" target="_blank"&gt;clojure-control&lt;/a&gt; 0.2.1. There is no new feature for it as a plugin. But some new features from upstream are supported and should be highlighted:&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;SSH command DSL&lt;/h3&gt;
This feature is committed by me and first announced in clojure-control 0.2.1. With this DSL, you can write your SSH command with some predefined macros which is more readable and enjoyable.&lt;/p&gt;

&lt;p&gt;For example, an ssh task can be defined as:&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
   (deftask :restart-server "restart your jetty server"&lt;br /&gt;
          []&lt;br /&gt;
          (ssh (cd "/opt/jetty/bin"&lt;br /&gt;
                 (run "./jetty.sh restart"))))&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;which equals to :&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
   (deftask :restart-server "restart your jetty server"&lt;br /&gt;
          []&lt;br /&gt;
          (ssh "cd /opt/jetty/bin; ./jetty/sh restart"))&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;For full document of this DSL, please check the &lt;a href="https://github.com/killme2008/clojure-control/blob/master/README.md" target="_blank"&gt;README&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;Improved syntax for defcluster&lt;/h3&gt;
ssh/scp/rsync options in defcluster could be written as a vector:&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(defcluster :mycluster&lt;br /&gt;
  :ssh-options "-p 44"&lt;br /&gt;
  :scp-options "-v"&lt;br /&gt;
  :rsync-options ["-arz" "--delete"]&lt;br /&gt;
  :clients [&lt;br /&gt;
    { :host "c.domain.com" :user "clogin" :ssh-options "-v -p 43"}&lt;br /&gt;
  ]&lt;br /&gt;
  :user "login"&lt;br /&gt;
  :addresses ["a.domain.com" "b.domain.com"])[/cc]&lt;br /&gt;
(code quote from official announcement)&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;Parallel support&lt;/h3&gt;
Tasks could be executed in parallel if you add option &lt;code&gt;:parallel true&lt;/code&gt; on defcluster.&lt;/p&gt;

&lt;p&gt;Finally, to use this plugin, add dev-dependency to your project.clj:&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
        :dev-dependencies [[lein-control "0.2.1"]]&lt;br /&gt;
[/cc]&lt;br /&gt;
More detailed instructions could be found &lt;a href="https://github.com/sunng87/lein-control/blob/master/README" target="_blank"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;BTW, this is the last release of lein-control as a standalone artifact. I will be working on merging lein-control into clojure-control. Soon you will have a few flexible ways to use clojure-control.
&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Clojalk SCM Visualization</title><link href="https://sunng.info/blog/clojalk-scm-visualization.html" rel="alternate"></link><published>2011-10-30T00:00:00+08:00</published><updated>2011-10-30T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-10-30:/blog/clojalk-scm-visualization.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;clojalk&lt;/li&gt;
&lt;li&gt;visualization
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;最近有一个小工具非常流行（如果我没有火星的话），&lt;a href="http://code.google.com/p/gource/" target="_blank"&gt;gource&lt;/a&gt;，可以将你的代码历史可视化出来。&lt;a href="http://www.youtube.com/watch?v=ELSsGXGHfZM" target="_blank"&gt;这里&lt;/a&gt;有reddit的代码历史，fogus也把写the …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;clojalk&lt;/li&gt;
&lt;li&gt;visualization
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;最近有一个小工具非常流行（如果我没有火星的话），&lt;a href="http://code.google.com/p/gource/" target="_blank"&gt;gource&lt;/a&gt;，可以将你的代码历史可视化出来。&lt;a href="http://www.youtube.com/watch?v=ELSsGXGHfZM" target="_blank"&gt;这里&lt;/a&gt;有reddit的代码历史，fogus也把写the joy of clojure做成了&lt;a href="http://blog.fogus.me/2011/10/27/joy-of-clojure-the-movie/" target="_blank"&gt;这样&lt;/a&gt;的视频。&lt;/p&gt;

&lt;p&gt;凑个热闹，来看看我的&lt;a href="https://github.com/sunng87/clojalk"&gt;clojalk&lt;/a&gt;项目可视化
&lt;embed src="http://player.youku.com/player.php/sid/XMzE3NzU4NjE2/v.swf" quality="high" width="480" height="400" align="middle" allowscriptaccess="sameDomain" allowfullscreen="true" type="application/x-shockwave-flash"&gt;&lt;/embed&gt;
And the video link on &lt;a href="http://vimeo.com/31328528" target="_blank"&gt;vimeo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;除了一名contributor，只有一个commiter。又是一幕Forever alone。
&lt;img src="http://i.imgur.com/FXcNw.png" alt="forever alone" /&gt;&lt;/p&gt;

&lt;p&gt;PS：&lt;br /&gt;
gource上的wiki里，ffmpeg如果报错File for preset 'slow' not found的话，去掉ffmpeg的-vpre slow就OK了。
&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Visualize Reddit upvotes by subreddit</title><link href="https://sunng.info/blog/visualize-reddit-upvotes-by-subreddit.html" rel="alternate"></link><published>2011-10-27T00:00:00+08:00</published><updated>2011-10-27T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-10-27:/blog/visualize-reddit-upvotes-by-subreddit.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;reddit&lt;/li&gt;
&lt;li&gt;visualization
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;这是我reddit上所有的upvote在各个subreddit上的分布情况，这个情况还是可以说明我是个普通青年。
&lt;img src="http://i.imgur.com/jfXyK.png" alt="Reddit upvotes visualization" width="500px" /&gt;&lt;/p&gt;

&lt;p&gt;排在前几位的分别是
&lt;ul&gt;
    &lt;li&gt;Programming&lt;/li&gt;
    &lt;li&gt;Linux&lt;/li&gt;
    &lt;li&gt;Python&lt;/li&gt;
    &lt;li&gt;f7u12&lt;/li&gt;
    &lt;li&gt;Clojure&lt;/li&gt;
    &lt;li&gt;Ubuntu&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;如果你还不 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;reddit&lt;/li&gt;
&lt;li&gt;visualization
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;这是我reddit上所有的upvote在各个subreddit上的分布情况，这个情况还是可以说明我是个普通青年。
&lt;img src="http://i.imgur.com/jfXyK.png" alt="Reddit upvotes visualization" width="500px" /&gt;&lt;/p&gt;

&lt;p&gt;排在前几位的分别是
&lt;ul&gt;
    &lt;li&gt;Programming&lt;/li&gt;
    &lt;li&gt;Linux&lt;/li&gt;
    &lt;li&gt;Python&lt;/li&gt;
    &lt;li&gt;f7u12&lt;/li&gt;
    &lt;li&gt;Clojure&lt;/li&gt;
    &lt;li&gt;Ubuntu&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;如果你还不了解什么是Reddit：Reddit是一个巨大的社会书签+论坛网站，他的频道叫做subreddit，每个频道有一个相应的主题，涵盖了从IT技术到新闻到生活的各个角落。&lt;/p&gt;

&lt;p&gt;这些数据是通过下面的Clojure程序获得（使用reddit.clj库），并通过jfreechart展现出来的。
&lt;script src="https://gist.github.com/1315572.js"&gt; &lt;/script&gt;
&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>普通青年、二逼青年与文艺青年的Java代码缩进</title><link href="https://sunng.info/blog/pu-tong-qing-nian-er-bi-qing-nian-yu-wen-yi-qing-nian-de-javadai-ma-suo-jin.html" rel="alternate"></link><published>2011-10-26T00:00:00+08:00</published><updated>2011-10-26T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-10-26:/blog/pu-tong-qing-nian-er-bi-qing-nian-yu-wen-yi-qing-nian-de-javadai-ma-suo-jin.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;programming
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;普通青年&lt;/p&gt;

&lt;p&gt;[cc lang="java"]&lt;br /&gt;
while(true) {&lt;br /&gt;
    if (something) {&lt;br /&gt;
        System.out.println(something);&lt;br /&gt;
        break;&lt;br /&gt;
    }&lt;br /&gt;
}
[/cc]&lt;br /&gt;
特点： tab与空格混用，无其他特点。&lt;br /&gt;
常见于：各类代码仓库。&lt;/p&gt;

&lt;p&gt;二逼青年&lt;/p&gt;

&lt;p&gt;[cc lang="java …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;programming
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;普通青年&lt;/p&gt;

&lt;p&gt;[cc lang="java"]&lt;br /&gt;
while(true) {&lt;br /&gt;
    if (something) {&lt;br /&gt;
        System.out.println(something);&lt;br /&gt;
        break;&lt;br /&gt;
    }&lt;br /&gt;
}
[/cc]&lt;br /&gt;
特点： tab与空格混用，无其他特点。&lt;br /&gt;
常见于：各类代码仓库。&lt;/p&gt;

&lt;p&gt;二逼青年&lt;/p&gt;

&lt;p&gt;[cc lang="java"]&lt;br /&gt;
while(true) &lt;br /&gt;
{&lt;/p&gt;

&lt;p&gt;    if (something) &lt;br /&gt;
    {&lt;br /&gt;
        System.out.println(something);&lt;br /&gt;
        break;&lt;br /&gt;
    }&lt;/p&gt;

&lt;p&gt;}&lt;br /&gt;
[/cc]&lt;br /&gt;
特点：总担心代码不够长&lt;br /&gt;
见于：各类劣质技术书籍&lt;/p&gt;

&lt;p&gt;文艺青年&lt;br /&gt;
[cc lang="java"]&lt;br /&gt;
while(true) &lt;br /&gt;
  {&lt;br /&gt;
  if (something) &lt;br /&gt;
    {&lt;br /&gt;
      System.out.println(something);&lt;br /&gt;
      break;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
[/cc]&lt;br /&gt;
特点：普通Java青年永远不会理解的缩进，lisp程序员会心一笑&lt;br /&gt;
见于：Clojure源码
&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Substract a vector from a matrix in Octave</title><link href="https://sunng.info/blog/substract-a-vector-from-a-matrix-in-octave.html" rel="alternate"></link><published>2011-10-21T00:00:00+08:00</published><updated>2011-10-21T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-10-21:/blog/substract-a-vector-from-a-matrix-in-octave.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;math&lt;/li&gt;
&lt;li&gt;octave
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;假设你有一个矩阵：&lt;/p&gt;

&lt;p&gt;[cc lang="text"]&lt;br /&gt;
A = [1,2;3,4;5,6]&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;以及一个矢量：&lt;/p&gt;

&lt;p&gt;[cc lang="text"]&lt;br /&gt;
B = [2,5]&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;你希望对A的每一行元素对元素地减B，例如第一 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;math&lt;/li&gt;
&lt;li&gt;octave
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;假设你有一个矩阵：&lt;/p&gt;

&lt;p&gt;[cc lang="text"]&lt;br /&gt;
A = [1,2;3,4;5,6]&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;以及一个矢量：&lt;/p&gt;

&lt;p&gt;[cc lang="text"]&lt;br /&gt;
B = [2,5]&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;你希望对A的每一行元素对元素地减B，例如第一行&lt;br /&gt;
[cc lang="text"]&lt;br /&gt;
[1-2,2-5]&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;但你不希望用循环完成这个工作，那么你可能想到利用B创建一个和A一样维度的矩阵然后进行.-：&lt;br /&gt;
[cc lang="text"]&lt;br /&gt;
C = [2,5;2,5;2,5]&lt;br /&gt;
A .- C&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;在Octave中可以利用repmat这个函数获得C：&lt;br /&gt;
[cc lang="text"]&lt;br /&gt;
C = repmat(B, length(A), 1)&lt;br /&gt;
[/cc]&lt;br /&gt;
但是对于大矩阵来说这是一种对内存的浪费。&lt;/p&gt;

&lt;p&gt;更好的方法是利用bsxfun：&lt;br /&gt;
[cc lang="text"]&lt;br /&gt;
bsxfun(@minus, A, B)&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;bsxfun在octave的文档中似乎鲜有提及。
&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>丽水留影</title><link href="https://sunng.info/blog/li-shui-liu-ying.html" rel="alternate"></link><published>2011-10-20T00:00:00+08:00</published><updated>2011-10-20T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-10-20:/blog/li-shui-liu-ying.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;D60&lt;/li&gt;
&lt;li&gt;Zhejiang
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;这周去丽水参加大学同学的婚礼，浙江南部又是另一番不同的景色。而且这一带没有过度的旅游开 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;D60&lt;/li&gt;
&lt;li&gt;Zhejiang
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;这周去丽水参加大学同学的婚礼，浙江南部又是另一番不同的景色。而且这一带没有过度的旅游开发，也没有工业污染，青山绿水完完全全的原生态。要是度假真是不错的选择，何况这里还有认识的同学。&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6263288831/" title="resized-DSC_0126 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6156/6263288831_8574bd5c3d.jpg" width="335" height="500" alt="resized-DSC_0126" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6263288835/" title="resized-DSC_0129 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6098/6263288835_c5711ec5f1.jpg" width="335" height="500" alt="resized-DSC_0129" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6263288841/" title="resized-DSC_0245 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6179/6263288841_3bf7e7f3e5.jpg" width="335" height="500" alt="resized-DSC_0245" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Grails的核心依赖必须保证项目中版本一致！</title><link href="https://sunng.info/blog/grailsde-he-xin-yi-lai-bi-xu-bao-zheng-xiang-mu-zhong-ban-ben-yi-zhi.html" rel="alternate"></link><published>2011-10-15T00:00:00+08:00</published><updated>2011-10-15T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-10-15:/blog/grailsde-he-xin-yi-lai-bi-xu-bao-zheng-xiang-mu-zhong-ban-ben-yi-zhi.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;Grails&lt;/li&gt;
&lt;li&gt;maven
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;好久没写语录式大标题了，实在是今天玩Grails受害很深很深。&lt;/p&gt;

&lt;p&gt;Grails因为本身是通过ivy来管理依赖的，虽然后续的版本和maven的集成不断加深，但是本身还是通过ivy来解析pom.xml。也就是 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;Grails&lt;/li&gt;
&lt;li&gt;maven
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;好久没写语录式大标题了，实在是今天玩Grails受害很深很深。&lt;/p&gt;

&lt;p&gt;Grails因为本身是通过ivy来管理依赖的，虽然后续的版本和maven的集成不断加深，但是本身还是通过ivy来解析pom.xml。也就是说最后的活还是ivy干的。如果你的项目比较大，选择了通过maven来管理项目，而你的依赖中又牵扯了很多其他的依赖，那么你危险了。&lt;/p&gt;

&lt;p&gt;&lt;blockquote&gt;:: UNRESOLVED DEPENDENCIES ::&lt;/blockquote&gt;&lt;/p&gt;

&lt;p&gt;:: log4j#log4j;1.2.16: configuration not found in log4j#log4j;1.2.16: 'master'. It was required from ...;1.7.0 runtime&lt;/p&gt;

&lt;p&gt;对maven用户这是一条多么发指的报错！（比如&lt;a href="http://forum.springsource.org/showthread.php?104033-Can-not-load-a-log4J-library" target="_blank"&gt;这个&lt;/a&gt;）&lt;/p&gt;

&lt;p&gt;它的原因其实是你的dependency中有!=1.2.16版本你的log4j，比如1.2.12，即使你在pom里将它添加到了exclusion中也没有用，必须要保持版本的一致！至于你用什么办法让他们保持一致，还是干脆放弃用maven管理你的grails项目：It's up to you.&lt;/p&gt;

&lt;p&gt;同样的问题还出现在其他核心依赖上，例如commons-pool。
&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>You say goodbye and I say hello.</title><link href="https://sunng.info/blog/you-say-goodbye-and-i-say-hello.html" rel="alternate"></link><published>2011-10-13T00:00:00+08:00</published><updated>2011-10-13T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-10-13:/blog/you-say-goodbye-and-i-say-hello.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags: []
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;iframe width="560" height="315" src="http://www.youtube.com/embed/HBZ8ulc5NTg" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;/p&gt;

&lt;p&gt;You say yes, I say no&lt;br /&gt;
You say stop and I say go, go, go&lt;br /&gt;
Oh, no&lt;br /&gt;
You say goodbye and I say &lt;strong&gt;hello world&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="http://www.lysator.liu.se/c/dmr/dmr.gif" alt="Dennis Ritchie" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="http://www.globalnerdy.com/wordpress/wp-content/uploads/2008/09/the_c_programming_language.jpg" alt="the C programming language" /&gt;&lt;/p&gt;

&lt;p&gt;[cc lang="C"]&lt;br /&gt;
#include&lt;stdio.h /&gt;&lt;/p&gt;

&lt;p&gt;int main(){&lt;br /&gt;
    printf("Dennis Ritchie, %d-%d\n", 1941, 2011);&lt;br /&gt;
    return 0;&lt;br /&gt;
}
[/cc]&lt;/p&gt;

&lt;p&gt;RIP …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags: []
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;iframe width="560" height="315" src="http://www.youtube.com/embed/HBZ8ulc5NTg" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;/p&gt;

&lt;p&gt;You say yes, I say no&lt;br /&gt;
You say stop and I say go, go, go&lt;br /&gt;
Oh, no&lt;br /&gt;
You say goodbye and I say &lt;strong&gt;hello world&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="http://www.lysator.liu.se/c/dmr/dmr.gif" alt="Dennis Ritchie" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="http://www.globalnerdy.com/wordpress/wp-content/uploads/2008/09/the_c_programming_language.jpg" alt="the C programming language" /&gt;&lt;/p&gt;

&lt;p&gt;[cc lang="C"]&lt;br /&gt;
#include&lt;stdio.h /&gt;&lt;/p&gt;

&lt;p&gt;int main(){&lt;br /&gt;
    printf("Dennis Ritchie, %d-%d\n", 1941, 2011);&lt;br /&gt;
    return 0;&lt;br /&gt;
}
[/cc]&lt;/p&gt;

&lt;p&gt;RIP.
&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Voting with your reddit account on RageViewer</title><link href="https://sunng.info/blog/voting-with-your-reddit-account-on-rageviewer.html" rel="alternate"></link><published>2011-10-12T00:00:00+08:00</published><updated>2011-10-12T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-10-12:/blog/voting-with-your-reddit-account-on-rageviewer.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;reddit
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;I just implemented a new feature on &lt;a href="http://rageviewer.cloudfoundry.com/index.html" title="rage viewer" target="_blank"&gt;RageViewer&lt;/a&gt; which has been requested for a long time. Now you can login with your Reddit account and up-vote/down-vote rages without turning to Reddit website.&lt;/p&gt;

&lt;p&gt;Again, I made a rage comic for this …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;reddit
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;I just implemented a new feature on &lt;a href="http://rageviewer.cloudfoundry.com/index.html" title="rage viewer" target="_blank"&gt;RageViewer&lt;/a&gt; which has been requested for a long time. Now you can login with your Reddit account and up-vote/down-vote rages without turning to Reddit website.&lt;/p&gt;

&lt;p&gt;Again, I made a rage comic for this feature.
&lt;img src="http://i.imgur.com/Zg9TU.jpg" alt="voting rage" /&gt;&lt;/p&gt;

&lt;p&gt;To be able to vote items with your Reddit account, a Reddit session is maintained in the memory of RageViewer. Rage Viewer is a pure open source software so it will never store or log your password in any form. You can verify the source code on &lt;a href="https://github.com/sunng87/rageviewer"&gt;github&lt;/a&gt; if you have concern about the privacy. (Currently, Reddit doesn't offer any authorization like OAuth so we can only access your reddit account with your password. Sorry for that.)&lt;/p&gt;

&lt;p&gt;For those still have no idea about RageViewer. RageViewer is for browsing rage comics smoothly. It loads most recent comics from &lt;a href="http://www.reddit.com/r/fffffffuuuuuuuuuuuu/" target="_blank"&gt;fffffffuuuuuuuuuuuu&lt;/a&gt; and some other subreddits into the database. It gives you a modern and clean UI for your best experience.&lt;/p&gt;

&lt;p&gt;Also important, RageViewer is a full-stack clojure web application that written in the JVM functional language: Clojure(server-side) and ClojureScript(broser-side).&lt;/p&gt;

&lt;p&gt;BTW, a new version of Reddit.clj (0.3.0) is available in clojars repository. (the new RageViewer is also based on it). In 0.3.0, reddit.clj is compatible with Clojure 1.3.0. And a "enhance" function is removed that you are no long required to "enhance" a reddit client before you write data to Reddit. Now you have read-write permission once you create the client with reddit.clj.core/login .&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>The distribution of intelligence</title><link href="https://sunng.info/blog/the-distribution-of-intelligence.html" rel="alternate"></link><published>2011-10-10T00:00:00+08:00</published><updated>2011-10-10T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-10-10:/blog/the-distribution-of-intelligence.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;heatcanvas&lt;/li&gt;
&lt;li&gt;visualization
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;This could be a big title for the content.&lt;/p&gt;

&lt;p&gt;I just work out a heat map based on &lt;a href="http://maps.google.com/maps/ms?ie=UTF&amp;msa=0&amp;msid=203716810039202316490.0004aeb1b1a01b1d3b9af" target="_blank"&gt;the data&lt;/a&gt; of users who enrolled online courses offered by Stanford.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://imgur.com/rWUkH"&gt;&lt;img src="http://i.imgur.com/rWUkH.png" width="500" alt="" title="Hosted by imgur.com" /&gt;&lt;/a&gt;
(Click to enlarge)&lt;/p&gt;

&lt;p&gt;I think you already have your ideas about this …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;heatcanvas&lt;/li&gt;
&lt;li&gt;visualization
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;This could be a big title for the content.&lt;/p&gt;

&lt;p&gt;I just work out a heat map based on &lt;a href="http://maps.google.com/maps/ms?ie=UTF&amp;msa=0&amp;msid=203716810039202316490.0004aeb1b1a01b1d3b9af" target="_blank"&gt;the data&lt;/a&gt; of users who enrolled online courses offered by Stanford.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://imgur.com/rWUkH"&gt;&lt;img src="http://i.imgur.com/rWUkH.png" width="500" alt="" title="Hosted by imgur.com" /&gt;&lt;/a&gt;
(Click to enlarge)&lt;/p&gt;

&lt;p&gt;I think you already have your ideas about this map. New England, California and Central Europe has higher density than any other area of the world. Also you can find some light in South America, East Coast Australia, India and China. I think this could be an overview of how intelligence distributed around the globe.
&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>山水城林夕阳晚景</title><link href="https://sunng.info/blog/shan-shui-cheng-lin-xi-yang-wan-jing.html" rel="alternate"></link><published>2011-10-06T00:00:00+08:00</published><updated>2011-10-06T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-10-06:/blog/shan-shui-cheng-lin-xi-yang-wan-jing.html</id><content type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;D60&lt;/li&gt;
&lt;li&gt;Nanjing&lt;/li&gt;
&lt;li&gt;photo
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6214194078/" title="resized-DSC_0004.JPG by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6032/6214194078_164b4fd0d6.jpg" width="335" height="500" alt="resized-DSC_0004.JPG" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6214193770/" title="resized-DSC_0003.JPG by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6115/6214193770_2f83cac6dc.jpg" width="500" height="335" alt="resized-DSC_0003.JPG" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6213680807/" title="resized-DSC_0005.JPG by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6235/6213680807_6290a29d96.jpg" width="335" height="500" alt="resized-DSC_0005.JPG" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6214194920/" title="resized-DSC_0007.JPG by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6165/6214194920_d84ba70a3d.jpg" width="335" height="500" alt="resized-DSC_0007.JPG" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6214195222/" title="resized-DSC_0010.JPG by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6112/6214195222_cf60cbdd63.jpg" width="335" height="500" alt="resized-DSC_0010.JPG" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6214195508/" title="resized-DSC_0021.JPG by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6112/6214195508_f073ea481a.jpg" width="335" height="500" alt="resized-DSC_0021.JPG" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6214195812/" title="resized-DSC_0030.JPG by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6154/6214195812_82cabd03ae.jpg" width="500" height="335" alt="resized-DSC_0030.JPG" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6213682553/" title="resized-DSC_0032.JPG by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6169/6213682553_9f5999f6a1.jpg" width="500" height="335" alt="resized-DSC_0032.JPG" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6213682811/" title="resized-DSC_0034.JPG by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6108/6213682811_4b7ac16f72.jpg" width="500" height="335" alt="resized-DSC_0034.JPG" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6214196954/" title="resized-DSC_0035.JPG by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6095/6214196954_45e5bb7384.jpg" width="500" height="335" alt="resized-DSC_0035.JPG" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6214197296/" title="resized-DSC_0038.JPG by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6151/6214197296_376fe786ca.jpg" width="335" height="500" alt="resized-DSC_0038.JPG" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6213684031/" title="resized-DSC_0044.JPG by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6151/6213684031_397a26197f.jpg" width="335" height="500" alt="resized-DSC_0044.JPG" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6213684287/" title="resized-DSC_0049.JPG by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6101/6213684287_fab9ed2a1a.jpg" width="500" height="335" alt="resized-DSC_0049.JPG" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;南京玄武湖台城。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>GPS数据采集与OpenStreetMap编辑</title><link href="https://sunng.info/blog/gpsshu-ju-cai-ji-yu-openstreetmapbian-ji.html" rel="alternate"></link><published>2011-10-05T00:00:00+08:00</published><updated>2011-10-05T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-10-05:/blog/gpsshu-ju-cai-ji-yu-openstreetmapbian-ji.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;GIS&lt;/li&gt;
&lt;li&gt;gps&lt;/li&gt;
&lt;li&gt;OpenStreetMap
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;简单介绍一下通过GPS采集道路数据并上传到OpenStreetMap的流程。&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;采集&lt;/h3&gt;
首先你需要一个GPS数据记录器，或者叫做GPS Logger。这类产品在淘宝上可以找到很多，台 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;GIS&lt;/li&gt;
&lt;li&gt;gps&lt;/li&gt;
&lt;li&gt;OpenStreetMap
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;简单介绍一下通过GPS采集道路数据并上传到OpenStreetMap的流程。&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;采集&lt;/h3&gt;
首先你需要一个GPS数据记录器，或者叫做GPS Logger。这类产品在淘宝上可以找到很多，台湾的长天(HOLUX)是相对价廉物美的品牌。我的设备是HOLUX m1000c，下文以此为例。&lt;/p&gt;

&lt;p&gt;m1000c的使用非常简单。开机之后，指示GPS的黄灯点亮，设备开始搜索GPS卫星。根据你所出的位置、遮挡情况以及天气情况，搜星的时间略有不同。当GPS黄灯开始闪烁时Logger即开始记录数据，默认情况下m1000c每隔5秒记录一次数据。每次重启后，Logger都会新开一条记录。&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;导入&lt;/h3&gt;
对Windows用户，将Logger通过USB线与电脑连接后，可以利用随机附带的软件将数据导入为GPX格式。对Linux用户，可以利用&lt;a href="http://www.gpsbabel.org/" target="_blank"&gt;gpsbabel&lt;/a&gt;导入数据：
&lt;em&gt;gpsbabel -t -i m241 -f /dev/ttyACM0 -o gpx -F myfile.gpx&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;其中：
&lt;ul&gt;
    &lt;li&gt;-t 表示数据为track类型&lt;/li&gt;
    &lt;li&gt;-i m241 输入格式为HOLUX m241，m1000c使用的是这种格式&lt;/li&gt;
    &lt;li&gt;-f /dev/ttyACM0 输入设备是/dev/ttyACM0&lt;/li&gt;
    &lt;li&gt;-o gpx 输出格式是gpx&lt;/li&gt;
    &lt;li&gt;-F 输出文件myfile.gpx&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;在一些发行版上，需要root权限访问/dev/ttyACM0，所以不要忘记将输出的文件chown给普通用户。&lt;/p&gt;

&lt;p&gt;导入完成后可以利用下面的命令清空Logger
&lt;em&gt;gpsbabel -i m241,erase_only=1 -f /dev/ttyACM0&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;在Windows和Linux上都可以使用&lt;a href="http://sourceforge.net/apps/mediawiki/viking/index.php?title=Main_Page" target="_blank"&gt;Viking&lt;/a&gt;查看导入的数据：
&lt;ul&gt;
    &lt;li&gt;启动viking&lt;/li&gt;
    &lt;li&gt;添加一个OSM地图图层： Layers-&gt;New Map Layer&lt;/li&gt;
    &lt;li&gt;导入GPX数据： File-&gt;Append File...&lt;/li&gt;
    &lt;li&gt;下载OSM地图，右键点击左侧的Map图层，Download Missing Onscreen Maps&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6213079184/" title="Screenshot at 2011-10-05 09:45:20 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6103/6213079184_f653ca2ca6.jpg" width="500" height="387" alt="Screenshot at 2011-10-05 09:45:20" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;编辑&lt;/h3&gt;
Windows和Linux用户都可以通过&lt;a href="http://merkaartor.be/" target="_blank"&gt;Merkaartor&lt;/a&gt;编辑OSM数据。&lt;/p&gt;

&lt;p&gt;启动Merkaartor，导入GPS数据： File-&gt;Import。&lt;br /&gt;
选择左侧的GPS图层，对需要导入的数据，右键点击Extract Drawing Layer将GPS转换为可编辑数据。
&lt;a href="http://www.flickr.com/photos/40741608@N08/6213247680/" title="Screenshot at 2011-10-05 10:57:17 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6167/6213247680_046e360c63.jpg" width="500" height="303" alt="Screenshot at 2011-10-05 10:57:17" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;根据实际情况编辑道路数据的属性。对GPS转换的数据，选择菜单Feature-&gt;Force Upload，将其加入dirty set准备上传。点击Upload即可将数据上传到OpenStreetMap。&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>游浙江古镇</title><link href="https://sunng.info/blog/you-zhe-jiang-gu-zhen.html" rel="alternate"></link><published>2011-10-04T00:00:00+08:00</published><updated>2011-10-04T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-10-04:/blog/you-zhe-jiang-gu-zhen.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;D60&lt;/li&gt;
&lt;li&gt;photo&lt;/li&gt;
&lt;li&gt;Travel&lt;/li&gt;
&lt;li&gt;Zhejiang
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;这居然是我第一次踏上浙江的土地。&lt;/p&gt;

&lt;p&gt;浙江西塘，是个人很多车也很多的地方，油炸臭豆腐烤肉串 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;D60&lt;/li&gt;
&lt;li&gt;photo&lt;/li&gt;
&lt;li&gt;Travel&lt;/li&gt;
&lt;li&gt;Zhejiang
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;这居然是我第一次踏上浙江的土地。&lt;/p&gt;

&lt;p&gt;浙江西塘，是个人很多车也很多的地方，油炸臭豆腐烤肉串更多的地方。
&lt;a href="http://www.flickr.com/photos/40741608@N08/6211015162/" title="resized-DSC_0005 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6074/6211015162_020eab7ba0.jpg" width="500" height="335" alt="resized-DSC_0005" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6210481747/" title="resized-DSC_0009 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6091/6210481747_7c06f7b887.jpg" width="500" height="335" alt="resized-DSC_0009" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6210482047/" title="resized-DSC_0016 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6049/6210482047_fc42431fa8.jpg" width="500" height="335" alt="resized-DSC_0016" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6210482559/" title="resized-DSC_0023 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6207/6210482559_2be2f0ddaa.jpg" width="500" height="335" alt="resized-DSC_0023" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6210994852/" title="resized-DSC_0026 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6217/6210994852_d425d1267a.jpg" width="335" height="500" alt="resized-DSC_0026" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6210995314/" title="resized-DSC_0029 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6041/6210995314_c4b00f5501.jpg" width="335" height="500" alt="resized-DSC_0029" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6210995528/" title="resized-DSC_0038 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6236/6210995528_f667a61b76.jpg" width="500" height="335" alt="resized-DSC_0038" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6210604611/" title="DSC_0047 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6034/6210604611_ba3142ca63.jpg" width="335" height="500" alt="DSC_0047" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6210995836/" title="resized-DSC_0039 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6184/6210995836_5ae720e69f.jpg" width="335" height="500" alt="resized-DSC_0039" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6210996338/" title="resized-DSC_0063 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6106/6210996338_62639caf34.jpg" width="500" height="335" alt="resized-DSC_0063" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6210996674/" title="resized-DSC_0066 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6155/6210996674_75389558d8.jpg" width="335" height="500" alt="resized-DSC_0066" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6210485223/" title="resized-DSC_0071 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6107/6210485223_230baf13b5.jpg" width="500" height="335" alt="resized-DSC_0071" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;浙江乌镇是一个人更多车更多，但是没有油炸臭豆腐烤肉串而只有红烧羊肉的地方。
&lt;a href="http://www.flickr.com/photos/40741608@N08/6211081358/" title="DSC_0148 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6237/6211081358_a4e67f9962.jpg" width="500" height="335" alt="DSC_0148" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6211017762/" title="resized-DSC_0075 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6163/6211017762_d21f239276.jpg" width="335" height="500" alt="resized-DSC_0075" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6211018872/" title="resized-DSC_0097 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6064/6211018872_50a273be0f.jpg" width="335" height="500" alt="resized-DSC_0097" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6210503729/" title="resized-DSC_0088 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6229/6210503729_b5f2976c81.jpg" width="335" height="500" alt="resized-DSC_0088" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6211018500/" title="resized-DSC_0102 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6213/6211018500_e86613b631.jpg" width="336" height="500" alt="resized-DSC_0102" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6210504015/" title="resized-DSC_0119 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6120/6210504015_33d54a3c1e.jpg" width="335" height="500" alt="resized-DSC_0119" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6211016652/" title="resized-DSC_0128 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6037/6211016652_2009babdce.jpg" width="500" height="335" alt="resized-DSC_0128" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6210505059/" title="resized-DSC_0135 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6085/6210505059_8a2841f5c2.jpg" width="500" height="335" alt="resized-DSC_0135" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6210507171/" title="resized-DSC_0140 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6085/6210507171_5e3715e65e.jpg" width="335" height="500" alt="resized-DSC_0140" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6211017390/" title="resized-DSC_0137 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6103/6211017390_2b5222d4b9.jpg" width="335" height="500" alt="resized-DSC_0137" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6211019570/" title="resized-DSC_0141 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6034/6211019570_982301a25c.jpg" width="335" height="500" alt="resized-DSC_0141" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;回程的路上很多游客反映，乌镇没有西塘好玩。我想还是油炸臭豆腐符合大家的口味，羊肉不是所有人都吃得惯的。&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Upgraded to GNOME 3.2</title><link href="https://sunng.info/blog/upgraded-to-gnome-32.html" rel="alternate"></link><published>2011-10-02T00:00:00+08:00</published><updated>2011-10-02T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-10-02:/blog/upgraded-to-gnome-32.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;archilinux&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;linux
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;ArchLinux最大的魅力就在于Rolling Release，所有的悲喜剧你都比别人早一步见证。&lt;/p&gt;

&lt;p&gt;升级到GNOME 3.2后，我的gnome-settings-daemon不能正常工作，导致gtk+的主题都无效。如果没有 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;archilinux&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;linux
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;ArchLinux最大的魅力就在于Rolling Release，所有的悲喜剧你都比别人早一步见证。&lt;/p&gt;

&lt;p&gt;升级到GNOME 3.2后，我的gnome-settings-daemon不能正常工作，导致gtk+的主题都无效。如果没有经验你可能不太容易注意到它的真实原因。最后找到了同病相怜的人，这个问题被报告在&lt;a href="https://bugzilla.gnome.org/show_bug.cgi?id=660664" target="_blank"&gt;这里&lt;/a&gt;。恰好是在我发现这个问题几个小时之前。在gnome解决这个问题之前，有一个简单的workaround：
&lt;em&gt;sudo mv /usr/lib/gnome-settings-daemon-3.0/libcolor.so /usr/lib/gnome-settings-daemon-3.0/libcolor.so~&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;库加载失败后gnome-settings-daemon会自动禁用这个插件，避免出现Segmentation fault。以上的操作，at your own risk。&lt;/p&gt;

&lt;p&gt;此外，gnome-shell升级到3.2以后有些api的变化，我更新了exaile豆瓣电台的gnome-shell插件，你可以顺手git pull一下。
&lt;a href="http://www.flickr.com/photos/40741608@N08/6203348334/" title="Screenshot at 2011-10-02 17:48:51 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6162/6203348334_c71d757170.jpg" width="500" height="157" alt="Screenshot at 2011-10-02 17:48:51" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Moving to ArchLinux</title><link href="https://sunng.info/blog/moving-to-archlinux.html" rel="alternate"></link><published>2011-10-01T00:00:00+08:00</published><updated>2011-10-01T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-10-01:/blog/moving-to-archlinux.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;ArchLinux&lt;/li&gt;
&lt;li&gt;linux
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6198252182/" title="DSC_0004 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6022/6198252182_656282b489.jpg" width="335" height="500" alt="DSC_0004" /&gt;&lt;/a&gt;
这是用新入的Nikon 55-300mm的长焦镜头300端排的远处的塔吊，它与本文没有直接关系。如果实在要计算间接关系，它是我等待fedora无尽的启动时间里消磨时光的手段之一。&lt;/p&gt;

&lt;p&gt;这周开始fedora彻底崩溃了，现 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;ArchLinux&lt;/li&gt;
&lt;li&gt;linux
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6198252182/" title="DSC_0004 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6022/6198252182_656282b489.jpg" width="335" height="500" alt="DSC_0004" /&gt;&lt;/a&gt;
这是用新入的Nikon 55-300mm的长焦镜头300端排的远处的塔吊，它与本文没有直接关系。如果实在要计算间接关系，它是我等待fedora无尽的启动时间里消磨时光的手段之一。&lt;/p&gt;

&lt;p&gt;这周开始fedora彻底崩溃了，现在在默认的run-level下NetworkManager根本无法启动，次次3分钟超时。还由于不明的原因，以越来越大的概率，gdm会僵死在启动启动之前。如果切换到terminal，输入完用户名居然连password都不会prompt出来。有时甚至在runlevel3里都无法启动。本想忍下去等到下个月f16发布，但是今天晚上已经彻底无法进入系统了，算了，其实也就是挥挥刀的事情。&lt;/p&gt;

&lt;p&gt;Ubuntu的回头路是走不得的，索性切换到了Arch，从此享受rolling release，不再在每年的4月10月里蠢蠢欲动惊慌失措。重装的经验和上次一样：看准分区，/home留着/干掉；在创建用户的时候看准原先用户的uid，直接用这个id创建新用户，这样$HOME自动就归属新用户了。Arch仓库里的东西甚至要比fedora还多，没有那么多洁癖，甚至Skype和IDEA的社区版都直接进了仓库。我现在年纪大了，能从仓库安装我是不会自己再去下载了。&lt;/p&gt;

&lt;p&gt;其实Arch也是老朋友了，直不过以前一直把它憋在VirtualBox里，现在它从VBox里爬出来了，Ubuntu和Windows这些当年的host们被踹进去乖乖作guest了，翻身农奴这就起来把歌唱。&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>I cloned Beanstalkd with Clojure</title><link href="https://sunng.info/blog/i-cloned-beanstalkd-with-clojure.html" rel="alternate"></link><published>2011-09-24T00:00:00+08:00</published><updated>2011-09-24T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-09-24:/blog/i-cloned-beanstalkd-with-clojure.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;beanstalkd&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;opensource&lt;/li&gt;
&lt;li&gt;project
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;I just cloned &lt;a href="http://kr.github.com/beanstalkd/"&gt;Beanstalkd&lt;/a&gt; with clojure and you can find this project on &lt;a href="https://github.com/sunng87/clojalk"&gt;github&lt;/a&gt;. It's a light weight task queue that "producers" could put tasks in and "workers" are blocked to reserve them and process them. It will help you …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;beanstalkd&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;opensource&lt;/li&gt;
&lt;li&gt;project
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;I just cloned &lt;a href="http://kr.github.com/beanstalkd/"&gt;Beanstalkd&lt;/a&gt; with clojure and you can find this project on &lt;a href="https://github.com/sunng87/clojalk"&gt;github&lt;/a&gt;. It's a light weight task queue that "producers" could put tasks in and "workers" are blocked to reserve them and process them. It will help you to split expensive operations (sending email, etc) to background.&lt;/p&gt;

&lt;p&gt;The features:
&lt;ul&gt;
    &lt;li&gt;Almost compatible with Beanstalkd protocol&lt;/li&gt;
    &lt;li&gt;Full support on Beanstalkd verbs: put/reserve/release/bury/kick&lt;/li&gt;
    &lt;li&gt;Persistence with binary write-ahead logs&lt;/li&gt;
    &lt;li&gt;Simple JMX monitoring&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;The networking layer is based on @&lt;a href="https://github.com/ztellman"&gt;ztellman&lt;/a&gt;'s libraries, aleph, lamina and gloss. And I also received great help from him on implementing the protocol with gloss. &lt;/p&gt;

&lt;p&gt;This is not my first clojure project but the largest one. I add &lt;a href="https://github.com/fogus/marginalia"&gt;marginalia&lt;/a&gt; in the dev-dependencies, you can generate well formatted code and docs with `lein marg`.&lt;/p&gt;

&lt;p&gt;By the way, this project is not well tested. I'm still working on it to find potential issues and to fix them. Do not use it on any production environment before you have carefully gone through the code base. I just made this announcement here to let people know this project if they are interested in this topic. &lt;/p&gt;

&lt;p&gt;And always, Any fork/contribution is welcomed.
&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Beginning Emacs for Clojure</title><link href="https://sunng.info/blog/beginning-emacs-for-clojure.html" rel="alternate"></link><published>2011-09-19T00:00:00+08:00</published><updated>2011-09-19T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-09-19:/blog/beginning-emacs-for-clojure.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;emacs
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;没错我开始用Emacs了！对于一个使用lisp方言的开发的人来说，Emacs无疑是正统，是professional的象征。&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6162700542/" title="Screenshot--home-sun-projects-clojalk-src-clojalk-wal.clj by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6172/6162700542_50be6d256f.jpg" width="500" height="303" alt="Screenshot--home-sun-projects-clojalk-src-clojalk-wal.clj" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;其实用Emacs，远不需要太多的配置，就可以创建一个高效率的 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;emacs
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;没错我开始用Emacs了！对于一个使用lisp方言的开发的人来说，Emacs无疑是正统，是professional的象征。&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6162700542/" title="Screenshot--home-sun-projects-clojalk-src-clojalk-wal.clj by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6172/6162700542_50be6d256f.jpg" width="500" height="303" alt="Screenshot--home-sun-projects-clojalk-src-clojalk-wal.clj" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;其实用Emacs，远不需要太多的配置，就可以创建一个高效率的开发环境。我用了两天不到时间，当然还包括过去三年甚至更长时间里多次试图学习Emacs并最终半途而废残存的精力。&lt;/p&gt;

&lt;p&gt;一步一步来，fedora系统的用户，比如&lt;a href="http://superuser.com/questions/242587/install-gnu-emacs-gui-in-fedora"&gt;这位&lt;/a&gt;，还有我，都会奇怪，命名有emacs，可是为什么没有GUI呢。因为系统自带的只是一个简单的版本，用yum安装一下emacs这个包才算是真正完整。&lt;/p&gt;

&lt;p&gt;接下来要跳关了，大家注意。&lt;a href="https://github.com/technomancy/emacs-starter-kit"&gt;Emacs-starter-kit&lt;/a&gt;是一个帮助初学者预先配置emacs的一套默认配置文件，有了这个配置文件，也算是爬上了巨人的肩膀，省去了很多探路的时间。安装方式，把这个git仓库的master分支打包下载到~/.emacs.d/里即可（展开到这个目录）。启动emacs后，会自动安装。（作者名字很熟悉，对了，就是leiningen的作者）&lt;/p&gt;

&lt;p&gt;由于package.el已经配置好了，接下来就可以安装一些必要的mode，两天的时间初探，我安装了：
&lt;ul&gt;
    &lt;li&gt;clojure-mode&lt;/li&gt;
    &lt;li&gt;clojurescript-mode&lt;/li&gt;
    &lt;li&gt;auto-complete&lt;/li&gt;
    &lt;li&gt;color-theme-tangotango&lt;/li&gt;
    &lt;li&gt;slime&lt;/li&gt;
    &lt;li&gt;slime-repl&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;安装方式：&lt;br /&gt;
M-x package-install&lt;br /&gt;
输入包名即可。&lt;/p&gt;

&lt;p&gt;clojure-mode和clojurescript-mode是编辑clojure的mode，不多说。&lt;/p&gt;

&lt;p&gt;&lt;h4&gt;配色&lt;/h4&gt;
color-theme我本来是习惯用solarized，不过尝试了几次color-theme-solarized这个包应该是有一些问题。退而选择了tangotango。要默认选择这个颜色，在init.el里加：&lt;br /&gt;
[cc lang="lisp"]&lt;br /&gt;
(require 'color-theme-tangotango)&lt;br /&gt;
(color-theme-tangotango)&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;&lt;h4&gt;代码提示&lt;/h4&gt;
auto-complete是代码提示和自动补全插件，安装完成后要简单配置一下才能使用：&lt;br /&gt;
[cc lang="lisp"]&lt;br /&gt;
(require 'auto-complete-config)&lt;br /&gt;
(add-to-list 'ac-dictionary-directories "~/.emacs.d/elpa/auto-complete-1.4.20110207/dict/")&lt;br /&gt;
(ac-config-default)&lt;br /&gt;
[/cc]&lt;br /&gt;
当然网上有更多的高级配置，可以搜一下。&lt;/p&gt;

&lt;p&gt;&lt;h4&gt;REPL&lt;/h4&gt;
clojure的IDE必须要有repl的支持，在emacs中，repl通过slime这个mode来实现。此外还要额外安装swank：
&lt;em&gt;lein plugin install swank-clojure 1.3.2&lt;/em&gt;
在项目目录通过&lt;em&gt;lein swank&lt;/em&gt;启动。在emacs中：
&lt;em&gt;M-x slime-connect&lt;/em&gt;
选择swank启动的host和port即可。这是一个支持tab提示的强大REPL，并且可以引用lein项目中的代码。&lt;/p&gt;

&lt;p&gt;Edit/20110920&lt;br /&gt;
其实只需要在clojure-mode中通过M-x clojure-jack-in 即可启动swank和slime。&lt;/p&gt;

&lt;p&gt;&lt;h4&gt;ParEdit&lt;/h4&gt;
ParEdit是编辑括号的利器，Emacs-Starter-Kit已经把这个包配置好了。ParEdit不仅会自动打印括号，最强大的是还能阻止用户误删或误输入括号！有了它基本上括号的问题就轻松多了。&lt;br /&gt;
（当然，有时候需要强制删掉括号&lt;em&gt;C-u DEL&lt;/em&gt;，也可能要强制输入括号&lt;em&gt;C-q (&lt;/em&gt;或&lt;em&gt;C-q )&lt;/em&gt;）&lt;/p&gt;

&lt;p&gt;&lt;h4&gt;TagList&lt;/h4&gt;
很遗憾ctags不支持clojure，而且emacs似乎也没有一个比较好的TagList。我看到有人提到imenu在一定程度上倒是可以做到类似的功能：
&lt;em&gt;M-x imenu&lt;tab&gt;&lt;tab /&gt;&lt;/tab&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;总得来说，捅破了窗户纸，用emacs编辑clojure还是最好的选择！
&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>流水帐</title><link href="https://sunng.info/blog/liu-shui-zhang.html" rel="alternate"></link><published>2011-09-17T00:00:00+08:00</published><updated>2011-09-17T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-09-17:/blog/liu-shui-zhang.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;最近没有什么特别值得一提的事情，或者说都还在进行中还没有到值得一提的时候。&lt;/p&gt;

&lt;p&gt;中秋节休息之后 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;最近没有什么特别值得一提的事情，或者说都还在进行中还没有到值得一提的时候。&lt;/p&gt;

&lt;p&gt;中秋节休息之后的第一天清早去驾校考交规理论，这个事没什么可说的，除了在4sq上又拓展了一点疆土，后来我又后悔没把GPS带着错过了一些还没有数据的道路。倒是那天去和回分别坐错了车，想十年前本人对城市交通网络了然于胸，除了视力不好把48路看成46路就没有坐错过车走错过路。结果现在公交线路的数量翻了一番，城市的交通网络也更复杂了。人脑则不具备scalability，活地图的时代一去不复返了。&lt;/p&gt;

&lt;p&gt;第二件值得一说的是我在VirtualBox里成功安装了ArchLinux和GNOME Shell桌面。我工作的机器是有点古老的Dell OptiPlex 760，显卡还是ATI的Radeon HD 3420。为了开发的方便，我们装的都是CentOS 5，没错是5！！这周上班后升到了8G内存我就开始琢磨开个虚拟机体会一下现代生活。多余的不说了，几个注意事项：
&lt;ul&gt;
&lt;li&gt;Host要装ATI的fglrx驱动，xorg-x11-drv-ati直接也能用，但是性能非常差&lt;/li&gt;
&lt;li&gt;VirtualBox要在4.0.4以上，我看网上说的，具体能个版本也不知道，但是用最新的就可以&lt;/li&gt;
&lt;li&gt;3D加速要开，在虚拟机的设置里&lt;/li&gt;
&lt;li&gt;最最重要的一点！不要从虚拟机的菜单里安装Guest Addon，那个对Arch不管用。要用pacman安装仓库里，包名我忘了，请自行-Ss&lt;/li&gt;
&lt;li&gt;别忘了把rc.vboxadd加入rc.conf的Deamons里&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;这周最诡异的问题是在配Netty的Pipeline时，我用了：&lt;br /&gt;
[cc lang="java"]&lt;br /&gt;
Channels.pipeline(new HttpMessageDecoder(), new HttpMessageEncoder(), myhandler);&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;结果稍微上一点压力居然出现了很多decoder异常，什么包不全、头不全、空指针什么的。不明真相最后改用例子里的：&lt;br /&gt;
[cc lang="java"]&lt;br /&gt;
ChannelPipeline cp = Channels.pipeline();&lt;br /&gt;
cp.addLast("decoder", new HttpMessageDecoder());&lt;br /&gt;
cp.addLast("encoder", new HttpMessageEncoder());&lt;br /&gt;
cp.addLast("handler", myhandler);&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;就成功了。也许是顺序的原因也许是名字的原因，可如果这个名字真的具有意义的话为什么没有常量可用呢？不去追究具体原因了，记住就好。&lt;/p&gt;

&lt;p&gt;此外遇到的bug还包括clojure中transient数据结构的问题，我有一个transient的map，但是persistent之后发现只有前9（依稀好像是）次assoc!的内容保存了，后面却都丢失了。1.2里的transient都被标记了alpha，不过印象1.3的changes里好像也没提transient。&lt;/p&gt;

&lt;p&gt;另一件与clojure有关的诡异事件是一个test case的failure report：
&lt;blockquote&gt;FAIL in (test-read-job) (wal.clj:61) &lt;br /&gt;
expected: (= (job k) (rjob k)) &lt;br /&gt;
actual: (not (= "tomcat" "tomcat"))&lt;/blockquote&gt;
不过最后好在是冤有头债有主，睁着眼有时也要说下滑。在从stream中读字节流多读了一个byte，后来用这一组byte去new String的时候，就变成了一个不可见字符。结果在命令行里的输出就是这么骇人听闻，还是在IDEA里跑这个测试才发现错误的那个字符串后面跟了一个方块。至于为什么多读了一个byte，copy/paste害死人啊。&lt;/p&gt;

&lt;p&gt;以上这些牢骚是关于clojure的，但是这篇就不加clojure的tag了，否则被planet clojure收录去我又给中文世界丢脸了。&lt;/p&gt;

&lt;p&gt;周五晚上我又更新了sunng.info的首页，以后也许我会定期的放一些得意的照片（或者照片上有得意的东西）上去做背景，以免被人看到后发现太单调乏味。&lt;/p&gt;

&lt;p&gt;最后还要感谢一下twitter网友Kymair这周将Rage Viewer&lt;a href="http://rageviewer.herokuapp.com/index.html"&gt;部署到了Heroku&lt;/a&gt;上。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>RageViewer updated2</title><link href="https://sunng.info/blog/rageviewer-updated2.html" rel="alternate"></link><published>2011-09-07T00:00:00+08:00</published><updated>2011-09-07T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-09-07:/blog/rageviewer-updated2.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;project
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;I just added some new features to &lt;a href="http://rageviewer.cloudfoundry.com/index.html" target="_blank"&gt;RageViewer&lt;/a&gt;, a rage comic viewer written in Clojure and ClojureScript. Issue on a recent update of CloudFoundry has been fixed. So I can update this application as normal.&lt;/p&gt;

&lt;p&gt;Since there are a lot of over-18 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;project
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;I just added some new features to &lt;a href="http://rageviewer.cloudfoundry.com/index.html" target="_blank"&gt;RageViewer&lt;/a&gt;, a rage comic viewer written in Clojure and ClojureScript. Issue on a recent update of CloudFoundry has been fixed. So I can update this application as normal.&lt;/p&gt;

&lt;p&gt;Since there are a lot of over-18 content on rage comics, RageViewer now prompts you to confirm if it is suitable to display NSFW (Not Suit For Work, marked by author) content. If rejected, a placeholder comic (made by me) will be displayed to you.
&lt;img alt="" src="http://rageviewer.cloudfoundry.com/nsfw-placeholder.jpg" title="nsfw placeholder" class="alignnone" width="500" height="375" /&gt;&lt;/p&gt;

&lt;p&gt;Also, a lot of rage comic related subreddits are now supported by RageViewer. You can find an indicator on the top bar for current channel. Click it and you can switch to a different channel. &lt;/p&gt;

&lt;p&gt;Bugs are fixed when there is a ? on the end of image url. "Show most recent hot comics" button also works now.&lt;/p&gt;

&lt;p&gt;Let me know if you have any ideas or questions about this site.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Squealer (a test framework for Pig script) is using jip</title><link href="https://sunng.info/blog/squealer-a-test-framework-for-pig-script-is-using-jip.html" rel="alternate"></link><published>2011-09-06T00:00:00+08:00</published><updated>2011-09-06T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-09-06:/blog/squealer-a-test-framework-for-pig-script-is-using-jip.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;jip&lt;/li&gt;
&lt;li&gt;jython&lt;/li&gt;
&lt;li&gt;opensource
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="https://bitbucket.org/markroddy/squealer/overview"&gt;Squealer&lt;/a&gt; is a framework written in Jython to test your Apache Pig scripts, by &lt;a href="http://dibon.homelinux.org/"&gt;Mark Roddy&lt;/a&gt;. It's now using &lt;a href="http://pypi.python.org/pypi/jip"&gt;jip&lt;/a&gt; to resolve Java dependencies. On huge dependencies of Hadoop, jip could be great helpful to setup Squealer.&lt;/p&gt;

&lt;p&gt;To get started, it …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;jip&lt;/li&gt;
&lt;li&gt;jython&lt;/li&gt;
&lt;li&gt;opensource
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="https://bitbucket.org/markroddy/squealer/overview"&gt;Squealer&lt;/a&gt; is a framework written in Jython to test your Apache Pig scripts, by &lt;a href="http://dibon.homelinux.org/"&gt;Mark Roddy&lt;/a&gt;. It's now using &lt;a href="http://pypi.python.org/pypi/jip"&gt;jip&lt;/a&gt; to resolve Java dependencies. On huge dependencies of Hadoop, jip could be great helpful to setup Squealer.&lt;/p&gt;

&lt;p&gt;To get started, it is recommended to create a standalone Jython environment for Squealer:
&lt;em&gt;virtualenv -p /usr/local/bin/jython --no-site-packages squealer-env&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Activate the environment
&lt;em&gt;cd squealer-env&lt;/em&gt;
&lt;em&gt;. bin/activate&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Install jip with pip
&lt;em&gt;pip install jip&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Download squealer from bitbucket project page, extract it to somewhere. Install it with jip:
&lt;em&gt;jython setup.py install&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Dependencies will be downloaded from Maven Central. You just wait for it to finish.&lt;/p&gt;

&lt;p&gt;Start a Jython interpreter with '&lt;em&gt;jython-all&lt;/em&gt;' and now you can import squealer.
&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>吉尔157</title><link href="https://sunng.info/blog/ji-er-157.html" rel="alternate"></link><published>2011-09-04T00:00:00+08:00</published><updated>2011-09-04T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-09-04:/blog/ji-er-157.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;D60&lt;/li&gt;
&lt;li&gt;model
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;这是1CM的ZIL157卡车模型，虽说是五十年代的苏联车，可是咱们看着也眼熟，因为它就是解放卡车(CA30)的原型。这是我的第 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;D60&lt;/li&gt;
&lt;li&gt;model
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;这是1CM的ZIL157卡车模型，虽说是五十年代的苏联车，可是咱们看着也眼熟，因为它就是解放卡车(CA30)的原型。这是我的第二个模型。万事开头难，可是做到第二个了怎么还是很难，嗯，说明这头还没有开完。难就难在，打开包装发现少了两根车轴！这车的版件是散装的，也弄不清是丢了还是本来就缺。无奈，最后用的是回形针的铁丝代替，所以后面后轮有粗糙了。但是总体来说，有了上一辆车的经验，加上配置的新的工具笔刀，这次还是挺顺利的。&lt;/p&gt;

&lt;p&gt;这是ZIL-157的实物图
&lt;img src="http://i.imgur.com/C06OG.jpg" title="Hosted by imgur.com" width="500" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6111338393/" title="DSC_0001 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6070/6111338393_1a552b378f.jpg" alt="DSC_0001" height="335" width="500" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;157的备胎放在底盘和货架之间
&lt;a href="http://www.flickr.com/photos/40741608@N08/6111884676/" title="DSC_0002 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6085/6111884676_633ae87396.jpg" alt="DSC_0002" height="335" width="500" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6111341519/" title="DSC_0004 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6071/6111341519_d8a8f322a4.jpg" alt="DSC_0004" height="335" width="500" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;和ATZ油罐车的对照
&lt;a href="http://www.flickr.com/photos/40741608@N08/6111889674/" title="DSC_0008 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6182/6111889674_0ed6812fba.jpg" alt="DSC_0008" height="335" width="500" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="zemanta-pixie"&gt;&lt;img class="zemanta-pixie-img" alt="" src="http://img.zemanta.com/pixy.gif?x-id=6fc1b35e-afcd-8591-9cbd-894bf8be2230" /&gt;&lt;/div&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>盛大的中秋礼物</title><link href="https://sunng.info/blog/sheng-da-de-zhong-qiu-li-wu.html" rel="alternate"></link><published>2011-09-03T00:00:00+08:00</published><updated>2011-09-03T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-09-03:/blog/sheng-da-de-zhong-qiu-li-wu.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;snda
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;真没想到，离开盛大快一年了，人事部门还记着给我寄东西。过年收到大礼包算是惊喜，这次收到月饼就 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;snda
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;真没想到，离开盛大快一年了，人事部门还记着给我寄东西。过年收到大礼包算是惊喜，这次收到月饼就是有点感动了。难得啊，居然还有人惦记着咱，而且因为离开上海当时登记的电话也失效了，负责的人事还辗转了一番才找到我。当然这个不是针对我，所有的离职员工貌似都收到了盛大寄来的中秋礼物。能做到这点的公司，全世界不多吧。&lt;/p&gt;

&lt;p&gt;拆开邮包是一如既往的红得喜人的logo
&lt;a href="http://www.flickr.com/photos/40741608@N08/6108619640/" title="DSC_0001 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6080/6108619640_b3dd2fa262.jpg" alt="DSC_0001" height="335" width="500" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6108071971/" title="DSC_0002 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6187/6108071971_172b77714c.jpg" alt="DSC_0002" height="500" width="335" /&gt;&lt;/a&gt;
（插一句，终于有盛大的环保袋了，以前用的都是08年找工作时候顺的百度腾讯支付宝的）&lt;/p&gt;

&lt;p&gt;月饼包装盒
&lt;a href="http://www.flickr.com/photos/40741608@N08/6108073551/" title="DSC_0003 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6210/6108073551_eb1f2db129.jpg" alt="DSC_0003" height="335" width="500" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;月饼和茶叶
&lt;a href="http://www.flickr.com/photos/40741608@N08/6108075239/" title="DSC_0004 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6195/6108075239_3d13e10e03.jpg" alt="DSC_0004" height="335" width="500" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;我琢磨了半天，请教了原来的同事才知道这是个U盘。
&lt;a href="http://www.flickr.com/photos/40741608@N08/6108076517/" title="DSC_0005 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6079/6108076517_f972c472d2.jpg" alt="DSC_0005" height="335" width="500" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;这是盛大在线（SDO）的企业文化宣传册
&lt;a href="http://www.flickr.com/photos/40741608@N08/6108626622/" title="DSC_0006 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6090/6108626622_e17a01d480.jpg" alt="DSC_0006" height="500" width="335" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;我这里就不为前前东家摇旗呐喊了，说得多了也有吃人嘴短的嫌疑。总之能有今天，离不开在盛大在线一年多的工作和学习，离不开同事们的帮助。希望公司能够越来越强大！&lt;br /&gt;&lt;br /&gt;&lt;div class="zemanta-pixie"&gt;&lt;img class="zemanta-pixie-img" alt="" src="http://img.zemanta.com/pixy.gif?x-id=b9d6ce29-52b2-8e5b-9e0a-6318d6a3cc11" /&gt;&lt;/div&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Clojure client for Beanstalkd forked</title><link href="https://sunng.info/blog/clojure-client-for-beanstalkd-forked.html" rel="alternate"></link><published>2011-08-28T00:00:00+08:00</published><updated>2011-08-28T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-08-28:/blog/clojure-client-for-beanstalkd-forked.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;beanstalkd&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;project
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="https://github.com/kr/beanstalkd"&gt;Beanstalkd&lt;/a&gt; is a distributed task queue written by Keith Rarick. I just forked the clojure client as the original version is no long active. Some features are added:
&lt;ul&gt;
    &lt;li&gt;Add commands: stats-tube, stats-job, pause-tube, list-tubes, list-tube-used and list-tubes-watched&lt;/li&gt;
    &lt;li&gt;Use yaml library to …&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;beanstalkd&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;project
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="https://github.com/kr/beanstalkd"&gt;Beanstalkd&lt;/a&gt; is a distributed task queue written by Keith Rarick. I just forked the clojure client as the original version is no long active. Some features are added:
&lt;ul&gt;
    &lt;li&gt;Add commands: stats-tube, stats-job, pause-tube, list-tubes, list-tube-used and list-tubes-watched&lt;/li&gt;
    &lt;li&gt;Use yaml library to decode stats output into clojure data structure.&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;Use it in your leiningen project:&lt;br /&gt;
[org.clojars.sunng/beanstalk "1.0.5"]&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Clojure Developers You Should Follow</title><link href="https://sunng.info/blog/clojure-developers-you-should-follow.html" rel="alternate"></link><published>2011-08-27T00:00:00+08:00</published><updated>2011-08-27T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-08-27:/blog/clojure-developers-you-should-follow.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;他山
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;opensource
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;介绍一下Clojure社区的活跃人物，有助于大家更好地学习和了解clojure，以下罗列的前提是包含但不限于，排名亦不分先后。&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;Rich …&lt;/h3&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;他山
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;opensource
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;介绍一下Clojure社区的活跃人物，有助于大家更好地学习和了解clojure，以下罗列的前提是包含但不限于，排名亦不分先后。&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;Rich Hickey (richhickey)&lt;/h3&gt;
Clojure的创始人。&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;Michael Fogus (fogus)&lt;/h3&gt;
The joy of clojure一书的作者，clojure社区的活跃人物，Clojure/core的成员。参与和维护着很多clojure项目。&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;James Reeves (weavejester)&lt;/h3&gt;
Compojure等一系列web框架的作者。是Clojure web开发方面的先驱，还维护着大量的clojure项目。&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;Mark McGranaghan (mmcgrana)&lt;/h3&gt;
Clojure web规范Ring的创始人，还维护着clj-redis等项目。&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;Stuart Hallowa (stuarthalloway)&lt;/h3&gt;
Programming Clojure一书的作者，Clojure的核心开发人员之一。&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;Christophe Grand (cgrand)&lt;/h3&gt;
cgrand是clojure web开发方面的专家，维护了enlive moustache等库。&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;Phil Hagelberg (technomancy)&lt;/h3&gt;
Clojure构建工具leiningen的作者。&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;Zach Tellman (ztellman)&lt;/h3&gt;
Zach是框架aleph/lamina/gloss的作者，是clojure网络编程的专家。&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;Nathan Marz (nathanmarz)&lt;/h3&gt;
Nathan是twitter的clojure dev，维护着一些与hadoop相关的clojure项目。&lt;/p&gt;

&lt;p&gt;以上列举的人都可以在github和twitter上找到。&lt;br /&gt;&lt;br /&gt;&lt;div class="zemanta-pixie"&gt;&lt;img class="zemanta-pixie-img" alt="" src="http://img.zemanta.com/pixy.gif?x-id=eafeb545-d70d-8011-8df5-66febda32431" /&gt;&lt;/div&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>苏制油罐车模型</title><link href="https://sunng.info/blog/su-zhi-you-guan-che-mo-xing.html" rel="alternate"></link><published>2011-08-20T00:00:00+08:00</published><updated>2011-08-20T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-08-20:/blog/su-zhi-you-guan-che-mo-xing.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;D60&lt;/li&gt;
&lt;li&gt;model
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;前段时间说发展兴趣爱好，于是又多了一个心灵手巧的爱好：静态模型。昨天买了一套油罐车模型（&lt;a href="http://www.icm.com.ua/catalogue/technics/30-atz-5-4320.html" target="_blank"&gt;ATZ …&lt;/a&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;D60&lt;/li&gt;
&lt;li&gt;model
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;前段时间说发展兴趣爱好，于是又多了一个心灵手巧的爱好：静态模型。昨天买了一套油罐车模型（&lt;a href="http://www.icm.com.ua/catalogue/technics/30-atz-5-4320.html" target="_blank"&gt;ATZ 5-4320&lt;/a&gt;）1：72，今天上午花了一上午时间就把它组装起来了。这种静态模型需要用胶水把各个零件粘合起来。所以对心灵手巧的要求程度比较高，作为一个小时候手工课沿着线剪纸都剪不齐的娃来说，其实这是一种挑战。
&lt;a href="http://www.flickr.com/photos/40741608@N08/6061428870/" title="DSC_0001 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6070/6061428870_d2272d9efb.jpg" alt="DSC_0001" height="335" width="500" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6061429306/" title="DSC_0002 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6087/6061429306_563625982b.jpg" alt="DSC_0002" height="500" width="335" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6061429838/" title="DSC_0003 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6074/6061429838_00b03f0885.jpg" alt="DSC_0003" height="335" width="500" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6061430266/" title="DSC_0004 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6065/6061430266_23e5ab8524.jpg" alt="DSC_0004" height="335" width="500" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6061430728/" title="DSC_0005 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6187/6061430728_7a244ab09c.jpg" alt="DSC_0005" height="335" width="500" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6061431154/" title="DSC_0006 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6079/6061431154_e6ba703e2d.jpg" alt="DSC_0006" height="500" width="335" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6061431724/" title="DSC_0007 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6195/6061431724_1911f762e9.jpg" alt="DSC_0007" height="500" width="335" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/6060880533/" title="DSC_0008 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6069/6060880533_6c63f02e41.jpg" alt="DSC_0008" height="335" width="500" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;这个是乌克兰的品牌叫做ICM，1：72的模型通常在50元上下。质量一般，有的小零件刚刚还没有拆下来就损坏了。当然全看个人喜好，我之所以买这个来入门主要是出于随便。静态模型入门的工具，需要一个水口钳（用来把零件剪下来），一瓶模型专用胶水（味道不太好），一把镊子，一把钢尺，一张砂纸。网上还说的角刀一类，暂时可以不用。&lt;/p&gt;

&lt;p&gt;上面的照片是还没有上漆的效果，等到我把第二个也做出来，再买一点专用的油漆把它刷出来。敬请留意。&lt;/p&gt;

&lt;p&gt;我小时候的愿望就是住一楼有个院子，在一角能开辟一个沙盘，把我的小人兵通通部署上去。我小时候确实就生活在这种没有硝烟的战争里。。。&lt;br /&gt;&lt;br /&gt;&lt;div class="zemanta-pixie"&gt;&lt;img class="zemanta-pixie-img" alt="" src="http://img.zemanta.com/pixy.gif?x-id=24a3725a-4543-86d9-840b-190b1f2b79f0" /&gt;&lt;/div&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Clojure Chinese User Group第一次线下聚会</title><link href="https://sunng.info/blog/clojure-chinese-user-groupdi-yi-ci-xian-xia-ju-hui.html" rel="alternate"></link><published>2011-08-06T00:00:00+08:00</published><updated>2011-08-06T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-08-06:/blog/clojure-chinese-user-groupdi-yi-ci-xian-xia-ju-hui.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;clojure
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;今天下午赶到上海参加了clojure中文用户的第一次线下聚会，见到了国内clojure的的主要用户。感谢一直在努力组织这次活动的朋友，还有提供场地的朋 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;clojure
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;今天下午赶到上海参加了clojure中文用户的第一次线下聚会，见到了国内clojure的的主要用户。感谢一直在努力组织这次活动的朋友，还有提供场地的朋友。&lt;/p&gt;

&lt;p&gt;这是我关于clojure构建工具、生命周期管理的slides&lt;/p&gt;

&lt;p&gt;&lt;div style="width:425px" id="__ss_8785604"&gt; &lt;strong style="display:block;margin:12px 0 4px"&gt;&lt;a href="http://www.slideshare.net/sunng87/clojure-cnclojuremeetup" title="Clojure cnclojure-meetup" target="_blank"&gt;Clojure cnclojure-meetup&lt;/a&gt;&lt;/strong&gt; &lt;iframe src="http://www.slideshare.net/slideshow/embed_code/8785604" marginwidth="0" marginheight="0" frameborder="0" height="355" scrolling="no" width="425"&gt;&lt;/iframe&gt; &lt;div style="padding:5px 0 12px"&gt; View more &lt;a href="http://www.slideshare.net/" target="_blank"&gt;presentations&lt;/a&gt; from &lt;a href="http://www.slideshare.net/sunng87" target="_blank"&gt;sunng87&lt;/a&gt; &lt;/div&gt; &lt;/div&gt;&lt;/p&gt;

&lt;p&gt;欢迎对clojure感兴趣的朋友加入&lt;a href="http://cnlojure.org/" target="_blank"&gt;中文用户邮件列表&lt;/a&gt;，希望这样的聚会活动可以长期的办下去。&lt;br /&gt;&lt;br /&gt;&lt;div class="zemanta-pixie"&gt;&lt;img class="zemanta-pixie-img" alt="" src="http://img.zemanta.com/pixy.gif?x-id=4f567446-b5a0-8664-be07-796569d5b7c1" /&gt;&lt;/div&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Using Google closure library with ClojureScript</title><link href="https://sunng.info/blog/using-google-closure-library-with-clojurescript.html" rel="alternate"></link><published>2011-08-05T00:00:00+08:00</published><updated>2011-08-05T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-08-05:/blog/using-google-closure-library-with-clojurescript.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;clojurescript&lt;/li&gt;
&lt;li&gt;javascript
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Google closure library is shipped with ClojureScript, and could be compiled with ClojureScript into a minimized javascript file. So closure library is doubtlessly the first candidate when you are considering to use an external Javascript library in your cljs browser …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;clojurescript&lt;/li&gt;
&lt;li&gt;javascript
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Google closure library is shipped with ClojureScript, and could be compiled with ClojureScript into a minimized javascript file. So closure library is doubtlessly the first candidate when you are considering to use an external Javascript library in your cljs browser application.&lt;/p&gt;

&lt;p&gt;However, different from clojure's interoperability with Java, ClojureScript has its own characteristics when you are interoperating with JavaScript and JavaScript based libraries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Clojure types are not fully compatible with JavaScript types&lt;/strong&gt;
In ClojureScript, you can never treat a Clojure map as a JavaScript object although they have similar characteristics. You have to do some conversion before passing a clojure map to javascript functions. Matthew Gilliard made &lt;a href="https://gist.github.com/1098417#file_ajax.cljs" target="_blank"&gt;a sample&lt;/a&gt; of such conversion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JavaScript package is not Clojure namespace&lt;/strong&gt;
This could be a common mistake for ClojureScript newbie. Actually, JavaScript doesn't have concept of "Package" or "Namespace". Many JavaScript libraries(dojo, Google Closure) made enhancement on this. ClojureScript also takes advantage of this mechanism. So before you start to coding with closure, you may browse &lt;a href="http://closure-library.googlecode.com/svn/docs/index.html" target="_blank"&gt;closure library API document&lt;/a&gt;, and find a module called &lt;em&gt;goog.net&lt;/em&gt; which includes lots of types. Then you write this:&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(ns myjs&lt;br /&gt;
  (:require [goog.net :as gnet]))&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;But compiler shows you "ERROR: JSC_MISSING_PROVIDE_ERROR. required "goog.net" namespace never&lt;br /&gt;
provided at ... ". This is not a PATH issue. The root cause is that closure module has a lower granularity than Clojure ones. Types are often contained in their own modules. You can find closure source code in &lt;em&gt;clojurescript/closure/library/closure&lt;/em&gt;. Modules are declare with &lt;em&gt;goog.provide&lt;/em&gt; function. Thus, you should require this name instead of the logical module name.&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(ns myjs&lt;br /&gt;
  (:require [goog.net.XhrIo :as gxhr]))&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;In addition, ClojureScript does not support 'use'.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Just use full name for JavaScript class&lt;/strong&gt;
For functions contains in some module, you can refer it with the clojure way:&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(ns myjs&lt;br /&gt;
  (:require [goog.dom :as dom]))&lt;br /&gt;
(dom/$ "element-id")&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;But for classes, just use the full name and ignore the module alias.&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(ns myjs&lt;br /&gt;
  (:require [goog.net.XhrIo :as gxhr]))&lt;br /&gt;
(def xhr (goog.net.XhrIo.))&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;These are basic tips before you start using Closure with ClojureScript. Leveraging on Google's closure library, you can create cross-browser JavaScript application with Clojure easily.&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>RageViewer updated</title><link href="https://sunng.info/blog/rageviewer-updated.html" rel="alternate"></link><published>2011-08-03T00:00:00+08:00</published><updated>2011-08-03T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-08-03:/blog/rageviewer-updated.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;cloudfoundry&lt;/li&gt;
&lt;li&gt;project
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Weekend project "RageViewer"最近新增了一些功能：
&lt;ul&gt;
    &lt;li&gt;界面更新&lt;/li&gt;
    &lt;li&gt;Rages现在持久化到redis中，支持permalink&lt;/li&gt;
    &lt;li&gt;程序可以直接部署到cloudfoundry上&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;RageViewer现在也有一个cloudfoundry上部署的版本：
&lt;a href="http://rageviewer.cloudfoundry.com/index.html" target="_blank"&gt;http://rageviewer.cloudfoundry.com/index.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;对本地开 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;cloudfoundry&lt;/li&gt;
&lt;li&gt;project
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Weekend project "RageViewer"最近新增了一些功能：
&lt;ul&gt;
    &lt;li&gt;界面更新&lt;/li&gt;
    &lt;li&gt;Rages现在持久化到redis中，支持permalink&lt;/li&gt;
    &lt;li&gt;程序可以直接部署到cloudfoundry上&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;RageViewer现在也有一个cloudfoundry上部署的版本：
&lt;a href="http://rageviewer.cloudfoundry.com/index.html" target="_blank"&gt;http://rageviewer.cloudfoundry.com/index.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;对本地开发、调试，现在需要在本机上启动一个监听默认端口的redis，执行&lt;em&gt;lein ring server&lt;/em&gt;即可。对部署在cloudfoundry的情况，受益于cloudfoundry基于环境变量的数据库配置机制，不需要做任何额外的配置和修改：执行&lt;em&gt;lein ring uberwar &amp;&amp; vmc update your-app-name&lt;/em&gt; 即可。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Geohash rewritten with Clojure</title><link href="https://sunng.info/blog/geohash-rewritten-with-clojure.html" rel="alternate"></link><published>2011-07-29T00:00:00+08:00</published><updated>2011-07-29T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-07-29:/blog/geohash-rewritten-with-clojure.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;geohash&lt;/li&gt;
&lt;li&gt;project
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Sorry but this is another release announcement.&lt;/p&gt;

&lt;p&gt;I just rewrite my &lt;a href="https://github.com/sunng87/node-geohash" target="_blank"&gt;node-geohash&lt;/a&gt; module with clojure. You can include it with leiningen according to the instructions shows &lt;a href="http://clojars.org/org.clojars.sunng/geohash" target="_blank"&gt;here&lt;/a&gt;. The project is hosted on &lt;a href="https://bitbucket.org/sunng/clojure-geohash" target="_blank"&gt;bitbucket&lt;/a&gt; this time. The API is rather simple, please …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;geohash&lt;/li&gt;
&lt;li&gt;project
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Sorry but this is another release announcement.&lt;/p&gt;

&lt;p&gt;I just rewrite my &lt;a href="https://github.com/sunng87/node-geohash" target="_blank"&gt;node-geohash&lt;/a&gt; module with clojure. You can include it with leiningen according to the instructions shows &lt;a href="http://clojars.org/org.clojars.sunng/geohash" target="_blank"&gt;here&lt;/a&gt;. The project is hosted on &lt;a href="https://bitbucket.org/sunng/clojure-geohash" target="_blank"&gt;bitbucket&lt;/a&gt; this time. The API is rather simple, please just follow the README doc.&lt;/p&gt;

&lt;p&gt;It's totally different experience to write such kind of algorithm in a functional language. You have to translate loops to recursions, also make sure they are available for tail recursion optimization. I really enjoy coding in clojure.&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Clojure-Control plugin for leiningen</title><link href="https://sunng.info/blog/clojure-control-plugin-for-leiningen.html" rel="alternate"></link><published>2011-07-28T00:00:00+08:00</published><updated>2011-07-28T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-07-28:/blog/clojure-control-plugin-for-leiningen.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;lein&lt;/li&gt;
&lt;li&gt;project
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="https://github.com/killme2008/clojure-control/" target="_blank"&gt;Clojure-Control&lt;/a&gt; is the clojure port of node-control, developed by &lt;a href="http://www.blogjava.net/killme2008/" target="_blank"&gt;killme2008&lt;/a&gt;. Clojure-Control allows you to define clusters and tasks in a DSL and execute them with simple command.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/sunng87/lein-control" target="_blank"&gt;Lein-control&lt;/a&gt; is a leiningen(clojure build tool) plugin, with which you can integrate Clojure-Control …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;lein&lt;/li&gt;
&lt;li&gt;project
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="https://github.com/killme2008/clojure-control/" target="_blank"&gt;Clojure-Control&lt;/a&gt; is the clojure port of node-control, developed by &lt;a href="http://www.blogjava.net/killme2008/" target="_blank"&gt;killme2008&lt;/a&gt;. Clojure-Control allows you to define clusters and tasks in a DSL and execute them with simple command.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/sunng87/lein-control" target="_blank"&gt;Lein-control&lt;/a&gt; is a leiningen(clojure build tool) plugin, with which you can integrate Clojure-Control into your build tasks. It could be helpful to execute deployment tasks on several machines.&lt;/p&gt;

&lt;p&gt;To use lein-control, simply add this to your project.clj . And download dependencies with &lt;em&gt;lein deps&lt;/em&gt;.&lt;br /&gt;
:dev-dependencies [[lein-control "0.1.0"]]&lt;/p&gt;

&lt;p&gt;Create a sample control file in your project home:
&lt;em&gt;lein control init&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Check your cluster configuration:
&lt;em&gt;lein control show cluster-name&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Execute tasks against particular cluster:
&lt;em&gt;lein control run cluster-name task-name&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Enjoy your deployment !&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>'Reddit.clj: clojure wrapper for Reddit API'</title><link href="https://sunng.info/blog/redditclj-clojure-wrapper-for-reddit-api.html" rel="alternate"></link><published>2011-07-26T00:00:00+08:00</published><updated>2011-07-26T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-07-26:/blog/redditclj-clojure-wrapper-for-reddit-api.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;reddit
published: true
comments: false&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;As mentioned in &lt;a href="http://sunng.info/blog/2011/07/weekend-project-rageviewer/" target="_blank"&gt;Rage Viewer&lt;/a&gt;, I have another clojure library to communicate with Reddit. Now it's public available as "&lt;a href="https://github.com/sunng87/reddit.clj" target="_blank"&gt;reddit.clj&lt;/a&gt;".&lt;/p&gt;

&lt;p&gt;Reddit.clj is managed with leiningen. The current reddit.clj release is &lt;del datetime="2011-07-26T13:29:58+00:00"&gt;0.1.3&lt;/del&gt; 0.2.0. You can …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;reddit
published: true
comments: false&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;As mentioned in &lt;a href="http://sunng.info/blog/2011/07/weekend-project-rageviewer/" target="_blank"&gt;Rage Viewer&lt;/a&gt;, I have another clojure library to communicate with Reddit. Now it's public available as "&lt;a href="https://github.com/sunng87/reddit.clj" target="_blank"&gt;reddit.clj&lt;/a&gt;".&lt;/p&gt;

&lt;p&gt;Reddit.clj is managed with leiningen. The current reddit.clj release is &lt;del datetime="2011-07-26T13:29:58+00:00"&gt;0.1.3&lt;/del&gt; 0.2.0. You can include it in your project.clj:&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(defproject xxxx "1.0.0-SNAPSHOT"&lt;br /&gt;
  :dependencies [[reddit.clj "0.2.0"]])&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;This is a simple tutorial for you to getting started :&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
;; include reddit.clj&lt;br /&gt;
(require '[reddit.clj.core :as reddit])&lt;br /&gt;
;; create a reddit client with reddit/login.&lt;br /&gt;
;; you can also ignore the arguments to use it&lt;br /&gt;
;; anonymously&lt;br /&gt;
(def rc (reddit/login "your-reddit-name" "your-reddit-passwd"))&lt;/p&gt;

&lt;p&gt;;; load reddits from subreddit "clojure", and print titles&lt;br /&gt;
(doseq&lt;br /&gt;
 [title (map :title (reddit/reddits rc "clojure"))]&lt;br /&gt;
  (println title))&lt;/p&gt;

&lt;p&gt;;; enhance reddit client to enable writing, for login user only&lt;br /&gt;
;; this operation will retrieve reddit user modhash under the hood&lt;br /&gt;
(def rc (reddit/enhance rc))&lt;/p&gt;

&lt;p&gt;;; vote-up a thing on reddit&lt;br /&gt;
(reddit/vote-up rc "t3_iz61z")&lt;/p&gt;

&lt;p&gt;;; you may also submit links to reddit.&lt;br /&gt;
;; permalink will be returned when success.&lt;br /&gt;
;; be careful to use this API because reddit may ask you for a&lt;br /&gt;
;; captcha. But as a library, reddit.clj will return nil on this case.&lt;br /&gt;
;; use it at your own risk.&lt;br /&gt;
(reddit/submit-link rc "title" "url" "subreddit-name")&lt;/p&gt;

&lt;p&gt;[/cc]&lt;/p&gt;

&lt;p&gt;Detailed API documents are listed &lt;a href="http://sunng87.github.com/reddit.clj/reddit.clj.core-api.html" target="_blank"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This library should be useful to crawl data from reddit which is rich of information. Reddit.clj is hosted on &lt;a href="https://github.com/sunng87/reddit.clj" target="_blank"&gt;github&lt;/a&gt;.&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>'Weekend Project: RageViewer'</title><link href="https://sunng.info/blog/weekend-project-rageviewer.html" rel="alternate"></link><published>2011-07-25T00:00:00+08:00</published><updated>2011-07-25T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-07-25:/blog/weekend-project-rageviewer.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;project
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;blockquote&gt;"Weekend is for weekend projects."&lt;/blockquote&gt;&lt;/p&gt;

&lt;p&gt;介绍Weekend Project前，先说个事。早上HN上&lt;a href="http://www.romku.com/blog/2011/07/my-weekend-project-what-can-you-really-do-in-a-weekend" target="_blank"&gt;有人说&lt;/a&gt;：你们这些个人，自个偷偷摸摸搞了半年一年的东西，然后 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;project
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;blockquote&gt;"Weekend is for weekend projects."&lt;/blockquote&gt;&lt;/p&gt;

&lt;p&gt;介绍Weekend Project前，先说个事。早上HN上&lt;a href="http://www.romku.com/blog/2011/07/my-weekend-project-what-can-you-really-do-in-a-weekend" target="_blank"&gt;有人说&lt;/a&gt;：你们这些个人，自个偷偷摸摸搞了半年一年的东西，然后发到HN上说这是两天就搞定的weekend project，你们恶心不恶心。本人郑重声明，下面这个东西确实是标准的weekend project，只用了周六下午和晚上的时间，结果周日还因为元气大伤只改了一点小东西。因为确实只有不到两天的effort，所以就不发到HN上丢人现眼了。&lt;/p&gt;

&lt;p&gt;话说，比起HN，我更常看reddit。于是就有了这个weekend project，&lt;strong&gt;&lt;a href="http://sunng.info:8080/rageviewer/index.html" target="_blank"&gt;Rage Viewer&lt;/a&gt;&lt;/strong&gt;，可以浏览reddit ffffffffuuuuuuu上最新的rage comic。&lt;/p&gt;

&lt;p&gt;功能很简单，唯一值得一提的是它是统统用clojure写成的，后端是clojure，前端是最近公布的clojurescript编译的js。之所以这么搞，不为别的，只为cool。这个和每天早晨五点起床化妆是一个道理。&lt;/p&gt;

&lt;p&gt;在github上可以找到这个项目，他可以随意跑在任何地方，只要三个步骤：
&lt;ul&gt;
    &lt;li&gt;git clone git://github.com/sunng87/rageviewer.git&lt;/li&gt;
    &lt;li&gt;lein deps&lt;/li&gt;
    &lt;li&gt;lein ring server&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;欢迎feedback。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>ClojureScript Recipes</title><link href="https://sunng.info/blog/clojurescript-recipes.html" rel="alternate"></link><published>2011-07-24T00:00:00+08:00</published><updated>2011-07-24T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-07-24:/blog/clojurescript-recipes.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;clojure javascript
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;一周前左右有人说javascript是assembly language for the web, 结果一周不到clojurescript发布了。闹了半天clojure 1.3迟迟不发布是因为effort都迫不及待地转移到向javascript迁移上去了。简单地说cljs是clojure在javascript上的实现，通过cljsc可以把clojure编译成js，运行在浏览器 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;clojure javascript
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;一周前左右有人说javascript是assembly language for the web, 结果一周不到clojurescript发布了。闹了半天clojure 1.3迟迟不发布是因为effort都迫不及待地转移到向javascript迁移上去了。简单地说cljs是clojure在javascript上的实现，通过cljsc可以把clojure编译成js，运行在浏览器里或是Node环境里。&lt;/p&gt;

&lt;p&gt;目前clojurescript还没有正式的发布版本，需要从github签出开发版本。
&lt;ol&gt;
    &lt;li&gt;cljs是在Oracle JDK上开发的，引用了sun.org.mozilla.javascript.internal.Context，这个类在OpenJDK里叫做sun.org.mozilla.javascript.Context。所以没有办法，暂时只能在Oracle JDK上用clojurescript。&lt;/li&gt;
    &lt;li&gt;cljs自带的所有脚本，启动jvm时，heap size都是开2G的。可怜我所有的内存才2.5G，还是分布在两台电脑上（#@&amp;amp;*……@￥@！）。不过我手动把它改为512M后cljsc还是依然可以正常运行的。&lt;/li&gt;
    &lt;li&gt;cljs与javascript的互操作是最麻烦的部分，一般情况cljs通过(js*)这个form来访问javascript数据和对象，比如访问document：(js* "document")。进而访问getElementById时，即(.getElementById (js* "document") "some-id")。&lt;/li&gt;
    &lt;li&gt;cljs访问js对象时，通过(aget)而不是(.属性名)，例如(aget rage "ups")编译后为rage.ups，如果(.ups rage)就会被当作函数调用。不过奇怪的是如果(.title rage)依然会被编译成rage.title。这种不一致的情况在对.url也存在。当然用aget是可以获得一致的结果的（aget在clojure里是根据索引取java数组的form，在cljs里可以支持js object了）。&lt;/li&gt;
    &lt;li&gt;当访问无参数的js对象时，cljs与clojure有不同。例如在clojure里(.toString date)，而在cljs里，这样写编译的结果是date.toString，注意没有括号，直接访问这个function对象了。所以在cljs里，正确的写法有些变化(. date (toString))。&lt;/li&gt;
    &lt;li&gt;创建js对象，可以通过(js-obj)这个form&lt;/li&gt;
    &lt;li&gt;修改dom属性，需要用(set!)这个form，如 (set! (.src img) "http://xxx")。如果把cljs用在网页里，类似这样的操作比比皆是，这和clojure大不相同。&lt;/li&gt;
    &lt;li&gt;cljs里没有STM，暂时也不支持ref。可以直接用def定义变量，随意地访问和修改，js环境是单线程环境。&lt;/li&gt;
    &lt;li&gt;对于要对外访问的方法，在声明时加上^:export可以让编译器不修改方法的名字。&lt;/li&gt;
&lt;/ol&gt;
用cljs开发有趣归有趣，调试还是很困难的，建议开发的时候就写好打log的代码(js* "console.log")，因为目前cljsc编译的速度也不快，反复地修改代价还是挺高的。当然，为了cool，以上都不是问题。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>使用defrecord与defprotocol的注意事项</title><link href="https://sunng.info/blog/shi-yong-defrecordyu-defprotocolde-zhu-yi-shi-xiang.html" rel="alternate"></link><published>2011-07-22T00:00:00+08:00</published><updated>2011-07-22T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-07-22:/blog/shi-yong-defrecordyu-defprotocolde-zhu-yi-shi-xiang.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;programming
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;简单地说，protocol是clojure中的接口，record是clojure中的数据类型。&lt;/p&gt;

&lt;p&gt;可以通过这样的code定义一个protocol&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(defprotoco DummyProtocol&lt;br /&gt;
  "doc string..."&lt;br /&gt;
  (method-one [self x] "doc string..."))&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;需要注意的是，protocol里所有方法的第一个参数都是self/this参数（类似python），从第二 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;programming
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;简单地说，protocol是clojure中的接口，record是clojure中的数据类型。&lt;/p&gt;

&lt;p&gt;可以通过这样的code定义一个protocol&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
(defprotoco DummyProtocol&lt;br /&gt;
  "doc string..."&lt;br /&gt;
  (method-one [self x] "doc string..."))&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;需要注意的是，protocol里所有方法的第一个参数都是self/this参数（类似python），从第二个开始才是调用时传入的参数。如果方法要重载呢？&lt;/p&gt;

&lt;p&gt;[cc lang="clojure"]&lt;br /&gt;
(defprotocol DummyProtocol&lt;br /&gt;
  "doc string..."&lt;br /&gt;
  (method-one [self x] [self x y] "doc string")&lt;br /&gt;
)
[/cc]&lt;/p&gt;

&lt;p&gt;Apress的 Practical Clojure 书里的例子，给重载的参数表加上了括号，这样会导致编译错误（&lt;a href="http://book.douban.com/annotation/13819191/" target="_blank"&gt;注记&lt;/a&gt;）。&lt;/p&gt;

&lt;p&gt;定义一个record实现protocol&lt;/p&gt;

&lt;p&gt;[cc lang＝"clojure"]&lt;br /&gt;
(defrecord DummyRecord [a b c]&lt;br /&gt;
  DummyProtocol&lt;br /&gt;
  (method-one [self x] (+ a x))&lt;br /&gt;
  (method-one [self x y] (+ a x y)))&lt;br /&gt;
[/cc]&lt;br /&gt;
Practical Clojure里关于这部分的代码，又丢掉了self参数（&lt;a href="http://book.douban.com/annotation/13819264/" target="_blank"&gt;注记&lt;/a&gt;）。&lt;/p&gt;

&lt;p&gt;最后还有一个问题，如果直接use你的ns，你会发现调用record时出现：&lt;br /&gt;
java.lang.IllegalArgumentException: Unable to resolve classname: DummyRecord&lt;/p&gt;

&lt;p&gt;怎么回事，不是都use了吗？原因是record被编译成了java对象，所以引用时要用java对象的引用方式，import之。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>工作第二年</title><link href="https://sunng.info/blog/gong-zuo-di-er-nian.html" rel="alternate"></link><published>2011-07-18T00:00:00+08:00</published><updated>2011-07-18T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-07-18:/blog/gong-zuo-di-er-nian.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;Life&lt;/li&gt;
&lt;li&gt;work
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;p&gt;这个周末就是我正式工作两年的日子了，第二年过得稍微有些起伏，晚上写了一点总结。但是刚才读 …&lt;/p&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;Life&lt;/li&gt;
&lt;li&gt;work
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;p&gt;这个周末就是我正式工作两年的日子了，第二年过得稍微有些起伏，晚上写了一点总结。但是刚才读了一下觉得不太满意，也不太合适发出来。这样，即将跨入三年级的时候，我就改改风格谈谈愿望吧。&lt;/p&gt;
&lt;p&gt;1. 把日常工作做好，积累技术，流程，项目管理的经验&lt;br /&gt;
2. 业余时间拓展周边领域的知识：数据挖掘，数据分析，可视化方面&lt;br /&gt;
3. 希望能主导去做成一点有始有终的事情&lt;br /&gt;
4. 希望不久的将来能参与一个志同道合齐心协力（self-motivated）的团队一起做好一个产品&lt;/p&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>HeatCanvas support for Leaflet</title><link href="https://sunng.info/blog/heatcanvas-support-for-leaflet.html" rel="alternate"></link><published>2011-07-18T00:00:00+08:00</published><updated>2011-07-18T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-07-18:/blog/heatcanvas-support-for-leaflet.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;GIS&lt;/li&gt;
&lt;li&gt;heatcanvas&lt;/li&gt;
&lt;li&gt;javascript&lt;/li&gt;
&lt;li&gt;map
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://leaflet.cloudmade.com/" title="leaflet" target="_blank"&gt;Leaflet&lt;/a&gt; is a light weight web mapping library developed by &lt;a href="http://cloudmade.com/" title="cloudmade" target="_blank"&gt;Cloudmade&lt;/a&gt;. Leaflet is designed for compatibility with both desktop browser and mobile browser.&lt;/p&gt;

&lt;p&gt;HeatCanvas-Leaflet extension enables heat map on Leaflet. You can create heat map layer and add it to …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;GIS&lt;/li&gt;
&lt;li&gt;heatcanvas&lt;/li&gt;
&lt;li&gt;javascript&lt;/li&gt;
&lt;li&gt;map
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://leaflet.cloudmade.com/" title="leaflet" target="_blank"&gt;Leaflet&lt;/a&gt; is a light weight web mapping library developed by &lt;a href="http://cloudmade.com/" title="cloudmade" target="_blank"&gt;Cloudmade&lt;/a&gt;. Leaflet is designed for compatibility with both desktop browser and mobile browser.&lt;/p&gt;

&lt;p&gt;HeatCanvas-Leaflet extension enables heat map on Leaflet. You can create heat map layer and add it to Leatlet map:&lt;br /&gt;
[cc lang="javascript"]&lt;br /&gt;
var heatmap = new L.TileLayer.HeatCanvas("Heat Canvas", map, {},&lt;br /&gt;
                        {'step':0.3, 'degree':HeatCanvas.QUAD, 'opacity':0.7});&lt;br /&gt;
heatmap.pushData(32.1104, 118.0852, 14);&lt;br /&gt;
//push more data ...&lt;br /&gt;
map.addLayer(heatmap);&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;You can find live demo on:
&lt;a href="http://sunng87.github.com/heatcanvas/leaflet.html" title="heatcanvas demo" target="_blank"&gt;http://sunng87.github.com/heatcanvas/leaflet.html&lt;/a&gt;
Mobile browser is also supported. (Tested on Firefox Android and default Android browser)&lt;/p&gt;

&lt;p&gt;Find more about HeatCanvas on &lt;a href="https://github.com/sunng87/heatcanvas"&gt;github page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Leaflet is still buggy for extension. There is a latlng-pixel coordinate conversion &lt;a href="https://github.com/CloudMade/Leaflet/issues/190" target="_blank"&gt;issue&lt;/a&gt; in low zoom level, affects this demo. Hope it could be fixed soon.&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Staged Event-Driven Architecture</title><link href="https://sunng.info/blog/staged-event-driven-architecture.html" rel="alternate"></link><published>2011-07-13T00:00:00+08:00</published><updated>2011-07-13T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-07-13:/blog/staged-event-driven-architecture.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;Architecture
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;按照传统的编写应用程序的思路，当server接到请求，包装完成之后，分配到线程池中交给一个线程完成，返回。Java的servlet容器就是这么设计的，这么 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;Architecture
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;按照传统的编写应用程序的思路，当server接到请求，包装完成之后，分配到线程池中交给一个线程完成，返回。Java的servlet容器就是这么设计的，这么多年大部分的应用程序也是在这种模式下编写的。当任务的代价比较大，比如多次和数据库交互，并且可能和后端其他服务通信，这是线程被任务占用的时间就相对较长。如果请求的并发量很大，容器的线程池耗尽，新的请求就无法被处理，导致并发性无法提高，吞吐量也无法提高。&lt;/p&gt;

&lt;p&gt;实际上在大部分应用程序里，所谓代价很高的任务，往往都是I/O Bound。涉及网络通信，磁盘读写，线程被迫wait，CPU却是空闲的。所以，I/O Bound的程序理论上都还有优化的空间。于是有了这种Staged Event-Driven Architecture，即SEDA。&lt;/p&gt;

&lt;p&gt;SEDA的思路是将原先由一个线程完成的任务，分割为相对独立的多个阶段。每个阶段由专用的一组线程负责执行，阶段之间用过队列交互。又是上次提到的老话：If you cannot split it, you cannot scale it.&lt;/p&gt;

&lt;p&gt;例如，我们的业务逻辑是读取请求，操作数据库，与后端通信，操作数据库，写回结果。如果采用NIO的方式，网络通信可以认为是非阻塞的。而目前与数据库的交互，通常还是阻塞式的风格。这样这五个阶段分别是非阻塞、阻塞、非阻塞、阻塞、非阻塞。组塞操作容易成为性能瓶颈，CPU时间用于等待，占用率不高，所以对组塞操作可以分配相对多的线程；非阻塞操作速度较快，为了避免快速的切换导致sy升高，采用和CPU核数一样多的线程即可。&lt;/p&gt;

&lt;p&gt;这样，原本一个线程反复等待的阻塞操作，变成了部分线程等待的同时，其他线程仍然在处理自己的业务，有效使用CPU。利用的多核的优势，提高了CPU的占用，即提高了系统的处理能力。&lt;/p&gt;

&lt;p&gt;而对外部接口来说，当读取数据的线程在读取完毕之后，将任务dispatch到相应队列即返回，前端可以保证很高的处理速度，并发性也可以保证。任务被积压在阻塞操作的队列中，而消费阻塞操作的线程要多于提供者，在一定程度上也保证了处理速度。再退一步说，当阻塞操作的速度却是无法消费大量任务时，前端可以根据队列的大小判断当前系统的load，拒绝服务。&lt;/p&gt;

&lt;p&gt;当然，采用这种方式，只有在并发量提高到一定程度，并发成为系统瓶颈时才能体现价值。就单个操作而言，由于队列的传递，他的latency一定是有所上升的。&lt;/p&gt;

&lt;p&gt;关于SEDA，可以参考&lt;a href="http://matt-welsh.blogspot.com/"&gt;Matt Welsh&lt;/a&gt;的相关论文：The Staged Event-Driven Architecture for Highly-Concurrent Server Applications。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>自制山寨摩卡</title><link href="https://sunng.info/blog/zi-zhi-shan-zhai-mo-qia.html" rel="alternate"></link><published>2011-07-09T00:00:00+08:00</published><updated>2011-07-09T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-07-09:/blog/zi-zhi-shan-zhai-mo-qia.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;coffee&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;一共也没去过几次咖啡店，但是几乎每次要的都是摩卡，感情特深。所谓摩卡就是不明颗粒+奶油(optional)+牛 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;coffee&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;一共也没去过几次咖啡店，但是几乎每次要的都是摩卡，感情特深。所谓摩卡就是不明颗粒+奶油(optional)+牛奶+咖啡+巧克力（排名分先后）。我晕，扯这些没用的干啥，先上图：
&lt;a href="http://www.flickr.com/photos/40741608@N08/5921438312/" title="39171a47e65850f64ab50b4188e3c1846d177c05_wmeg_00001 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm7.static.flickr.com/6141/5921438312_459e4d874d.jpg" width="300" height="500" alt="39171a47e65850f64ab50b4188e3c1846d177c05_wmeg_00001" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;这是自制摩卡，奶油暂时还做不来，所以放弃了。不明颗粒太麻烦，就拿巧克力代替了。&lt;/p&gt;

&lt;p&gt;值得一提的是这次的巧克力是从可可粉熬过来的。先拿一个小的奶锅装一点牛奶，待烧开以后加两勺可可粉和一定量的白糖，然后就在小火上搅拌即可。有点类似过去搅咖哩，你不搅它它必然糊掉。等到液体粘稠到一定程度以后出锅就可以了。在实际操作里，我的牛奶加多了，以至于最后也没有真正的粘稠，这个过程有待优化。如果觉得这个过程麻烦的同学，可以直接买巧克力酱，或者拿现成的巧克力熬。当然如果你从可可粉开始熬的话，可定制性更强一些。这个直接JDBC，或是用Hibernate类似。&lt;/p&gt;

&lt;p&gt;出锅的巧克力放一边，凉了也不太要紧，我还滴了一点香草糖浆，不过似乎那味道只有在一开始，后来就消失了。&lt;/p&gt;

&lt;p&gt;拿一个大一些的杯子，把巧克力倒进去垫底。继续做咖啡，打牛奶。完成以后依次把咖啡和牛奶倒进杯子。摩卡好像不需要太多泡沫，打牛奶的时候可以收敛一些，不过看个人爱好了。我现在还处在杯子里没有泡沫就没有成就感的阶段，so&lt;/p&gt;

&lt;p&gt;这个山寨版本还是很简单的，连我都可以轻松搞定。如果你觉得太山寨了，实在看不下去，千万不要人身攻击我。。。&lt;/p&gt;

&lt;p&gt;味道？还行吧。&lt;/p&gt;

&lt;p&gt;Edit(20100711)：老师指点说你没有可可脂是永远熬不出粘稠的感觉的。我明白真相了。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Failure Detection</title><link href="https://sunng.info/blog/failure-detection.html" rel="alternate"></link><published>2011-07-03T00:00:00+08:00</published><updated>2011-07-03T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-07-03:/blog/failure-detection.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;algorithm&lt;/li&gt;
&lt;li&gt;distribute
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;分布式系统中检测节点的工作情况，最直观的方法是采用心跳包的方式，通过定时发送心跳包，如果 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;algorithm&lt;/li&gt;
&lt;li&gt;distribute
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;分布式系统中检测节点的工作情况，最直观的方法是采用心跳包的方式，通过定时发送心跳包，如果对端节点没有正常返回，则认为此节点处在failure状态，这时系统需要采取一定的措施来保证正常运行。目前，Zookeeper就是采用这种方式（org.apache.zookeeper.ClientCnxn）。&lt;/p&gt;

&lt;p&gt;但是为了避免由于网络抖动等等意外因素导致的误报，通常在心跳包的基础上做一些改进。我在盛大做服务的路由代理时，代理和后端的服务通过心跳包保持连接。当心跳包出现异常，或服务调用出现异常（类似HTTP 500的系统异常），代理会记录当时的时间戳。根据配置，代理会存储最近50次的时间戳，并且检查他们的方差，当方差小于一个阈值时，就认定服务为failure状态，将服务从列表中剔除并发送报警。在一些测试里，这套机制是可以按照预期工作的。可惜这个系统最后没有上生产系统，所以也没有实际的运维经验。&lt;/p&gt;

&lt;p&gt;最近看到了Cassandra使用的Failure Detection机制，叫做Phi Failure Detection Model。这个思路在一定程度上跟我的方式类似，在Cassandra的实现里，他会存储最近1000次失败的信息（org.apache.cassandra.gms.FailureDetector#sampleSize_）。此外，Cassandra存储的是失败的间隔，而不是绝对的时间戳（首次失败，存储的是心跳间隔的二分之一）。判断失败的φ值是通过这个公式就算：
&lt;font face="monospace"&gt;φ = (now - last_failure) * lnE / mean(interval_samples)&lt;/font&gt;
当φ值大于配置的threshold时，Cassandra的Gossip机制认为这个节点为failure。默认的配置里，这个threshold值为8. 关于Phi Failure Detection Model，可以参考论文：Information Propagation on the Phi Failure Detector&lt;/p&gt;

&lt;p&gt;而为了提高Zookeeper的Failure Detection的质量，去年的&lt;a href="https://issues.apache.org/jira/browse/ZOOKEEPER-702"&gt;一个Google Summer of Code项目&lt;/a&gt;为Zookeeper引入了一些流行的检测模型，包括前面提到的Phi Failure Detector，以及Bertier的方法和Chen的方法。&lt;/p&gt;

&lt;p&gt;Chen方法以一个固定的alpha常数，加平均到达时间作为估计的timeout值。&lt;/p&gt;

&lt;p&gt;在Bertier方法中，用于判断心跳包失败的timeout时间通过三个参数gamma/beta/phi被动态修正：
&lt;font face="monospace"&gt;error = now -ea - delay;&lt;/font&gt; （收到心跳包的时间-估计的心跳包到达时间-估计的延迟）
&lt;font face="monospace"&gt;delay = delay + gamma * error;&lt;/font&gt; （根据gamma和error修正延迟）
&lt;font face="monospace"&gt;var = var + gamma*(abs(error) - var);&lt;/font&gt; （error值的variation，即&lt;font face="monospace"&gt;var&lt;sub&gt;n&lt;/sub&gt;=Σ g(1-g)&lt;sup&gt;n-i&lt;/sup&gt;*e&lt;sub&gt;i&lt;/sub&gt;&lt;/font&gt;）
&lt;font face="monospace"&gt;ea = now + avg(‌interval);&lt;/font&gt; （根据心跳包的平均间隔修正估计时间）
&lt;font face="monospace"&gt;timeout = avg(interval) + beta*delay + phi*var&lt;/font&gt; (计算新的timeout)&lt;br /&gt;
假如心跳包在timeout后到达，timeout还需要在上面的基础上加上常数moderationStep。&lt;/p&gt;

&lt;p&gt;GSoC项目里，gamma/beta/phi/moderationStep的默认值分别为0.1/1.0/4.0/500。Bertier方法的timeout值根据网络情况自适应。&lt;/p&gt;

&lt;p&gt;评价一种Failure Detector的标准，包括它发现failure节点的时间和误报率。&lt;a href="http://wiki.apache.org/hadoop/ZooKeeper/GSoCFailureDetector#Experimentation"&gt;这里&lt;/a&gt;有GSoC项目中作者进行的实验及结论，可供参考。&lt;/p&gt;

&lt;p&gt;&lt;div class="zemanta-pixie"&gt;&lt;img class="zemanta-pixie-img" alt="" src="http://img.zemanta.com/pixy.gif?x-id=957af0b9-0304-8a1e-9128-4be44f8e7b1e" /&gt;&lt;/div&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>最近的一些想法</title><link href="https://sunng.info/blog/zui-jin-de-yi-xie-xiang-fa.html" rel="alternate"></link><published>2011-06-30T00:00:00+08:00</published><updated>2011-06-30T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-06-30:/blog/zui-jin-de-yi-xie-xiang-fa.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;work
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;p&gt;最近有机会带着两个弟兄做一个老产品的技术改造。起因是测试发现产品的并发性很差，反馈回开发 …&lt;/p&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;work
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;p&gt;最近有机会带着两个弟兄做一个老产品的技术改造。起因是测试发现产品的并发性很差，反馈回开发部门作下一个版本的改进。一查代码发现在网络通信时，原本异步的NIO，发送线程居然被强行加了wait同步等待远程返回。线程不能被释放，前端的请求被排队，吞吐量根本上不去。&lt;/p&gt;
&lt;p&gt;这是root cause，其实进了代码里面的问题更多。于是产生了对这种feature driven development的怀疑。我曾经觉得列出 feature，做好代价估计，然后大家各自去实现的方式很理性很可控。如果宏观地看，黑盒地看，这种方式对项目管理很友好。但是最近看这种方式下开发出来的产品，问题还是很多的。当开发人员面对一个feature却缺少对产品整体架构的足够了解，他很容易倾向于用quick and dirty的方式\打补丁的方式解决问题，而忽略新feature对架构整体的挑战。长期下来，软件的功能不断演进，架构却停留在早期，维护成本越来越高，神秘的陷阱越来越多。这时，每次发布都要通过测试部门的蛮力测试，这个过程中除了修bug还要克服无数的invalid的bug。What the hell!!!&lt;/p&gt;
&lt;p&gt;另一方面，从开发人员的角度，我认为Don't repeat yourself是每个程序员必须牢记在心的编程行为准则。无论技术好坏，经验是否丰富，只要坚持这个原则，程序员总会趋向于做一些抽象做一些设计，我想是不会写出太让人发指的代码的。最近查看遗留代码，里面实在是太多copy/paste了。对新增的功能，甚至通过重载加copy/paste来实现。如果不能怀疑同事能力的话，只好认为这是工作态度问题了。管理者往往是不会看到这么细节的问题（其实是严重的问题）。&lt;/p&gt;
&lt;p&gt;面对这样的系统，我最初的想法是通通推倒然后模块化。虽然考虑了工作量，可是尝试了一周还是发现老的基础根本无法剥离开，多年前的设计者根本就没有考虑模块化/解耦合的问题。有句话说得好: if you cannot split it, you cannot scale it. （对数据显而易见，对程序其实也一样）然而放弃这么大规模的久经考验的代码又不现实。这周开始又只好改变策略小步快跑，虽然不治本，但是就这个阶段来说，看到改造的效果对我们这个小组意义更大。这是在内心的洁癖和现实的局面间做的不难的取舍。&lt;/p&gt;
&lt;p&gt;尽管困难重重，还是争取抓住这个机会能对自己有个提升。需要驱动弟兄们接受和实现自己的想法，和自个一个人闷头干是完全不同的。&lt;/p&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>'ngeohash: node module for geohash algorithm'</title><link href="https://sunng.info/blog/ngeohash-node-module-for-geohash-algorithm.html" rel="alternate"></link><published>2011-06-24T00:00:00+08:00</published><updated>2011-06-24T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-06-24:/blog/ngeohash-node-module-for-geohash-algorithm.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;GIS&lt;/li&gt;
&lt;li&gt;javascript&lt;/li&gt;
&lt;li&gt;nodejs&lt;/li&gt;
&lt;li&gt;opensource
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;ngeohash是一个geohash的javascript实现，之所以叫做ngeohash是因为到了Publish的时候才发现已经有geohash这个module了。这么令人沮丧的事就不多说了。&lt;/p&gt;

&lt;p&gt;安装
&lt;font face="monospace"&gt;npm install ngeohash
&lt;/font&gt;&lt;/p&gt;

&lt;p&gt;使用&lt;br /&gt;
var geohash = require('ngeohash');&lt;br /&gt;
sys.puts(geohash.encode(32.1717, 118.2342));&lt;/p&gt;

&lt;p&gt;详 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;GIS&lt;/li&gt;
&lt;li&gt;javascript&lt;/li&gt;
&lt;li&gt;nodejs&lt;/li&gt;
&lt;li&gt;opensource
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;ngeohash是一个geohash的javascript实现，之所以叫做ngeohash是因为到了Publish的时候才发现已经有geohash这个module了。这么令人沮丧的事就不多说了。&lt;/p&gt;

&lt;p&gt;安装
&lt;font face="monospace"&gt;npm install ngeohash
&lt;/font&gt;&lt;/p&gt;

&lt;p&gt;使用&lt;br /&gt;
var geohash = require('ngeohash');&lt;br /&gt;
sys.puts(geohash.encode(32.1717, 118.2342));&lt;/p&gt;

&lt;p&gt;详细&lt;br /&gt;
访问github的相关页面：&lt;a href="https://github.com/sunng87/node-geohash"&gt;https://github.com/sunng87/node-geohash
&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="zemanta-pixie"&gt;&lt;img class="zemanta-pixie-img" alt="" src="http://img.zemanta.com/pixy.gif?x-id=62cf2f06-ce63-87a6-a76b-5899455660e7" /&gt;&lt;/div&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Checkout my forked version of reddit-is-fun</title><link href="https://sunng.info/blog/checkout-my-forked-version-of-reddit-is-fun.html" rel="alternate"></link><published>2011-06-15T00:00:00+08:00</published><updated>2011-06-15T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-06-15:/blog/checkout-my-forked-version-of-reddit-is-fun.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;android&lt;/li&gt;
&lt;li&gt;opensource&lt;/li&gt;
&lt;li&gt;reddit
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;每天上下班要花掉两个多小时，这一路只好浏览一些新闻、reddit以及HN。reddit的app相对来说不算多，找了一圈倒是在market里没找到的reddit-is-fun最好用，功能最全。HN的app最近添加的一个小功能非常实用，通过 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;android&lt;/li&gt;
&lt;li&gt;opensource&lt;/li&gt;
&lt;li&gt;reddit
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;每天上下班要花掉两个多小时，这一路只好浏览一些新闻、reddit以及HN。reddit的app相对来说不算多，找了一圈倒是在market里没找到的reddit-is-fun最好用，功能最全。HN的app最近添加的一个小功能非常实用，通过viewtext.org打开链接。使用这个服务，可以提取出页面的正文部分，既减少了流量又提高了阅读体验。于是我想到给reddit-is-fun添加这个功能。reddit-is-fun是个GPLv3的软件，程序本身就在github上，对Android一无所知的我花了两个小时完成了这个功能。感谢IDEA，感谢IDEA的Android插件。&lt;/p&gt;

&lt;p&gt;给作者发了&lt;a href="https://github.com/talklittle/reddit-is-fun/pull/149"&gt;pull request&lt;/a&gt;，不过作者最近似乎没有什么实质性的动作。不等他了，今天先把包含这个功能的fork放出来，大家如果感兴趣可以下载安装。这个fork的版本为1.1.4-sunng0，应该不会影响未来升级的吧（我猜啊）。&lt;/p&gt;

&lt;p&gt;可以通过这个码下载：
&lt;img src="https://chart.googleapis.com/chart?cht=qr&amp;amp;chs=300x300&amp;amp;chl=https://github.com/downloads/sunng87/reddit-is-fun/reddit_is_fun-sunng-fork.apk" alt="https://github.com/downloads/sunng87/reddit-is-fun/reddit_is_fun-sunng-fork.apk" /&gt;&lt;/p&gt;

&lt;p&gt;链接地址：
&lt;a href="https://github.com/downloads/sunng87/reddit-is-fun/reddit_is_fun-sunng-fork.apk"&gt;https://github.com/downloads/sunng87/reddit-is-fun/reddit_is_fun-sunng-fork.apk&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="zemanta-pixie"&gt;&lt;img class="zemanta-pixie-img" alt="" src="http://img.zemanta.com/pixy.gif?x-id=9d42ee6d-b2e5-8b52-a96c-e6e2dcd87c53" /&gt;&lt;/div&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Update on HeatCanvas and gefr</title><link href="https://sunng.info/blog/update-on-heatcanvas-and-gefr.html" rel="alternate"></link><published>2011-06-14T00:00:00+08:00</published><updated>2011-06-14T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-06-14:/blog/update-on-heatcanvas-and-gefr.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;gefr&lt;/li&gt;
&lt;li&gt;heatcanvas&lt;/li&gt;
&lt;li&gt;javascript&lt;/li&gt;
&lt;li&gt;jython&lt;/li&gt;
&lt;li&gt;opensource&lt;/li&gt;
&lt;li&gt;project
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;h3&gt;HeatCanvas的百度地图扩展&lt;/h3&gt;&lt;/p&gt;

&lt;p&gt;感谢@lbt05姐的无私贡献，现在HeatCanvas又增加了百度地图API支持。你可以在你的百度地图中使用heat map了。详情可以参考&lt;a href="http://nihuajie.com/blog/?p=469"&gt;@lbt05姐撰写的文档&lt;/a&gt;，而&lt;a href="http://sunng87.github.com/heatcanvas/baidumap.html"&gt;这里&lt;/a&gt;是一个简单的 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;gefr&lt;/li&gt;
&lt;li&gt;heatcanvas&lt;/li&gt;
&lt;li&gt;javascript&lt;/li&gt;
&lt;li&gt;jython&lt;/li&gt;
&lt;li&gt;opensource&lt;/li&gt;
&lt;li&gt;project
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;h3&gt;HeatCanvas的百度地图扩展&lt;/h3&gt;&lt;/p&gt;

&lt;p&gt;感谢@lbt05姐的无私贡献，现在HeatCanvas又增加了百度地图API支持。你可以在你的百度地图中使用heat map了。详情可以参考&lt;a href="http://nihuajie.com/blog/?p=469"&gt;@lbt05姐撰写的文档&lt;/a&gt;，而&lt;a href="http://sunng87.github.com/heatcanvas/baidumap.html"&gt;这里&lt;/a&gt;是一个简单的live demo。此外，@lbt05姐还贡献了GoogleMap扩展的patch，帮助我解决了地图拖动后Canvas无法覆盖viewport的bug。再次感谢&lt;a href="http://twitter.com/lbt05"&gt;@lbt05&lt;/a&gt;姐。&lt;/p&gt;

&lt;p&gt;百度地图支持已经汇入主干，可以在&lt;a href="https://github.com/sunng87/heatcanvas"&gt;github&lt;/a&gt;找到它。&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;Gefr新增 Jetty WSGI Bridge&lt;/h3&gt;&lt;/p&gt;

&lt;p&gt;昨天为&lt;a href="https://bitbucket.org/sunng/gefr"&gt;gefr&lt;/a&gt;新增了Jetty服务器的支持，这样你可以将自己的Python WSGI程序运行在成熟的Jetty服务器上。性能是大家关心的因素，下面是在我的本机（32位CentOS，双核2.8GHz，4G内存）上一个粗略的测试结果：&lt;/p&gt;

&lt;p&gt;100并发，20000请求：
&lt;blockquote&gt;
Server Software:        gefr-jetty/0.3dev&lt;br /&gt;
Server Hostname:        localhost&lt;br /&gt;
Server Port:            8088&lt;/blockquote&gt;&lt;/p&gt;

&lt;p&gt;Document Path:          /&lt;br /&gt;
Document Length:        19 bytes&lt;/p&gt;

&lt;p&gt;Concurrency Level:      100&lt;br /&gt;
Time taken for tests:   3.120827 seconds&lt;br /&gt;
Complete requests:      20000&lt;br /&gt;
Failed requests:        0&lt;br /&gt;
Write errors:           0&lt;br /&gt;
Total transferred:      2584515 bytes&lt;br /&gt;
HTML transferred:       380665 bytes&lt;br /&gt;
Requests per second:    6408.56 [#/sec] (mean)&lt;br /&gt;
Time per request:       15.604 [ms] (mean)&lt;br /&gt;
Time per request:       0.156 [ms] (mean, across all concurrent requests)&lt;br /&gt;
Transfer rate:          808.44 [Kbytes/sec] received&lt;/p&gt;

&lt;p&gt;Connection Times (ms)&lt;br /&gt;
              min  mean[+/-sd] median   max&lt;br /&gt;
Connect:        0    4 114.1      0    3000&lt;br /&gt;
Processing:     0    7  18.5      5    1776&lt;br /&gt;
Waiting:        0    6  18.5      4    1776&lt;br /&gt;
Total:          0   12 115.8      6    3015&lt;/p&gt;

&lt;p&gt;Percentage of the requests served within a certain time (ms)&lt;br /&gt;
  50%      6&lt;br /&gt;
  66%      7&lt;br /&gt;
  75%      8&lt;br /&gt;
  80%      8&lt;br /&gt;
  90%     10&lt;br /&gt;
  95%     13&lt;br /&gt;
  98%     70&lt;br /&gt;
  99%     74&lt;br /&gt;
 100%   3015 (longest request)
&lt;/p&gt;

&lt;p&gt;如果打开KeepAlive，吞吐量可以达到10000以上：&lt;/p&gt;

&lt;p&gt;&lt;blockquote&gt;
Server Software:        gefr-jetty/0.3dev&lt;br /&gt;
Server Hostname:        localhost&lt;br /&gt;
Server Port:            8088&lt;/blockquote&gt;&lt;/p&gt;

&lt;p&gt;Document Path:          /&lt;br /&gt;
Document Length:        19 bytes&lt;/p&gt;

&lt;p&gt;Concurrency Level:      100&lt;br /&gt;
Time taken for tests:   1.749189 seconds&lt;br /&gt;
Complete requests:      20000&lt;br /&gt;
Failed requests:        0&lt;br /&gt;
Write errors:           0&lt;br /&gt;
Keep-Alive requests:    20000&lt;br /&gt;
Total transferred:      3062142 bytes&lt;br /&gt;
HTML transferred:       380266 bytes&lt;br /&gt;
Requests per second:    11433.87 [#/sec] (mean)&lt;br /&gt;
Time per request:       8.746 [ms] (mean)&lt;br /&gt;
Time per request:       0.087 [ms] (mean, across all concurrent requests)&lt;br /&gt;
Transfer rate:          1709.36 [Kbytes/sec] received&lt;/p&gt;

&lt;p&gt;Connection Times (ms)&lt;br /&gt;
              min  mean[+/-sd] median   max&lt;br /&gt;
Connect:        0    0   0.8      0      23&lt;br /&gt;
Processing:     0    8   8.0      6      85&lt;br /&gt;
Waiting:        0    8   8.0      6      85&lt;br /&gt;
Total:          0    8   8.1      6      85&lt;/p&gt;

&lt;p&gt;Percentage of the requests served within a certain time (ms)&lt;br /&gt;
  50%      6&lt;br /&gt;
  66%      7&lt;br /&gt;
  75%      8&lt;br /&gt;
  80%      9&lt;br /&gt;
  90%     11&lt;br /&gt;
  95%     20&lt;br /&gt;
  98%     41&lt;br /&gt;
  99%     43&lt;br /&gt;
 100%     85 (longest request)
&lt;/p&gt;

&lt;p&gt;你可以根据&lt;a href="http://gefr.rtfd.org/"&gt;gefr的文档&lt;/a&gt;安装它。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>'jip.embed: On-the-fly classpath resolution for Jython'</title><link href="https://sunng.info/blog/jipembed-on-the-fly-classpath-resolution-for-jython.html" rel="alternate"></link><published>2011-06-10T00:00:00+08:00</published><updated>2011-06-10T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-06-10:/blog/jipembed-on-the-fly-classpath-resolution-for-jython.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;jip&lt;/li&gt;
&lt;li&gt;jython&lt;/li&gt;
&lt;li&gt;opensource&lt;/li&gt;
&lt;li&gt;project
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;jip 0.7 introduces a module called jip.embed, which allows you to add libraries to your code in the runtime as you declare them. With jip.embed, you don't have to download jars manually and append them to your …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;jip&lt;/li&gt;
&lt;li&gt;jython&lt;/li&gt;
&lt;li&gt;opensource&lt;/li&gt;
&lt;li&gt;project
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;jip 0.7 introduces a module called jip.embed, which allows you to add libraries to your code in the runtime as you declare them. With jip.embed, you don't have to download jars manually and append them to your -Dpython.path. You just pick your editor, import jip.embed, code your business, then save and run it.&lt;/p&gt;

&lt;p&gt;Code example:&lt;/p&gt;

&lt;p&gt;[cc lang="python"]&lt;br /&gt;
import jip.embed&lt;br /&gt;
jip.embed.require('commons-lang:commons-lang:2.6')&lt;br /&gt;
from org.apache.commons.lang import StringUtils&lt;/p&gt;

&lt;p&gt;print StringUtils.reverse('jip rocks')&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;Output:&lt;br /&gt;
[cc]&lt;br /&gt;
skcor pij&lt;br /&gt;
[/cc]&lt;br /&gt;
(jip will print some log here if dependencies are included for first time)&lt;/p&gt;

&lt;p&gt;So please check out my new released jip, 0.7:
&lt;a href="https://github.com/sunng87/jip"&gt;https://github.com/sunng87/jip&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For virtualenv user, you can install full-featured jip via pip:
&lt;font face="monospace"&gt;$ pip install jip&lt;/font&gt;&lt;/p&gt;

&lt;p&gt;To install jip globally, download the package from &lt;a href="http://pypi.python.org/pypi/jip"&gt;python cheese shop&lt;/a&gt; and run:
&lt;font face="monospace"&gt;$ jython setup.py install &lt;/font&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="zemanta-pixie"&gt;&lt;img class="zemanta-pixie-img" alt="" src="http://img.zemanta.com/pixy.gif?x-id=36db589c-cfdd-8173-8d89-3b7aeb29cb5c" /&gt;&lt;/div&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>OpenLayers extension for HeatCanvas</title><link href="https://sunng.info/blog/openlayers-extension-for-heatcanvas.html" rel="alternate"></link><published>2011-06-06T00:00:00+08:00</published><updated>2011-06-06T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-06-06:/blog/openlayers-extension-for-heatcanvas.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;heatcanvas&lt;/li&gt;
&lt;li&gt;javascript&lt;/li&gt;
&lt;li&gt;OpenStreetMap&lt;/li&gt;
&lt;li&gt;project
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;img src="http://i.imgur.com/rtEuI.png" alt="" title="Hosted by imgur.com" /&gt;&lt;/p&gt;

&lt;p&gt;As the screenshot shows, now you can embed HeatCanavs in your OpenLayers application, as well as OpenSteetMap.&lt;/p&gt;

&lt;p&gt;Three steps to create such kind of map:&lt;/p&gt;

&lt;p&gt;1. Create HeatCanvas layer:&lt;br /&gt;
[cc lang="javascript"]&lt;br /&gt;
// constructor params:&lt;br /&gt;
// name, OpenLayers map instance, OpenLayers layer …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;heatcanvas&lt;/li&gt;
&lt;li&gt;javascript&lt;/li&gt;
&lt;li&gt;OpenStreetMap&lt;/li&gt;
&lt;li&gt;project
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;img src="http://i.imgur.com/rtEuI.png" alt="" title="Hosted by imgur.com" /&gt;&lt;/p&gt;

&lt;p&gt;As the screenshot shows, now you can embed HeatCanavs in your OpenLayers application, as well as OpenSteetMap.&lt;/p&gt;

&lt;p&gt;Three steps to create such kind of map:&lt;/p&gt;

&lt;p&gt;1. Create HeatCanvas layer:&lt;br /&gt;
[cc lang="javascript"]&lt;br /&gt;
// constructor params:&lt;br /&gt;
// name, OpenLayers map instance, OpenLayers layer options, HeatCanvas options&lt;br /&gt;
var heatmap = new OpenLayers.Layer.HeatCanvas("HeatCanvas", map, {},&lt;br /&gt;
        {'step':0.3, 'degree':HeatCanvas.QUAD, 'opacity':0.8});&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;2. Feed some data on layer:&lt;br /&gt;
[cc lang="javascript"]&lt;br /&gt;
heatmap.pushData(latitude, longitude, value);&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;3. Add layer to map:&lt;br /&gt;
[cc lang="javascript"]&lt;br /&gt;
map.addLayer(heatmap);&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;The live demo is hosted on github:
&lt;a href="http://sunng87.github.com/heatcanvas/openstreetmap.html"&gt;http://sunng87.github.com/heatcanvas/openstreetmap.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/sunng87/heatcanvas"&gt;HeatCanavs&lt;/a&gt; is a heat map implementation on HTML5 canvas and WebWorker API.&lt;br /&gt;&lt;br /&gt;&lt;div class="zemanta-pixie"&gt;&lt;img class="zemanta-pixie-img" alt="" src="http://img.zemanta.com/pixy.gif?x-id=c938a108-0a36-8a66-b89a-693e8cf88e05" /&gt;&lt;/div&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Take my coffee</title><link href="https://sunng.info/blog/take-my-coffee.html" rel="alternate"></link><published>2011-06-04T00:00:00+08:00</published><updated>2011-06-04T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-06-04:/blog/take-my-coffee.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;当时&lt;/li&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;coffee&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;是coffee不是Java也不是CoffeeScript。买了两个月的咖啡机，到今天才终于把磨豆机和拉花杯都制备齐了。我上周用搪瓷缸打牛 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;当时&lt;/li&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;coffee&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;是coffee不是Java也不是CoffeeScript。买了两个月的咖啡机，到今天才终于把磨豆机和拉花杯都制备齐了。我上周用搪瓷缸打牛奶场面真是残不忍睹。不过我发现其实设备区别不大，蒸汽的掌握还是很重要的；牛奶的选择也很重要，鉴于家里没有其他牛奶可选，这部分跳过。蒸汽喷嘴与牛奶的举例很重要，我的咖啡机上还有个橡胶嘴我今天把它彻底扔掉了，直接用金属的喷嘴即可。按照某视频介绍，蒸汽嘴距离牛奶表面3毫米。这个距离如果太大就会导致牛奶乱喷，如果直接把喷嘴伸进牛奶我还没有尝试。&lt;/p&gt;

&lt;p&gt;打好的牛奶就是这个样子，对不住大家的是，焦点跑了。
&lt;a href="http://www.flickr.com/photos/40741608@N08/5795245711/" title="IMG_0647 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm3.static.flickr.com/2368/5795245711_d40c2afed9.jpg" alt="IMG_0647" height="500" width="375" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;加上我们刚喷好的咖啡
&lt;a href="http://www.flickr.com/photos/40741608@N08/5795806680/" title="IMG_0650 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm4.static.flickr.com/3622/5795806680_6bcb9c4cab.jpg" alt="IMG_0650" height="375" width="500" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;就变成了这个样子
&lt;a href="http://www.flickr.com/photos/40741608@N08/5795248727/" title="IMG_0651 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm4.static.flickr.com/3647/5795248727_8d7a99fd7d.jpg" alt="IMG_0651" height="375" width="500" /&gt;&lt;/a&gt;
（我晕，焦点又跑了）&lt;/p&gt;

&lt;p&gt;这个算是cappuccino吗。。。&lt;/p&gt;

&lt;p&gt;最后还有一点咖啡，不要浪费了，搞点艺术创作：
&lt;a href="http://www.flickr.com/photos/40741608@N08/5795810074/" title="IMG_0652 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm4.static.flickr.com/3608/5795810074_a7061ca08e.jpg" alt="IMG_0652" height="375" width="500" /&gt;&lt;/a&gt;
焦点你最后终于来了。&lt;br /&gt;&lt;br /&gt;&lt;div class="zemanta-pixie"&gt;&lt;img class="zemanta-pixie-img" alt="" src="http://img.zemanta.com/pixy.gif?x-id=5c368ad7-7cee-8bbf-a47f-4c2001f11dd1" /&gt;&lt;/div&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>HeatCanvas hits 1.0, and public available</title><link href="https://sunng.info/blog/heatcanvas-hits-10-and-public-available.html" rel="alternate"></link><published>2011-06-03T00:00:00+08:00</published><updated>2011-06-03T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-06-03:/blog/heatcanvas-hits-10-and-public-available.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;heatcanvas&lt;/li&gt;
&lt;li&gt;html5&lt;/li&gt;
&lt;li&gt;javascript&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;As described in Wikipedia, a heat map is a graphical representation of data in a two-dimensional table. HeatCanvas enables heat map on HTML5 canvas. With HeatCanvas, you can visualize your data on modern web browser without server-side support.&lt;/p&gt;

&lt;p&gt;HeatCanvas …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;heatcanvas&lt;/li&gt;
&lt;li&gt;html5&lt;/li&gt;
&lt;li&gt;javascript&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;As described in Wikipedia, a heat map is a graphical representation of data in a two-dimensional table. HeatCanvas enables heat map on HTML5 canvas. With HeatCanvas, you can visualize your data on modern web browser without server-side support.&lt;/p&gt;

&lt;p&gt;HeatCanvas is based on &lt;a href="http://sunng.info/blog/2010/02/sunngs-canvas-based-heatmap-api/"&gt;the prototype&lt;/a&gt; I wrote 15 months ago. I just rewrite the whole with WebWorker API to keep user away from UI frozen and annoying slow-script warning. HeatCanvas is implemented as pixel based, so the image quality is great.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://i.imgur.com/gNHOw.png" alt="" title="Hosted by imgur.com" /&gt;&lt;/p&gt;

&lt;p&gt;The API is rather simple. There are only three steps to create a basic heat map.&lt;/p&gt;

&lt;p&gt;1. Create the heat map object:&lt;br /&gt;
[cc lang="javascript"]&lt;br /&gt;
var heatmap = new HeatCanvas('canvasId');&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;2. Add some data to heat map:&lt;br /&gt;
[cc lang="javascript"]&lt;br /&gt;
heatmap.push(223, 98, 10); // x,y and value for this point&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;3. Render it:&lt;br /&gt;
[cc lang="javascript"]&lt;br /&gt;
heatmap.render();&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;In contrast to the base API, HeatCanvas also supports flexible options to customize the rendering. Even custom colour scheme is allowed. You can &lt;a href="https://github.com/sunng87/heatcanvas/blob/master/README.md"&gt;refer to the doc&lt;/a&gt; for detail.&lt;/p&gt;

&lt;p&gt;And for your convenience, we have a GoogleMap plugin, &lt;b&gt;HeatCanvasOverlayView&lt;/b&gt;, that wraps HeatCanvas. You can use it in your GoogleMap application.&lt;/p&gt;

&lt;p&gt;For live demos, you can find at:
&lt;a href="http://sunng87.github.com/heatcanvas/"&gt;http://sunng87.github.com/heatcanvas/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As always, the project is hosted on github:
&lt;a href="https://github.com/sunng87/heatcanvas"&gt;https://github.com/sunng87/heatcanvas&lt;/a&gt; .&lt;/p&gt;

&lt;p&gt;The three javascript files are only necessary in your application, thus, please ignore the htmls in your deployment. Any feedback is welcomed and also please kindly let me know your application using this API.&lt;br /&gt;&lt;br /&gt;&lt;div class="zemanta-pixie"&gt;&lt;img class="zemanta-pixie-img" alt="" src="http://img.zemanta.com/pixy.gif?x-id=9343259e-039c-8fdf-8721-dbd6277de695" /&gt;&lt;/div&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>网站五月访问量的可视化</title><link href="https://sunng.info/blog/wang-zhan-wu-yue-fang-wen-liang-de-ke-shi-hua.html" rel="alternate"></link><published>2011-06-01T00:00:00+08:00</published><updated>2011-06-01T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-06-01:/blog/wang-zhan-wu-yue-fang-wen-liang-de-ke-shi-hua.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;visualization&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;img src="http://i.imgur.com/cc0wj.png" alt="" title="Hosted by imgur.com" /&gt;&lt;/p&gt;

&lt;p&gt;今天用正在调试中的heatmap做了一个网站访问量的可视化。数据从google analytics得到，再通过我本地的geonames数据库做了geocoding（用google的就太慢了）。&lt;a href="http://sunng.info/heatmap/googlemap.html"&gt;这里&lt;/a&gt;有一个live版本。&lt;/p&gt;

&lt;p&gt;关于这个heatmap目前还在开发中，很快会介绍 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;visualization&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;img src="http://i.imgur.com/cc0wj.png" alt="" title="Hosted by imgur.com" /&gt;&lt;/p&gt;

&lt;p&gt;今天用正在调试中的heatmap做了一个网站访问量的可视化。数据从google analytics得到，再通过我本地的geonames数据库做了geocoding（用google的就太慢了）。&lt;a href="http://sunng.info/heatmap/googlemap.html"&gt;这里&lt;/a&gt;有一个live版本。&lt;/p&gt;

&lt;p&gt;关于这个heatmap目前还在开发中，很快会介绍给大家的。&lt;br /&gt;&lt;br /&gt;&lt;div class="zemanta-pixie"&gt;&lt;img class="zemanta-pixie-img" alt="" src="http://img.zemanta.com/pixy.gif?x-id=a1aebb1c-f22a-8723-aff4-16528e75dae4" /&gt;&lt;/div&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>手动设置Fedora15网卡</title><link href="https://sunng.info/blog/shou-dong-she-zhi-fedora15wang-qia.html" rel="alternate"></link><published>2011-05-28T00:00:00+08:00</published><updated>2011-05-28T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-05-28:/blog/shou-dong-she-zhi-fedora15wang-qia.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;fedora&lt;/li&gt;
&lt;li&gt;linux
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;今天上午的Fedora更新，版本为&lt;font face="monospace"&gt;0.8.999.3&lt;/font&gt;的NetworkManager会致使NetworkManager和network service的版本不一致，从而导致网络功能无法使用。而这个时候要通过&lt;font face="monospace"&gt;yum downgrade NetworkManager*&lt;/font&gt;降级 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;fedora&lt;/li&gt;
&lt;li&gt;linux
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;今天上午的Fedora更新，版本为&lt;font face="monospace"&gt;0.8.999.3&lt;/font&gt;的NetworkManager会致使NetworkManager和network service的版本不一致，从而导致网络功能无法使用。而这个时候要通过&lt;font face="monospace"&gt;yum downgrade NetworkManager*&lt;/font&gt;降级又没有网络连接可用，悲剧。&lt;/p&gt;

&lt;p&gt;这时只有手动设置网卡了。&lt;font face="monospace"&gt;service network restart&lt;/font&gt;和 &lt;font face="monospace"&gt;service NetworkManager restart&lt;/font&gt;都报告失败，&lt;font face="monospace"&gt;ifconfig&lt;/font&gt;查看网卡，只有本地回环启动。&lt;/p&gt;

&lt;p&gt;接下来接上网线，通过 &lt;font face="monospace"&gt;ifconfig -a&lt;/font&gt; 查看所有的网卡接口，例如我的接口叫做&lt;font face="monospace"&gt;em2&lt;/font&gt;.&lt;/p&gt;

&lt;p&gt;启动网卡
&lt;font face="monospace"&gt;ifconfig em2 up&lt;/font&gt;&lt;/p&gt;

&lt;p&gt;查看网卡情况
&lt;font face="monospace"&gt;ifconfig em2&lt;/font&gt;&lt;/p&gt;

&lt;p&gt;如果没有获得ip，可以通过dhcpclient获得ip
&lt;font face="monospace"&gt;dhclient -4 em2&lt;/font&gt;&lt;/p&gt;

&lt;p&gt;再查看网卡情况，如果获得了ip，可以尝试&lt;font face="monospace"&gt;ping www.baidu.com&lt;/font&gt;，如果出现network unreachable的报错，需要再配置一下路由信息，其中gw是你的网关：
&lt;font face="monospace"&gt;route add default gw 192.168.1.1&lt;/font&gt;&lt;/p&gt;

&lt;p&gt;这时你的网络应该暂时可用了，立刻执行&lt;font face="monospace"&gt;yum downgrade NetworkManager*&lt;/font&gt;降级吧。&lt;br /&gt;&lt;br /&gt;&lt;div class="zemanta-pixie"&gt;&lt;img class="zemanta-pixie-img" alt="" src="http://img.zemanta.com/pixy.gif?x-id=551018aa-7e6c-81e6-b79a-bc1751d9b7be" /&gt;&lt;/div&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>jip 0.5.1 released</title><link href="https://sunng.info/blog/jip-051-released.html" rel="alternate"></link><published>2011-05-15T00:00:00+08:00</published><updated>2011-05-15T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-05-15:/blog/jip-051-released.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;jip&lt;/li&gt;
&lt;li&gt;jython&lt;/li&gt;
&lt;li&gt;project
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;I just rolled out &lt;a href="https://github.com/sunng87/jip/tree/v0.5.1"&gt;jip 0.5.1&lt;/a&gt; as a bugfix version of 0.5. It has been published to &lt;a href="http://pypi.python.org/pypi/jip"&gt;pypi&lt;/a&gt; and you can install it with &lt;font face="monospace"&gt;pip install jip&lt;/font&gt; in your virtualenv.&lt;/p&gt;

&lt;p&gt;From 0.5, you have new features available …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;jip&lt;/li&gt;
&lt;li&gt;jython&lt;/li&gt;
&lt;li&gt;project
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;I just rolled out &lt;a href="https://github.com/sunng87/jip/tree/v0.5.1"&gt;jip 0.5.1&lt;/a&gt; as a bugfix version of 0.5. It has been published to &lt;a href="http://pypi.python.org/pypi/jip"&gt;pypi&lt;/a&gt; and you can install it with &lt;font face="monospace"&gt;pip install jip&lt;/font&gt; in your virtualenv.&lt;/p&gt;

&lt;p&gt;From 0.5, you have new features available:
&lt;ul&gt;
&lt;li&gt;User-Agent '&lt;font face="monospace"&gt;jip/0.5&lt;/font&gt;' is added to http request.&lt;/li&gt;
&lt;li&gt;New command `&lt;font face="monospace"&gt;freeze&lt;/font&gt;` just like pip.&lt;/li&gt;
&lt;li&gt;Improved jar downloading. By default, at most 3 jars are downloaded in parall.&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;In 0.5.1, bugs were fixed:
&lt;ul&gt;
&lt;li&gt;Repositories defined in pom are now included for &lt;font face="monospace"&gt;install&lt;/font&gt; command.&lt;/li&gt;
&lt;li&gt;Placeholder resolving of &lt;font face="monospace"&gt;#{pom.groupId}&lt;/font&gt; is corrected. (&lt;a href="http://repo1.maven.org/maven2/com/thoughtworks/paranamer/paranamer-ant/2.2/paranamer-ant-2.2.pom"&gt;instance&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;font face="monospace"&gt;urllib2.URLError&lt;/font&gt; is caught to prevent dump.&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;For any problem and feature request, please refer to &lt;a href="https://github.com/sunng87/jip/issues"&gt;github issue tracker&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;div class="zemanta-pixie"&gt;&lt;img class="zemanta-pixie-img" alt="" src="http://img.zemanta.com/pixy.gif?x-id=cd9406ce-d82b-81e1-b547-7c00f498be8b" /&gt;&lt;/div&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>用coffeescript重写了首页</title><link href="https://sunng.info/blog/yong-coffeescriptzhong-xie-liao-shou-ye.html" rel="alternate"></link><published>2011-05-13T00:00:00+08:00</published><updated>2011-05-13T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-05-13:/blog/yong-coffeescriptzhong-xie-liao-shou-ye.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;coffeescript&lt;/li&gt;
&lt;li&gt;javascript&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;下午有时间看了一下&lt;a href="http://jashkenas.github.com/coffee-script/"&gt;coffeescript&lt;/a&gt;，发现非常优雅。过去对JavaScript的工作，从dojo到jquery主要是在库的层面，这次coffeescript的出现是在语言层面对javascript做了一次加强。现在对语言的加强不再需要mozilla/microsoft/google …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;coffeescript&lt;/li&gt;
&lt;li&gt;javascript&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;下午有时间看了一下&lt;a href="http://jashkenas.github.com/coffee-script/"&gt;coffeescript&lt;/a&gt;，发现非常优雅。过去对JavaScript的工作，从dojo到jquery主要是在库的层面，这次coffeescript的出现是在语言层面对javascript做了一次加强。现在对语言的加强不再需要mozilla/microsoft/google/apple几家坐下来慢慢聊了，民间的力量就可以实现。&lt;/p&gt;

&lt;p&gt;coffeescript的几个特点也属于博采众长：
&lt;ul&gt;
&lt;li&gt;类似python的，以缩进取代代码块&lt;/li&gt;
&lt;li&gt;类似ruby/perl，方法调用括号可选&lt;/li&gt;
&lt;li&gt;类似ruby，无须return&lt;/li&gt;
&lt;li&gt;正常的类声明语法&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;在nodejs的环境里可以通过npm安装coffer-script，利用它的命令行程序，可以把coffeescript编译成javascript。不过最给力的是可以在网页里通过&lt;a href="http://jashkenas.github.com/coffee-script/#scripts"&gt;引入coffee-script.js&lt;/a&gt;来直接执行（其实也是编译一下）coffeescript。&lt;/p&gt;

&lt;p&gt;于是我顺手重写了一下首页的js，改写成coffeescript。因为少打很多() {}，手可以控制在键盘中心的，敲字的效率大大提高。用空你也可以尝试一下。&lt;br /&gt;&lt;br /&gt;&lt;div class="zemanta-pixie"&gt;&lt;img class="zemanta-pixie-img" alt="" src="http://img.zemanta.com/pixy.gif?x-id=c7456ff3-1bf5-8c54-b037-86800bd4f910" /&gt;&lt;/div&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>安装Exaile豆瓣电台插件及Gnome-Shell扩展</title><link href="https://sunng.info/blog/an-zhuang-exailedou-ban-dian-tai-cha-jian-ji-gnome-shellkuo-zhan.html" rel="alternate"></link><published>2011-05-09T00:00:00+08:00</published><updated>2011-05-09T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-05-09:/blog/an-zhuang-exailedou-ban-dian-tai-cha-jian-ji-gnome-shellkuo-zhan.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;exaile&lt;/li&gt;
&lt;li&gt;gnome-shell
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;介绍一下新版本的Exaile豆瓣电台插件和新的GNOME-Shell扩展的安装。&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;下载&lt;/h3&gt;
从github上下载相应的版本：
&lt;pre&gt;$ wget --no-check-certificate https://github.com/sunng87/exaile-doubanfm-plugin/zipball/0.0.10-dbus&lt;/pre&gt;
&lt;pre&gt;$ wget --no-check-certificate https://github.com/sunng87/exaile-doubanfm-gnome-shell-extension/zipball/0.0.1&lt;/pre&gt;
&lt;pre&gt;$ ls
sunng87-exaile-doubanfm-gnome-shell-extension-0.0.1-0-g5cc29c6.zip
sunng87-exaile-doubanfm-plugin-0.0 …&lt;/pre&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;exaile&lt;/li&gt;
&lt;li&gt;gnome-shell
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;介绍一下新版本的Exaile豆瓣电台插件和新的GNOME-Shell扩展的安装。&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;下载&lt;/h3&gt;
从github上下载相应的版本：
&lt;pre&gt;$ wget --no-check-certificate https://github.com/sunng87/exaile-doubanfm-plugin/zipball/0.0.10-dbus&lt;/pre&gt;
&lt;pre&gt;$ wget --no-check-certificate https://github.com/sunng87/exaile-doubanfm-gnome-shell-extension/zipball/0.0.1&lt;/pre&gt;
&lt;pre&gt;$ ls
sunng87-exaile-doubanfm-gnome-shell-extension-0.0.1-0-g5cc29c6.zip
sunng87-exaile-doubanfm-plugin-0.0.10-dbus-0-g90f7175.zip
&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;解压安装&lt;/h3&gt;
如果你已经安装过exaile豆瓣电台插件，可以先删除旧版本：
&lt;pre&gt;$ rm -rf ~/.local/share/exaile/plugins/doubanfm&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;解压缩下载的文件：
&lt;pre&gt;$ unzip sunng87-exaile-doubanfm-plugin-0.0.10-dbus-0-g90f7175.zip
$ unzip sunng87-exaile-doubanfm-gnome-shell-extension-0.0.1-0-g5cc29c6.zip&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;移动到指定的目录：
&lt;pre&gt;$ mv sunng87-exaile-doubanfm-plugin-90f7175/ ~/.local/share/exaile/plugins/doubanfm
$ mv sunng87-exaile-doubanfm-gnome-shell-extension-5cc29c6/ ~/.local/share/gnome-shell/extensions/exaile-doubanfm-gnome-shell-extension&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;配置&lt;/h3&gt;
启动Exaile，通过菜单“编辑-&amp;gt;首选项-&amp;gt;插件”，激活doubanfm，&lt;b&gt;禁用lastfm covers&lt;/b&gt;。如图设置插件：
&lt;img src="http://i.imgur.com/TFKU3.png" title="Hosted by imgur.com" /&gt;&lt;/p&gt;

&lt;p&gt;重启GNOME-Shell，按下alt+f2，输入r，回车。&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;使用&lt;/h3&gt;
启动exaile，可以通过文件菜单打开豆瓣电台频道。可以通过ctrl+d或者视图菜单切换到豆瓣电台视图：
&lt;img src="http://i.imgur.com/1vPpz.png" title="Hosted by imgur.com" /&gt;&lt;/p&gt;

&lt;p&gt;当然也可以通过gnome-shell扩展的菜单控制豆瓣电台的播放。&lt;br /&gt;&lt;br /&gt;&lt;div class="zemanta-pixie"&gt;&lt;img class="zemanta-pixie-img" alt="" src="http://img.zemanta.com/pixy.gif?x-id=ef5913a1-4c72-827d-837e-15abc25dcc92" /&gt;&lt;/div&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>GNOME-Shell Extension for Exaile DoubanFM Plugin</title><link href="https://sunng.info/blog/gnome-shell-extension-for-exaile-doubanfm-plugin.html" rel="alternate"></link><published>2011-05-07T00:00:00+08:00</published><updated>2011-05-07T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-05-07:/blog/gnome-shell-extension-for-exaile-doubanfm-plugin.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;dbus&lt;/li&gt;
&lt;li&gt;Douban&lt;/li&gt;
&lt;li&gt;exaile&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;gnome-shell&lt;/li&gt;
&lt;li&gt;javascript
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;从有这个动机到基本可用，花了整整一天的时间。现在可以通过GNOME-Shell的一个小菜单从外部控制Exaile豆瓣电台了。它的意思就是说如果你 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;dbus&lt;/li&gt;
&lt;li&gt;Douban&lt;/li&gt;
&lt;li&gt;exaile&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;gnome-shell&lt;/li&gt;
&lt;li&gt;javascript
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;从有这个动机到基本可用，花了整整一天的时间。现在可以通过GNOME-Shell的一个小菜单从外部控制Exaile豆瓣电台了。它的意思就是说如果你使用Gnome-Shell，你可以把Exaile界面扔到一个你不想看到的地方去，比如第n+1个workspace。然后通过左上角的菜单来控制豆瓣电台的播放。见多识广的你不会对此感到太惊奇，其实它和Ubuntu上的Sound Menu Indicator类似，不过是专门给豆瓣电台设计的。
&lt;a href="http://www.flickr.com/photos/40741608@N08/5696429062/" title="Exaile with doubanfm plugin by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5104/5696429062_7f62a4d7f2.jpg" alt="Exaile with doubanfm plugin" height="313" width="500" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;要实现这个功能，需要播放器通过dbus来expose一些接口供外部程序调用，它和Sound Menu是一样的，通过dbus来解耦。开发起来比较困难的是目前GNOME-Shell的API还不成熟，还没有文档，传统的javascript object inspect方法似乎在这个mozjs的环境里也不能用，所以实在是无从知道一些API。更不要提通过gjs来调用dbus了。网上有一些文档起了很大的作用，如果你也感兴趣，这些文章是：
&lt;ul&gt;&lt;li&gt;开发GNOME-Shell Extension：&lt;a href="http://blog.fpmurphy.com/2011/04/gnome-3-shell-extensions.html"&gt;http://blog.fpmurphy.com/2011/04/gnome-3-shell-extensions.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;gjs调用dbus： &lt;a href="http://blog.roodo.com/rocksaying/archives/14229429.html"&gt;blog.roodo.com/rocksaying/archives/14229429.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;支持dbus的exaile-doubanfm-plugin会在stable之后合并到主干供大家使用。现在和gjs一样，它的API也不稳定，所以放在分支中：
&lt;a href="https://github.com/sunng87/exaile-doubanfm-plugin/tree/dbus"&gt;https://github.com/sunng87/exaile-doubanfm-plugin/tree/dbus&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;gnome-shell extension的代码现在也放在github上（其实还不成熟的东西放到bitbucket比较好的）。有兴趣可以了解：
&lt;a href="https://github.com/sunng87/exaile-doubanfm-gnome-shell-extension"&gt;https://github.com/sunng87/exaile-doubanfm-gnome-shell-extension&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;以上都是unstable，仅供吊胃口之用。&lt;br /&gt;&lt;br /&gt;&lt;div class="zemanta-pixie"&gt;&lt;img class="zemanta-pixie-img" alt="" src="http://img.zemanta.com/pixy.gif?x-id=a656b959-d8af-8db6-8cbc-e246c779cf1c" /&gt;&lt;/div&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>去西安</title><link href="https://sunng.info/blog/qu-xi-an.html" rel="alternate"></link><published>2011-05-03T00:00:00+08:00</published><updated>2011-05-03T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-05-03:/blog/qu-xi-an.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;当时&lt;/li&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;vocation&lt;/li&gt;
&lt;li&gt;Xi'an
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;除了02年在北京打了几天酱油，我还没有因为打着去玩的旗号出过远门（到镇江这里就不算远门了）。今年五一痛下决心 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;当时&lt;/li&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;vocation&lt;/li&gt;
&lt;li&gt;Xi'an
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;除了02年在北京打了几天酱油，我还没有因为打着去玩的旗号出过远门（到镇江这里就不算远门了）。今年五一痛下决心，卷入五一滚滚洪流，抱着轻度腹胀不下火线的精神，突破了我所到达的经度下限。
&lt;iframe width="425" height="350" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="http://www.openstreetmap.org/export/embed.html?bbox=108.378,33.958,109.39,34.539&amp;amp;layer=mapnik" style="border: 1px solid black"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;small&gt;&lt;a href="http://www.openstreetmap.org/?lat=34.2485&amp;amp;lon=108.884&amp;amp;zoom=10&amp;amp;layers=M"&gt;View Larger Map&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;我坐的K560是一趟开到延安老区的车，老区人民伤不起，一路上尽给到乌鲁木齐到拉萨的车让道了。晚上在上铺作原地翻滚，发现铁皮太厚根本没有信号，为了不虚此行，早晨7点就从上面跳下来，在窗边占个座位打开GPS。抬头一看已经到了开封，不对，才到开封。
&lt;a href="http://www.openstreetmap.org/user/Sunng/traces/1001751"&gt;http://www.openstreetmap.org/user/Sunng/traces/1001751&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;到了西安已经是下午三点半，找到&lt;a href="http://torrge.me/"&gt;老支书&lt;/a&gt;，见到身强体壮的新版寇巨头。晚上我们就在曲江新区迷了路，冥冥之中深夜里摸到了我国第一个官二代并富二代，胡亥长眠的地方。最后还好老支书迷途知返，及时拿出诺基亚表明自己身份，与二代们划清界限，我们才又绕道大雁塔回了住处。
&lt;a href="http://www.flickr.com/photos/40741608@N08/5682156883/" title="DSC_0018 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5069/5682156883_de539a9bc6.jpg" alt="DSC_0018" height="335" width="500" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;第二天是逛城墙。逛城墙之前先说一下出发前吃的腊汁肉揪片，太好吃了，就是油有点大：我当时就指出如果是吃面条吃出高血脂脂肪肝什么的，跟那帮大鱼大肉的比起来实在是有点冤枉。扯回来，西安现存的城墙是明代以后的，保存完好，不过不大，GPS显示周长在15公里左右。从城墙的东西南北四个门上去都可以租到自行车，默认是100分钟，基本上可以很宽裕地把全程骑行下来。
&lt;a href="http://www.openstreetmap.org/user/Sunng/traces/1001753"&gt;http://www.openstreetmap.org/user/Sunng/traces/1001753&lt;/a&gt;
&lt;a href="http://www.flickr.com/photos/40741608@N08/5682732066/" title="DSC_0025 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5023/5682732066_9d8ea3a532.jpg" alt="DSC_0025" height="500" width="335" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;那天天气不太好，本来想拍拍城墙上夕阳斜照点点余辉什么的，结果相机没有被淋雨已经万幸了。况且西安经度比较低，太阳落山的时间要晚不少，执着于点点余辉的话，挑个好天先去吃一顿在上城墙吧。
&lt;a href="http://www.flickr.com/photos/40741608@N08/5682165935/" title="DSC_0035 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5021/5682165935_ea19227047.jpg" alt="DSC_0035" height="335" width="500" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;主题来了，下了城墙我们直奔&lt;del datetime="2011-05-04T12:35:03+00:00"&gt;钟&lt;/del&gt;鼓楼后面的回民街。用老支书的话说，这都是硬菜，花样繁多个个顶事，瓶颈不再别处，在你的胃。去了才发现战斗力低下，我们前后去了两次，吃了羊肉馅饼、羊肉串、镜糕、桂花糕、灌汤包、小酥肉。我就不一一描述了，说得再好吃也得真正去了才能体会。去不了就看看吧：
&lt;a href="http://www.flickr.com/photos/40741608@N08/5683869110/" title="DSC_0056 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5024/5683869110_2fe22459fe.jpg" alt="DSC_0056" height="335" width="500" /&gt;&lt;/a&gt;
&lt;a href="http://www.flickr.com/photos/40741608@N08/5683303271/" title="DSC_0059 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5102/5683303271_4f4490d2b9.jpg" alt="DSC_0059" height="500" width="336" /&gt;&lt;/a&gt;
&lt;a href="http://www.flickr.com/photos/40741608@N08/5683869494/" title="DSC_0058 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5190/5683869494_7ea613f211.jpg" alt="DSC_0058" height="335" width="500" /&gt;&lt;/a&gt;
&lt;a href="http://www.flickr.com/photos/40741608@N08/5683305289/" title="DSC_0067 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5105/5683305289_f0f81393ab.jpg" alt="DSC_0067" height="335" width="500" /&gt;&lt;/a&gt;
&lt;a href="http://www.flickr.com/photos/40741608@N08/5683872666/" title="DSC_0069 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5227/5683872666_fd2a4f9c18.jpg" alt="DSC_0069" height="500" width="336" /&gt;&lt;/a&gt;
这个是卖桂花糕的大叔，pose很专业，但是遇到不专业的人拍照。大叔我对不起你啊！
&lt;a href="http://www.flickr.com/photos/40741608@N08/5683307385/" title="DSC_0077 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5065/5683307385_615a7883d0.jpg" alt="DSC_0077" height="500" width="336" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;更多照片请移步到&lt;a href="http://www.flickr.com/photos/40741608@N08/"&gt;flickr&lt;/a&gt;。此外您应当理解，在这种环境下，是没有心思好好拍照的。&lt;/p&gt;

&lt;p&gt;最后感谢老支书无微不至的接待。&lt;br /&gt;&lt;br /&gt;&lt;div class="zemanta-pixie"&gt;&lt;img class="zemanta-pixie-img" alt="" src="http://img.zemanta.com/pixy.gif?x-id=20f8d469-f3be-8f4a-8906-0f0eccb11b3b" /&gt;&lt;/div&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Convert a Python function to Java anonymous class</title><link href="https://sunng.info/blog/convert-a-python-function-to-java-anonymous-class.html" rel="alternate"></link><published>2011-04-29T00:00:00+08:00</published><updated>2011-04-29T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-04-29:/blog/convert-a-python-function-to-java-anonymous-class.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;jython&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;When calling Java with Jython, anonymous inner class might be an issue because there is no such equivalent in Python.&lt;/p&gt;

&lt;p&gt;In GUI programming, jython made additional effort on AWT event processing. You can pass a python function as some types …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;jython&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;When calling Java with Jython, anonymous inner class might be an issue because there is no such equivalent in Python.&lt;/p&gt;

&lt;p&gt;In GUI programming, jython made additional effort on AWT event processing. You can pass a python function as some types of event listener.&lt;br /&gt;
[cc lang="python"]&lt;br /&gt;
def change_text(event):&lt;br /&gt;
    print 'Clicked!'&lt;/p&gt;

&lt;p&gt;button = JButton('Click Me!', actionPerformed=change_text)&lt;br /&gt;
frame.add(button)&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;Described in the &lt;a href="http://www.jython.org/jythonbook/en/1.0/GUIApplications.html"&gt;Definitive Guide of Jython&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;blockquote&gt;This works because Jython is able to automatically recognize events in Java code if they have corresponding addEvent()* and *removeEvent() methods. Jython takes the name of the event and makes it accessible using the nice Python syntax as long as the event methods are public. &lt;/blockquote&gt;&lt;/p&gt;

&lt;p&gt;However, it does not work with Runnable, Callable and many other interfaces designed for anonymous usage.&lt;/p&gt;

&lt;p&gt;To solve the incompatibility, I created a small decorator that converts Python function to a Java object.
&lt;script src="https://gist.github.com/947926.js"&gt; &lt;/script&gt;&lt;/p&gt;

&lt;p&gt;Thanks to python's dynamic magic, we can create class in runtime and assign modified method to a particular instance. The conversion is performed once the function loaded. Also, you can pass something as arguments to constructor.&lt;/p&gt;

&lt;p&gt;With anonymous_class decorator, the example above can be written as:&lt;br /&gt;
[cc lang="python"]&lt;br /&gt;
from javax.swing import JButton, JFrame&lt;br /&gt;
from java.awt.event import ActionListener&lt;/p&gt;

&lt;p&gt;frame = JFrame('Hello, Jython!',&lt;br /&gt;
            defaultCloseOperation = JFrame.EXIT_ON_CLOSE,&lt;br /&gt;
            size = (300, 300)&lt;br /&gt;
        )&lt;/p&gt;

&lt;p&gt;@anonymous_class(ActionListener, "actionPerformed")&lt;br /&gt;
def change_text(dummy, event):&lt;br /&gt;
    print 'Clicked!'&lt;/p&gt;

&lt;p&gt;button = JButton('Click Me!', actionPerformed=change_text)&lt;br /&gt;
frame.add(button)&lt;br /&gt;
frame.visible = True&lt;br /&gt;
[/cc]&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>GPars的Actor实现</title><link href="https://sunng.info/blog/gparsde-actorshi-xian.html" rel="alternate"></link><published>2011-04-28T00:00:00+08:00</published><updated>2011-04-28T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-04-28:/blog/gparsde-actorshi-xian.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;groovy&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;opensource
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Actor是一种Continuation技术，可以在少量的线程运行大量Actor对象。Actor对象之间通过消息机制进行交互。而Actor本身线程安全，这样的模型使并发编程的复杂度降低，同时也在一定的 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;groovy&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;opensource
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Actor是一种Continuation技术，可以在少量的线程运行大量Actor对象。Actor对象之间通过消息机制进行交互。而Actor本身线程安全，这样的模型使并发编程的复杂度降低，同时也在一定的场景下实现了可扩展性。&lt;/p&gt;

&lt;p&gt;gpars是Java和Groovy都可以使用的并行编程库，他实现了Actor、Agent、DataFlow等模型，旨在为Groovy提供高层的并行编程模型。以下分析gpars 0.12中非阻塞Actor的实现。&lt;/p&gt;

&lt;p&gt;[cc lang="groovy"]&lt;br /&gt;
@Grab("org.codehaus.gpars:gpars:0.11")&lt;br /&gt;
import groovyx.gpars.actor.Actors&lt;/p&gt;

&lt;p&gt;def worker = Actors.actor {&lt;br /&gt;
    loop {&lt;br /&gt;
        react {&lt;br /&gt;
            reply it.reverse()&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;/p&gt;

&lt;p&gt;def console = Actors.actor {&lt;br /&gt;
    worker &lt;&lt; "Hello GPars"&lt;br /&gt;
    react {&lt;br /&gt;
        println it&lt;br /&gt;
    }&lt;br /&gt;
}&lt;/p&gt;

&lt;p&gt;console.join()&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;首先，在工厂类中Actors里会初始化一个DefaultPGroup用来封装后台的线程池、管理actors。Actors默认使用ResizeablePool，他是JDK Concurrent Framework中的ThreadPoolExecutor的封装，coreSize和maxSize不同所以称Resizeable。&lt;/p&gt;

&lt;p&gt;Actors的工厂方法actor生成的是DefaultActor，它是非阻塞actor的默认实现。（ActorGroup:67）&lt;/p&gt;

&lt;p&gt;DefaultActor的构造方法接受一个Groovy的闭包对象，将其封装为DefaultActorClosure对象后，调用其父类AbstractLoopingActor的initialize方法（DefaultActor：73）.&lt;/p&gt;

&lt;p&gt;initialize方法创建一个Runnable对象AsyncMessagingCore，并将线程池传递给core对象。（AbstractLoopingActor:57）AsyncMessagingCore对象负责消息的传递和处理，是线程池处理的目标对象。&lt;/p&gt;

&lt;p&gt;调用start启动actor后，actor会向自己发送一个start消息（AbstractLoopingActor:173）.&lt;br /&gt;
core获得start消息后，调用DefaultActor覆盖的handleStart方法（DefaultActor:328）。&lt;/p&gt;

&lt;p&gt;在handleStart中，actor会调用用户传入的闭包方法。上面的例子是一种典型的用法，loop是DefaultActor中的方法，loop也并不是无限空转的，他仅在收到消息被时被触发（DefaultActor:191）react也是DefaultActor中的方法，它将nextContinuation方法设为内部闭包对象，用来处理actor接收的消息。&lt;/p&gt;

&lt;p&gt;向Actor发送消息，是通过actor的send方法和重载的leftShift运算符进行操作。（AbstractLoopingActor:236）actor调用core的store方法，将ActorMessage压入core的队列中。入队列之后，core会检查锁对象activeUpdater，判断当前core是否在线程处理中，如果不在，则将core加入线程池中处理。activeUpdater是一个AtomicIntegerFieldUpdater对象，他的compareAndSet可以保证原子性。而通过activeUpdater也可以保证同一时刻只有一个core被线程池处理，从而使actor的线程不安全代码也线程安全地运行。&lt;/p&gt;

&lt;p&gt;进入线程池后，core首先将自己放进threadlocal对象中，并保存当前线程的引用。然后会循环消费MessageQueue中的消息直至Queue的可处理部分为空。（AsyncMessagingCore:126）。handleMessage在AbstractLoopingActor中被覆盖，会根据消息的类型进行分发调用（前面提到的start消息就是一种）。默认的业务消息，在DefaultActorClojure中调用DefaultActor的onMessage方法处理。&lt;/p&gt;

&lt;p&gt;onMessage中，react的闭包会被调用来处理业务。之后nextContinuation被置为null，这时loop闭包被重新调用，react闭包重新被赋给nextContinuation。这部分代码就是前面所说的loop并非空转，而是在消息处理完成后重新准备而已。&lt;/p&gt;

&lt;p&gt;此外，core的MessageQueue的实现是DefaultMessageQueue。它使用两个LinkedList作为输入（向actor输入）队列和输出队列，当输入队列为空时，通过同步方法swap交换输入输出队列。swap是整个actor系统里唯一一个同步方法。这样的机制保证actor的core在线程池中处理时，外界仍然可以向actor发送消息，消息会在actor被调度出线程池之前全部处理掉。不过，他的前提是只有一个线程读这个队列，这个条件在actor系统里，通过core对象的activeUpdater可以有效的保证。&lt;/p&gt;

&lt;p&gt;Actor模式采用这种onDemand方式的线程使用，允许大量的actor共存，并只有活跃的actor会占用线程，非活跃状态的actor处在dettach状态，并不消耗计算资源，取消了空转的loop。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>'GPS Track of Train K221: Nanjing to Guangzhou'</title><link href="https://sunng.info/blog/gps-track-of-train-k221-nanjing-to-guangzhou.html" rel="alternate"></link><published>2011-04-26T00:00:00+08:00</published><updated>2011-04-26T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-04-26:/blog/gps-track-of-train-k221-nanjing-to-guangzhou.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;GIS&lt;/li&gt;
&lt;li&gt;gps
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;GPS Logger完成了它有生以来最长的一次旅行，1477分钟，行程1767公里，途径江苏、安徽、江西、湖南、广东，包含了宁芜线、浙赣线、京广线等。有了GPS终端，贴地的旅途就变 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;GIS&lt;/li&gt;
&lt;li&gt;gps
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;GPS Logger完成了它有生以来最长的一次旅行，1477分钟，行程1767公里，途径江苏、安徽、江西、湖南、广东，包含了宁芜线、浙赣线、京广线等。有了GPS终端，贴地的旅途就变成一件非常严肃的事情。这周末去西安，这个GPS也是16个小时上铺车程唯一的安慰了。
&lt;a href="http://www.flickr.com/photos/40741608@N08/5657757338/" title="Railway-K221 - Viking by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5305/5657757338_9bbcc6af42.jpg" alt="Railway-K221 - Viking" height="302" width="500" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;这个数据分享在OpenStreetMap上，也许你会感兴趣：
&lt;a href="http://www.openstreetmap.org/user/Sunng/traces/996794"&gt;http://www.openstreetmap.org/user/Sunng/traces/996794&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="zemanta-pixie"&gt;&lt;img class="zemanta-pixie-img" alt="" src="http://img.zemanta.com/pixy.gif?x-id=e4cdedff-8fec-852d-876b-830e16846036" /&gt;&lt;/div&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>告别Ubuntu，迁移到Fedora 15，GNOME 3</title><link href="https://sunng.info/blog/gao-bie-ubuntuqian-yi-dao-fedora-15gnome-3.html" rel="alternate"></link><published>2011-04-22T00:00:00+08:00</published><updated>2011-04-22T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-04-22:/blog/gao-bie-ubuntuqian-yi-dao-fedora-15gnome-3.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;fedora&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;linux
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/5643299802/" title="Fedora15/Gnome3 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5068/5643299802_787791dd52.jpg" alt="Fedora15/Gnome3" height="313" width="500" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;昨晚终于下了决心告别Ubuntu的紫色，迎接新的GNOME3桌面。这个想法有一段时间了，Ubuntu从09年生日的时候安装在这台电脑上，已经将近两年的时间，经历了9 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;fedora&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;linux
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/5643299802/" title="Fedora15/Gnome3 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5068/5643299802_787791dd52.jpg" alt="Fedora15/Gnome3" height="313" width="500" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;昨晚终于下了决心告别Ubuntu的紫色，迎接新的GNOME3桌面。这个想法有一段时间了，Ubuntu从09年生日的时候安装在这台电脑上，已经将近两年的时间，经历了9.04, 9.10, 10.04, 10.10, 11.04五个版本，安装了GNOME, awesome, KDE, LXDE, Openbox, wmii, GNOME3, Ubuntu Netbook等等桌面环境，可以想想那种混乱。而且通过升级获得的新版本，并不能体会到快速开机这样的特性。这次unity发布以后，用了半个月，没有什么眼前一亮的感觉，操作也不太方便，于是换发行版的想法就更加强烈了。&lt;/p&gt;

&lt;p&gt;我的系统之前有三个分区，分别挂载/，/home以及/opt。这次更新理想的方式就是格式化/，保留/home和/opt。之前比较讲究规范，所以/下面基本上没有什么需要备份的东西，配置文件都放在/home下面。这样用fedora的livecd安装程序手动配置磁盘分区，只格式化/就可以了。安装的过程非常顺利，重启以后就进入用户配置的界面了。&lt;/p&gt;

&lt;p&gt;要把原先的/home/sun分配给新的sun用户，这里我走了一点弯路。系统可以把/home/sun目录配置给新的sun用户作为$HOME，但是目录的owner并不会根据用户名称来匹配。Ubuntu默认第一个用户的UID是1000，而Fedora创建的新用户默认是500. 我现在不确定如果把第一个用户的ID手动指定为1000会不会就直接成功。不过这个问题不大，只要启动之后用root用户把/home/sun目录chown给新的sun用户就可以了，当然文件很多是需要消耗一些时间的。&lt;/p&gt;

&lt;p&gt;进入GNOME3桌面之后发现无线网络连不上，而且不弹出密码输入框。因为我最近改了无线的密码，而NetworkManager还是从gnome-keyring里去读旧的密码，所以总是连接失败。这时&lt;font face="monospace"&gt;rm -r ~/.gnome-keyring/&lt;/font&gt; 删除旧密码即可。&lt;/p&gt;

&lt;p&gt;桌面的第二个问题是桌面背景仍然是旧的配置（只有颜色没有图片，因为GNOME3默认桌面不由Nautilus管理），这时要毫不留情地删掉所有GNOME2的配置： &lt;font face="monospace"&gt;rm -rf ~/.gnome2 ~/.gconf ~/.gconfd &lt;/font&gt;&lt;/p&gt;

&lt;p&gt;第三个问题，gnome的applition菜单非常混乱，还存着Ubuntu系统时候的内容。根据freedesktop的标准，用户的菜单配置保存在&lt;font face="monospace"&gt;~/.config/menus&lt;/font&gt;里，而用户菜单会从&lt;font face="monospace"&gt;~/.local/share/applications&lt;/font&gt;下读取所有的.desktop文件，甚至包括wine也会在这里创建自己的菜单。对付这些内容，一删了之： &lt;font face="monospace"&gt;rm -rf ~/.local/share/applications/*&lt;/font&gt;。这样菜单就只会从/usr/share/applications/下读取.desktop文件，这些都是最新安装的。&lt;/p&gt;

&lt;p&gt;然后，然后就没有了！就可以开始享受fresh install了，甚至你打开thunderbird，所有的邮件都在那里，什么都不用再做。
&lt;a href="http://www.flickr.com/photos/40741608@N08/5643386496/" title="Window Selector by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5041/5643386496_153d59d4b1.jpg" alt="Window Selector" height="313" width="500" /&gt;&lt;/a&gt;
如果要比较gnome-shell和unity的话：
&lt;ol&gt;
&lt;li&gt;gs的启动器比unity要好用，unity的启动器搜索程序之后必须点击图标才能启动，gs可以直接回车启动第一个&lt;/li&gt;
&lt;li&gt;gs的dock比unity要好用，其实都与windows7的任务栏类似，但是 unity里打开了一个终端之后，再点这个图标就转到终端，如果想新开一个就无奈了，右键点击也没有新开的选项。（难道是shift+click，来不及试了。。。）&lt;/li&gt;
&lt;li&gt;unity的indicator比gs的tray强大多了，这是ubuntu的强项，从10.04开始就用自己的indicator，到了11.04已经有成熟的API，除了官方的，还有很多程序支持，还有天气、系统监控的indicator。gs抛弃了原来的panel applets，现在看天气、看系统状况都无从谈起。尤其是看不到cpu使用情况，心里总是不踏实，天知道firefox+flash又把你的系统烧了多久。&lt;/li&gt;
&lt;/ol&gt;&lt;/p&gt;

&lt;p&gt;总体来说这次重装效果非常好，基本上没有留下任何瑕疵。断断续续用了四年多的Ubuntu，再见了～&lt;br /&gt;&lt;br /&gt;&lt;div class="zemanta-pixie"&gt;&lt;img class="zemanta-pixie-img" alt="" src="http://img.zemanta.com/pixy.gif?x-id=98d4edfa-b22d-81bd-b0cc-4b2b6afdacb9" /&gt;&lt;/div&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Exaile sound menu support updated, now compatible with Natty</title><link href="https://sunng.info/blog/exaile-sound-menu-support-updated-now-compatible-with-natty.html" rel="alternate"></link><published>2011-04-17T00:00:00+08:00</published><updated>2011-04-17T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-04-17:/blog/exaile-sound-menu-support-updated-now-compatible-with-natty.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;exaile&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Finally it was fixed. I just updated the plugin according to the new &lt;a href="https://wiki.ubuntu.com/SoundMenu#For%20Natty"&gt;SoundMenu registration process&lt;/a&gt;. Now you can download the head version from github and extract it under &lt;font face="monospace"&gt;~/.local/share/exaile/plugins&lt;/font&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://i.imgur.com/z2YSG.png" title="Hosted by imgur.com" /&gt;&lt;/p&gt;

&lt;p&gt;You can fire an issue on github if …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;exaile&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Finally it was fixed. I just updated the plugin according to the new &lt;a href="https://wiki.ubuntu.com/SoundMenu#For%20Natty"&gt;SoundMenu registration process&lt;/a&gt;. Now you can download the head version from github and extract it under &lt;font face="monospace"&gt;~/.local/share/exaile/plugins&lt;/font&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://i.imgur.com/z2YSG.png" title="Hosted by imgur.com" /&gt;&lt;/p&gt;

&lt;p&gt;You can fire an issue on github if you have problem of using it.&lt;br /&gt;&lt;br /&gt;&lt;div class="zemanta-pixie"&gt;&lt;img class="zemanta-pixie-img" alt="" src="http://img.zemanta.com/pixy.gif?x-id=ebfcb568-71a3-85d5-a381-5a9865617aae" /&gt;&lt;/div&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>'Don''t repeat yourself: distribute jython package with jip.dist'</title><link href="https://sunng.info/blog/dont-repeat-yourself-distribute-jython-package-with-jipdist.html" rel="alternate"></link><published>2011-04-14T00:00:00+08:00</published><updated>2011-04-14T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-04-14:/blog/dont-repeat-yourself-distribute-jython-package-with-jipdist.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;jip&lt;/li&gt;
&lt;li&gt;jython&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;As a new feature in &lt;b&gt;jip&lt;/b&gt; 0.4, we can use some helpers from &lt;font face="monospace"&gt;jip.dist&lt;/font&gt; to simplify package distribution. With jip.dist, you can define Java dependencies for your jython package. In an environment with jip, dependencies will be automatically …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;jip&lt;/li&gt;
&lt;li&gt;jython&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;As a new feature in &lt;b&gt;jip&lt;/b&gt; 0.4, we can use some helpers from &lt;font face="monospace"&gt;jip.dist&lt;/font&gt; to simplify package distribution. With jip.dist, you can define Java dependencies for your jython package. In an environment with jip, dependencies will be automatically installed when user uses pip to get you package.&lt;/p&gt;

&lt;p&gt;We have two different approaches allow you to choose.&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;Approach 1, Define dependencies in POM&lt;/h3&gt;
This is the standard maven way. To describe your jython package and its dependencies, create a pom.xml in your project. The directory hierarchy looks like:
&lt;pre&gt;
├── app
│&amp;nbsp;&amp;nbsp; ├── module1
│&amp;nbsp;&amp;nbsp; │&amp;nbsp;&amp;nbsp; ├── __init__.py
│&amp;nbsp;&amp;nbsp; ├── core.py
│&amp;nbsp;&amp;nbsp; ├── __init__.py
├── LICENSE
├── MANIFEST.in
├── pom.xml
├── README
└── setup.py
&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;In pom.xml, just add dependencies as you do with Maven.&lt;br /&gt;
[cc lang="xml"]
&lt;project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" &lt;br="" /&gt;  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"&amp;gt;&lt;/p&gt;

&lt;p&gt;    ...&lt;/p&gt;

&lt;p&gt;    &lt;dependencies&gt;
        &lt;dependency&gt;
            &lt;groupid&gt;org.slf4j&lt;/groupid&gt;
            &lt;artifactid&gt;slf4j-api&lt;/artifactid&gt;
            &lt;version&gt;1.6.1&lt;/version&gt;
        &lt;/dependency&gt;&lt;/dependencies&gt;&lt;/p&gt;

&lt;p&gt;        &lt;dependency&gt;
            &lt;groupid&gt;org.slf4j&lt;/groupid&gt;
            &lt;artifactid&gt;slf4j-log4j12&lt;/artifactid&gt;
            &lt;version&gt;1.6.1&lt;/version&gt;
        &lt;/dependency&gt;&lt;/p&gt;

&lt;p&gt;        ...&lt;/p&gt;

&lt;p&gt;    &lt;/p&gt;

&lt;p&gt;    &lt;repositories&gt;
        &lt;repository&gt;
            &lt;id&gt;sonatype-oss-sonatype&lt;/id&gt;
            &lt;url&gt;http://oss.sonatype.org/content/repositories/snapshots/&lt;/url&gt;
        &lt;/repository&gt;
    &lt;/repositories&gt;

[/cc]&lt;br /&gt;
You can also define repositories in pom.xml if you use custom repository. (for example, jboss, java.net)&lt;/p&gt;

&lt;p&gt;Remember to add pom.xml in your &lt;font face="monospace"&gt;MANIFEST.in&lt;/font&gt;, to ensure the file will be packaged into source package:&lt;br /&gt;
[cc lang="text"]&lt;br /&gt;
include pom.xml&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;Approach 2, Define dependencies with Python&lt;/h3&gt;
You may be tired with endless XML configuration. jip allows you to define dependencies with python, just like gradle with groovy.&lt;/p&gt;

&lt;p&gt;In your setup.py, add something like:&lt;br /&gt;
[cc lang="python"]&lt;br /&gt;
requires_java = {&lt;br /&gt;
    'dependencies':[&lt;br /&gt;
        ## (groupdId, artifactId, version)&lt;br /&gt;
        ('org.slf4j', 'slf4j-api', '1.6.1'),&lt;br /&gt;
        ('org.slf4j', 'slf4j-log4j12', '1.6.1'),&lt;br /&gt;
        ('info.sunng.soldat', 'soldat', '1.0-SNAPSHOT'),&lt;br /&gt;
        ('org.apache.mina', 'mina-core', '2.0.2')&lt;br /&gt;
    ],&lt;br /&gt;
    'repositories':[&lt;br /&gt;
        ('sonatype-oss-snapshot', 'http://oss.sonatype.org/content/repositories/snapshots/')&lt;br /&gt;
    ]&lt;br /&gt;
}
[/cc]&lt;/p&gt;

&lt;p&gt;Then pass it to &lt;font face="monospace"&gt;setup()&lt;/font&gt;. The keyword argument &lt;font face="monospace"&gt;require_java&lt;/font&gt; is jip specific.&lt;br /&gt;
[cc lang="python"]&lt;br /&gt;
setup(&lt;br /&gt;
    ...&lt;br /&gt;
    requires_java=requires_java,&lt;br /&gt;
    ...)&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;Use jip's setup wrapper&lt;/h3&gt;
To use jip's power, the only difference is to use &lt;font face="monospace"&gt;setup()&lt;/font&gt; from &lt;font face="monospace"&gt;jip.dist&lt;/font&gt; instead of setuptools or distutils.&lt;br /&gt;
[cc lang="python"]&lt;br /&gt;
from jip.dist import setup&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;Then publish your jython package to Python Cheese Shop:
&lt;font face="monospace"&gt;$ jython setup.py sdist upload&lt;/font&gt;&lt;/p&gt;

&lt;p&gt;Internally, jip uses setuptools. So you can still do &lt;font face="monospace"&gt;jython setup.py develop&lt;/font&gt; .&lt;/p&gt;

&lt;p&gt;And jip 0.4 is available under MIT License. You are free to use jip.dist in your code.&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;For your user&lt;/h3&gt;
You should write a guide forcing users to use your jython application within &lt;a href="http://www.virtualenv.org"&gt;virtualenv&lt;/a&gt;. And install jip as a prerequisite:
&lt;font face="monospace"&gt;$ pip install jip&lt;/font&gt;&lt;/p&gt;

&lt;p&gt;Then simply install your package with pip:
&lt;font face="monospace"&gt;$ pip install &amp;lt;your-package-name&amp;gt;&lt;/font&gt;&lt;/p&gt;

&lt;p&gt;No additional step required!&lt;/p&gt;

&lt;p&gt;So please just release your jython package with &lt;b&gt;jip&lt;/b&gt; !&lt;/p&gt;

&lt;p&gt;For more information:
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://pypi.python.org/pypi/jip/"&gt;http://pypi.python.org/pypi/jip/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/sunng87/jip"&gt;https://github.com/sunng87/jip&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="zemanta-pixie"&gt;&lt;img class="zemanta-pixie-img" alt="" src="http://img.zemanta.com/pixy.gif?x-id=5c436f7f-49fe-8f60-9a90-1331f01105a9" /&gt;&lt;/div&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Enhanced jip to simplify Jython module distribution</title><link href="https://sunng.info/blog/enhanced-jip-to-simplify-jython-module-distribution.html" rel="alternate"></link><published>2011-04-10T00:00:00+08:00</published><updated>2011-04-10T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-04-10:/blog/enhanced-jip-to-simplify-jython-module-distribution.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;jip&lt;/li&gt;
&lt;li&gt;jython&lt;/li&gt;
&lt;li&gt;project
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;p&gt;As you might notice, the installation of gefr is too complex, requiring several manual actions. User have to remember the long maven coodinator to resolve dependencies. wtf!&lt;/p&gt;
&lt;p&gt;So I have been working whole day to simplify the process.  &lt;/p&gt;
&lt;p&gt;Currently, gefr's …&lt;/p&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;jip&lt;/li&gt;
&lt;li&gt;jython&lt;/li&gt;
&lt;li&gt;project
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;p&gt;As you might notice, the installation of gefr is too complex, requiring several manual actions. User have to remember the long maven coodinator to resolve dependencies. wtf!&lt;/p&gt;
&lt;p&gt;So I have been working whole day to simplify the process.  &lt;/p&gt;
&lt;p&gt;Currently, gefr's approach is to upload pom.xml to a public maven repositoy (sonatype oss). And the source is uploaded to pypi. Pip will find the source and install it. Jip will find the plain pom and resolve it. Because once pip finished the installation, the source package will be erased and jip can never find the pom. So I have to distribute them seperately.&lt;/p&gt;
&lt;p&gt;It would be better to invoke jip right before pip exits. I did some investigation about post-install script. And lucky enough, distutils allows you to override default install command. We can use it to invoke jip, in the scope of setup scrript. It does make sense. &lt;/p&gt;
&lt;p&gt;Another problem is the original design of .jip configuration file. From jip 0.2, .jip is available as environmen-scoped: we define some custom repositories and the whole environment shares them. But if we have multiple projects in the same environment, jip may waste time to find public artifact in a private repository. Even worse, jip may load invalid artifact from private repos. In the new design, private repositories are defined in pom.xml, as project-scoped, just like most java build tools. &lt;/p&gt;
&lt;p&gt;With new jip, to distribute a jython package, you should write a pom in the same way of Java, specifying the dependencies and custom repositories if you have. Then modify your MANIFEST.in to include it to your source package. At last, in the setup.py, define a new install command to call jip and pass the command into the setup() . From the new approach, only 'pip install' is required for end user. Super easy!&lt;/p&gt;
&lt;p&gt;Upon the new usage of jip(as a library), I am also considering to migrate from GPL to LGPL or MIT. I have little knowledge about conflicts between licenses. So if you have some ideas or concerns, feel free to let me know.&lt;/p&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Gefr with multiple backends support</title><link href="https://sunng.info/blog/gefr-with-multiple-backends-support.html" rel="alternate"></link><published>2011-04-09T00:00:00+08:00</published><updated>2011-04-09T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-04-09:/blog/gefr-with-multiple-backends-support.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;gefr&lt;/li&gt;
&lt;li&gt;jython&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;wsgi
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;I have been working on &lt;a href="https://bitbucket.org/sunng/gefr"&gt;gefr&lt;/a&gt; this week to add apache MINA backend for it. The goal of gefr is to provide adapters between Python's WSGI and Java network infrastructures. In cPython world, most WSGI servers are implemented with C …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;gefr&lt;/li&gt;
&lt;li&gt;jython&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;wsgi
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;I have been working on &lt;a href="https://bitbucket.org/sunng/gefr"&gt;gefr&lt;/a&gt; this week to add apache MINA backend for it. The goal of gefr is to provide adapters between Python's WSGI and Java network infrastructures. In cPython world, most WSGI servers are implemented with C code(fapws, meinheld, etc.), which is not applicable on Jython platform. However, the Java world has its own reliable network foundations. So my gefr turns them into WSGI servers. The ultimate goal of gefr is to make it possible to port any of your WSGI web applications to Jython seamlessly.&lt;/p&gt;

&lt;p&gt;There has been many attempts to use Jython in Java web development. Most of them is focusing on applying Jython in Servlet framework. This is helpful, but it doesn't take the whole advantage of Python web programming. The most valuable thing in Python web development is its various web frameworks and agile tools. But inside servlet framework, we can hardly apply them, the only things we have is Jython's beautiful syntax. That should not be enough.&lt;/p&gt;

&lt;p&gt;OK. You are eager to meet gefr?  Let's get hands dirty with some setup. I just assume that you have some essential software installed in the right place. Is it true for you?&lt;/p&gt;

&lt;p&gt;Create Jython virtual environment:
&lt;font face="monospace"&gt;
$ virtualenv -p /usr/local/bin/jython gefr-test&lt;br /&gt;
$ cd gefr-test&lt;br /&gt;
$ source bin/activate
&lt;/font&gt;&lt;/p&gt;

&lt;p&gt;Install jip. &lt;a href="https://github.com/sunng87/jip"&gt;jip&lt;/a&gt; is a great tool to manage Java dependencies for your Jython application. jip&amp;gt;=0.3 is required in this guide, and pip will handle this for you.
&lt;font face="monospace"&gt;$ pip install jip&lt;/font&gt;&lt;/p&gt;

&lt;p&gt;Install gefr. Since gefr is a pure Python package, pip is still helpful.
&lt;font face="monospace"&gt;$ pip install gefr&lt;/font&gt;&lt;/p&gt;

&lt;p&gt;Now create a jip configuration file called &lt;b&gt;.jip&lt;/b&gt; in your virtual home. We should add sonatype oss repository.&lt;br /&gt;
[cc lang="ini"]&lt;br /&gt;
[repos:oss]&lt;br /&gt;
uri=http://oss.sonatype.org/content/repositories/snapshots/&lt;br /&gt;
type=remote&lt;/p&gt;

&lt;p&gt;[repos:central]&lt;br /&gt;
uri=http://repo1.maven.org/maven2/&lt;br /&gt;
type=remote&lt;/p&gt;

&lt;p&gt;[repos:local]&lt;br /&gt;
uri=/home/sun/.m2/repository/&lt;br /&gt;
type=local&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;New in jip 0.3, we can use a single command to resolve dependencies of gefr.
&lt;font face="monospace"&gt;$ jip install-dependencies info.sunng.gefr:gefr:0.2-SNAPSHOT&lt;/font&gt;&lt;/p&gt;

&lt;p&gt;OK. Then everything is ready. Create your first Jython WSGI application. Actually, there is no difference.&lt;br /&gt;
I just copy the example of gefr here, you can find it in the code repository.&lt;br /&gt;
[cc lang="python"]&lt;br /&gt;
#! /usr/bin/python&lt;/p&gt;

&lt;p&gt;__author__="Sun Ning &lt;classicning@gmail.com&gt;"&lt;br /&gt;
__date__ ="$Jan 5, 2011 10:14:27 PM$"&lt;/p&gt;

&lt;p&gt;import sys&lt;br /&gt;
from gefr.core import Gefr&lt;/p&gt;

&lt;p&gt;def simple(environ, start_response):&lt;br /&gt;
    """a static app"""&lt;br /&gt;
    headers = []&lt;br /&gt;
    headers.append(('Content-Type', "text/html"))&lt;br /&gt;
    start_response("200 OK", headers)&lt;/p&gt;

&lt;p&gt;    return ["&lt;h1&gt;it works.&lt;/h1&gt;"]&lt;/p&gt;

&lt;p&gt;if __name__ == "__main__":&lt;br /&gt;
    from optparse import OptionParser&lt;br /&gt;
    parser = OptionParser()&lt;br /&gt;
    parser.add_option('-b', '--backend', dest='backend', help='server backend')&lt;br /&gt;
    (options, args) = parser.parse_args()&lt;/p&gt;

&lt;p&gt;    if options.backend == 'soldat':&lt;br /&gt;
        from gefr.backends.soldat import SoldatBackend as backend&lt;br /&gt;
    elif options.backend == 'mina':&lt;br /&gt;
        from gefr.backends.mina import MinaBackend as backend&lt;br /&gt;
    else:&lt;br /&gt;
        print 'backend %s not supported.' % options.backend&lt;br /&gt;
        sys.exit(1)&lt;/p&gt;

&lt;p&gt;    g = Gefr(simple, backend)&lt;br /&gt;
    g.start()&lt;br /&gt;
[/cc]&lt;br /&gt;
With the command line options, you can control which backend to be used. Now start it with:
&lt;font face="monospace"&gt;$ jython-all example.py -b soldat&lt;/font&gt;
or
&lt;font face="monospace"&gt;$ jython-all example.py -b mina&lt;/font&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://bitbucket.org/sunng/soldat"&gt;Soldat&lt;/a&gt; is another network framework written by myself. And MINA, you must know. Open your browser, redirect it to http://localhost:8080 , now you see it works.&lt;/p&gt;

&lt;p&gt;Sure, that's not enough for your needs. Let's have an advanced example. Among all the web frameworks in Python, I like &lt;a href="https://github.com/defnull/bottle"&gt;bottle&lt;/a&gt; most. So we just build a simple application with GET and POST in bottle.&lt;/p&gt;

&lt;p&gt;&lt;font face="monospace"&gt;$ pip install bottle&lt;br /&gt;
$ pip install mako&lt;br /&gt;
$ mkdir bottleapp&lt;br /&gt;
$ cd bottleapp&lt;/font&gt;&lt;/p&gt;

&lt;p&gt;The bottle application looks like:&lt;br /&gt;
echo.py&lt;br /&gt;
[cc lang="python"]&lt;br /&gt;
# -*- coding:utf-8 -*-&lt;/p&gt;

&lt;p&gt;from bottle import Bottle, mako_view, request&lt;br /&gt;
from gefr.core import Gefr&lt;br /&gt;
from gefr.backends.mina import MinaBackend&lt;/p&gt;

&lt;p&gt;bapp = Bottle()&lt;/p&gt;

&lt;p&gt;@bapp.get('/')&lt;br /&gt;
@mako_view('edit')&lt;br /&gt;
def display_form():&lt;br /&gt;
    return dict(ip=request.environ.get('REMOTE_ADDR'))&lt;/p&gt;

&lt;p&gt;@bapp.post('/')&lt;br /&gt;
@mako_view('show')&lt;br /&gt;
def display_content():&lt;br /&gt;
    content = request.POST.get('content')&lt;br /&gt;
    return dict(content=content)&lt;/p&gt;

&lt;p&gt;Gefr(bapp, MinaBackend).start()&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;The simple mako templates are:&lt;/p&gt;

&lt;p&gt;edit.tpl&lt;br /&gt;
[cc lang="html"]
&lt;html&gt;
&lt;head&gt;
    &lt;title&gt;Bottle on MINA&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;Bottle on MINA&lt;/h1&gt;
    &lt;p&gt;User from ${ip}, leave your words please:&lt;/p&gt;
    &lt;form action="/" method="post"&gt;
        &lt;textarea cols="60" rows="20" name="content"&gt;&lt;/textarea&gt;
        &lt;p&gt;&lt;input type="submit" /&gt;&lt;/p&gt;
    &lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;show.tpl&lt;br /&gt;
[cc lang="html"]
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;Bottle on MINA&lt;/title&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;h1&gt;Bottle on MINA&lt;/h1&gt;
        &lt;pre&gt;${content | h}&lt;/pre&gt;&lt;/body&gt;&lt;/html&gt;&lt;/p&gt;

&lt;p&gt;    

[/cc]&lt;/p&gt;

&lt;p&gt;Start the bottle app:
&lt;font face="monospace"&gt;$ jython-all echo.py&lt;/font&gt;&lt;/p&gt;

&lt;p&gt;This is rather simple but it covers the most basic features of a web server.&lt;/p&gt;

&lt;p&gt;I should say thank you for reaching here. Gefr is still an experimental project in early development. So there must be bugs and potential performance improvements in it. Feel free to contact me if you are also interested in it. The project is hosted on bitbucket:
&lt;a href="https://bitbucket.org/sunng/gefr"&gt;https://bitbucket.org/sunng/gefr&lt;/a&gt;&lt;/classicning@gmail.com&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="zemanta-pixie"&gt;&lt;img class="zemanta-pixie-img" alt="" src="http://img.zemanta.com/pixy.gif?x-id=473ecd63-8d12-88ec-aa8e-ff8244fe6861" /&gt;&lt;/div&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>jip 0.2 released</title><link href="https://sunng.info/blog/jip-02-released.html" rel="alternate"></link><published>2011-04-05T00:00:00+08:00</published><updated>2011-04-05T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-04-05:/blog/jip-02-released.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;jip&lt;/li&gt;
&lt;li&gt;jython&lt;/li&gt;
&lt;li&gt;maven&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;As you may know, jip is a dependency management tool for Jython/Java development. It resolves and downloads Java packages from maven-compatible repositories. jip also follows some best practices, encouraging you to use a portable and standalone environment …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;jip&lt;/li&gt;
&lt;li&gt;jython&lt;/li&gt;
&lt;li&gt;maven&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;As you may know, jip is a dependency management tool for Jython/Java development. It resolves and downloads Java packages from maven-compatible repositories. jip also follows some best practices, encouraging you to use a portable and standalone environment (virtualenv) for your Jython development.&lt;/p&gt;

&lt;p&gt;It has been four months since the initial release. In version 0.2, I made following changes for you:
&lt;ul&gt;
&lt;li&gt;Improved console output format&lt;/li&gt;
&lt;li&gt;Correct scope dependency management inheritance&lt;/li&gt;
&lt;li&gt;Snapshot management, alpha&lt;/li&gt;
&lt;li&gt;Environment independent configuration&lt;/li&gt;
&lt;li&gt;Bug fixes&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;You can find the typical usage doc on github:
&lt;a href="https://github.com/sunng87/jip"&gt;https://github.com/sunng87/jip&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;jip-0.2 has been published to pypi so you can install or upgrade it with:&lt;br /&gt;
easy_install -U jip&lt;/p&gt;

&lt;p&gt;Please do remember to use it inside virtualenv!&lt;br /&gt;&lt;br /&gt;&lt;div class="zemanta-pixie"&gt;&lt;img class="zemanta-pixie-img" alt="" src="http://img.zemanta.com/pixy.gif?x-id=6d854cec-aede-8421-9daa-99288bc44ead" /&gt;&lt;/div&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>南京的第一个周末</title><link href="https://sunng.info/blog/nan-jing-de-di-yi-ge-zhou-mo.html" rel="alternate"></link><published>2011-04-03T00:00:00+08:00</published><updated>2011-04-03T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-04-03:/blog/nan-jing-de-di-yi-ge-zhou-mo.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;Life&lt;/li&gt;
&lt;li&gt;LilyStudio&lt;/li&gt;
&lt;li&gt;Nanjing
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;回了南京，最大的变化就是心里踏实多了，从上大学以后几乎就没有再在家里好好呆过。虽然年纪 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;Life&lt;/li&gt;
&lt;li&gt;LilyStudio&lt;/li&gt;
&lt;li&gt;Nanjing
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;回了南京，最大的变化就是心里踏实多了，从上大学以后几乎就没有再在家里好好呆过。虽然年纪轻轻就告老还乡有点不太给力，但是这样实在是比在外乡遥遥无期地飘着感觉要好（飘着好，遥遥无期地飘着不好）。&lt;/p&gt;

&lt;p&gt;上班的地方在江宁，你可以从sunng.info页面的location标签上找到我。那个园区应该是聚集了一些软件公司，不过规模比显然比不上浦东软件园（还一二三期）。当然啦，规模大有个毛用，稍微晚几分钟食堂还要排队，快算了吧。&lt;/p&gt;

&lt;p&gt;交通比想象的要方便，换一次公交车就从家门口到园区门口了。即使是早高峰时间，也只有在市区的一条路上稍微堵一下，大部分时间都是飞驰的专车，夫复何求！！换车的地方在未来的京沪高铁附近，将来南京南站也建在那一带，施工的时候还是尘土飞扬的，这点比张江要差点。（嗯，也就跟张江比比吧）&lt;/p&gt;

&lt;p&gt;小公司有小公司的好处，新人去了没有繁文缛节的洗脑。开发岗位的直接用linux桌面，我把它看作一种福利，虽然是个rhel5，但好歹有自己配置的余地。当然即使这样，Windows还是少不了，Office是主要的原因，不过现在虚拟机是它永恒的家了。其他嘛，印象深刻的是公司的OpenDNS规则配得很厉害，好在firefox有个配置项叫做network.proxy.socks_remote_dns，你懂的。&lt;/p&gt;

&lt;p&gt;周末在南京就可以随心所欲了。今天是新赛季中超联赛第一轮，下午3点半奥体又要热闹了。去年舜天队不给力，在盛大又特别忙，全年我就看了第一个主场的比赛，结果还输了（十年来第一次在现场输球）！！初中高中的时候要忙学习，大学的时候工作室周六开会，所以这么多年都没有条件保持看球的出勤率。现在时候到了，这是一种怎样的情怀了。&lt;/p&gt;

&lt;p&gt;除去安逸的事情以外，我最近在看UEAP，再和@rlove的Linux System Programming对照看比较有感觉，不过我好像动手偏少了。另外还要看一下一个python的wsgi server叫做meinheld它为什么这么快呢。。&lt;/p&gt;

&lt;p&gt;最后还要恭喜工作室战友、某著名互联网企业优秀员工、抢了04地理（我几乎忘了我是地理学院的了）第一婚的成功人士&lt;a href="http://goodtiger.net"&gt;tiger&lt;/a&gt;新婚愉快！（除了The connection was reset以外就是成功人士的定义了）&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>gefr API updates</title><link href="https://sunng.info/blog/gefr-api-updates.html" rel="alternate"></link><published>2011-03-28T00:00:00+08:00</published><updated>2011-03-28T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-03-28:/blog/gefr-api-updates.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;gefr&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;jython&lt;/li&gt;
&lt;li&gt;opensource&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;wsgi
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;早上收到&lt;a href="http://jythonet.appspot.com"&gt;jythonet&lt;/a&gt;作者的一封邮件，受到启发，我打算扩展一下gefr这个WSGI adapter。原本gefr是写给soldat用来测试的，结果发现现在出现了 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;gefr&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;jython&lt;/li&gt;
&lt;li&gt;opensource&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;wsgi
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;早上收到&lt;a href="http://jythonet.appspot.com"&gt;jythonet&lt;/a&gt;作者的一封邮件，受到启发，我打算扩展一下gefr这个WSGI adapter。原本gefr是写给soldat用来测试的，结果发现现在出现了买椟还珠的效果，既然大家更加关注这个，我决定多花一些时间在上面。不过话说回来gefr确实是Jython Web程序的新思路，过去大家都在想怎样把Jython放进Servlet中，gefr的思路是把Java的服务器实现放到WSGI后面。未来会有gefr-netty, gefr-mina, gefr-servlet等等出现，如果真的存在一个可靠的backend，也许这种思路也不失为一个办法，毕竟python的web框架更吸引人，而java的基础设施相对可靠，各取所长。&lt;/p&gt;

&lt;p&gt;根据这个目的，现在gefr 0.2的API更新为：
&lt;script src="https://bitbucket.org/sunng/gefr/src/8490748055f1/src/examples/example.py?embed=t"&gt;&lt;/script&gt;&lt;/p&gt;

&lt;p&gt;即在创建Gefr的时候需要告知backend类型，目前还只有soldat一种后端。未来也可能通过自动检测让这个参数成为可选。&lt;/p&gt;

&lt;p&gt;gefr 0.2已经在开发中，您可以通过这里关注：
&lt;a href="https://bitbucket.org/sunng/gefr/overview"&gt;https://bitbucket.org/sunng/gefr/overview&lt;/a&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Setting up soldat and gefr</title><link href="https://sunng.info/blog/setting-up-soldat-and-gefr.html" rel="alternate"></link><published>2011-03-27T00:00:00+08:00</published><updated>2011-03-27T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-03-27:/blog/setting-up-soldat-and-gefr.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;jip&lt;/li&gt;
&lt;li&gt;jython&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;soldat&lt;/li&gt;
&lt;li&gt;wsgi
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;本文介绍soldat服务器和gefr WSGI适配器的环境搭建，以及jip的基本使用。&lt;/p&gt;

&lt;p&gt;&lt;h4&gt;安装python工具&lt;/h4&gt;
virtualenv和pip是python开发的关键工具&lt;br /&gt;
sudo apt-get install python-virtualenv&lt;br /&gt;
sudo apt-get install python-pip&lt;/p&gt;

&lt;p&gt;jython需要您手动下载安装。推荐安装到/usr/local/下，并建立软连接 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;jip&lt;/li&gt;
&lt;li&gt;jython&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;soldat&lt;/li&gt;
&lt;li&gt;wsgi
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;本文介绍soldat服务器和gefr WSGI适配器的环境搭建，以及jip的基本使用。&lt;/p&gt;

&lt;p&gt;&lt;h4&gt;安装python工具&lt;/h4&gt;
virtualenv和pip是python开发的关键工具&lt;br /&gt;
sudo apt-get install python-virtualenv&lt;br /&gt;
sudo apt-get install python-pip&lt;/p&gt;

&lt;p&gt;jython需要您手动下载安装。推荐安装到/usr/local/下，并建立软连接到/usr/local/bin/中，下文将假设您是这么做的。&lt;/p&gt;

&lt;p&gt;&lt;h4&gt;创建虚拟jython环境&lt;/h4&gt;
virtualenv -p /usr/local/bin/jython gefr-test&lt;br /&gt;
cd gefr-test&lt;br /&gt;
source bin/activate&lt;/p&gt;

&lt;p&gt;&lt;h4&gt;安装jip&lt;/h4&gt;
pip install jip&lt;/p&gt;

&lt;p&gt;&lt;h4&gt;配置jip&lt;/h4&gt;
在$HOME下创建文件.jip，内容为：&lt;/p&gt;

&lt;p&gt;[cc lang="ini"]&lt;br /&gt;
[repos:oss]&lt;br /&gt;
uri=http://oss.sonatype.org/content/repositories/snapshots/&lt;br /&gt;
type=remote&lt;/p&gt;

&lt;p&gt;[repos:central]&lt;br /&gt;
uri=http://repo1.maven.org/maven2/&lt;br /&gt;
type=remote&lt;/p&gt;

&lt;p&gt;[repos:local]&lt;br /&gt;
uri=/home/sun/.m2/repository/&lt;br /&gt;
type=local&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;&lt;h4&gt;安装soldat&lt;/h4&gt;
配置完jip后，可以使用jip来安装soldat&lt;br /&gt;
jip install info.sunng.soldat:soldat:1.0-SNAPSHOT&lt;/p&gt;

&lt;p&gt;文件将被下载到 javalib 目录中，您可以检查安装的正确性：&lt;br /&gt;
$ ls javalib/&lt;br /&gt;
log4j-1.2.16.jar     slf4j-log4j12-1.6.1.jar&lt;br /&gt;
slf4j-api-1.6.1.jar  soldat-1.0-SNAPSHOT.jar&lt;/p&gt;

&lt;p&gt;&lt;h4&gt;安装gefr&lt;/h4&gt;
pip install gefr==0.1dev2&lt;/p&gt;

&lt;p&gt;&lt;h4&gt;创建一个简单的Python WSGI程序&lt;/h4&gt;
创建test.py&lt;/p&gt;

&lt;p&gt;[cc lang="python"]&lt;br /&gt;
from gefr import Gefr&lt;/p&gt;

&lt;p&gt;def wsgiapp(environ, start_response):&lt;br /&gt;
    status = '200 OK'&lt;br /&gt;
    res_body = "&lt;html&gt;&lt;head&gt;&lt;title&gt;Welcome&lt;/title&gt;&lt;/head&gt;&lt;body&gt;&lt;h1&gt;It works!&lt;/h1&gt;&lt;/body&gt;&lt;/html&gt;"&lt;br /&gt;
    res_headers = [('Content-Type', 'text/html'),&lt;br /&gt;
            ('Content-Length', str(len(res_body)))]&lt;br /&gt;
    start_response(status, res_headers)&lt;br /&gt;
    return [res_body]&lt;/p&gt;

&lt;p&gt;Gefr(wsgiapp, host='0.0.0.0', port=8000).start()&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;&lt;h4&gt;启动服务&lt;/h4&gt;
使用jip附带的jython-all&lt;br /&gt;
jython-all test.py&lt;/p&gt;

&lt;p&gt;打开浏览器，访问 http://localhost:8000/&lt;/p&gt;

&lt;p&gt;&lt;h4&gt;用ab测试服务性能&lt;/h4&gt;
$ ab -n 10000 -c 100 http://localhost:8000/
&lt;pre&gt;
This is ApacheBench, Version 2.3 &lt;$Revision: 655654 $&gt;
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;Benchmarking localhost (be patient)&lt;br /&gt;
Completed 1000 requests&lt;br /&gt;
Completed 2000 requests&lt;br /&gt;
Completed 3000 requests&lt;br /&gt;
Completed 4000 requests&lt;br /&gt;
Completed 5000 requests&lt;br /&gt;
Completed 6000 requests&lt;br /&gt;
Completed 7000 requests&lt;br /&gt;
Completed 8000 requests&lt;br /&gt;
Completed 9000 requests&lt;br /&gt;
Completed 10000 requests&lt;br /&gt;
Finished 10000 requests&lt;/p&gt;

&lt;p&gt;Server Software:        gefr/0.1dev&lt;br /&gt;
Server Hostname:        localhost&lt;br /&gt;
Server Port:            8000&lt;/p&gt;

&lt;p&gt;Document Path:          /&lt;br /&gt;
Document Length:        79 bytes&lt;/p&gt;

&lt;p&gt;Concurrency Level:      100&lt;br /&gt;
Time taken for tests:   2.539 seconds&lt;br /&gt;
Complete requests:      10000&lt;br /&gt;
Failed requests:        0&lt;br /&gt;
Write errors:           0&lt;br /&gt;
Total transferred:      1640000 bytes&lt;br /&gt;
HTML transferred:       790000 bytes&lt;br /&gt;
Requests per second:    3938.11 [#/sec] (mean)&lt;br /&gt;
Time per request:       25.393 [ms] (mean)&lt;br /&gt;
Time per request:       0.254 [ms] (mean, across all concurrent requests)&lt;br /&gt;
Transfer rate:          630.71 [Kbytes/sec] received&lt;/p&gt;

&lt;p&gt;Connection Times (ms)&lt;br /&gt;
              min  mean[+/-sd] median   max&lt;br /&gt;
Connect:        0    0   0.6      0       8&lt;br /&gt;
Processing:     9   25  22.4     21     239&lt;br /&gt;
Waiting:        9   25  22.4     21     239&lt;br /&gt;
Total:         13   25  22.3     21     239&lt;/p&gt;

&lt;p&gt;Percentage of the requests served within a certain time (ms)&lt;br /&gt;
  50%     21&lt;br /&gt;
  66%     22&lt;br /&gt;
  75%     22&lt;br /&gt;
  80%     23&lt;br /&gt;
  90%     26&lt;br /&gt;
  95%     40&lt;br /&gt;
  98%     65&lt;br /&gt;
  99%    233&lt;br /&gt;
 100%    239 (longest request)
&lt;/p&gt;

&lt;p&gt;Good Luck!&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>'"Update ${new Random().nextInt()}"'</title><link href="https://sunng.info/blog/update-new-randomnextint.html" rel="alternate"></link><published>2011-03-24T00:00:00+08:00</published><updated>2011-03-24T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-03-24:/blog/update-new-randomnextint.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;Life&lt;/li&gt;
&lt;li&gt;Shanghai
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;多少年没写Update体了，看Samson的Update Series都到了53了，咱就只好来这么个标题了。&lt;/p&gt;

&lt;p&gt;&lt;ol&gt;
&lt;li&gt;头等大事，officially announce 一下艰难决定，经过慎重考虑我决定 …&lt;/li&gt;&lt;/ol&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;Life&lt;/li&gt;
&lt;li&gt;Shanghai
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;多少年没写Update体了，看Samson的Update Series都到了53了，咱就只好来这么个标题了。&lt;/p&gt;

&lt;p&gt;&lt;ol&gt;
&lt;li&gt;头等大事，officially announce 一下艰难决定，经过慎重考虑我决定离开上海了。从09年3月第一次到上海到张江，现在整好两年的时间。一方面是获得了大城市的工作机会，另一方面也被在这里边缘的生活闹够了。于是这个悲喜交加的本命年春天决定告老还乡卸甲归田，过没有追求没有房东的土著生活。&lt;/li&gt;
&lt;li&gt;雁过留声踏雪留痕，我在晨晖路1001号半年最大的影响就是让这里又多了两个kindle用户，其他嘛，惭愧了。。。&lt;/li&gt;
&lt;li&gt;要说在上海有什么舍不得的话，就是这里的朋友了，同学同事真不少。有一起干活的，有一起吃饭的，有一起听歌的，各种都有，回了南京就没有这种条件了。不过其实大家在上海很忙一个月也聚不上一回，这么想的话没什么区别，赶上什么RubyConf之类的就又过来了，给沪宁线作贡献是咱的宿命。（话说今年的这些会什么时候开啊，我夏天的衣服还没有攒够呢）&lt;/li&gt;
&lt;li&gt;新赛季中超3号就揭幕了，今年舜天队的比赛可能连电视转播都没有了，你说不回南京能行吗！！能行吗！！从96年开始，今年就是第16年了。&lt;/li&gt;
&lt;li&gt;本来想说最近一两个月荒废得厉害，我回家以后要怎么怎么的，在这还是改为此处省去多少多少字吧。。。&lt;/li&gt;
&lt;/ol&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Exaile DoubanFM Plugin 0.0.9</title><link href="https://sunng.info/blog/exaile-doubanfm-plugin-009.html" rel="alternate"></link><published>2011-03-19T00:00:00+08:00</published><updated>2011-03-19T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-03-19:/blog/exaile-doubanfm-plugin-009.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;Douban&lt;/li&gt;
&lt;li&gt;exaile
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;新版本的Exaile豆瓣电台插件。这个版本的变更：
&lt;ul&gt;
&lt;li&gt;新增分享菜单，分享当前音轨到豆瓣、新浪、开心001、人人&lt;/li&gt;
&lt;li&gt;改进了推荐方式，采 …&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;Douban&lt;/li&gt;
&lt;li&gt;exaile
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;新版本的Exaile豆瓣电台插件。这个版本的变更：
&lt;ul&gt;
&lt;li&gt;新增分享菜单，分享当前音轨到豆瓣、新浪、开心001、人人&lt;/li&gt;
&lt;li&gt;改进了推荐方式，采用豆瓣电台的永久链接&lt;/li&gt;
&lt;li&gt;修改了0.0.8以来存在的登录bug&lt;/li&gt;
&lt;li&gt;修改了0.0.8以来存在的加载播放列表bug&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/5539013314/" title="Doubanfm Exaile new by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5092/5539013314_be4d6ff6ee.jpg" width="500" height="243" alt="Doubanfm Exaile new" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;从github获得新版本：
&lt;a href="https://github.com/sunng87/exaile-doubanfm-plugin/downloads"&gt;https://github.com/sunng87/exaile-doubanfm-plugin/downloads&lt;/a&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Continuous Learning</title><link href="https://sunng.info/blog/continuous-learning.html" rel="alternate"></link><published>2011-03-14T00:00:00+08:00</published><updated>2011-03-14T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-03-14:/blog/continuous-learning.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;methodology&lt;/li&gt;
&lt;li&gt;tech
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;一直想总结一下学习的方法，今天看到Programmer的97件事里，作者叫做Clint Shank的Continuous Learning这篇，感觉比较靠谱，跟大伙分享一下。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;阅读书籍 …&lt;/strong&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;methodology&lt;/li&gt;
&lt;li&gt;tech
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;一直想总结一下学习的方法，今天看到Programmer的97件事里，作者叫做Clint Shank的Continuous Learning这篇，感觉比较靠谱，跟大伙分享一下。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;阅读书籍、杂志、博客、Twitter和各种网站。如果希望深入了解一个主题，可以加入一些邮件列表。&lt;/strong&gt;这个比较基本，程序员需要有开放的、稳定的、最新的信息渠道。除了上面提的方式，我比较推荐dzone和reddit上的相关频道，这两个书签网站可以帮你过滤排序每天技术圈的热点和新闻。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;如果你确实希望沉浸于一种技术，那么你需要着手写一些代码。&lt;/strong&gt;这个不说了，如果还认为自己是Programmer的话，写一些代码是最最基本的事情。要了解一种技术，写一些代码，跑一个demo是最最基本的。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;尽可能和一位导师，或者一个顶尖的家伙一起工作。尽管从任何人身上学到东西，但是你可以从一个更聪明更有经验的人身上学到完整的（a whole lot more）。&lt;/strong&gt;这个体会比较深，刚搬进工作室的时候坐在@samson959旁边，虽然只有短短几个月，但是跟着入了门，少走了很多很多弯路。再后来在盛大也从老大身上学到不少东西。这样的机会并不是在哪里都有的。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;找一位虚拟导师。这是在上一条无法满足的情况下。在Web上找一位自己喜欢的作家，订阅他们的blog。&lt;/strong&gt;这条比较有意思，你可以拜一位网上的大牛为师，关注他的博客、Twitter以及Github，关注大牛关注的事情、做的事情。这点很有帮助，最早做前端的时候我关注过当时还在雅虎的陈贤安，后来在盛大关注过新浪的杨卫华，以前他们的博客、Twitter质量不错而且经常更新。不过现在前者转行做了苹果开发的Freelancer，后者也许是太忙了，不好找了。其实就是这样，真正牛的人几乎是没有时间做这样的分享的。技术圈毕竟不是娱乐圈，整天在twitter上大放厥词从早说到晚的往往不是你需要关注的，这需要你有一些辨别能力。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;去了解你使用的框架和库。了解他们的工作原理可以帮助你更好的使用他们。如果是开源的，你可以用debugger单步跟所有的代码了解内部的工作原理，你可以看到由优秀的程序员编写和审察的代码。&lt;/strong&gt;这也是很重要的，好在现在有了github让这变得更简单。我自己曾经花过一些时间在redis上，用gdb单步跟踪来浏览程序确实是学习的好方法，尤其是对于向C这样本身程序结构并非非常清晰的项目，通过调试器来了解程序的组织事半功倍，远远高效于单纯的阅读。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;无论合适遇到问题或修正了一个bug，尽可能去了解其后真正发生的事情。可以通过Google去了解网上已经存在的相似问题。&lt;/strong&gt;这又是很重要的一点，现在除了Google，你还可以直接到StackOverflow上去搜索、提问和解答。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;分享一些东西本身就是最好的学习方法。当有人将要听你介绍或问你问题时，你会拥有很大的动力去学习。&lt;/strong&gt;在盛大的时候，每个部门定期会有分享会，在其他很多公司肯定也有这样的活动。我也曾经去做过这样的分享，真要说收获不是这种活动搞的多成功，而是之前准备的阶段。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;参加一个学习小组、本地用户组。&lt;/strong&gt;这个我没有经验，不过各地的LUG都很活跃，一定是很多人乐在其中的。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;参加会议，或者看会议录像、slides。&lt;/strong&gt;这也很有趣，我参加的第一个活动是08年北京的Perl&amp;PostgreSQL会议，后来在上海参加过KongfuRails，RubyConf还有Apache Road Show。不过会议上能获得什么特别有价值的东西也有限，主要还是开阔眼界，了解社区的动态，了解同行的关注点。我们还可以从InfoQ上看到SpringOne, QCon, StrangeLoop等等会议的视频，既可以了解一些技术，又可以锻炼一下听力。能够收录在InfoQ里的Session质量应该都还是不错的，除此以外，还可以通过Slideshare搜索一些特定主题的slides。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;收听podcast。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;使用静态检查工具提高你的代码质量。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;遵循Pragmatic Programmers中的建议，并每年学习一门新的语言。至少是一种新的技术或工具。这种发散可以给你新的想法，运用在你现在的技术栈中。&lt;/strong&gt;如果你今年还没有什么想法的话，推荐clojure或者nodejs，一个是函数式一个是全异步。（如果你已经都精通了别说我老土啊）&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;不要局限在技术中，了解一些你工作的领域知识，可以更好地了解需求，解决业务问题。&lt;/strong&gt;技术的方向很多，SAP在这方面是个典型。SAP的优势不是基础技术，了解行业了解业务是SAP成功的最重要因素。这点在SAP圈子里容易有更深刻的体会，大部分技术人员更希望能够直接面对客户，了解客户的真实业务，并以此为提升自身价值的途径。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;回学校返工。&lt;/strong&gt;不用说了，从工作岗位回到学校的，一般都有明确的目标。系统地深入地学习还是得在心无旁骛的校园里。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Tiny LifeStream widget available on sunng.info</title><link href="https://sunng.info/blog/tiny-lifestream-widget-available-on-sunnginfo.html" rel="alternate"></link><published>2011-03-11T00:00:00+08:00</published><updated>2011-03-11T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-03-11:/blog/tiny-lifestream-widget-available-on-sunnginfo.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;daf&lt;/li&gt;
&lt;li&gt;javascript&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;首先推荐samson的&lt;a href="http://stdout.samsonw.info/"&gt;stdout&lt;/a&gt;，这是一个LifeStream Web应用（针对Loser的LifeStream称为LoserStream）。你可以从&lt;a href="https://github.com/samsonw/stdout"&gt;github&lt;/a&gt;上获得代码，参考samson的&lt;a href="http://blog.samsonis.me/2011/03/stdout-deployment/"&gt;deployment&lt;/a&gt;和&lt;a href="http://blog.samsonis.me/2011/03/stdout-customization/"&gt;customization&lt;/a&gt;文档，在一二三四五六 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;daf&lt;/li&gt;
&lt;li&gt;javascript&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;首先推荐samson的&lt;a href="http://stdout.samsonw.info/"&gt;stdout&lt;/a&gt;，这是一个LifeStream Web应用（针对Loser的LifeStream称为LoserStream）。你可以从&lt;a href="https://github.com/samsonw/stdout"&gt;github&lt;/a&gt;上获得代码，参考samson的&lt;a href="http://blog.samsonis.me/2011/03/stdout-deployment/"&gt;deployment&lt;/a&gt;和&lt;a href="http://blog.samsonis.me/2011/03/stdout-customization/"&gt;customization&lt;/a&gt;文档，在一二三四五六七八九十十一十二……分钟内搭建一个自己stdout，至于是不是LoserStream你自己看着办。&lt;/p&gt;

&lt;p&gt;本来打算在自己的主机上搭一个实例的，怎奈怎奈我那CentOS5的主机。所以我说我们这些CentOS用户最伤不起了！！！源里有Ruby有个毛用！！1.8.4，泥玛gem都装不上！！还要去找1.8.6的代码来编译，有没有！！！好不容易装上gem了，gem 1.6.1说找不到Win32API！！！干掉重装1.4.5！再装ruby-sqlite3，源里的sqlite3版本又不行，装不上，有没有！！！有没有！！！&lt;/p&gt;

&lt;p&gt;最后我无奈了，还是用老朋友Yahoo Pipes吧。拖一拖拉一拉神马都有了，页面上整点咱最擅长的document.getElementById就成了。你可以在&lt;a href="http://sunng.info"&gt;http://sunng.info&lt;/a&gt;找到这个东西，现在这个源里收录了：
&lt;ul&gt;
&lt;li&gt;blog&lt;/li&gt;
&lt;li&gt;豆瓣&lt;/li&gt;
&lt;li&gt;github&lt;/li&gt;
&lt;li&gt;osm editing&lt;/li&gt;
&lt;li&gt;twitter&lt;/li&gt;
&lt;li&gt;reddit like&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;这些也是我主要混迹的地方了，遗憾就是bitbucket居然没有个人的RSS输出。倒是可以通过他们的API获得，不过在Pipes里要多拖点东西，所以我先琢磨一下再说吧。网易八方我说过了，不愧是互联网产品经理驱动的东西，连个RSS输出都木有。饭否嘛现在作为扯淡专用场所就不收录了。&lt;/p&gt;

&lt;p&gt;说实在的把这些东西都聚合在一起还是挺吓人的，所以我把他稍微藏了一下。&lt;/p&gt;

&lt;p&gt;还有，今天日本地震，祝daf同学平平安安平平安安。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Correct source file encoding with one liner</title><link href="https://sunng.info/blog/correct-source-file-encoding-with-one-liner.html" rel="alternate"></link><published>2011-03-08T00:00:00+08:00</published><updated>2011-03-08T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-03-08:/blog/correct-source-file-encoding-with-one-liner.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;bash&lt;/li&gt;
&lt;li&gt;shell
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://amoeba.meidusa.com/wordpress/"&gt;Amoeba&lt;/a&gt;项目最早的代码可以追溯到2008年了，其中有多个作者贡献代码，因为一直在Windows下开发，所以没有使用UTF8编码，最近大 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;bash&lt;/li&gt;
&lt;li&gt;shell
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://amoeba.meidusa.com/wordpress/"&gt;Amoeba&lt;/a&gt;项目最早的代码可以追溯到2008年了，其中有多个作者贡献代码，因为一直在Windows下开发，所以没有使用UTF8编码，最近大家统一到UTF8，遇到了代码编码不正确的问题。&lt;/p&gt;

&lt;p&gt;于是我们需要统一解决一下这个问题：
&lt;code&gt;iconv -f gbk -t utf8 -o ConnectionManager.java ConnectionManager.java&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;这样可以把gbk编码的源文件转换为UTF8，原地转换。&lt;/p&gt;

&lt;p&gt;推广到整个代码目录，用find和xargs做，xargs通过-I来制定一个占位符。
&lt;code&gt;find . -name "*.java" -type f -perm +600 -print | xargs -I _ iconv -f gbk -t utf8 -o _ -c _&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;结果发现iconv运行中报了错，进一步检查发现一部分代码正常转换了，另一部分却乱码了。原来，两个作者的代码分别是gbk和gb2312，这iconv转换的时候两种编码并不兼容。这就麻烦了，必须对代码分别处理才可以，区别代码的编码，暂时就用Java源文件里的作者名字。又看了一下find似乎没有对文件内容过滤的条件，不过不要紧，我们可以用xargs做：
&lt;code&gt;find . -name "*.java" -type f -perm +600 -print | xargs -I _ sh -c 'grep -q hexianmao _ &amp;&amp; iconv -f gb2312 -t utf8 -o _ -c _ '&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;对这位hexianmao作者的代码，我们利用grep进程的返回值来进行判断。grep的-q参数相当于&gt;/dev/null。这里有一个tricky的地方，再xargs里我们不能直接用&amp;&amp;来组合命令，不过可以通过sh -c这样的方式，并且其中的占位符会被xargs合适地替换掉。&lt;/p&gt;

&lt;p&gt;这样执行之后，这位作者的gb2312代码就被成功转换了。而另一部分作者的gbk代码也可以用同样方式解决了。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>When nodejs meets mapnik</title><link href="https://sunng.info/blog/when-nodejs-meets-mapnik.html" rel="alternate"></link><published>2011-03-05T00:00:00+08:00</published><updated>2011-03-05T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-03-05:/blog/when-nodejs-meets-mapnik.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;javascript&lt;/li&gt;
&lt;li&gt;mapnik&lt;/li&gt;
&lt;li&gt;node&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;tilelive.js是一个基于nodejs的web地图工具，帮助用户快速查看他们创建的地图样式。tilelive.js是Mapbox众多产品中的一个，你可以通过&lt;a href="http://mapbox.github.com/tilemill/"&gt;tilemill&lt;/a&gt;了解更多。&lt;/p&gt;

&lt;p&gt;要运行tilelive.js，你需要 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;javascript&lt;/li&gt;
&lt;li&gt;mapnik&lt;/li&gt;
&lt;li&gt;node&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;tilelive.js是一个基于nodejs的web地图工具，帮助用户快速查看他们创建的地图样式。tilelive.js是Mapbox众多产品中的一个，你可以通过&lt;a href="http://mapbox.github.com/tilemill/"&gt;tilemill&lt;/a&gt;了解更多。&lt;/p&gt;

&lt;p&gt;要运行tilelive.js，你需要安装一系列软件。&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;安装mapnik&lt;/h3&gt;
首先需要安装mapnik2，注意并非是目前的稳定版本0.7.1，而是开发版本。&lt;br /&gt;
签出SVN代码：
&lt;code&gt;svn co http://svn.mapnik.org/trunk mapnik-trunk&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;按照Mapnik的&lt;a href="http://trac.mapnik.org/browser/trunk/INSTALL"&gt;文档&lt;/a&gt;描述进行安装。&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;安装node依赖&lt;/h3&gt;
推荐你安装最新的node 0.4.2和npm 0.3.1。&lt;/p&gt;

&lt;p&gt;安装npm后执行
&lt;code&gt;npm config set unsafe-perm true&lt;/code&gt;
去除超级用户运行npm时的警告。&lt;/p&gt;

&lt;p&gt;接下来就可以安装一系列node modules了：
&lt;code&gt;sudo npm install mapnik&lt;/code&gt;
&lt;code&gt;sudo npm install carto&lt;/code&gt;
&lt;code&gt;sudo npm install generic-pool&lt;/code&gt;
&lt;code&gt;sudo npm install underscore&lt;/code&gt;
&lt;code&gt;sudo npm install node-get&lt;/code&gt;
&lt;code&gt;sudo npm install step&lt;/code&gt;
&lt;code&gt;sudo npm install express&lt;/code&gt;
&lt;code&gt;sudo npm install sqlite3&lt;/code&gt;
&lt;code&gt;sudo npm install compress&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;签出 tilelive.js&lt;/h3&gt;
tilelive.js并不兼容最新的node 0.4.x和express 2.0.0，所以你可以签出我fork的版本：
&lt;code&gt;git clone git://github.com/sunng87/tilelive.js.git&lt;/code&gt;
&lt;code&gt;git checkout new-fork&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;运行：
&lt;code&gt;./bin/tilelive_server.js examples/stysheet.xml&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;打开浏览器，访问 http://localhost:8888/ 即可
&lt;a href="http://www.flickr.com/photos/40741608@N08/5498409697/" title="screenshot_001 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5177/5498409697_6bf280e8fb.jpg" width="500" height="405" alt="screenshot_001" /&gt;&lt;/a&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Python3 script to query free TOEFL seats</title><link href="https://sunng.info/blog/python3-script-to-query-free-toefl-seats.html" rel="alternate"></link><published>2011-02-28T00:00:00+08:00</published><updated>2011-02-28T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-02-28:/blog/python3-script-to-query-free-toefl-seats.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;应同学要求，写了一个爬托福考试空闲考位的脚本。这是第一次真正用Python3写。&lt;/p&gt;

&lt;p&gt;&lt;script src="https://gist.github.com/846150.js?file=gistfile1.py"&gt;&lt;/script&gt;&lt;/p&gt;

&lt;p&gt;通过调用get_seat_status，传入省名（如'Jiangsu' …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;应同学要求，写了一个爬托福考试空闲考位的脚本。这是第一次真正用Python3写。&lt;/p&gt;

&lt;p&gt;&lt;script src="https://gist.github.com/846150.js?file=gistfile1.py"&gt;&lt;/script&gt;&lt;/p&gt;

&lt;p&gt;通过调用get_seat_status，传入省名（如'Jiangsu'），时间（如'201104'），您可以得到这样的数据结构：&lt;br /&gt;
[cc lang="python"]&lt;br /&gt;
[ [datetime.datetime(2011, 4, 23, 10, 0),  'STN80085B',  '南通市教育装备与勤工俭学管理中心',  '1415',  True],&lt;br /&gt;
 [datetime.datetime(2011, 4, 23, 10, 0), 'STN80086A', '扬州大学', '1415', True]]&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;下面是一个多线程调用的例子：&lt;br /&gt;
[cc lang="python"]&lt;br /&gt;
#! /usr/bin/python3&lt;/p&gt;

&lt;p&gt;import threading&lt;br /&gt;
import pprint&lt;br /&gt;
from toeflgraber import get_seat_status&lt;/p&gt;

&lt;p&gt;# 查询江浙沪 2011年3月和4月的考位&lt;br /&gt;
locations = ['Jiangsu', 'Shanghai', 'Zhejiang']&lt;br /&gt;
months = ['201103', '201104']&lt;/p&gt;

&lt;p&gt;def descartes(x, y):&lt;br /&gt;
    for i in x:&lt;br /&gt;
        for j in y:&lt;br /&gt;
            yield (i, j)&lt;/p&gt;

&lt;p&gt;lock = threading.RLock()&lt;br /&gt;
tasks = len(locations) * len(months)&lt;br /&gt;
e = threading.Event()&lt;br /&gt;
results = []&lt;/p&gt;

&lt;p&gt;def task(location, month):&lt;br /&gt;
    global results, tasks&lt;br /&gt;
    all_seats = get_seat_status(location, month)&lt;br /&gt;
    available_seats = filter(lambda x: x[4], all_seats)&lt;br /&gt;
    with lock:&lt;br /&gt;
        results.extend(available_seats)&lt;br /&gt;
        tasks -= 1&lt;br /&gt;
        if tasks == 0:&lt;br /&gt;
            e.set()&lt;/p&gt;

&lt;p&gt;for t in descartes(locations, months):&lt;br /&gt;
    t = threading.Thread(target=task, args=t)&lt;br /&gt;
    t.daemon = True&lt;br /&gt;
    t.start()&lt;/p&gt;

&lt;p&gt;e.wait()&lt;br /&gt;
pprint.pprint(results)&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;Enjoy it.&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>宁夏川两头头尖</title><link href="https://sunng.info/blog/zhu-xia-chuan-liang-tou-tou-jian.html" rel="alternate"></link><published>2011-02-27T00:00:00+08:00</published><updated>2011-02-27T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-02-27:/blog/zhu-xia-chuan-liang-tou-tou-jian.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;Music
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;前天在豆瓣电台上听到赵牧阳的《&lt;a href="http://site.douban.com/zhaomuyang/?s=24204"&gt;黄河谣&lt;/a&gt;》，循声找过去发现这两天在上海有小演出，一时兴起叫上纯西 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;Music
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;前天在豆瓣电台上听到赵牧阳的《&lt;a href="http://site.douban.com/zhaomuyang/?s=24204"&gt;黄河谣&lt;/a&gt;》，循声找过去发现这两天在上海有小演出，一时兴起叫上纯西北人&lt;a href="http://www.phio.me/"&gt;亚洲&lt;/a&gt;就决定去听听。&lt;/p&gt;

&lt;p&gt;地点在上海电视台后面的Wooden Box酒吧。鉴于是第一次在上海听这样的演出，所以也没有什么可比较的。酒馆不大，为了能占个好位置，9点开始的演出我们一行人7点半就坐在了整个酒馆最好的座位上，开始等着。事后证明这是正确的选择，节目开始整个酒馆连个落脚的地方都没有。&lt;/p&gt;

&lt;p&gt;大约9点半赵牧阳出现了，先挂了一副艺术品，抱歉我实在没看懂画上画什么。头戴雷锋帽，身穿，身穿还是土豆上大家能看到那个视频的衣服，还专门做出了一道汗渍。当然啦，沧桑是装不出来的，吼两声就知道有没有。&lt;/p&gt;

&lt;p&gt;第一环节是赵牧阳弹三弦唱秦腔，大约唱了三首。具体唱什么现在都忘掉了，以前觉得三弦一响都是何玉笙，何勇，现在老观念改改了。亚洲基本上都能听懂，而且能听出出处，虽然是新唱，但都是老曲老调，所以正宗西北人听得透。我之前补习过张佺和苏阳，宁夏川两头头尖，黄河水流过了家流过了兰州我还是知道一点的。&lt;/p&gt;

&lt;p&gt;第二个环节是赵牧阳和酒吧老板出门抽烟，场子里周朝的吉他solo。&lt;a href="http://www.douban.com/people/2664402/"&gt;周朝&lt;/a&gt;这个人我之前还不知道，不过去的好多人是周朝的朋友，以至于很多人不知道赵牧阳是谁，还得跟我这样的打听。周朝的吉他谈得没得说，在豆瓣上听几首就知道了。&lt;/p&gt;

&lt;p&gt;第三阶段，三人合作，牧阳打鼓，周朝吉他，酒吧老板弹贝斯。到了兴头上赵牧阳会吼两嗓子，周朝顺势来了一首Another brick in the wall。后来又请上来一个女的，据说也是德国回来的，乍看还有点龚琳娜的意思，一开口整个一个有过之无不及，让人怀疑是不是德国走一趟都这股味了。四个人各显神通，即兴演出，场面越来越尽兴，那种感觉，等过几天土豆优酷上看视频吧！&lt;/p&gt;

&lt;p&gt;为了赶末班地铁我不得不提前一个小时走了，最后肯定错过不少好场面。西北民谣确实挺适合我们干旱地区生长的人，听得带劲听得舒心，今后有机会还是想看看苏阳张佺什么的。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Using dojo on Chrome/Chromium from local file system</title><link href="https://sunng.info/blog/using-dojo-on-chromechromium-from-local-file-system.html" rel="alternate"></link><published>2011-02-21T00:00:00+08:00</published><updated>2011-02-21T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-02-21:/blog/using-dojo-on-chromechromium-from-local-file-system.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;Chrome&lt;/li&gt;
&lt;li&gt;dojo&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;dojo.require uses XMLHttpRequest to load additional components. On default Chrome settings, XMLHttpRequest on local file system is not allowed. This will break whole dojo system.&lt;/p&gt;

&lt;p&gt;To change the default policy, you can start Chrome with a command line option …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;Chrome&lt;/li&gt;
&lt;li&gt;dojo&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;dojo.require uses XMLHttpRequest to load additional components. On default Chrome settings, XMLHttpRequest on local file system is not allowed. This will break whole dojo system.&lt;/p&gt;

&lt;p&gt;To change the default policy, you can start Chrome with a command line option &lt;i&gt;--allow-file-access-from-files&lt;/i&gt;.&lt;/p&gt;

&lt;p&gt;This issue is a well known one for Chromium project and still under active discussion:
&lt;a href="http://code.google.com/p/chromium/issues/detail?id=40787"&gt;http://code.google.com/p/chromium/issues/detail?id=40787&lt;/a&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Fix ogg/oga not play in Firefox 3.6</title><link href="https://sunng.info/blog/fix-oggoga-not-play-in-firefox-36.html" rel="alternate"></link><published>2011-02-15T00:00:00+08:00</published><updated>2011-02-15T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-02-15:/blog/fix-oggoga-not-play-in-firefox-36.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;apache&lt;/li&gt;
&lt;li&gt;firefox&lt;/li&gt;
&lt;li&gt;html
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Native audio support was introduced in since Firefox 3.5 . Ogg is one of the media format supported by Firefox. However, sometimes you may find it doesn't work even if you set the right source path.  And you just check …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;apache&lt;/li&gt;
&lt;li&gt;firefox&lt;/li&gt;
&lt;li&gt;html
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Native audio support was introduced in since Firefox 3.5 . Ogg is one of the media format supported by Firefox. However, sometimes you may find it doesn't work even if you set the right source path.  And you just check the network status of audio element:&lt;/p&gt;

&lt;p&gt;&lt;i&gt;document.getElementsByTagName("audio")[0].networkState&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;Then you get the constant of a 4, which is HTMLMediaElement.NETWORK_NO_SOURCE.&lt;/p&gt;

&lt;p&gt;This is because firefox checks the Content-Type header to make sure it's a media file. (Webkit based browsers don't have this restriction.) However, ogg format is not configured on most http servers. You can check the content type by:
&lt;i&gt;curl -I &amp;lt;url-to-media-file&amp;gt;&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;Take Apache as example, you can add following content to your configuration file:
&lt;i&gt;AddType audio/ogg .oga&lt;/i&gt;
&lt;i&gt;AddType video/ogg .ogv .ogg&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;For more, check this article:
&lt;a href="https://developer.mozilla.org/en/Configuring_servers_for_Ogg_media"&gt;https://developer.mozilla.org/en/Configuring_servers_for_Ogg_media&lt;/a&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>pgpoolII的管理功能</title><link href="https://sunng.info/blog/pgpooliide-guan-li-gong-neng.html" rel="alternate"></link><published>2011-02-14T00:00:00+08:00</published><updated>2011-02-14T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-02-14:/blog/pgpooliide-guan-li-gong-neng.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;postgresql
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;pgpool是postgresql的中间件，他的主要功能包括：
&lt;ul&gt;
&lt;li&gt;Connection Pooling 连接池&lt;/li&gt;
&lt;li&gt;Replication 双写&lt;/li&gt;
&lt;li&gt;Load Balance 负载均衡&lt;/li&gt;
&lt;li&gt;Limiting Exceeding Connections 连接限制&lt;/li&gt;
&lt;li&gt;Parallel Query 并行查询&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;除此以外，pgpool还提供了对数据库节点的健康检查功能，自 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;postgresql
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;pgpool是postgresql的中间件，他的主要功能包括：
&lt;ul&gt;
&lt;li&gt;Connection Pooling 连接池&lt;/li&gt;
&lt;li&gt;Replication 双写&lt;/li&gt;
&lt;li&gt;Load Balance 负载均衡&lt;/li&gt;
&lt;li&gt;Limiting Exceeding Connections 连接限制&lt;/li&gt;
&lt;li&gt;Parallel Query 并行查询&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;除此以外，pgpool还提供了对数据库节点的健康检查功能，自动fail over。&lt;/p&gt;

&lt;p&gt;对pgpool的管理，pgpool本身除了工作端口（默认5433）以外，还开放一个pcp端口9898，类似FTP的控制端口。通过pcp端口可以获取pgpool的运行时信息，也可以进行运行时的操作。这些功能罗列在：&lt;br /&gt;
http://pgpool.projects.postgresql.org/pgpool-II/doc/pgpool-en.html#reference&lt;/p&gt;

&lt;p&gt;封装这些命令的二进制文件也随着pgpool发行，由于是通过网络通信，所以并不要求这些工具与pgpool安装在同一台机器上。&lt;/p&gt;

&lt;p&gt;除此以外，为了用户友好，pgpool还有一个具有UI的用户管理工具。这个工具的功能包括：
&lt;ul&gt;
&lt;li&gt;系统信息，主要是pcp的信息&lt;/li&gt;
&lt;li&gt;配置文件编辑，这个功能似乎要求pgpooladmin和pgpool安装在一台机器上以便直接都写配置文件&lt;/li&gt;
&lt;li&gt;切分规则编辑&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;另外，pgpool还通过一些自定义的SQL语句允许用户从工作端口提取一些配置信息和运行时信息：&lt;br /&gt;
http://pgpool.projects.postgresql.org/pgpool-II/doc/pgpool-en.html#show-commands&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>春节留影，牛</title><link href="https://sunng.info/blog/chun-jie-liu-ying-niu.html" rel="alternate"></link><published>2011-02-08T00:00:00+08:00</published><updated>2011-02-08T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-02-08:/blog/chun-jie-liu-ying-niu.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;photo d60
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;家里有两头牛，准确说本来是一头，一个半月前添了新成员。仔细看，都是双眼皮。&lt;/p&gt;

&lt;p&gt;瞧这一家子
&lt;a href="http://www.flickr.com/photos/40741608@N08/5428020614/" title="DSC_0008 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5176/5428020614_d5f4b5083f.jpg" width="500" height="336" alt="DSC_0008" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;她是我们 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;photo d60
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;家里有两头牛，准确说本来是一头，一个半月前添了新成员。仔细看，都是双眼皮。&lt;/p&gt;

&lt;p&gt;瞧这一家子
&lt;a href="http://www.flickr.com/photos/40741608@N08/5428020614/" title="DSC_0008 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5176/5428020614_d5f4b5083f.jpg" width="500" height="336" alt="DSC_0008" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;她是我们家的老成员了
&lt;a href="http://www.flickr.com/photos/40741608@N08/5428021062/" title="DSC_0019 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5055/5428021062_c46b7471a5.jpg" width="336" height="500" alt="DSC_0019" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;小牛好像还没有长好牙，现在还不吃草，只是添添秸秆，饿了还是得找妈
&lt;a href="http://www.flickr.com/photos/40741608@N08/5428021524/" title="DSC_0028 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5135/5428021524_c0bf0c060e.jpg" width="500" height="336" alt="DSC_0028" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;后面是第二次拍的，到这次的时候小牛已经认识我了，一点都不怕，镜头感十足
&lt;a href="http://www.flickr.com/photos/40741608@N08/5427424857/" title="DSC_0078 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5018/5427424857_c98605f729.jpg" width="500" height="336" alt="DSC_0078" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;双眼皮，长睫毛，和你一样
&lt;a href="http://www.flickr.com/photos/40741608@N08/5428028372/" title="DSC_0084 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5093/5428028372_5bfd43ee62.jpg" width="500" height="336" alt="DSC_0084" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;我最喜欢这张，已经用它做头像了
&lt;a href="http://www.flickr.com/photos/40741608@N08/5427425847/" title="DSC_0086 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5179/5427425847_3e521300cd.jpg" width="500" height="336" alt="DSC_0086" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;也别光顾自己臭美，大牛妈妈还是要不停地吃草来养活这孩子
&lt;a href="http://www.flickr.com/photos/40741608@N08/5428029332/" title="DSC_0087 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5093/5428029332_c5e79768c0.jpg" width="500" height="336" alt="DSC_0087" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;:-;
&lt;a href="http://www.flickr.com/photos/40741608@N08/5428029732/" title="DSC_0088 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5217/5428029732_42c637c270.jpg" width="500" height="336" alt="DSC_0088" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;一头秀发
&lt;a href="http://www.flickr.com/photos/40741608@N08/5428030234/" title="DSC_0095 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5015/5428030234_c24e5d25cf.jpg" width="500" height="336" alt="DSC_0095" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;狮子王？
&lt;a href="http://www.flickr.com/photos/40741608@N08/5428030674/" title="DSC_0099 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5057/5428030674_9d998469c3.jpg" width="500" height="336" alt="DSC_0099" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/5427427943/" title="DSC_0101 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5220/5427427943_5a23fa59a6.jpg" width="500" height="336" alt="DSC_0101" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;最后一张，英俊潇洒
&lt;a href="http://www.flickr.com/photos/40741608@N08/5428031642/" title="DSC_0103 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5011/5428031642_5933e9c844.jpg" width="500" height="336" alt="DSC_0103" /&gt;&lt;/a&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>春节留影，农村</title><link href="https://sunng.info/blog/chun-jie-liu-ying-nong-cun.html" rel="alternate"></link><published>2011-02-08T00:00:00+08:00</published><updated>2011-02-08T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-02-08:/blog/chun-jie-liu-ying-nong-cun.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;D60&lt;/li&gt;
&lt;li&gt;photo
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;北方农村的冬天外屋凋敝满眼看不到一点绿色，空气里弥漫烧煤和烧秸秆的烟气。这个时候的夕阳 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;D60&lt;/li&gt;
&lt;li&gt;photo
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;北方农村的冬天外屋凋敝满眼看不到一点绿色，空气里弥漫烧煤和烧秸秆的烟气。这个时候的夕阳还是很到位的。&lt;/p&gt;

&lt;p&gt;来张逆光
&lt;a href="http://www.flickr.com/photos/40741608@N08/5428019644/" title="DSC_0001 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5292/5428019644_5d56dc1a49.jpg" width="336" height="500" alt="DSC_0001" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;树上的鸟巢
&lt;a href="http://www.flickr.com/photos/40741608@N08/5428020106/" title="DSC_0004 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5134/5428020106_e41b799d73.jpg" width="500" height="336" alt="DSC_0004" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;浓雾里的村中一角
&lt;a href="http://www.flickr.com/photos/40741608@N08/5427420123/" title="DSC_0043 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5218/5427420123_80bd528a85.jpg" width="500" height="336" alt="DSC_0043" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;村道
&lt;a href="http://www.flickr.com/photos/40741608@N08/5428024168/" title="DSC_0045 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5053/5428024168_8d02d4d63c.jpg" width="336" height="500" alt="DSC_0045" /&gt;&lt;/a&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>春节留影，烟火</title><link href="https://sunng.info/blog/chun-jie-liu-ying-yan-huo.html" rel="alternate"></link><published>2011-02-08T00:00:00+08:00</published><updated>2011-02-08T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-02-08:/blog/chun-jie-liu-ying-yan-huo.html</id><content type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;D60&lt;/li&gt;
&lt;li&gt;photo
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;村里能放烟花，是到了后几天才想起来的事。在妹妹的协助下实验了一把，ISO1600，快门优先半秒曝光&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/5427422005/" title="DSC_0052 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5093/5427422005_a8f6e9f503.jpg" width="336" height="500" alt="DSC_0052" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/5428025508/" title="DSC_0056 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5131/5428025508_ce400ba7a8.jpg" width="336" height="500" alt="DSC_0056" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/5427422971/" title="DSC_0064 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5296/5427422971_75d3d2230c.jpg" width="336" height="500" alt="DSC_0064" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/5428026548/" title="DSC_0066 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5017/5428026548_c3bbbf3b18.jpg" width="336" height="500" alt="DSC_0066" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/5428026928/" title="DSC_0074 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5259/5428026928_f4d123382b.jpg" width="336" height="500" alt="DSC_0074" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/5428027396/" title="DSC_0076 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5139/5428027396_96af163ab3.jpg" width="336" height="500" alt="DSC_0076" /&gt;&lt;/a&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Breaking changes in compojure 0.6.0</title><link href="https://sunng.info/blog/breaking-changes-in-compojure-060.html" rel="alternate"></link><published>2011-02-02T00:00:00+08:00</published><updated>2011-02-02T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-02-02:/blog/breaking-changes-in-compojure-060.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;compojure&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;在compojure 0.6.0里，默认的middleware被移除了。因为还没有正式发布，所以网上几乎没有相关的文档说明，而0.5.x的例子已经没能正常工作了。&lt;/p&gt;

&lt;p&gt;[cc lang="clojure …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;compojure&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;在compojure 0.6.0里，默认的middleware被移除了。因为还没有正式发布，所以网上几乎没有相关的文档说明，而0.5.x的例子已经没能正常工作了。&lt;/p&gt;

&lt;p&gt;[cc lang="clojure"]&lt;br /&gt;
;...&lt;br /&gt;
(defroutes root&lt;br /&gt;
    (GET "/" {params :params} (str params)))&lt;/p&gt;

&lt;p&gt;(run-jetty root {:port 8080}))&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;这样在0.5.x中可以正确运行的代码，在0.6.0中params变成了空的map。&lt;/p&gt;

&lt;p&gt;在0.6.0中，compojure引入了一个新的ns叫做compojure.handler，其中包含两个function， api和site，它们包含了一些默认的middleware，适合相应的开发场景。为了让代码能够工作，在新版本中：&lt;br /&gt;
[cc lang="clojure"]&lt;br /&gt;
;...&lt;br /&gt;
(use 'compojure.handler)&lt;br /&gt;
(defroutes root&lt;br /&gt;
    (GET "/" {params :params} (str params)))&lt;/p&gt;

&lt;p&gt;(def app (site root))&lt;/p&gt;

&lt;p&gt;(run-jetty root {:port 8080}))&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;详细可以在这里找到
&lt;a href="https://groups.google.com/group/compojure/browse_thread/thread/4f8574d808ddf53e"&gt;https://groups.google.com/group/compojure/browse_thread/thread/4f8574d808ddf53e&lt;/a&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>包管理与路径管理</title><link href="https://sunng.info/blog/bao-guan-li-yu-lu-jing-guan-li.html" rel="alternate"></link><published>2011-01-22T00:00:00+08:00</published><updated>2011-01-22T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-01-22:/blog/bao-guan-li-yu-lu-jing-guan-li.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;maven&lt;/li&gt;
&lt;li&gt;nodejs&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;tools
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;现在几乎每一种语言都有一些依赖管理工具，或者是中央的包仓库。比如这些：
&lt;ul&gt;
&lt;li&gt;Java: maven, ivy, gradle&lt;/li&gt;
&lt;li&gt;Ruby: gems …&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;maven&lt;/li&gt;
&lt;li&gt;nodejs&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;tools
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;现在几乎每一种语言都有一些依赖管理工具，或者是中央的包仓库。比如这些：
&lt;ul&gt;
&lt;li&gt;Java: maven, ivy, gradle&lt;/li&gt;
&lt;li&gt;Ruby: gems&lt;/li&gt;
&lt;li&gt;Python: easy_install, pip&lt;/li&gt;
&lt;li&gt;Clojure: leinigen&lt;/li&gt;
&lt;li&gt;Groovy: maven, grape, gradle&lt;/li&gt;
&lt;li&gt;Hashkell: cabal&lt;/li&gt;
&lt;li&gt;PHP: pear&lt;/li&gt;
&lt;li&gt;Nodejs: npm&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;这些工具在管理包和路径时都会采用各不相同的策略，有的是通过自身实现，有的是借助语言平台本身的特点。&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;Java&lt;/h3&gt;
其中最注明的Maven，它的方式是在POM文件中定义你的依赖，Maven会在本地仓库中维护这些依赖。Maven的本地仓库默认是在$HOME/.m2/repository目录下，是用户独立的，当然要下载一个依赖也不需要root权限。而在通过Maven运行Java项目时，Maven插件会自动管理classpath，你并不需要把这些依赖从本地仓库里拷贝出来而单独维护一个所谓lib目录（这样也不好管理）。这是Maven的方式，目前看来，这也是最经典的一种方式。除了maven本身的特性以外，这也和Java的classpath机制有关。&lt;/p&gt;

&lt;p&gt;最近势头很猛的Java构建工具gradle，方式也与Maven类似，它会把下载的依赖存放在$HOME/.gradle/cache目录里，并自动管理classpath。&lt;/p&gt;

&lt;p&gt;同样是Java，与maven相对应的是ant+ivy。ivy可以为你管理依赖，但是ivy不会帮你管理classpath。ivy的包管理，是以project为scope的，你需要维护一个lib目录来存放这些下载的包，再通过传统的ant的方式去管理classpath，从而使项目可以进行编译和运行。&lt;/p&gt;

&lt;p&gt;在Java世界里，还有一个特别的工具叫做grape，它是专门用于groovy的轻量级的依赖解决方案。grape是以脚本为scope，在需要依赖的脚本中通过@Grab声明依赖，grape工具可以从maven仓库中下载依赖到$HOME/.groovy/grapes中，并把相关的依赖加入groovy的classpath。除此以外，grape还有一个命令行工具帮助你手动下载依赖到本地仓库。grape的内部是基于ivy的，不过它的方式比ant要自动化很多。&lt;/p&gt;

&lt;p&gt;在Java世界里还有一个特例，是Clojure的依赖管理工具leiningen。leiningen本身也比较简单，它的方式与ivy相同，会解析project.clj文件中定义的依赖关系，并下载到当前的工程目录下的lib中。lein是鼓励通过uberjar的方式把依赖统统打包的，所以它并没有classpath的管理功能。&lt;/p&gt;

&lt;p&gt;总体来说，Java世界的工具和Java是相似的，其最大特点就是System independent，安装包不需要root权限，每次的运行都需要管理classpath。作为开发人员，classpath中有哪些可以访问的类库是可以控制的，这也使Java程序的移植性得到良好的控制和管理。&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;Python&lt;/h3&gt;
与Java不同，Python通常作为系统分发的一部分，他的包管理和PATH管理要相对混乱一些。通常我们有两种方式来安装一个Python的软件包：
&lt;ul&gt;
&lt;li&gt;sudo apt-get install python-redis&lt;/li&gt;
&lt;li&gt;sudo easy_install redis&lt;/li&gt;
&lt;/ul&gt;
一种是通过系统的包管理工具（如apt-get）从系统的软件仓库里安装，一种是通过python自己的包管理工具（如easy_install / pip）从Python Cheese Shop中下载安装。这两种安装方式有什么不同呢。以Ubuntu为例，通过apt-get安装的python包通常会被放在 /usr/share/pyshared 或 /usr/lib/python2.6/dist-packages 中，相对应的，由easy_install安装的Python包，则存放在 /usr/local/lib/python2.6/dist-packages 中。Python启动后可以通过查看sys.path来了解当前的path情况。&lt;/p&gt;

&lt;p&gt;除了安装到系统目录，easy_install可以通过 --user 选项来把软件包安装到用户目录 $HOME/.local/lib/python2.6/site-packages。不过无论是系统级别还是用户级别，python都很难在启动时管理Path，即任何时候python都可以访问安装在系统中的所有软件包。这导致了混乱的情况，导致编写的python软件难以进行依赖管理和移植（即使没有定义在setup.py中，很多依赖还是可以访问的）。&lt;/p&gt;

&lt;p&gt;由此virtualenv营运而生，virtualenv帮助你创建一个独立的python运行环境。激活这个小环境之后，easy_install/pip仅仅安装软件到小环境，python仅能访问环境内部的site-packages，这样整个环境中的依赖关系就非常清楚，也保障了程序的移植性。这样，就将原本系统scope的python包管理级别改进为项目级别。我之前写的jip也是将依赖下载或拷贝到virtualenv的小环境中，并且修改jython的启动脚本修改PYTHONPATH的设置，保证Java依赖对Jython的透明可访问。&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;Nodejs&lt;/h3&gt;
nodejs是一个新兴的生态系统，一个包管理工具对其也是必不可少。npm是目前整个社区都比较认可的工具。&lt;/p&gt;

&lt;p&gt;不过目前npm并不好用。npm默认会把自己安装到 node安装前缀的目录，比如node安装时你选择了默认前缀/usr/local，那么npm会把自己安装到/usr/local/lib/node里。这个目录是系统级别的，所以需要root权限，而npm本身又不鼓励用户用root权限来安装软件包（安全问题）。所以作者说希望用户把/usr/local/lib/node权限授予用户，或者把node安装到用户目录里。这两种方式其实都不太优雅。&lt;/p&gt;

&lt;p&gt;Ruby的gems在这方面最符合unix哲学，即用户知道自己在做什么。如果用户以root权限运行gem  install，gem会把软件包安装到系统目录中对所有用户可用，而如果以普通用户权限运行，则安装到用户目录 $HOME/.gem 中仅当前用户可见。&lt;/p&gt;

&lt;p&gt;nodejs在加载软件包时，会在require.paths中的几个目录里查找，前两个都是用户目录，所以npm也并非一定要把包安装到系统目录里去。虽然现在可以用过修改.npmrc文件在修改npm的默认行为，不过在这个CoC的时代，显然太繁琐了。&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;Best Pratice&lt;/h3&gt;
总结一下，包管理和路径管理的最佳实践应该是：语言平台有CoC的路径机制，包管理器有基于环境变量、用户权限的判断执行合适行为。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Update on exaile-soundmenu-indicator and exaile-doubanfm-plugin</title><link href="https://sunng.info/blog/update-on-exaile-soundmenu-indicator-and-exaile-doubanfm-plugin.html" rel="alternate"></link><published>2011-01-14T00:00:00+08:00</published><updated>2011-01-14T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-01-14:/blog/update-on-exaile-soundmenu-indicator-and-exaile-doubanfm-plugin.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;Douban&lt;/li&gt;
&lt;li&gt;exaile&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;h3&gt;Exaile-soundmenu-indicator&lt;/h3&gt;
As many users complaint about the "minimise to sound menu" issue, I updated exaile-soundmenu-indicator plugin. Now it is basically compliant with specification of &lt;a href="https://wiki.ubuntu.com/SoundMenu"&gt;SoundMenu&lt;/a&gt;: it will keep playing if you click the close button while the player is playing …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;Douban&lt;/li&gt;
&lt;li&gt;exaile&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;h3&gt;Exaile-soundmenu-indicator&lt;/h3&gt;
As many users complaint about the "minimise to sound menu" issue, I updated exaile-soundmenu-indicator plugin. Now it is basically compliant with specification of &lt;a href="https://wiki.ubuntu.com/SoundMenu"&gt;SoundMenu&lt;/a&gt;: it will keep playing if you click the close button while the player is playing, and will exit if not playing. However, to be able to complete the functionality, you have to commentify the line &lt;strong&gt;1506&lt;/strong&gt; of &lt;strong&gt;/usr/lib/exaile/xlgui/main.py&lt;/strong&gt;, which is "&lt;strong&gt;return true&lt;/strong&gt;" of method "&lt;strong&gt;delete_event&lt;/strong&gt;" (On Ubuntu 10.10, exaile 0.3.2.0-ubuntu3). &lt;strong&gt;Otherwise&lt;/strong&gt;, whenever you close the window it won't exit.&lt;/p&gt;

&lt;p&gt;I know this is a bad idea to require user to modify the source code, but it‘s not possible to override the behavior of a GTK callback, especially when you do not have the handler_id of the callback. If you do not mind the incompliant of closing behavior, you can just keep it as is, and exiting by menu and CRTL+Q.&lt;/p&gt;

&lt;p&gt;Grab the snapshot of github repository to get the up-to-date version:
&lt;a href="https://github.com/sunng87/Exaile-Soundmenu-Indicator"&gt;https://github.com/sunng87/Exaile-Soundmenu-Indicator&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;Exaile-doubanfm-plugin&lt;/h3&gt;
Exaile豆瓣电台插件更新。豆瓣最近调整了登录的策略：
&lt;ul&gt;&lt;li&gt;用户在首次访问豆瓣时被设置cookie bid&lt;/li&gt;
&lt;li&gt;用户提交登陆表单时被要求提交cookie bid，否则不予通过。&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;此外，这次更新开始使用HTTPS提交用户登录信息。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/sunng87/exaile-doubanfm-plugin"&gt;https://github.com/sunng87/exaile-doubanfm-plugin&lt;/a&gt;，请下载最新0.0.6c。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>HTML5 Form in Opera 11</title><link href="https://sunng.info/blog/html5-form-in-opera-11.html" rel="alternate"></link><published>2011-01-12T00:00:00+08:00</published><updated>2011-01-12T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-01-12:/blog/html5-form-in-opera-11.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;css&lt;/li&gt;
&lt;li&gt;html&lt;/li&gt;
&lt;li&gt;opera
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;严格意义上我现在已经不能算是个web guy了，不过托Web开放的特性，咱也能评论一下。最近做个小的界面，考虑到要 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;css&lt;/li&gt;
&lt;li&gt;html&lt;/li&gt;
&lt;li&gt;opera
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;严格意义上我现在已经不能算是个web guy了，不过托Web开放的特性，咱也能评论一下。最近做个小的界面，考虑到要面向未来，HTML5又提供了丰富的表单增强。这些特性中，Firefox 3.6基本上还没有支持，Chromium 8.0有少部分支持，而真正支持比较完整的是Opera 11.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;autofocus&lt;/strong&gt;
页面载入后自动将焦点转到某个input，就如以前的onload时候xxx.focus()&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;required&lt;/strong&gt;
指定某个input为必填，提交时会有validation，在Opera11里还会给你这样的警告并且禁止提交：
&lt;a href="http://www.flickr.com/photos/40741608@N08/5349345730/" title="Screenshot - 01122011 - 10:53:41 PM by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5083/5349345730_f6696fd07e.jpg" width="329" height="144" alt="Screenshot - 01122011 - 10:53:41 PM" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;email / url&lt;/strong&gt;
指定某个input的特殊类型，提交时也会有对应的格式检查：
&lt;a href="http://www.flickr.com/photos/40741608@N08/5348736441/" title="Screenshot - 01122011 - 10:54:41 PM by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5081/5348736441_78accc61ac.jpg" width="344" height="140" alt="Screenshot - 01122011 - 10:54:41 PM" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;这样的提示可能满足不了你的替用户着想的产品经理，但是你清楚他帮你省了多少事情。而且，在DiveInfoHtml5里提到，iphone甚至为这个input做了优化，在软件盘上把空格键变得很小（因为输入邮件地址用不着），并且提供了一个@键；这就是工程师的用户体验啊。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;placeholder&lt;/strong&gt;
在input里显示提示文本：
&lt;a href="http://www.flickr.com/photos/40741608@N08/5349345562/" title="Screenshot - 01122011 - 10:52:18 PM by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5209/5349345562_d658665b92.jpg" width="441" height="63" alt="Screenshot - 01122011 - 10:52:18 PM" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;注意到后面的红色星号了吗。这本来是我设计的一个workaround，既然Firefox 3都还不支持required validation，那么我们通过CSS来显示一个必填的提示：&lt;br /&gt;
[cc lang="css"]&lt;br /&gt;
input[required="required"]:after {&lt;br /&gt;
    content: '(*)';&lt;br /&gt;
    color: #F00;&lt;br /&gt;
}
[/cc]&lt;/p&gt;

&lt;p&gt;结果这个伪类最后只有在Opera里实现了预期的效果，看&lt;a href="http://stackoverflow.com/questions/2587669/css-after-pseudo-element-on-input-field"&gt;在Stackoverflow上的讨论&lt;/a&gt;才得知input的:after和:before伪类只有Opera支持。&lt;/p&gt;

&lt;p&gt;HTML5 Form这部分是一个真正务实的标准，你可以看到几乎每一个特性都是我们日常每时每刻都需要，几乎每个网站的JS都要实现的功能。标准是沟通上下游（浏览器和应用开发人员）的契约，一个真正反应相互需求的标准才是有实际价值的。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>soldat &amp; gefr</title><link href="https://sunng.info/blog/soldat-gefr.html" rel="alternate"></link><published>2011-01-08T00:00:00+08:00</published><updated>2011-01-08T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-01-08:/blog/soldat-gefr.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;gefr&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;jython&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;soldat
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;我的这一套stack正在走向完整。上次贴了一张soldat-http的图，现在基于soldat的wsgi服务器也已经有了一个基本可以运行的实现，名字叫做gefr（我的命名出处参考&lt;a href="http://en.wikipedia.org/wiki/Rank_insignia_of_the_German_armed_forces"&gt;这里&lt;/a&gt;）。现在gefr上已经可以跑基于bottle框架的wsgi程序了，也 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;gefr&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;jython&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;soldat
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;我的这一套stack正在走向完整。上次贴了一张soldat-http的图，现在基于soldat的wsgi服务器也已经有了一个基本可以运行的实现，名字叫做gefr（我的命名出处参考&lt;a href="http://en.wikipedia.org/wiki/Rank_insignia_of_the_German_armed_forces"&gt;这里&lt;/a&gt;）。现在gefr上已经可以跑基于bottle框架的wsgi程序了，也就是说一些基于python的web应用可能可以通过jython来运行在soldat上。为了搞定jython的环境，这几天我还花了不少时间做了jip帮我从maven仓库里自动下载依赖的jar包。&lt;/p&gt;

&lt;p&gt;soldat和gefr的代码都放在我的bitbucket上：
&lt;ul&gt;
&lt;li&gt;soldat &lt;a href="https://bitbucket.org/sunng/soldat"&gt;https://bitbucket.org/sunng/soldat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;gefr &lt;a href="https://bitbucket.org/sunng/gefr"&gt;https://bitbucket.org/sunng/gefr&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;此外，这两个项目也分别发布到了&lt;a href="https://oss.sonatype.org/content/repositories/snapshots/"&gt;sonatype oss仓库&lt;/a&gt;和&lt;a href="http://pypi.python.org/pypi/gefr"&gt;python cheese shop&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;现在还有几个问题：
&lt;ul&gt;
&lt;li&gt;soldat在读buffer的时候先获得buffer的limit，再去读相应长度的buffer有时会出现BufferUnderflowException。这个可能存在线程安全问题，现在还没发现。&lt;/li&gt;
&lt;li&gt;gefr启动之后可以通过在jvisualvm里找到这个进程，但是绑定profiler之后很诡异的是gefr就不再处理请求了。&lt;/li&gt;
&lt;li&gt;直接用soldat的处理http请求，吞吐量可以上万；但是在上面加上jython的gefr，再加上bottle框架，同样的功能吞吐量就剩下原来的十分之一了。就是因为没法做profile，所以还不知道时间花到哪里去了。&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;简单地 announce 一下，这样我有更多的动力来继续把这两个小东西做好。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>jip 0.1</title><link href="https://sunng.info/blog/jip-01.html" rel="alternate"></link><published>2011-01-04T00:00:00+08:00</published><updated>2011-01-04T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-01-04:/blog/jip-01.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;jip&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;The original idea is to create a standalone jython environment, I took &lt;a href="http://sunng.info/blog/2010/12/setup-a-jython-development-environment/"&gt;traditional Java tools, ant and ivy&lt;/a&gt;, to resolve Java dependencies. But  the XMLs seem to be verbose and not pythonic. So I decide to create something like …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;jip&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;The original idea is to create a standalone jython environment, I took &lt;a href="http://sunng.info/blog/2010/12/setup-a-jython-development-environment/"&gt;traditional Java tools, ant and ivy&lt;/a&gt;, to resolve Java dependencies. But  the XMLs seem to be verbose and not pythonic. So I decide to create something like pip, that is, resolves and installs dependencies in a pythonic way.  It is jip.&lt;/p&gt;

&lt;p&gt;jip will automatically download jars and its &lt;strong&gt;non-optional&lt;/strong&gt; &lt;strong&gt;runtime&lt;/strong&gt; dependencies from Maven repositories. By default, jip will search your local repository and maven central repository for the requested artifact. Also, you can create a configuration file to overwrite this.&lt;/p&gt;

&lt;p&gt;Virtualenv is required by jip. You must run jip within a standalone environment, created by virtualenv:
&lt;i&gt;virtualenv -p /usr/local/bin/jython jython-env&lt;/i&gt;
&lt;i&gt;cd jython-env&lt;/i&gt;
&lt;i&gt;source bin/activate&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;Don't forget to activate it.&lt;/p&gt;

&lt;p&gt;Then download jip with pip:
&lt;i&gt;pip install jip&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;Now you have pip for python and jip for java. To install a Java package, just type:&lt;br /&gt;
jip install &amp;lt;groupId&amp;gt;:&amp;lt;artifactId&amp;gt;:&amp;lt;version&amp;gt;&lt;/p&gt;

&lt;p&gt;groupId+artifactId+version is known as the coordinate of a maven artifact. For example, you need spring-core in your jython development:
&lt;i&gt;jip install org.springframework:spring-core:3.0.5.RELEASE&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;The jars will be stored in &lt;i&gt;javalib&lt;/i&gt; directory:
&lt;i&gt;ls javalib&lt;/i&gt;
&lt;pre&gt;
commons-logging-1.1.1.jar     spring-core-3.0.5.RELEASE.jar
spring-asm-3.0.5.RELEASE.jar
&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;And When you installed jip, I will provide you a &lt;i&gt;jython-all&lt;/i&gt; command to include dependencies by default. So use &lt;i&gt;jython-all&lt;/i&gt; instead of &lt;i&gt;jython&lt;/i&gt; to run your program and the shell.&lt;/p&gt;

&lt;p&gt;For traditional Java user, there is a &lt;i&gt;resolve&lt;/i&gt; subcommand to download dependencies defined in a pom file. This is more maintainable, some of you may prefer this way to typing it one by one.
&lt;i&gt;jip resolve pom.xml&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;To define custom repositories, place a dot file &lt;i&gt;.jip&lt;/i&gt; in your home directory:&lt;br /&gt;
[cc lang='ini']&lt;br /&gt;
[jboss]&lt;br /&gt;
uri=http://repository.jboss.org/maven2/&lt;br /&gt;
type=remote&lt;/p&gt;

&lt;p&gt;[local]&lt;br /&gt;
uri=/home/sun/.m2/repository/&lt;br /&gt;
type=local&lt;/p&gt;

&lt;p&gt;[central]&lt;br /&gt;
uri=http://repo1.maven.org/maven2/&lt;br /&gt;
type=remote&lt;br /&gt;
[/cc]&lt;br /&gt;
You may have internal Nexus, just append to this file following the pattern.&lt;/p&gt;

&lt;p&gt;Finally, a clean subcommand to remove everything you downloaded.&lt;/p&gt;

&lt;p&gt;That's all. You can find the project at:
&lt;ul&gt;&lt;li&gt;&lt;a href="http://pypi.python.org/pypi/jip"&gt;http://pypi.python.org/pypi/jip&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/sunng87/jip"&gt;https://github.com/sunng87/jip&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;Your feedback is appreciate. Fire an issue in github if you find any bugs or new ideas about this tool.&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Happy New Year!</title><link href="https://sunng.info/blog/happy-new-year.html" rel="alternate"></link><published>2011-01-01T00:00:00+08:00</published><updated>2011-01-01T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2011-01-01:/blog/happy-new-year.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;boardgame
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/5311404087/" title="IMG_0276 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5081/5311404087_fea4651daf.jpg" width="500" height="375" alt="IMG_0276" /&gt;&lt;/a&gt;
今天基本上学会了农场主的基本规则，在我看来虽然复杂，但是多少也和波多黎各什么的类似，这大概 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;boardgame
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/5311404087/" title="IMG_0276 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5081/5311404087_fea4651daf.jpg" width="500" height="375" alt="IMG_0276" /&gt;&lt;/a&gt;
今天基本上学会了农场主的基本规则，在我看来虽然复杂，但是多少也和波多黎各什么的类似，这大概就是这类桌游的pattern吧。负责任地说，绝对没有两个人玩的晨昏对峙复杂（又是太空又是核武又是战争指数还有全球战场搞神马啊）。这点从桌游店就能看出来，去拿农场主的时候老板还会对你表示一下惊叹和欣赏；可是要玩晨昏对峙呢，根本店里根本找不到。高出不胜寒啊！你一生要是能找到一个棋逢对手的朋友一起玩晨昏对峙，也是一件幸事 -___-。&lt;/p&gt;

&lt;p&gt;Anyway，农场主也很好玩，我就喜欢这种卡牌多、道具多的桌游。桌游的道具模型都可以复用了，它的猪和卡卡颂商人扩展里的一模一样。&lt;/p&gt;

&lt;p&gt;专心休假，停止劳作。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Setup a Jython development environment</title><link href="https://sunng.info/blog/setup-a-jython-development-environment.html" rel="alternate"></link><published>2010-12-30T00:00:00+08:00</published><updated>2010-12-30T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-12-30:/blog/setup-a-jython-development-environment.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;ant&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;jython&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;This article will show you some basic steps to setup a standalone environment for Jython, with the power of some popular and standard tools.&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;0. Install essential tools&lt;/h3&gt;
Take Ubuntu Maverick as an example, before starting, you have to …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;ant&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;jython&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;This article will show you some basic steps to setup a standalone environment for Jython, with the power of some popular and standard tools.&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;0. Install essential tools&lt;/h3&gt;
Take Ubuntu Maverick as an example, before starting, you have to make sure these packages installed on your system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Essential Python tools&lt;/strong&gt; (Python is shipped with most Linux distribution by default)
&lt;i&gt;sudo apt-get install python-pip python-virtualenv&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Java&lt;/strong&gt;
&lt;i&gt;sudo apt-get install openjdk-6-jdk openjdk-6-jre&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Jython&lt;/strong&gt;
The Jython package in Ubuntu repository is obsolete. Download Jython installer from official website. A full installation is recommended here.
&lt;a href="http://jython.org/downloads.html"&gt;http://jython.org/downloads.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As an optional step, I often move Jython direcotry to /usr/local/share/ , and use a symbol link to add Jython to PATH:
&lt;i&gt;sudo ln -s /usr/local/share/jython/bin/jython /usr/local/bin/&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Install additional Java tools&lt;/strong&gt;
&lt;i&gt;sudo apt-get install ant ivy&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;1. Create a standalone environment&lt;/h3&gt;
Jython 2.5 is fully compatible with virtualenv, so you can create a standalone environment just like what you do with python:
&lt;i&gt;virtualenv -p /usr/local/bin/jython jython-env&lt;/i&gt;
&lt;i&gt;cd jython-env&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;List the directory, you will see these files and directories:
&lt;ul&gt;
&lt;li&gt;bin/&lt;/li&gt;
&lt;li&gt;cachedir/&lt;/li&gt;
&lt;li&gt;Lib/&lt;/li&gt;
&lt;li&gt;jython.jar&lt;/li&gt;
&lt;li&gt;registry&lt;/li&gt;
&lt;/ul&gt;
Source the activate script:
&lt;i&gt;source bin/activate&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;2. Download and install python packages&lt;/h3&gt;
Now the python environment is just ready, you can install python packages as you want. Install bottle, for example:
&lt;i&gt;pip install bottle&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;3. Download and install Java dependencies&lt;/h3&gt;
This is the different step. As you know, the most important feature of Jython is availability of Java libraries to Python code. So we need some additional tools to manage Java dependencies. Popular Java lifecycle management tool &lt;strong&gt;Maven&lt;/strong&gt; is not suitable here, because Maven is tightly depends on its archetype. &lt;strong&gt;Gradle&lt;/strong&gt; is also known as a powerful build tool with DSL support. However, Gradle uses Groovy as the scripting language to create build file. Bringing Groovy to a Jython project seems terribly strange.&lt;/p&gt;

&lt;p&gt;So I prefer the traditional Ant way to manage dependencies. First of all, create a build file with following content:
&lt;strong&gt;build.xml&lt;/strong&gt;
[cc lang="xml"]
&lt;project xmlns:ivy="antlib:org.apache.ivy.ant" name="jython-env" default="resolve"&gt;
    &lt;target name="resolve" description="retrieve dependencies"&gt;
        &lt;ivy:retrieve pattern="javalib/[artifact]-[revision].[ext]" type="jar" /&gt;
    &lt;/target&gt;
&lt;/project&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;Then create an ivy xml to configure dependencies:
&lt;strong&gt;ivy.xml&lt;/strong&gt;
[cc lang="xml"]
&lt;ivy-module version="2.0"&gt;
    &lt;info organisation="info.sunng" module="jython-env" /&gt;
    &lt;dependencies&gt;
        &lt;dependency org="commons-lang" name="commons-lang" rev="2.5" /&gt;
    &lt;/dependencies&gt;
&lt;/ivy-module&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;Also, as an optional step, you can overwrite default settings to configure ivy to use maven local repository.
&lt;strong&gt;ivysettings.xml&lt;/strong&gt;
[cc lang="xml"]
&lt;ivy-settings&gt;
    &lt;settings defaultresolver="maven-or-not" /&gt;
    &lt;resolvers&gt;
        &lt;chain name="maven-or-not" returnfirst="true"&gt;
            &lt;filesystem name="maven-local" m2compatible="true" /&gt;
            &lt;ibiblio name="ibiblio" m2compatible="true" /&gt;
        &lt;/chain&gt;
    &lt;/resolvers&gt;
&lt;/ivy-settings&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;All done, now you can download everything. In Ubuntu, default ivy installation, you have to specify ivy path in ant command line:
&lt;i&gt;ant -lib /usr/share/java/ivy.jar&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;You will see jars in &lt;i&gt;javalib&lt;/i&gt;.&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;4. Reconfigure start up script&lt;/h3&gt;
The default Jython start up script is not friendly to external Java dependencies. To include jars, you have to use such dirty command line parameters:
&lt;i&gt;jython -Dpython.path=javalib/commons-lang-2.5.jar:javalib/...&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;To get rid of this, first rename bin/jython to bin/startJython. (Follow the Groovy naming convention)&lt;/p&gt;

&lt;p&gt;Then create a new bin/jython script wraps the old one:&lt;br /&gt;
[cc lang="bash"]&lt;br /&gt;
#! /bin/bash&lt;/p&gt;

&lt;p&gt;JYTHON_CMD="startJython"&lt;br /&gt;
JAVA_LIBS="javalib/*.jar"&lt;br /&gt;
PYTHON_PATH=""&lt;/p&gt;

&lt;p&gt;for lib in $JAVA_LIBS&lt;br /&gt;
do&lt;br /&gt;
    PYTHON_PATH="$PYTHON_PATH:$lib"&lt;br /&gt;
done&lt;/p&gt;

&lt;p&gt;#echo $PYTHON_PATH&lt;br /&gt;
$JYTHON_CMD -Dpython.path=$PYTHON_PATH&lt;/p&gt;

&lt;p&gt;[/cc]&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;5. Run&lt;/h3&gt;
Finally, it comes to an end. Type jython to execute the interactive shell:
&lt;i&gt;jython&lt;/i&gt;
&lt;blockquort&gt;
Jython 2.5.1 (Release_2_5_1:6813, Sep 26 2009, 13:47:54)&lt;br /&gt;
[OpenJDK Server VM (Sun Microsystems Inc.)] on java1.6.0_20&lt;br /&gt;
Type "help", "copyright", "credits" or "license" for more information.&lt;br /&gt;
&gt;&gt;&gt; from org.apache.commons.lang import BitField&lt;br /&gt;
&gt;&gt;&gt; import bottle
&lt;/blockquort&gt;&lt;/p&gt;

&lt;p&gt;Both Python and Java dependency is accessible. Now it's time to get your new start.&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>My first http server, soldat-http</title><link href="https://sunng.info/blog/my-first-http-server-soldat-http.html" rel="alternate"></link><published>2010-12-28T00:00:00+08:00</published><updated>2010-12-28T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-12-28:/blog/my-first-http-server-soldat-http.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;http&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;project
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;2008年的冬天，有一天和一个不认识的网友聊天，他说，以前也对这些框架啦什么的感兴趣，后来就变了，他说他 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;http&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;project
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;2008年的冬天，有一天和一个不认识的网友聊天，他说，以前也对这些框架啦什么的感兴趣，后来就变了，他说他当时的目标是写一个HTTP服务器。我当时能够认同他的观点，不过没什么特别的共鸣。到了今天春天，项目开始了，周末老大休假回杭州，留下大伙大眼瞪小眼要联调。出现了问题发现自己一窍不通，然后开玩笑说一定要把老大的NIO框架看懂才跳槽。结果不幸还没来得及怎么看就闪人了，还好老大&lt;a href="http://code.google.com/p/amoeba/"&gt;开源&lt;/a&gt;，现在依然还可以偷偷看看。另外，我还隐约记得09年冬天的时候，似乎也给自己定过个类似一年之内写个HTTP服务器之类的目标。&lt;/p&gt;

&lt;p&gt;所以我说，自己写程序是一件&lt;a href="http://blog.samsonis.me/2010/12/wordpress-%e8%b1%86%e7%93%a3%e6%8f%92%e4%bb%b6-douban-collections-%e8%b1%86%e7%93%a3%e6%94%b6%e8%97%8f-0-9-0/#comment-1299"&gt;情怀驱动&lt;/a&gt;的事情：就像&lt;a href="http://blog.samsonis.me/"&gt;有些同学&lt;/a&gt;一再强调自己再也不写&lt;a href="http://wordpress.org/extend/plugins/douban-collections/"&gt;wordpress插件&lt;/a&gt;了，可是还是不断有&lt;a href="http://blog.samsonis.me/2010/12/wordpress-%E8%B1%86%E7%93%A3%E6%8F%92%E4%BB%B6-douban-collections-%E8%B1%86%E7%93%A3%E6%94%B6%E8%97%8F-0-9-0/"&gt;新版本发布&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;好吧，其实这个目标最后没有实现。目前这个版本仅仅是能够work，在2010年快要结束的时候赶紧把他搬出来，聊以自慰，嗯，我这一年不是一无所获。
&lt;a href="http://www.flickr.com/photos/40741608@N08/5300136156/" title="Screenshot - 12282010 - 09:17:59 PM by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5124/5300136156_7a29dfb2f9.jpg" width="500" height="396" alt="Screenshot - 12282010 - 09:17:59 PM" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;接下来我还会进一步完善它和底层的事件驱动框架，还有意完成一个Jython的WSGI实现。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>LM Tooling, 2010</title><link href="https://sunng.info/blog/lm-tooling-2010.html" rel="alternate"></link><published>2010-12-24T00:00:00+08:00</published><updated>2010-12-24T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-12-24:/blog/lm-tooling-2010.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;hudson&lt;/li&gt;
&lt;li&gt;maven&lt;/li&gt;
&lt;li&gt;netweaver
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;阿哈，大家都开始总结2010年了，我也想找个角度来总结一下今年。三句不理老本行，还是聊软件开发周边 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;hudson&lt;/li&gt;
&lt;li&gt;maven&lt;/li&gt;
&lt;li&gt;netweaver
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;阿哈，大家都开始总结2010年了，我也想找个角度来总结一下今年。三句不理老本行，还是聊软件开发周边的事情。Lifecycle Management Tooling，软件生命周期管理的工具和我一年的感受。&lt;/p&gt;

&lt;p&gt;一月份的时候我还有大把的业余时间，在做一个叫做&lt;a href="https://bitbucket.org/sunng/yan"&gt;Yan&lt;/a&gt;的验证码服务。这个项目从我刚一到盛大工作就开始了，不过因为没有特别的需求，最后变成了自己的开源项目。这个项目用Maven做dependency management，用hg做版本控制，代码放在bitbucket上。它对Maven的使用还很基本，是一个Single Module的小项目，因为服务本身已经实现了插件机制，本来我准备把它拆成Multimodule的Maven工程来着，不过后来因为比较忙，这件事就放掉了。那时我已经开始尝试用hg进行分支管理、每个release打tag，甚至还有一段时间会有一些两个分支的并行开发。这些基本上是实验性质的尝试，不过和后来相比也算是做的比较规范的了。&lt;/p&gt;

&lt;p&gt;三月份我注册了github，顺带就开始用git了。到目前为止，我对hg和git的区别还没有特别的感受，选择的主要依据其实bitbucket和github。相对social一些的项目，github是当仁不让的选择。bitbucket上人少比较清净，一些incubator项目放在上面，烂尾了也不至于太丢脸。扯远了，哈哈。&lt;/p&gt;

&lt;p&gt;之后我们开始做一个规模比较大的项目，我们使用Maven，使用Nexus，使用SVN。项目规模比较大，我们有数个Multimodule的Maven项目，遇到的第一个问题就是集成的成本非常大。最初几周，我都是在自己的机器上做集成，Windows，你懂的，又不太方便做自动化，结果整个过程非常痛苦。到了四月份项目还没有全部完成，已经不能忍了，于是我引入了Hudson。我们为每一个项目创建单独的build task，unstable的构建会有邮件通知开发人员。一方面把我从每天反复的手动集成打包中解脱出来，另一方面也促进了大家写高质量的UnitTest，因为一旦有测试不通过就会有一个虚构的200000000@snda.com的邮箱给大家发邮件，以至于最后几乎所有人都把这个地址拉黑了。&lt;/p&gt;

&lt;p&gt;写真正能够Automation的单元测试其实很不简单，尤其是DAL层。理想的solution是用内存数据库来做单元测试的数据源，不过在实际开发里，因为一些SQL会依赖特定的数据库，所以这个方案并不太理想。如果有事务的话，Spring的TestCase可以设置自动回滚，可是互联网项目，你也懂的，连一致性都搞成最终一致了，事务这么重的东西我们是不用的。&lt;/p&gt;

&lt;p&gt;我们遇到的另一个问题是，多个独立的Maven项目，在做集成时，会存在依赖的冲突。这类问题一旦出现，找原因的代价还是比较大的。这方面，老大有一个Best Practice，定义一个统一的、专门管理版本的pom，通过dependency management的import来使用，你可以&lt;a href="http://sunng.info/blog/2010/05/maven-recipe-0/"&gt;看这里&lt;/a&gt;来了解。&lt;/p&gt;

&lt;p&gt;我们suffer的另一个问题是SVN的分支管理，到了项目的后期，功能越来越多，也出现了两个分支的并行开发，回滚的情况也出现了。我们基本上可以保证每次发布都有tag，但是分支的管理实在是非常痛苦。我现在的认识是，两方面原因。第一，SVN的分支成本还是比较高的，远程整个目录拷贝一下，本地需要指向新的地址。第二，项目的计划不明确，根本没有办法去估计新的分支、新的版本。两个原因结合起来说，其实分支管理不完全是一个版本控制系统层面的问题，和整个项目规划都有一定的联系。&lt;/p&gt;

&lt;p&gt;到了后期，我们对Hudson的使用也有了一些最佳实践，结合ssh/scp的插件，task的trigger机制，我们有了自动化的部署，你可以在&lt;a href="http://sunng.info/blog/2010/09/hudson-tips/"&gt;这里&lt;/a&gt;了解。不过我们的自动化最后倒在公司强制的动态密码验证机制上，每次登录需要使用动态密码，这样一切最后又都回归了手动。&lt;/p&gt;

&lt;p&gt;除了在公司的项目，在9月份我又做了一个实验性的项目，叫做&lt;a href="https://github.com/sunng87/bason"&gt;Bason&lt;/a&gt;，他可以在编译时生成JavaBean到Mongodb BSON代码。也是用Maven管理，不过值得一提的是，我申请了Sonatype提供的&lt;a href="https://docs.sonatype.org/display/Repository/Sonatype+OSS+Maven+Repository+Usage+Guide"&gt;OSS Repository&lt;/a&gt;，这样可以把项目都deploy到公共的服务器上，使用会非常方便。还值得一提的是，OSS Repository的管理者是校友&lt;a href="http://www.juvenxu.com/"&gt;@Juvenxu&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;不过今年Maven并不是Java生命周期管理的唯一工具，还有一些工具也开始进入视线，当然我说的不是ant+ivy。前段时间发布0.9的&lt;a href="http://gradle.org/"&gt;Gradle&lt;/a&gt;是比较受关注的，它又回归了ant的task oriented ，结合Groovy的DSL和插件机制提供更高的灵活性。另外，断断续续学了一年的clojure，clojure的生态系统里已经有了一个叫做&lt;a href="https://github.com/technomancy/leiningen"&gt;leinigen&lt;/a&gt;的生命周期管理工具，在clojure的世界里，它非常好用。&lt;/p&gt;

&lt;p&gt;另外Python的世界，我逐渐开始强制自己用virtualenv来创建一个更可控，更具有移植性的python环境。随着年底开始的一个小项目，我应该对python项目的生命周期管理有更深的认识。&lt;/p&gt;

&lt;p&gt;年底到了SAP，开始在NetWeaver平台上做Abap开发。NetWeaver是一个复杂的平台，它的Scope也涵盖了生命周期管理。在NetWeaver中，开发一个非local的程序，需要首先创建一个Change Request，之后所有的变更，最后都会保存到这个ChangeRequest上，有些类似传统的分支。当开发完成后，开发人员把change request release掉，这部分代码会自动被transport到Test系统或者其他下游系统上。实际开发里，一个Change Request可能对应一个Ticket，一个特性。这样的平台上，开发人员需要额外操心的事情，非常少。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Exaile doubanfm plugin 0.0.6b</title><link href="https://sunng.info/blog/exaile-doubanfm-plugin-006b.html" rel="alternate"></link><published>2010-12-21T00:00:00+08:00</published><updated>2010-12-21T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-12-21:/blog/exaile-doubanfm-plugin-006b.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;Douban&lt;/li&gt;
&lt;li&gt;exaile&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;上周豆瓣电台增加了电影原声频道，这次的插件更新支持了这个新频道（OST）。&lt;/p&gt;

&lt;p&gt;下载地址&lt;br /&gt;
https://github.com/sunng87/exaile-doubanfm-plugin/downloads …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;Douban&lt;/li&gt;
&lt;li&gt;exaile&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;上周豆瓣电台增加了电影原声频道，这次的插件更新支持了这个新频道（OST）。&lt;/p&gt;

&lt;p&gt;下载地址&lt;br /&gt;
https://github.com/sunng87/exaile-doubanfm-plugin/downloads&lt;/p&gt;

&lt;p&gt;安装指南&lt;br /&gt;
https://github.com/sunng87/exaile-doubanfm-plugin/wiki/Installation&lt;/p&gt;

&lt;p&gt;欢迎使用！&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Blackboard Pattern</title><link href="https://sunng.info/blog/blackboard-pattern.html" rel="alternate"></link><published>2010-12-19T00:00:00+08:00</published><updated>2010-12-19T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-12-19:/blog/blackboard-pattern.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;pattern
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;昨天很虔诚地在图书馆把POSA第四卷里所有的Pattern Languages都抄了一遍。恰好遇到Modeling部分的第9个Pattern， Blackboard，没有感性认识。今天简单地搜索了一下。&lt;/p&gt;

&lt;p&gt;POSA上指出Blackboard模式用于解决尚没有绝对解决方案的问题，将几个子系统 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;pattern
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;昨天很虔诚地在图书馆把POSA第四卷里所有的Pattern Languages都抄了一遍。恰好遇到Modeling部分的第9个Pattern， Blackboard，没有感性认识。今天简单地搜索了一下。&lt;/p&gt;

&lt;p&gt;POSA上指出Blackboard模式用于解决尚没有绝对解决方案的问题，将几个子系统的知识组合起来提供一个近似的解决方案。在解决这类问题的过程中，几个子系统相互独立，Blackboard作为中央控制对子系统进行控制和反馈。在这个系统中有三个角色：
&lt;ul&gt;
&lt;li&gt;Control Shell: 系统对外的控制接口&lt;/li&gt;
&lt;li&gt;Blackboard: 中央的知识管理，接受Specialist运算的反馈，对Specialist进行调度&lt;/li&gt;
&lt;li&gt;Specialist: 具有专业知识的子系统，负责独立的运算&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;这里有一个描述Blackboard模式的代码例子：
&lt;a href="http://www.andypatterns.com/index.php/blog/blackboard_architectural_pattern/"&gt;http://www.andypatterns.com/index.php/blog/blackboard_architectural_pattern/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;参考：&lt;br /&gt;
http://www.vico.org/pages/PatronsDisseny/Pattern%20Blackboard/&lt;br /&gt;
http://en.wikipedia.org/wiki/Blackboard_system&lt;br /&gt;
http://chat.carleton.ca/~narthorn/project/patterns/BlackboardPattern-display.html&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>For 12.13</title><link href="https://sunng.info/blog/for-1213.html" rel="alternate"></link><published>2010-12-14T00:00:00+08:00</published><updated>2010-12-14T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-12-14:/blog/for-1213.html</id><content type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;Nanjing&lt;/li&gt;
&lt;li&gt;OpenStreetMap
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;For people of Nanjing, &lt;a href="http://en.wikipedia.org/wiki/Nanking_Massacre"&gt;December 13th&lt;/a&gt; is a day that they can never forget.&lt;/p&gt;

&lt;p&gt;The memorial hall of the victims in Nanjing massacre.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="425" height="350" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="http://www.openstreetmap.org/export/embed.html?bbox=118.73284,32.03447,118.74073,32.039&amp;amp;layer=mapnik" style="border: 1px solid black"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;small&gt;&lt;a href="http://www.openstreetmap.org/?lat=32.036735&amp;amp;lon=118.736785&amp;amp;zoom=16&amp;amp;layers=M"&gt;View Larger Map&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="http://en.wikipedia.org/wiki/John_Rabe"&gt;John Rabe&lt;/a&gt; house&lt;/p&gt;

&lt;p&gt;&lt;iframe width="425" height="350" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="http://www.openstreetmap.org/export/embed.html?bbox=118.77623,32.05136,118.780172,32.053625&amp;amp;layer=mapnik" style="border: 1px solid black"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;small&gt;&lt;a href="http://www.openstreetmap.org/?lat=32.0524925&amp;amp;lon=118.778201&amp;amp;zoom=17&amp;amp;layers=M"&gt;View Larger Map&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Douban is now available as a cover provider of CoverGloobus</title><link href="https://sunng.info/blog/douban-is-now-available-as-a-cover-provider-of-covergloobus.html" rel="alternate"></link><published>2010-12-11T00:00:00+08:00</published><updated>2010-12-11T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-12-11:/blog/douban-is-now-available-as-a-cover-provider-of-covergloobus.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;Douban&lt;/li&gt;
&lt;li&gt;foss
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;This is a feature requested by &lt;a href="http://www.douban.com/people/4350866/"&gt;a douban user&lt;/a&gt;, we need a provider for &lt;a href="https://launchpad.net/covergloobus"&gt;CoverGloobus&lt;/a&gt; to retrieve covers from douban.com&lt;/p&gt;

&lt;p&gt;I just modify the code of exaile-douban-cover, and adapt to CoverGloobus 1.7 API. In 1.7, CoverGloobus has simplfied …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;Douban&lt;/li&gt;
&lt;li&gt;foss
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;This is a feature requested by &lt;a href="http://www.douban.com/people/4350866/"&gt;a douban user&lt;/a&gt;, we need a provider for &lt;a href="https://launchpad.net/covergloobus"&gt;CoverGloobus&lt;/a&gt; to retrieve covers from douban.com&lt;/p&gt;

&lt;p&gt;I just modify the code of exaile-douban-cover, and adapt to CoverGloobus 1.7 API. In 1.7, CoverGloobus has simplfied SPI for cover providers, which has much enhancement from 1.6 . So this provider is only compatible with CoverGloobus 1.7 .&lt;/p&gt;

&lt;p&gt;The patch is now merged into the bazaar repository, you can find it at:&lt;br /&gt;
http://bazaar.launchpad.net/~gloobus-dev/covergloobus/trunk/files/head%3A/src/covers/&lt;/p&gt;

&lt;p&gt;Chinese user will benefit from this patch that CoverGloobus will try to download album art if it's not available from your music player.&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>欢迎加入OSM中文邮件列表</title><link href="https://sunng.info/blog/huan-ying-jia-ru-osmzhong-wen-you-jian-lie-biao.html" rel="alternate"></link><published>2010-12-07T00:00:00+08:00</published><updated>2010-12-07T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-12-07:/blog/huan-ying-jia-ru-osmzhong-wen-you-jian-lie-biao.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;OpenStreetMap
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;大家好，欢迎加入OSM中文邮件列表。现在OSM的中文用户、贡献者终于有一个交流平台了，在 OSM地图编辑的过程中，大家一定遇到过非常多的问题，今后可以 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;OpenStreetMap
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;大家好，欢迎加入OSM中文邮件列表。现在OSM的中文用户、贡献者终于有一个交流平台了，在 OSM地图编辑的过程中，大家一定遇到过非常多的问题，今后可以在邮件列表中共同探讨了。&lt;/p&gt;

&lt;p&gt;周一我在决定邀请一些用户加入列表之前，了解了一下国内OSM贡献者的情况。这个过程其实非常简单，也非常有趣，我查看了包括上海、苏州、杭 州、南京、北京、青岛、济南、沈阳、广州、合肥等几个城市的地图作者，管中窥豹，向几名其中非常活跃的中文用户发出了邀请。同时我也发现有一 些用户曾经非常活跃，但是最近一段时间提交的次数比较少，OSM是一个巨大、繁琐的工作，持续的贡献需要兴趣也需要恒心，过去大家孤军奋战， 因为一些原因很容易放弃这项工作，这次有了中文邮件列表，希望这个平台对OSM贡献者也能起到一个促进的作用，让制图更加有趣。&lt;/p&gt;

&lt;p&gt;自我介绍，我的OSM用户名是Sunng，从2008年开始关注OSM，不过到了今年上半年才开始提交地图，我主要在南京、上海活动，目前也 主要关注这两个地方的制图。我在Linux上使用Merkaator作为编辑器，数据来源主要是卫星地图和一个GPS Logger。&lt;/p&gt;

&lt;p&gt;上周，随着OSM的创始人Steve Coasts加入Bing，OSM和Bing的合作全面开始，社区动作非常快，目前Poltach2/JOSM/Merkaator等编辑器已经都支持以 Bing卫星地图为底图的编辑模式。这对中国贡献者来说是一个巨大的福音：Ｂing地图的分辨率要远高于之前Ｙahoo提 供的卫星地图。今后以卫星地图为数据源的制图工作会更加轻松、质量会更高。&lt;/p&gt;

&lt;p&gt;再提一个有趣的事情，我在浏览地图时，发现湖南南部的行政区数据明显比别处多，下载了数据才了解这是用户 wangchun(http://www.openstreetmap.org/user/wangchun) 的作品。 wangchun是OSM中国的活跃贡献者，他维护了很多铁路数据，此外，还包括钓鱼岛边界的这次编辑 (http://www.openstreetmap.org/browse/changeset/3937490)&lt;/p&gt;

&lt;p&gt;抛砖引玉，希望大家在OSM邮件列表上畅所欲言。也希望大家邀请身边的OSM用户、贡献者、潜在用户、潜在贡献者加入邮件列表，让OSM 中国社区更加壮大！&lt;/p&gt;

&lt;p&gt;$$$$$$$$$$$$$$$$$$$$$酱油分割线$$$$$$$$$$$$$$$$$$$$$&lt;/p&gt;

&lt;p&gt;OpenStreetMap(osm.org)是一个以Wiki方式绘制全球街道地图的项目，这个项目的所有数据都来自志愿者贡献，并以CC-BY-SA许可开放所有的地图和原始数据。&lt;br /&gt;
OpenStreetMap在国内也有一批贡献者，很多贡献可以追溯到2007年。目前，上海、杭州、北京的OSM已经初具规模，不过更多的城市需要志愿者加入。&lt;/p&gt;

&lt;p&gt;于是在这里邀请您成为OSM的贡献者，并加入到中文邮件列表的讨论中来(http://lists.openstreetmap.org/listinfo/talk-cn)。&lt;/p&gt;

&lt;p&gt;成为OSM的贡献者，你可以以这篇Wiki作为基础教程：&lt;br /&gt;
http://wiki.openstreetmap.org/wiki/Zh-hans:Beginners_Guide&lt;/p&gt;

&lt;p&gt;设备？&lt;br /&gt;
您可能需要一个GPS设备，但是这不是必须的。还可以通过对卫星地图进行数字化的方式参与到这项工作中，最近，OSM与Bing地图进行了合作，在国内也有了较高分辨率的卫星影像作底图。&lt;/p&gt;

&lt;p&gt;软件？&lt;br /&gt;
OSM有很多编辑器，您可以从Potlatch/JOSM/Merkaator等工具中选择一个。&lt;/p&gt;

&lt;p&gt;制图？&lt;br /&gt;
你需要了解一些基本的制图规范。OSM数据的基础单元是点、线和关系，您可以在这里找到所有的要素类型：&lt;br /&gt;
http://wiki.openstreetmap.org/wiki/Map_Features&lt;br /&gt;
也可以参考已有的数据，更可以在邮件列表中进行相关的讨论。&lt;/p&gt;

&lt;p&gt;欢迎你成为OSM的贡献者，加入到开放地图贡献者的行列中来！&lt;/p&gt;

&lt;p&gt;$$$$$$$$$$$$$$$$$$$$$酱油分割线$$$$$$$$$$$$$$$$$$$$$&lt;/p&gt;

&lt;p&gt;写了一晚上就写出这两段字来，现在不写大段文字表达能力迅速退化。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>I am Location Aware</title><link href="https://sunng.info/blog/i-am-location-aware.html" rel="alternate"></link><published>2010-12-03T00:00:00+08:00</published><updated>2010-12-03T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-12-03:/blog/i-am-location-aware.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;GIS&lt;/li&gt;
&lt;li&gt;gps&lt;/li&gt;
&lt;li&gt;OpenStreetMap
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;身为一个跟GIS沾过边的人，我直到今年才真正知道我在哪。尽管有了带GPS功能的手机，但是为了能离cartographer更进一步，今天我又买了一个 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;GIS&lt;/li&gt;
&lt;li&gt;gps&lt;/li&gt;
&lt;li&gt;OpenStreetMap
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;身为一个跟GIS沾过边的人，我直到今年才真正知道我在哪。尽管有了带GPS功能的手机，但是为了能离cartographer更进一步，今天我又买了一个专门的GPS Logger设备Holux M1000C。所谓GPS Logger就是以固定时间间隔采集GPS数据并记录的设备，现在国内的绝大多数GPS设备都是以导航为主，少有这样功能简单价格合理的GPS产品。
&lt;a href="http://www.flickr.com/photos/40741608@N08/5229058140/" title="DSC_0006 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5249/5229058140_cb0e8e44a2.jpg" width="500" height="335" alt="DSC_0006" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;这类设备，自带的软件当然都是Windows only的，不过在Linux上也不是没有办法。处理GPS数据最著名的工具叫做BT747，支持多种设备、格式，用Java写的，可以通过jnlp启动，quick and dirty一下还是很不错的。不过如果要找一个长相厮守的命令行工具，还是要用gpsbabel。&lt;/p&gt;

&lt;p&gt;以USB连接为例，将M1000C通过USB线连接到电脑，开启设备。可以用dmesg命令查看连接的USB设备，找到该设备（我的机器上设备叫做 /dev/ttyACM0）。M1000C使用的是MTK芯片，gpsbabel的命令为：
&lt;i&gt;gpsbabel -t -i m241 -f /dev/ttyACM0 -o gpx -F output.gpx&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;其中：
&lt;ul&gt;
&lt;li&gt;-t 表示track&lt;/li&gt;
&lt;li&gt;-i 输入格式，可以带参数如 -i m241,erase=1 表示导入并擦除设备上的数据&lt;/li&gt;
&lt;li&gt;-f 输入设备&lt;/li&gt;
&lt;li&gt;-o 输出格式，导出gpx格式&lt;/li&gt;
&lt;li&gt;-F 输出文件&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;有了GPS设备，现在可以名正言顺地为OpenStreetMap贡献了，今天揣着设备把下班的路记录了一下，而且还专门走了小区后面没有路灯的一条路上采集了一下数据，没有路灯还真有点...
&lt;a href="http://www.flickr.com/photos/40741608@N08/5228648495/" title="merkator by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5043/5228648495_accb4185cc.jpg" width="500" height="313" alt="merkator" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe width="425" height="350" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="http://www.openstreetmap.org/export/embed.html?bbox=121.62538,31.19607,121.64143,31.20659&amp;amp;layer=mapnik" style="border: 1px solid black"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;small&gt;&lt;a href="http://www.openstreetmap.org/?lat=31.20133&amp;amp;lon=121.633405&amp;amp;zoom=15&amp;amp;layers=M"&gt;View Larger Map&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;来吧，加入到OpenStreetMap的贡献者队伍里吧！&lt;/p&gt;

&lt;p&gt;The post is brought to you by &lt;a href="http://fedorahosted.org/lekhonee"&gt;lekhonee&lt;/a&gt; v0.7&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>dbus-python, in case you don't know</title><link href="https://sunng.info/blog/dbus-python-in-case-you-dont-know.html" rel="alternate"></link><published>2010-11-29T00:00:00+08:00</published><updated>2010-11-29T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-11-29:/blog/dbus-python-in-case-you-dont-know.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;dbus&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;上周用dbus-python写exaile插件，实现mpris2.0，非常痛苦，因为几乎没有完整的文档，只有一个&lt;a href="http://dbus.freedesktop.org/doc/dbus-python/doc/tutorial.html"&gt;tutorials&lt;/a&gt;帮助你入门。总结一下我这次 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;dbus&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;上周用dbus-python写exaile插件，实现mpris2.0，非常痛苦，因为几乎没有完整的文档，只有一个&lt;a href="http://dbus.freedesktop.org/doc/dbus-python/doc/tutorial.html"&gt;tutorials&lt;/a&gt;帮助你入门。总结一下我这次quick and dirty的开发所掌握的资料，当然，因为没有太多时间，所以也没有对dbus做更深入的了解，仅仅是in case you don't know。&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;DBus Interface Properties&lt;/h3&gt;
对mpris 2.0中定义的properties用dbus-python应该如何实现呢，似乎没有@dbus.service.properties这种decorator啊。&lt;a href="http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-properties"&gt;这里&lt;/a&gt;的文档对Properties进行了简单的说明，对于python程序，只能这样实现了：&lt;br /&gt;
[cc lang="python"]&lt;br /&gt;
    @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature='ss', out_signature='v')&lt;br /&gt;
    def Get(self, interface, prop):&lt;br /&gt;
        ...&lt;br /&gt;
    @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature='ssv')&lt;br /&gt;
    def Set(self, interface, prop, value):&lt;br /&gt;
        ...&lt;br /&gt;
    @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature='s', out_signature='a{sv}')&lt;br /&gt;
    def GetAll(self, interface):&lt;br /&gt;
        ...&lt;br /&gt;
[/cc]&lt;br /&gt;
程序里通过prop判断property名字，返回结果。没办法，就是这样。&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;指定返回数据的dbus类型&lt;/h3&gt;
对于dbus接口返回的variant类型，dbus-python会guess_signature。可是这部分的功能有的时候并不如我们想象，比如返回一个dict类型，其中的value有字符串有list有int，而out_signature是v。这种情况在Property Get的时候非常常见，如果不能手动指定类型，guess_signature得到的结果，value是统一类型，即，int会报错，如果没有int类型，会将所有字符串转成list（dbus的array）&lt;/p&gt;

&lt;p&gt;这种情况需要手动指定返回的类型，用dbus.types模块下的类型对python显示包装，对dict类型，需要用dbus.types.Dictionary()包装，并且在构造函数里传入signature='sv‘及相应的&lt;a href="http://dbus.freedesktop.org/doc/dbus-python/doc/tutorial.html#container-types"&gt;variant_level&lt;/a&gt;即可显示告知类型。同样，对list对象，需要用dbus.types.Array()包装。&lt;/p&gt;

&lt;p&gt;这样即可绕过guest_signature，如果用dbus-monitor查看，就可以看到类型为variant string了。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Exaile sound menu integration</title><link href="https://sunng.info/blog/exaile-sound-menu-integration.html" rel="alternate"></link><published>2010-11-28T00:00:00+08:00</published><updated>2010-11-28T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-11-28:/blog/exaile-sound-menu-integration.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;exaile&lt;/li&gt;
&lt;li&gt;opensource&lt;/li&gt;
&lt;li&gt;ubuntu&lt;/li&gt;
&lt;li&gt;work
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="https://wiki.kubuntu.org/SoundMenu"&gt;Sound menu indicator&lt;/a&gt; was introduced in Ubuntu Maverick(10.10).  User can control playback from this menu, and view current track information.&lt;/p&gt;

&lt;p&gt;The control is based on mpris spec 2.0, which is a common specification of controlling media …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;exaile&lt;/li&gt;
&lt;li&gt;opensource&lt;/li&gt;
&lt;li&gt;ubuntu&lt;/li&gt;
&lt;li&gt;work
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="https://wiki.kubuntu.org/SoundMenu"&gt;Sound menu indicator&lt;/a&gt; was introduced in Ubuntu Maverick(10.10).  User can control playback from this menu, and view current track information.&lt;/p&gt;

&lt;p&gt;The control is based on mpris spec 2.0, which is a common specification of controlling media player with dbus. To enable sound menu support, media player should implement mpris 2.0, and register itself with libindicate.&lt;/p&gt;

&lt;p&gt;So this week,  I have been working on an exaile plugin to support sound menu and finally got a workable version.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/5213528341/" title="Screenshot of exaile sound menu by 贝小塔, on Flickr"&gt;&lt;img src="http://farm6.static.flickr.com/5004/5213528341_3587622fb5.jpg" width="500" height="313" alt="Screenshot of exaile sound menu" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The code repo can be found at github:
&lt;a href="https://github.com/sunng87/Exaile-Soundmenu-Indicator"&gt;https://github.com/sunng87/Exaile-Soundmenu-Indicator&lt;/a&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Artificial301 Firefox Addon 1.1</title><link href="https://sunng.info/blog/artificial301-firefox-addon-11.html" rel="alternate"></link><published>2010-11-21T00:00:00+08:00</published><updated>2010-11-21T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-11-21:/blog/artificial301-firefox-addon-11.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;firefox&lt;/li&gt;
&lt;li&gt;project
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;由于一些众所周知的原因，一些链接仅仅是因为feedproxy.google.com这样的链接不能打开而无法访问（链接本身是可以访问的）。这个时候你需要Firefox插件 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;firefox&lt;/li&gt;
&lt;li&gt;project
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;由于一些众所周知的原因，一些链接仅仅是因为feedproxy.google.com这样的链接不能打开而无法访问（链接本身是可以访问的）。这个时候你需要Firefox插件：Artificial301。
&lt;a href="http://www.flickr.com/photos/40741608@N08/5191869453/" title="reload with artificial301 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4090/5191869453_a26474c5d8.jpg" width="500" height="313" alt="reload with artificial301" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;本次更新的1.1版本，新增了“Reload With Artificial301”的选项。&lt;/p&gt;

&lt;p&gt;下载页面：
&lt;a href="https://github.com/sunng87/Artificial301/downloads"&gt;https://github.com/sunng87/Artificial301/downloads&lt;/a&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>First Week</title><link href="https://sunng.info/blog/first-week.html" rel="alternate"></link><published>2010-11-20T00:00:00+08:00</published><updated>2010-11-20T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-11-20:/blog/first-week.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;sap&lt;/li&gt;
&lt;li&gt;work
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;这周是加入新公司的第一周，一切从新开始。这已经是两年里的第三家公司了，这个嘛，哎，比较坎坷。&lt;/p&gt;

&lt;p&gt;因 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;sap&lt;/li&gt;
&lt;li&gt;work
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;这周是加入新公司的第一周，一切从新开始。这已经是两年里的第三家公司了，这个嘛，哎，比较坎坷。&lt;/p&gt;

&lt;p&gt;因为长得比较苍凉，工作的时间又不长，大家都问你是研究生毕业吧，心里估计还有几分揣摩：这位兄台肯定是越挫越勇，不弃不馁，复读多年的。否定答复后，大家还会觉得我可能比较坎坷，读书比较晚，最后还要追问一句是哪年的。得到答案心中一凉，87年的都长成这样了，岁月不饶人啊。其实我没那么显老吧。&lt;/p&gt;

&lt;p&gt;公司的东西全部是封闭的，而且自成体系，所以基本上是从头开始了。封闭的副作用就是找人的成本比较高，反倒也促进了公司有完整的培训体系培训资料，新人入门也算是有章可循。周二的培训课程一下午三个小时，简直就是找回了大学上课的感觉。恰好会议室门口有个饮料机，一倒饮料就嗡嗡作响。这倒好，昏昏欲睡的时候，饮料机突然响了，有人说了打下课铃了，众人立刻精神百倍。原来上大学的时候下课就失眠的不只我一个。&lt;/p&gt;

&lt;p&gt;如果说公司有什么的特点的话，就是杯具特别多。你不用带着杯子走来走去，每一层楼都有大量的马克杯，统一印公司logo，用完只要放到回收点就可以了。而且公司特别喜欢杯具，周五临下班，又收到一个发杯具的通知。。。&lt;/p&gt;

&lt;p&gt;另外中午散步，发现很多人都走一条小道，我跟过去绕到几座楼后面，简直就是豁然开朗，仿佛看到桃花源一般。浦东软件园三期后面有一个规模很大的人工湖，还有小桥流水，中午散步还有波光粼粼，可以租脚踏船！这湖藏的很深，你不绕到湖边是根本看不见。想想中午吃完饭散步、去湖上划船，那是何等的惬意啊。而且盛大文学的办公楼窗户就正对着湖面，在这样的落地窗前面工作，那种感觉！想起以前，同样是一家集团，工作环境的差距怎么就这么大呢。&lt;/p&gt;

&lt;p&gt;同事谈论公司的时候，总是会用经济危机前、经济危机后来区分。比如危机前，培训材料都是打印成书的，危机后，大家就都看看pdf得了。作为一个和经济危机一起走上社会的人，我是真的很无奈。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>终于能用Unity和Gnome-shell了</title><link href="https://sunng.info/blog/zhong-yu-neng-yong-unityhe-gnome-shellliao.html" rel="alternate"></link><published>2010-11-14T00:00:00+08:00</published><updated>2010-11-14T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-11-14:/blog/zhong-yu-neng-yong-unityhe-gnome-shellliao.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;自从升级到Maverick之后，有两个问题就一直如鲠在喉，GNOME3会话和Ubuntu Netbook(Unity)会话能不能用。前者启动之后出现Panel后全部僵死，鼠标可以移动 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;自从升级到Maverick之后，有两个问题就一直如鲠在喉，GNOME3会话和Ubuntu Netbook(Unity)会话能不能用。前者启动之后出现Panel后全部僵死，鼠标可以移动但是任何操作都没有响应；后者桌面背景图片一闪就黑屏，没有任何响应。&lt;/p&gt;

&lt;p&gt;查看unity的xsession-error日志，其中的报错比较含糊，搜索也没有找到相似的问题。查看GNOME-Shell的xsession-error之后发现很多JS报错，搜索了一下，终于找到了一个bug报告：
&lt;a href="https://bugs.launchpad.net/ubuntu/+source/mutter/+bug/618907"&gt;https://bugs.launchpad.net/ubuntu/+source/mutter/+bug/618907&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;情况和我一样，都是之前曾经安装过PPA版本的GNOME-Shell尝鲜，升级之后出现了JS报错。按照说明打开synaptic，果然有两个包使用的是git版本，尽管PPA已经删除，但是已安装的软件包只会被标记为Local or Obsolete，恰好这个git版本是0.9.4比Maverick里的0.9.3还要高，这样更新的时候又不会提示。如果不细看还真的不容易发现。按下Ctrl+E， synaptic会提示Force Version对话框，选择仓库的旧版本，安装、重启。这下不仅GNOME-Shell正常工作了，同样依赖Mutter的Unity也正常了。&lt;/p&gt;

&lt;p&gt;这次悲剧，说明使用PPA还是要慎重，尤其是跨版本升级之后，要小心PPA被禁用但软件包没有被升级的情况。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Awesome autostart</title><link href="https://sunng.info/blog/awesome-autostart.html" rel="alternate"></link><published>2010-11-09T00:00:00+08:00</published><updated>2010-11-09T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-11-09:/blog/awesome-autostart.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;awesome&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;h3&gt;Problem&lt;/h3&gt;
让Awesome环境像Openbox一样在启动时，在根上下文中执行一个autostart脚本（启动程序、声明环境变量）。&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;Solution&lt;/h3&gt;
Ubuntu中GDM启动后会执行&lt;i&gt; /etc/gdm/Xsession&lt;/i&gt; 这个脚本，如果是custom会话（启动参 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;awesome&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;h3&gt;Problem&lt;/h3&gt;
让Awesome环境像Openbox一样在启动时，在根上下文中执行一个autostart脚本（启动程序、声明环境变量）。&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;Solution&lt;/h3&gt;
Ubuntu中GDM启动后会执行&lt;i&gt; /etc/gdm/Xsession&lt;/i&gt; 这个脚本，如果是custom会话（启动参数custom），会执行用户目录下的.xsession脚本，否则会执行 &lt;i&gt;/etc/X11/Xsession.d/&lt;/i&gt; 目录下的所有脚本。&lt;/p&gt;

&lt;p&gt;为了支持autostart脚本，可以在 &lt;i&gt;/etc/X11/Xsession.d/&lt;/i&gt; 里创建一个新的脚本 55awesome-autostart&lt;br /&gt;
[cc lang="bash"]&lt;br /&gt;
BASESTARTUP=$(basename "$STARTUP" | cut -d\  -f1)&lt;br /&gt;
if [ "$BASESTARTUP" = x-session-manager ]; then&lt;br /&gt;
    BASESTARTUP=$(basename $(readlink /etc/alternatives/x-session-manager))&lt;br /&gt;
fi&lt;br /&gt;
if [ "$BASESTARTUP" = "awesome" ]; then&lt;br /&gt;
    AWESOME_AUTOSTART=$HOME/.config/awesome/autostart.sh&lt;br /&gt;
    if [ -e "$AWESOME_AUTOSTART" ]; then&lt;br /&gt;
        . "$AWESOME_AUTOSTART"&lt;br /&gt;
    fi&lt;br /&gt;
fi&lt;br /&gt;
[/cc]&lt;br /&gt;
这个脚本会判断启动的session是否是awesome，如果是，并且autostart.sh存在的话，source这个文件。autostart.sh按照xdg的约定，放在配置文件目录 &lt;i&gt;$HOME/.config/awesome/&lt;/i&gt; 里。&lt;/p&gt;

&lt;p&gt;这样，在用户目录下的配置文件里，就可执行一些自启动程序，或者声明环境变量，典型的应用就是指定awesome会话专用的gtkrc：&lt;br /&gt;
[cc lang="bash"]&lt;br /&gt;
AWESOME_CONFIG_PATH=$HOME/.config/awesome&lt;br /&gt;
export GTK2_RC_FILES=$AWESOME_CONFIG_PATH/gtkrc&lt;/p&gt;

&lt;p&gt;xcompmgr -cCfF -t-5 -l-5 -r4.2 -o.55 -D6 &amp;&lt;br /&gt;
awsetbg -u feh -f .config/awesome/ubuntu.jpg&lt;br /&gt;
[/cc]&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>镇江和栖霞山</title><link href="https://sunng.info/blog/zhen-jiang-he-qi-xia-shan.html" rel="alternate"></link><published>2010-11-08T00:00:00+08:00</published><updated>2010-11-08T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-11-08:/blog/zhen-jiang-he-qi-xia-shan.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;Nanjing&lt;/li&gt;
&lt;li&gt;photo&lt;/li&gt;
&lt;li&gt;Tourism
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;放假在家，不甘寂寞，去了南京周边一些地方。回来才发现，去的居然都是寺庙！&lt;/p&gt;

&lt;p&gt;镇江的金山寺
&lt;a href="http://www.flickr.com/photos/40741608@N08/5145943014/" title="zhenjiang_01 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm2.static.flickr.com/1183/5145943014_8ddabc06b8.jpg" width="375" height="500" alt="zhenjiang_01" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/5145950226/" title="zhenjiang_03 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm2.static.flickr.com/1405/5145950226_a36c1626ef.jpg" width="375" height="500" alt="zhenjiang_03" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/5145952242/" title="zhenjiang_04 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4006/5145952242_8cd454dd90.jpg" width="500" height="375" alt="zhenjiang_04" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;从南京 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;Nanjing&lt;/li&gt;
&lt;li&gt;photo&lt;/li&gt;
&lt;li&gt;Tourism
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;放假在家，不甘寂寞，去了南京周边一些地方。回来才发现，去的居然都是寺庙！&lt;/p&gt;

&lt;p&gt;镇江的金山寺
&lt;a href="http://www.flickr.com/photos/40741608@N08/5145943014/" title="zhenjiang_01 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm2.static.flickr.com/1183/5145943014_8ddabc06b8.jpg" width="375" height="500" alt="zhenjiang_01" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/5145950226/" title="zhenjiang_03 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm2.static.flickr.com/1405/5145950226_a36c1626ef.jpg" width="375" height="500" alt="zhenjiang_03" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/5145952242/" title="zhenjiang_04 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4006/5145952242_8cd454dd90.jpg" width="500" height="375" alt="zhenjiang_04" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;从南京到镇江，坐火车大约30分钟。从火车站出来坐2路车，可以在去金山公园之前了解一下镇江的全貌。&lt;/p&gt;

&lt;p&gt;南京的栖霞山，山顶上有个老人放羊。下面这只大概是羊王一样的角色，那眼神
&lt;a href="http://www.flickr.com/photos/40741608@N08/5157032882/" title="IMG_0164 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4063/5157032882_8229c38243.jpg" width="500" height="375" alt="IMG_0164" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;下山蜿蜒的路
&lt;a href="http://www.flickr.com/photos/40741608@N08/5156435095/" title="IMG_0173 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4016/5156435095_76031f6ae9.jpg" width="375" height="500" alt="IMG_0173" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;在栖霞山可以看到整个仙林地区，随着地铁的开通，看来这里是未来发展的重心了。而我们曾经被圈养的江北浦口，恐怕是被彻彻底底的遗忘了。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>QQ360</title><link href="https://sunng.info/blog/qq360.html" rel="alternate"></link><published>2010-11-04T00:00:00+08:00</published><updated>2010-11-04T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-11-04:/blog/qq360.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;cnweb
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;作为一个Linux桌面用户，若不是早些时候幡然悔悟投靠了自由软件，今天怕是也难以有隔岸观狗，苟全性命于乱世 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;cnweb
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;作为一个Linux桌面用户，若不是早些时候幡然悔悟投靠了自由软件，今天怕是也难以有隔岸观狗，苟全性命于乱世的心境。其实我是有两个想法的，一个是新浪的UC，一个是360自己。&lt;/p&gt;

&lt;p&gt;我是在twitter上看到有同事说怎么UC没有动静。一说UC能想起来的人都应该想起来的。我不幸作为了见证UC历史的那一拨人，不幸就不幸在事隔多年我几乎都忘记自己这个身份了。2004年初，腾讯推出了一个2块钱注册QQ号的业务，从那个时候起的一段时间里，QQ号不再是像后来和先前这样免费注册。于是骂声一片，在深圳就冒出一个叫做朗玛UC的产品，就在那半年里人气飙升，虽然不及QQ，但是在国内也算是对QQ空前绝后的挑战了。那个时候的各种程序功能、营销手段也都是纯拷贝，我印象最深的是一个挂机时间换靓号的。因为这事我参与过，正好有高中同学弄到了学校服务器的权限，于是就替我就用金陵中学的Windows2000服务器，挂满了要求的时长，得到了6位UC号。&lt;/p&gt;

&lt;p&gt;后来腾讯意识到问题，放弃了2块钱的业务，这以后UC很快就走了下坡路，最终卖给了新浪，取代了新浪自家那个更烂的IM产品（连名字都想不起来了）。神奇的是，尽管不景气，但是新浪UC这么些年一直尴尬地活着，还在不断更新。&lt;/p&gt;

&lt;p&gt;如今机会来了，360跳出来，腾讯又受到了几年都没有过的挑战。不过有趣的是，这次大战一直是1v1，平时那些个心有不甘的六国似乎也没有一个愿意和360趟一滩浑水的。盛大是在糖果上打出来广告说支持360的，不过似乎没有造成太大的影响。其他都统统袖手旁观了，新浪也没有借这个机会忽悠一个自己的UC，我想如果整合新浪的微博、博客、播客，又能和MSN / Gtalk互通，也不是没有一点竞争力的吧。可惜没有准备好，眼看着事情渐渐平息，UC还是没有出头之日。&lt;/p&gt;

&lt;p&gt;又说360，360苦于什么，苦于揭竿而起却又无法真正挽救用户于水火。已经宣战了，却没有能力把驻外侨民转移到安全的地方。忠诚的用户也要做出这样的抉择，坚贞不屈还是忍辱负重，抗战到底还是卖国投敌，是从此走上一条孤独的、不再有企鹅联系人的羊肠修道，还是看见了当没看见知道了当不知道退一步海阔天空。苦啊！&lt;/p&gt;

&lt;p&gt;360为什么不搞一个自己的QQ客户端呢？从头开始太难，那何不借点东风。站在我的角度，如果能看到360组织一个团队来维护libpurple的QQ支持，是最好的结果。其实现在Windows上已经有使用libpurple的pidgin(gtk)和instantbird(xul)了，虽然用起来有点麻烦（正是需要专人维护的缘由），但是即使是在最严峻的那几个小时，这些用户用着非腾讯官方的产品也没有太大的问题。这对360来说，难道不是个机会吗？在libpurple的基础上，索性来个GPL的OpenQQ，按照中国用户的需求，想加什么功能随你了。不要托管给什么评测中心，直接放到github，让大家都看看，发动一场人民战争，你怕什么。不过当然了，这样坦荡荡也不是360的风格，踏踏实实做个开源产品更不是他们的风格。&lt;/p&gt;

&lt;p&gt;还是开头说了，隔岸观狗，不得不发。都是马后炮，姑且说说而已。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Bash Color Support for Bti</title><link href="https://sunng.info/blog/bash-color-support-for-bti.html" rel="alternate"></link><published>2010-10-31T00:00:00+08:00</published><updated>2010-10-31T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-10-31:/blog/bash-color-support-for-bti.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;c&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;opensource
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://github.com/gregkh/bti"&gt;Bti&lt;/a&gt; is well known as a command line twitter/identica/statusnet client, by &lt;a href="http://github.com/gregkh"&gt;Greg Kroah-Hartman&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I have been using this tool in CLI environment for a long time, found it difficult for human to parse output by eyes. So …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;c&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;opensource
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://github.com/gregkh/bti"&gt;Bti&lt;/a&gt; is well known as a command line twitter/identica/statusnet client, by &lt;a href="http://github.com/gregkh"&gt;Greg Kroah-Hartman&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I have been using this tool in CLI environment for a long time, found it difficult for human to parse output by eyes. So I forked it and added this bash color support, to provide some highlight on tweets.
&lt;a href="http://www.flickr.com/photos/40741608@N08/5130529830/" title="bti-bash-color by 贝小塔, on Flickr"&gt;&lt;img src="http://farm2.static.flickr.com/1346/5130529830_a1f8eaf793.jpg" width="416" height="500" alt="bti-bash-color" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To enable color support, add an option &lt;i&gt;--auto-color&lt;/i&gt; to bti command, or append &lt;i&gt;auto-color=yes&lt;/i&gt; to your configuration file($HOME/.bti)&lt;/p&gt;

&lt;p&gt;I have sent pull request to Greg, hope the patch can be merged into master. Before approved, you can check out &lt;a href="http://github.com/sunng87/bti"&gt;my fork&lt;/a&gt; and compile it yourself. (If liboauth not found, find it &lt;a href="http://liboauth.sourceforge.net/"&gt;here&lt;/a&gt;.)&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Installing Weather Indicator on Maverick</title><link href="https://sunng.info/blog/installing-weather-indicator-on-maverick.html" rel="alternate"></link><published>2010-10-29T00:00:00+08:00</published><updated>2010-10-29T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-10-29:/blog/installing-weather-indicator-on-maverick.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;The appindicator has been enhanced again in Ubuntu 10.10 Maverick. The new datetime indicator has been introduced in the replace Gnome datetime applet. However, the datetime indicator is quite simple, which drops support for EDS (Evolution data server), location and weather …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;The appindicator has been enhanced again in Ubuntu 10.10 Maverick. The new datetime indicator has been introduced in the replace Gnome datetime applet. However, the datetime indicator is quite simple, which drops support for EDS (Evolution data server), location and weather. So you need an indicator api based weather widget, and &lt;a href="http://www.omgubuntu.co.uk/2010/06/weather-indicator-applet-genesis-of-an-itch-into-an-app-updated-with-ppa/"&gt;omgubuntu&lt;/a&gt; says there is already such a project of &lt;a href="https://launchpad.net/weather-indicator"&gt;weather-indicator&lt;/a&gt;, on launchpad.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/5125055279/" title="indicator-weather by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4145/5125055279_19997d4c15_m.jpg" width="200" height="240" alt="indicator-weather" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, any attempt to install this package will result in a failure of 404. The ppa is no long maintained (Even the project has been in silent for a long time.) Fortunately, we can still build it from sources.&lt;/p&gt;

&lt;p&gt;First, check out the sources with bzr:
&lt;i&gt;bzr branch lp:weather-indicator&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;Then, change directory into &lt;i&gt;weather-indicator&lt;/i&gt;, package it with debuild:
&lt;i&gt;debuild binary&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;You will get the result in the parent directory:
&lt;i&gt;indicator-weather_10.07.16_all.deb&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;Then you simply install the debian package with dpkg:
&lt;i&gt;sudo dpkg -i indicator-weather_10.07.16_all.deb&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;Enjoy !&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Exaile 豆瓣电台插件 0.0.5</title><link href="https://sunng.info/blog/exaile-dou-ban-dian-tai-cha-jian-005.html" rel="alternate"></link><published>2010-10-26T00:00:00+08:00</published><updated>2010-10-26T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-10-26:/blog/exaile-dou-ban-dian-tai-cha-jian-005.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;exaile&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;趁热依旧打铁。Exaile 豆瓣电台插件 0.0.5&lt;/p&gt;

&lt;p&gt;&lt;h4&gt;Features&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;界面快捷键 加心f 跳过s 删除d&lt;/li&gt;
&lt;li&gt;调整了按钮的顺序，和官 …&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;exaile&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;趁热依旧打铁。Exaile 豆瓣电台插件 0.0.5&lt;/p&gt;

&lt;p&gt;&lt;h4&gt;Features&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;界面快捷键 加心f 跳过s 删除d&lt;/li&gt;
&lt;li&gt;调整了按钮的顺序，和官方一致&lt;/li&gt;
&lt;li&gt;增强的菜单，可以在豆瓣模式里切换频道、关闭Exaile，无须回到主界面&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;推荐当前歌曲到豆瓣&lt;/strong&gt;，可以在配置界面配置推荐语模板&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;&lt;h4&gt;Installation&lt;/h4&gt;
&lt;a href="http://github.com/sunng87/exaile-doubanfm-plugin/wiki/Installation"&gt;Wiki page&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;h4&gt;Downloads&lt;/h4&gt;
&lt;a href="http://github.com/sunng87/exaile-doubanfm-plugin/downloads"&gt;Download from Github&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;h4&gt;Screenshots&lt;/h4&gt;&lt;/p&gt;

&lt;p&gt;Douban Mode
&lt;a href="http://www.flickr.com/photos/40741608@N08/5117860492/" title="screenshot_002 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm2.static.flickr.com/1318/5117860492_8daa067abf.jpg" width="402" height="148" alt="screenshot_002" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enhanced Douban Mode Menu
&lt;a href="http://www.flickr.com/photos/40741608@N08/5117860126/" title="screenshot_001 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4106/5117860126_eb2ca09332.jpg" width="278" height="306" alt="screenshot_001" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;h4&gt;More ?&lt;/h4&gt;
&lt;a href="http://sunng.info/doubantu/"&gt;豆瓣 on Ubuntu&lt;/a&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>When I am 64</title><link href="https://sunng.info/blog/when-i-am-64.html" rel="alternate"></link><published>2010-10-23T00:00:00+08:00</published><updated>2010-10-23T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-10-23:/blog/when-i-am-64.html</id><content type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/5106315535/" title="DSC_0007 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm2.static.flickr.com/1237/5106315535_07df2a88bc.jpg" width="500" height="335" alt="DSC_0007" /&gt;&lt;/a&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Exaile 豆瓣电台插件 0.0.4</title><link href="https://sunng.info/blog/exaile-dou-ban-dian-tai-cha-jian-004.html" rel="alternate"></link><published>2010-10-21T00:00:00+08:00</published><updated>2010-10-21T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-10-21:/blog/exaile-dou-ban-dian-tai-cha-jian-004.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;Douban&lt;/li&gt;
&lt;li&gt;exaile&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;趁热打铁， Exaile豆瓣电台插件今天又有了一些更新：&lt;/p&gt;

&lt;p&gt;&lt;ol&gt;
&lt;li&gt;支持今天豆瓣新发布的摇滚、民谣、NewAge频道&lt;/li&gt;
&lt;li&gt;支持Exaile 0.3.2开始的界面透明&lt;/li&gt;
&lt;li&gt;与官方保持一致，在非个人频道时 …&lt;/li&gt;&lt;/ol&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;Douban&lt;/li&gt;
&lt;li&gt;exaile&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;趁热打铁， Exaile豆瓣电台插件今天又有了一些更新：&lt;/p&gt;

&lt;p&gt;&lt;ol&gt;
&lt;li&gt;支持今天豆瓣新发布的摇滚、民谣、NewAge频道&lt;/li&gt;
&lt;li&gt;支持Exaile 0.3.2开始的界面透明&lt;/li&gt;
&lt;li&gt;与官方保持一致，在非个人频道时禁用trash按钮&lt;/li&gt;
&lt;/ol&gt;&lt;/p&gt;

&lt;p&gt;下载
&lt;a href="http://github.com/sunng87/exaile-doubanfm-plugin/downloads"&gt;http://github.com/sunng87/exaile-doubanfm-plugin/downloads&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;很多朋友纠结于安装，安装确实稍显麻烦，大家注意：
&lt;ol&gt;
&lt;li&gt;确认你的Exaile版本0.3.2。&lt;/li&gt;
&lt;li&gt;下载，&lt;strong&gt;重命名下载的文件为doubanfm.tar.gz&lt;/strong&gt;（Exaile要求压缩包内的文件名要以压缩包的名字开头，否则报错；而github上使用相同的文件名用作下载会有潜在的问题）&lt;/li&gt;
&lt;li&gt;如果已经安装过这个插件，请删除 rm -r ~/.local/share/exaile/plugins/doubanfm&lt;/li&gt;
&lt;li&gt;打开Exaile，打开Preferences，到Plugins页，安装&lt;/li&gt;
&lt;li&gt;勾选doubanfm&lt;/li&gt;
&lt;li&gt;如果plugins树下没有出现Douban Radio，关闭Preferences重开，找到Douban Radio，设置用户名密码&lt;/li&gt;
&lt;li&gt;重启Exaile&lt;/li&gt;
&lt;li&gt;打开文件菜单，Open Douban.fm，选择你需要的频道&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;双击音轨或点击播放开始播放&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;切换到豆瓣视图，菜单View-&gt;Doubanfm Mode，或快捷键 Ctrl+alt+d&lt;/li&gt;
&lt;/ol&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/5101939181/" title="exaile douban radio by 贝小塔, on Flickr"&gt;&lt;img src="http://farm2.static.flickr.com/1434/5101939181_949841f861.jpg" width="402" height="142" alt="exaile douban radio" /&gt;&lt;/a&gt;
Enjoy！&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>豆瓣 on Ubuntu</title><link href="https://sunng.info/blog/dou-ban-on-ubuntu.html" rel="alternate"></link><published>2010-10-20T00:00:00+08:00</published><updated>2010-10-20T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-10-20:/blog/dou-ban-on-ubuntu.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;Douban&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;半年来，围绕豆瓣做了几个应用，今天上班路上突然心血来潮，决定汇总一下，推广一下。如果你是豆瓣用 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;Douban&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;半年来，围绕豆瓣做了几个应用，今天上班路上突然心血来潮，决定汇总一下，推广一下。如果你是豆瓣用户，也是Ubuntu用户，你可以尝试一下。&lt;/p&gt;

&lt;p&gt;&lt;a href="http://sunng.info/doubantu/"&gt;http://sunng.info/doubantu/&lt;/a&gt;
&lt;a href="http://www.flickr.com/photos/40741608@N08/5099029491/" title="douban on ubuntu by 贝小塔, on Flickr"&gt;&lt;img src="http://farm2.static.flickr.com/1208/5099029491_24b01f5a0a.jpg" width="500" height="361" alt="douban on ubuntu" /&gt;&lt;/a&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Python Web 开发框架 Bottle</title><link href="https://sunng.info/blog/python-web-kai-fa-kuang-jia-bottle.html" rel="alternate"></link><published>2010-10-17T00:00:00+08:00</published><updated>2010-10-17T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-10-17:/blog/python-web-kai-fa-kuang-jia-bottle.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;bottle&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;web&lt;/li&gt;
&lt;li&gt;wsgi
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://bottle.paws.de/"&gt;Bottle&lt;/a&gt;是一个非常精致的WSGI框架，它提供了Python Web开发中需要的基本支持：URL路由，Request/Response对象封装，模板支持，与WSGI服务器集成支持。整个框架的全部代码约有2000行，它的 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;bottle&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;web&lt;/li&gt;
&lt;li&gt;wsgi
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://bottle.paws.de/"&gt;Bottle&lt;/a&gt;是一个非常精致的WSGI框架，它提供了Python Web开发中需要的基本支持：URL路由，Request/Response对象封装，模板支持，与WSGI服务器集成支持。整个框架的全部代码约有2000行，它的核心部分没有其他任何依赖，只要有Python环境就可以运行。&lt;/p&gt;

&lt;p&gt;Bottle适用于小型的Web开发，在应用程序规模比较小的情况下可以实现快速开发。但是由于自身功能所限，对于大型的Web程序，Bottle的功能略显不足，程序员需要手动管理模块、数据库、配置等等，与Pylons等框架相比Bottle的优势就难以体现出来了。&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;快速入门&lt;/h3&gt;
通过一个简单的、典型的例子描述Bottle的使用：&lt;br /&gt;
[cc lang="python"]&lt;br /&gt;
from bottle import Bottle, run, mako_view, request&lt;/p&gt;

&lt;p&gt;myapp = Bottle()&lt;/p&gt;

&lt;p&gt;@myapp.get('/hello/:name/:count#\\d+#')&lt;br /&gt;
@mako_view('hello')&lt;br /&gt;
def hello(name, count):&lt;br /&gt;
    ip = request.environ.get('REMOTE_ADDR')&lt;br /&gt;
    return dict(n=name, c=int(count), ip=ip)&lt;/p&gt;

&lt;p&gt;run(app=myapp)&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;我们创建一个Bottle对象，通过decorator配置一条路由记录。Bottle的url映射支持具名参数，&lt;i&gt;“/hello/:name/:count#\\d+#”&lt;/i&gt; 格式的参数，可以匹配&lt;i&gt;/hello/(.+?)/(\d+?)&lt;/i&gt;的URL。在方法体中，通过environ字典获得客户端IP，这个操作和其他WSGI框架是一致的。接着通过一个字典类型将Model数据传递给View。View模板通过decorator定义，采用mako模板引擎实现，模板名为hello，他表示在当前目录下一个名叫hello.tpl的文件。&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;剖析&lt;/h3&gt;
Bottle主要可以分成4个模块：
&lt;ul&gt;
&lt;li&gt;Router和WSGI Application&lt;/li&gt;
&lt;li&gt;Request和Response, Web helpers&lt;/li&gt;
&lt;li&gt;Template Adapters&lt;/li&gt;
&lt;li&gt;WSGI Server Adapters&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;&lt;h4&gt;Router和WSGI Application&lt;/h4&gt;
一个Bottle对象是一个标准的WSGI App，这使他可以与很多WSGI Server集成。每个app下管理一个Router用于针对URL映射到handler方法。根据Python decorator的特性，路由规则是在程序启动时自动执行的。&lt;/p&gt;

&lt;p&gt;Bottle的route方法用于注册handler到router中。除此以外，还有很多function currying帮助您简单地路由GET / POST 等特定HTTP方法。&lt;/p&gt;

&lt;p&gt;Bottle最近以来支持了hook，可以注册一些方法在每个请求之前、或之后执行。&lt;/p&gt;

&lt;p&gt;当接受一个HTTP请求时，WSGI Server会调用WSGI App的wsgi方法。它的流程是：&lt;/p&gt;

&lt;p&gt;&lt;ol&gt;
&lt;li&gt;根据标准的WSGI接口规范，从WSGI environ中生成一个Request对象，初始化一个response对象。&lt;/li&gt;
&lt;li&gt;在寻找方法之前，实际上Bottle会自动执行您注册的before_hook。（Bottle._add_hook_wrapper）&lt;/li&gt;
&lt;li&gt;根据request.path的URL在router中寻找对应的handler方法（Bottle.match_url, Router.match）。这一步中除了找到合适的handler方法，还要负责提取url中的具名参数，将结果以tuple的形式返回。&lt;/li&gt;
&lt;li&gt;以返回的方法和参数，执行handler，获得返回值。&lt;/li&gt;
&lt;li&gt;执行所有的after_hooks。&lt;/li&gt;
&lt;li&gt; 根据返回的不同类型，写入Response的头部。在这一步，Bottle还会应用一些filter，在Bottle中，filter是用于处理handler返回类型的。例如，一个典型的filter即内置的dict2json，他将handler返回的dict类型自动换成json。（Bottle._cast）&lt;/li&gt;
&lt;li&gt;根据返回的HTTP状态码，对handler返回对象进行处理。调用WSGI Server的start_response方法将返回对象写给客户端。&lt;/li&gt;
&lt;/ol&gt;&lt;/p&gt;

&lt;p&gt;&lt;h4&gt;Request和Response, Web helpers&lt;/h4&gt;
Bottle对HTTP的请求和响应封装了Request和Response对象。它采用threadlocal的方式，由Bottle app管理生命周期。您可以在声明handler方法时不必像Java Servlet那样将他们以参数传入，这样增加了方法设计的灵活性，也使得单元测试变得相对轻松。&lt;/p&gt;

&lt;p&gt;Request对象是对WSGI environ属性的封装，可以从中取得的属性取决于WSGI Server对&lt;a href="http://www.python.org/dev/peps/pep-0333/#environ-variables"&gt;PEP333&lt;/a&gt;的实现。&lt;/p&gt;

&lt;p&gt;关于Request API，可以参考&lt;a href="http://bottle.paws.de/docs/dev/api.html#bottle.Request"&gt;文档&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;类似的，可以参考&lt;a href="http://bottle.paws.de/docs/dev/api.html#bottle.Response"&gt;Response API&lt;/a&gt;了解如何对HTTP响应进行操作&lt;/p&gt;

&lt;p&gt;和很多Web框架一样，你不必手动去设置HTTP重定向，helper方法会简化这些操作：&lt;i&gt;abort&lt;/i&gt;, &lt;i&gt;redirect&lt;/i&gt;, &lt;i&gt;static_file&lt;/i&gt;。当然，你也可以自己创建一些helper。&lt;/p&gt;

&lt;p&gt;&lt;h4&gt;Template Adapters&lt;/h4&gt;
template方法用于渲染视图，您可以使用不同的模板实现：mako, jinja2, cheetah, simpletal以及Bottle自己的简单实现。&lt;/p&gt;

&lt;p&gt;view作为一个decorator可以简化模板的选择。与route类似，作者也提供了一些function currying来支持mako_view这样简便的写法。&lt;/p&gt;

&lt;p&gt;&lt;h4&gt;Server Adapters&lt;/h4&gt;
Bottle的Server Adapters简直可以说是WSGI Server的博览会，从这里您可以了解目前比较流行的WSGI实现：
&lt;ul&gt;
&lt;li&gt;flup&lt;/li&gt;
&lt;li&gt;wsgiref&lt;/li&gt;
&lt;li&gt;cherrypy&lt;/li&gt;
&lt;li&gt;paste&lt;/li&gt;
&lt;li&gt;fapws3&lt;/li&gt;
&lt;li&gt;tornado&lt;/li&gt;
&lt;li&gt;Google Appengine&lt;/li&gt;
&lt;li&gt;twisted&lt;/li&gt;
&lt;li&gt;diesel&lt;/li&gt;
&lt;li&gt;meinheld&lt;/li&gt;
&lt;li&gt;gunicorn&lt;/li&gt;
&lt;li&gt;eventlet&lt;/li&gt;
&lt;li&gt;gevent&lt;/li&gt;
&lt;li&gt;rocket&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;当然，这些不是全部，如果要使用不在其中的WSGI Server，您只需实现一个ServerAdapter的run方法即可，需要做的就是将Bottle app传给server并启动它。&lt;/p&gt;

&lt;p&gt;内置的run方法用于启动服务，您还可以指定一个reloader参数使Bottle在后台检查源文件的修改情况，实现热加载。&lt;br /&gt;
除了run方式的启动，由于Bottle app本身就是一个符合标准的WSGI app，所以也可以通过一些服务器特有的方式启动服务，例如gunicorn：
&lt;i&gt;gunicorn -w 2 -D -b 127.0.0.1:18080 module:app&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;实战&lt;/h3&gt;&lt;/p&gt;

&lt;p&gt;&lt;h4&gt;Middlewares&lt;/h4&gt;
Middleware是WSGI的重要概念http://www.python.org/dev/peps/pep-0333/#middleware-components-that-play-both-sides 借助一些成熟的middleware可以添加一些Bottle目前不具备的功能：没错，Session。&lt;/p&gt;

&lt;p&gt;Session Middleware最著名的选择叫做Beaker，要其用Beaker，只需要一个装饰器模式的App声明即可，您可以参考Beaker的&lt;a href="http://beaker.groovie.org/configuration.html"&gt;文档&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Pylons以使用Middleware著称，而除了Routing这样核心的Middleware，包括Beaker和Authkit都可以应用在Bottle程序上。&lt;/p&gt;

&lt;p&gt;&lt;h4&gt;App Mounting&lt;/h4&gt;
Bottle的App提供一个很有用的mount方法帮助你将Web应用模块化。您可以将一个Bottle App挂载到一个父App上的某个路径，以父App启动后，父App可以为子App在一个路径下提供路由。&lt;/p&gt;

&lt;p&gt;不过你会遇到这样的问题：&lt;br /&gt;
[cc lang="python"]&lt;br /&gt;
child = Bottle()&lt;/p&gt;

&lt;p&gt;@child.get("/")&lt;br /&gt;
def hello():&lt;br /&gt;
    return "hello world"&lt;/p&gt;

&lt;p&gt;parent = Bottle()&lt;br /&gt;
parent.mount(child, "/child")&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;很自然的，您希望打开浏览器访问http://localhost:8000/child可能看到hello world，然后却得到404.这个问题不难察觉，于是访问http://localhost:8000/child/，工作正常。&lt;/p&gt;

&lt;p&gt;我曾给作者提过这个问题http://github.com/defnull/bottle/issues#issue/88，（补一句，Bottle的作者少有的Nice，对你提出的问题他通常都会认真解答），作者提了一个重定向的方式，不过还是没有决定把它直接加进mount方法里。&lt;/p&gt;

&lt;p&gt;那么，我们需要一个新的mount来支持http://localhost:8000/child&lt;/p&gt;

&lt;p&gt;[cc lang="python"]&lt;br /&gt;
def mount(parent, child, path):&lt;br /&gt;
    parent.mount(child, path)&lt;br /&gt;
    @parent.get(path)&lt;br /&gt;
    def redir():&lt;br /&gt;
        redirect(path+"/")&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;&lt;h4&gt;Google AppEngine&lt;/h4&gt;
因为有WSGI标准，你可以用Bottle开发Google AppEngine程序。您只需要在handler文件中加入这样的代码即可：&lt;br /&gt;
[cc lang="python"]&lt;br /&gt;
from google.appengine.ext.webapp.util import run_wsgi_app&lt;br /&gt;
def main():&lt;br /&gt;
    run_wsgi_app(my_bottle_app)&lt;/p&gt;

&lt;p&gt;if __name__ == '__main__':&lt;br /&gt;
    main()&lt;br /&gt;
[/cc]&lt;br /&gt;
依赖？Bottle本身是self-contained，不过你可能还需要一个强大一些的模板引擎来完成你的应用，比如Mako。这样，你需要把依赖和自己的程序一起上传到GAE。对于Mako来说，除了它自己，还依赖marksave，别忘了它。&lt;/p&gt;

&lt;p&gt;以上就是我对Bottle的理解和使用心得，希望您在合适的场景下使用Bottle，只有这样，才能感受到这个框架所带来的乐趣。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Kindle已过万重山</title><link href="https://sunng.info/blog/kindleyi-guo-mo-zhong-shan.html" rel="alternate"></link><published>2010-10-16T00:00:00+08:00</published><updated>2010-10-16T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-10-16:/blog/kindleyi-guo-mo-zhong-shan.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;friends&lt;/li&gt;
&lt;li&gt;kindle
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;h3&gt;契机。&lt;/h3&gt;
早就想买个类似技术的电子书，年初的时候就有过这样的打算，几乎到了只欠东风的节骨眼上 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;friends&lt;/li&gt;
&lt;li&gt;kindle
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;h3&gt;契机。&lt;/h3&gt;
早就想买个类似技术的电子书，年初的时候就有过这样的打算，几乎到了只欠东风的节骨眼上，还是畏惧了2000多的高价。事实证明等待是正确的，半年之后，盛大的&lt;a href="http://bambook.sdo.com"&gt;Bambook&lt;/a&gt;发布了，Kindle3的价格也出来了，从此6寸的电子书都降到了1000人民币以下。情绪逐渐接近临界，开始和samson讨论购买的计划，熟人代购or淘宝代购。不料看到goodtiger率先出手，积聚的能量瞬间爆发。不到24的小时之后，kindle三人组成立了。。。&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;动机。&lt;/h3&gt;
作为城市的低收入群体，我们有责任有义务把钱花到刀刃上。很多人说为神马不买ipad呢，功能强大得多。实际上，我的需求仅限于消费我~/Docuemnt/EBook/目录下永无止尽的文档。在电脑屏幕上，从Adobe Reader，到Simply a reader的&lt;a href="http://projects.gnome.org/evince/"&gt;Evince&lt;/a&gt;，到more than a reader的&lt;a href="http://okular.kde.org/"&gt;Okular&lt;/a&gt;，到apvlv，到zathura，即使支持了inverted colors这样的功能，终归还是不能满足舒适看书的需求。OK，只有看书这么简单，甚至你还要防止设备上多余的功能让你在看书的时候分神，你还有其他选择吗？&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;渠道。&lt;/h3&gt;
什么样的渠道让能量毫无顾虑的突破临界呢，当然是&lt;a href="http://pipitu.org/2010/03/10/nexus-one-%E9%A3%98%E6%B4%8B%E8%BF%87%E6%B5%B7%E5%85%A5%E6%89%8B%E8%AE%B0/"&gt;有人吃过螃蟹的渠道了&lt;/a&gt;。这是一个中继服务，他们为你生成一个美国的地址，你只需要用这个地址在Amazon网站上注册购买，货物会寄到该地址然后转寄到国内你的真实地址。9月7号，我在Amazon网站上pre-order了Kindle3，到了9月17号，Amazon发货；9月20号，包裹到达中继服务的美国地址；支付了国际邮递费用的，9月22号包裹发货，9月23号包裹离开美国。&lt;/p&gt;

&lt;p&gt;接下来悲剧就开始了，漫长的纠结的杳无音信，直到9月28号下午，突然收到一个陌生号码发来的短信：您的包裹需要交税！最终还是倒在了之前说的大boss手下。因为短信实在可疑，花了一天核实，到了9月30号下午终于把关税费用打给了对方。接着是漫长的十一假期，继续杳无音信。到了10月8号，终于上班了！打电话到这家公司，结果对方的电话居然坏掉，一接通就是杂音，根本没有办法对话。我心一凉，这回是不是要悲凉了。&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;实物&lt;/h3&gt;
好在这回没有背到那种程度，10月11号毫无征兆地接到快递的电话，我想这事成了！&lt;/p&gt;

&lt;p&gt;拿到Kindle，开封。一张简易说明书、一个机器，一根数据线。
&lt;a href="http://www.flickr.com/photos/40741608@N08/5085519950/" title="DSC_0003 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4091/5085519950_f23acc21f5.jpg" width="500" height="335" alt="DSC_0003" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;屏幕效果：
&lt;a href="http://www.flickr.com/photos/40741608@N08/5085548956/" title="DSC_0004 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4092/5085548956_47cdaaefeb.jpg" width="335" height="500" alt="DSC_0004" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;对pdf格式，kindle3虽然原生支持，但是由于pdf本身格式的限制，只能支持到区域缩放，而不支持字体缩放。6寸的屏幕阅读还是有难度的。好在，Amazon提供了格式转换服务，您可以参考&lt;a href="http://blog.samsonis.me/2010/09/update-42-before-kindle-arrives%E2%80%A6/"&gt;samson的预习作业&lt;/a&gt;把pdf转换成kindle格式。&lt;/p&gt;

&lt;p&gt;当然，kindle3内置一个浏览器，如果你能够忍受黑白屏幕和它的屏幕刷新速度，可以尝试一下。
&lt;a href="http://www.flickr.com/photos/40741608@N08/5085043781/" title="DSC_0005 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4111/5085043781_215ca3fa12.jpg" width="500" height="335" alt="DSC_0005" /&gt;&lt;/a&gt;
UA显示是Linux操作系统，Webkit核心。&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;费用&lt;/h3&gt;
最后透露一下费用。Kindle3 Wifi的价格是$139，Amazon美国邮递免运费。中继服务国际邮递的费用是$21.79，海关关税190元人民币。最近汇率动荡得厉害，今天的汇率已经是1:6.6405了。总价是&lt;br /&gt;
echo "(139+21.79)*6.6405+190" | bc&lt;br /&gt;
1257.7259&lt;/p&gt;

&lt;p&gt;今天看到淘宝上已经有1150的价格了，应该是少了关税。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>A fine day</title><link href="https://sunng.info/blog/a-fine-day.html" rel="alternate"></link><published>2010-10-11T00:00:00+08:00</published><updated>2010-10-11T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-10-11:/blog/a-fine-day.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;好久没有聊生活了，最近心绪比较杂乱，虽说不像以前那么忙，不过也没像以前那样有时间来贫一下。好 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;好久没有聊生活了，最近心绪比较杂乱，虽说不像以前那么忙，不过也没像以前那样有时间来贫一下。好在今天倒是这么多年难得的一个顺风日，一切顺利。&lt;/p&gt;

&lt;p&gt;上周体检今天出了结果，全部PASS。虽然有一点点心动过速，不过没有大碍。我一直担心的谷丙转氨酶这次终于没有出乱子。其实这半年加班加上压力比较大，有的时候能明显感觉到胸闷，体检之前心里还真没底。不过好在是还年轻，还能顶一顶。尽管是有紧张的因素，但是心动过速也算是个预警吧，以后要加强锻炼。这话不知道说了多少遍了。&lt;/p&gt;

&lt;p&gt;另一件事，Kindle终于毫无征兆的到了。9月6号我们kindle三人组（&lt;a href="http://blog.samsonis.me"&gt;1&lt;/a&gt;，&lt;a href="http://blog.goodtiger.net/"&gt;2&lt;/a&gt;）通过comgateway订了kindle3，到了9月17号Amazon发货，9月23号从comgateway发货。之后就杳无音信，从comgateway上的跟踪系统看根本就是绝望。结果到了9月28号正在跟samson闲聊的时候，突然收到一条莫名奇妙的短信，说您的货物已经到了北京库到家，要交税，真是又喜又悲。这还没完，花了一天confirm短信的真实性，直到30号中午才把税款打出去。接着就是无尽的国庆假期，漫漫7天过后，迫不及待打电话到库到家，结果8号一天占线，9号好不容易接通了，对方电话却坏了全是杂音跟不听不见说话。说实在的要不是有pipitu先前购物的经验，我这时候就觉得被骗了。好在，好在，今天上午顺丰快递总算是把东西送来了！&lt;/p&gt;

&lt;p&gt;有关kindle的细节，过两天再和大家慢慢唠。&lt;/p&gt;

&lt;p&gt;今天正好还是妈妈的生日，晚上我们找了一家山西饭馆饱餐一顿，感觉好极了！&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>The 4k Story</title><link href="https://sunng.info/blog/the-4k-story.html" rel="alternate"></link><published>2010-10-08T00:00:00+08:00</published><updated>2010-10-08T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-10-08:/blog/the-4k-story.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;mmap&lt;/li&gt;
&lt;li&gt;mongodb&lt;/li&gt;
&lt;li&gt;nosql&lt;/li&gt;
&lt;li&gt;redis
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;从 Redis 2.0 开始，Redis的作者就不断地被问道，你为什么要自己造一个VM轮子呢。尽管作者在&lt;a href="http://code.google.com/p/redis/wiki/FAQ#Do_you_plan_to_implement_Virtual_Memory_in_Redis?_Why_don%27t"&gt;FAQ&lt;/a&gt;里说明了，但是仍然有很多不同意 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;mmap&lt;/li&gt;
&lt;li&gt;mongodb&lt;/li&gt;
&lt;li&gt;nosql&lt;/li&gt;
&lt;li&gt;redis
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;从 Redis 2.0 开始，Redis的作者就不断地被问道，你为什么要自己造一个VM轮子呢。尽管作者在&lt;a href="http://code.google.com/p/redis/wiki/FAQ#Do_you_plan_to_implement_Virtual_Memory_in_Redis?_Why_don%27t"&gt;FAQ&lt;/a&gt;里说明了，但是仍然有很多不同意见。&lt;/p&gt;

&lt;p&gt;反向代理Varnish的开发人员，Poul-Henning Kamp 写了一篇文章，&lt;a href="http://www.varnish-cache.org/trac/wiki/ArchitectNotes"&gt;What's wrong with 1975 programming ?&lt;/a&gt;，锋芒毕露，矛头直指竞争对手Squid，顺便也打击一大片牵连到了Redis的作者Antirez。他说：
&lt;blockquote&gt;I have spent many years working on the FreeBSD kernel, and only rarely did I venture into userland programming, but when I had occation to do so, I invariably found that people programmed like it was still 1975. &lt;/blockquote&gt;
Kamp兄有来到user-space之后一夜回到解放前的感觉，又好像摇晃着饮料瓶子对着Antirez说：你Out啦！也许是因为作者就是个内核开发者，所以Varnish对操作系统的Virtual Memory机制充分信任，把Squid对内存的手动管理称为wasted work。"So Welcome to Varnish, a 2006 architecture program. "&lt;/p&gt;

&lt;p&gt;还有&lt;a href="http://blog.kennejima.com/post/1226487020/thoughts-on-redis"&gt;用户&lt;/a&gt;也提出
&lt;blockquote&gt;
Redis doesn’t use OS swap. According to Salvatore Sanfilippo, the creator of Redis, it was because the page size of 4KB was too big. I personally don’t think that helps but it’d be better if Redis preallocated specified amount of buffer pool and bring related objects to the same page to increase locality of reference, instead of letting the heap manager blindly fragment objects. In my opinion, the page size of 32 bytes is too small, considering that the hardware architectures and the compilers are optimized for the conventional page size. In that scale, even the latency of reading something from RAM could be dominant (RAM is too slow for CPU, therefore it’s got L1/L2 cache), and RAM has the pipelined burst mode to pre-fetche memory contents at a few clock cycles, before they are actually requested.
&lt;/blockquote&gt;&lt;/p&gt;

&lt;p&gt;5号，Antirez在博客上写了回击 &lt;a href="http://antirez.com/post/what-is-wrong-with-2006-programming.html"&gt;What's wrong with 2006 programming?&lt;/a&gt;，他认为：
&lt;ul&gt;
&lt;li&gt;OS Swap在一些情况下会导致客户端阻塞&lt;/li&gt;
&lt;li&gt;4K大小的Page可能包含很多key，其中总有一些被访问到，导致操作系统无法swap这些page&lt;/li&gt;
&lt;li&gt;使用自己实现的Paging为程序提供了极大的自由度，包括作者提到的2.2将会引入的数据压缩、新的数据结构以及自定义的过期算法&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;前段时间，Foursquare用MongoDB时，因为Sharding方法一些疏漏把大量的数据集中到了一台机器上，导致一台EC2实例内存耗尽无法工作。Mongodb的内部机制就是mmap，我的同事做过相关的测试，当内存耗尽时，读写操作都使用磁盘，这时mongodb的性能是完全无法使用的。事后10gen的开发人员Horowitz总结出现问题的原因&lt;a href="http://groups.google.com/group/mongodb-user/browse_thread/thread/528a94f287e9d77e"&gt;总结出现问题的原因&lt;/a&gt;时，其中很重要的一点是
&lt;blockquote&gt;Document size is less than 4k. Such documents, when moved, may be too small to free up pages and, thus, memory. &lt;/blockquote&gt;&lt;/p&gt;

&lt;p&gt;看了这个原因，Redis的作者Twitter上大喜："Real world instance of my 4k page + small objects concerns"&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>'Douban on Gwibber #2'</title><link href="https://sunng.info/blog/douban-on-gwibber-2.html" rel="alternate"></link><published>2010-10-07T00:00:00+08:00</published><updated>2010-10-07T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-10-07:/blog/douban-on-gwibber-2.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;Douban&lt;/li&gt;
&lt;li&gt;gwibber&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/5058425003/" title="Gwibber douban by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4145/5058425003_6bd9c4b6e7.jpg" width="397" height="500" alt="Gwibber douban" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Douban support on gwibber, tested on Gwibber &lt;strong&gt;2.32.0-ubuntu2&lt;/strong&gt; or &lt;strong&gt;2.32.0.1-ubuntu1&lt;/strong&gt; , Ubuntu Maverick.&lt;/p&gt;

&lt;p&gt;You can download it from bitbucket:
&lt;a href="http://bitbucket.org/sunng/gwibber-douban/downloads"&gt;http://bitbucket.org/sunng/gwibber-douban/downloads&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Installation:&lt;br /&gt;
Unpack and run install script with super user privilege:
&lt;i&gt;sudo bin …&lt;/i&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;Douban&lt;/li&gt;
&lt;li&gt;gwibber&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/5058425003/" title="Gwibber douban by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4145/5058425003_6bd9c4b6e7.jpg" width="397" height="500" alt="Gwibber douban" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Douban support on gwibber, tested on Gwibber &lt;strong&gt;2.32.0-ubuntu2&lt;/strong&gt; or &lt;strong&gt;2.32.0.1-ubuntu1&lt;/strong&gt; , Ubuntu Maverick.&lt;/p&gt;

&lt;p&gt;You can download it from bitbucket:
&lt;a href="http://bitbucket.org/sunng/gwibber-douban/downloads"&gt;http://bitbucket.org/sunng/gwibber-douban/downloads&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Installation:&lt;br /&gt;
Unpack and run install script with super user privilege:
&lt;i&gt;sudo bin/install.sh&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;We will create backup for you. To rollback, just run &lt;i&gt;sudo bin/uninstall.sh&lt;/i&gt; .&lt;/p&gt;

&lt;p&gt;Any feedback is welcome.&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Rhythmbox plugin for Douban FM</title><link href="https://sunng.info/blog/rhythmbox-plugin-for-douban-fm.html" rel="alternate"></link><published>2010-10-06T00:00:00+08:00</published><updated>2010-10-06T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-10-06:/blog/rhythmbox-plugin-for-douban-fm.html</id><content type="html">&lt;ul&gt;
&lt;li&gt;他山
tags:&lt;/li&gt;
&lt;li&gt;Douban&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://code.google.com/p/dbfmplugin/"&gt;Rhythmbox 豆瓣电台插件&lt;/a&gt;是由cdredfox基于&lt;a href="http://github.com/sunng87/exaile-doubanfm-plugin/blob/master/libdoubanfm.py"&gt;libdoubanfm&lt;/a&gt;开发的另一个Linux桌面豆瓣电台客户端。如果您是Rhythmbox的忠实用户，现在您有一个新的选择了。&lt;/p&gt;

&lt;p&gt;&lt;image src="http://hnyangfei.appspot.com/media/aglobnlhbmdmZWlyDQsSBU1lZGlhGImbBAw/%E8%96%9B%E5%87%AF%E7%90%AA%20-%20%E5%A4%8D%E5%88%BB%E5%9B%9E%E5%BF%86_007.png" width="500" /&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Douban on Gwibber</title><link href="https://sunng.info/blog/douban-on-gwibber.html" rel="alternate"></link><published>2010-10-05T00:00:00+08:00</published><updated>2010-10-05T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-10-05:/blog/douban-on-gwibber.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;Douban&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;gtk&lt;/li&gt;
&lt;li&gt;gwibber&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Now it's possible to use Gwibber as a desktop Douban.&lt;/p&gt;

&lt;p&gt;Setup a douban client in the account dialog:
&lt;a href="http://www.flickr.com/photos/40741608@N08/5053311735/" title="GwibberDoubanSetup by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4130/5053311735_5fc69747a5.jpg" width="500" height="259" alt="GwibberDoubanSetup" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Authorize Gwibber to access private data on Douban (OAuth):
&lt;a href="http://www.flickr.com/photos/40741608@N08/5053311377/" title="DoubanOAuthAuthorize by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4147/5053311377_43b0a4788e.jpg" width="500" height="394" alt="DoubanOAuthAuthorize" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Display Douban activities on Gwibber timeline:
&lt;a href="http://www.flickr.com/photos/40741608@N08/5053311635/" title="GwibberDouban by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4103/5053311635_ab6716100b.jpg" width="422" height="500" alt="GwibberDouban" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The code has …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;Douban&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;gtk&lt;/li&gt;
&lt;li&gt;gwibber&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Now it's possible to use Gwibber as a desktop Douban.&lt;/p&gt;

&lt;p&gt;Setup a douban client in the account dialog:
&lt;a href="http://www.flickr.com/photos/40741608@N08/5053311735/" title="GwibberDoubanSetup by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4130/5053311735_5fc69747a5.jpg" width="500" height="259" alt="GwibberDoubanSetup" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Authorize Gwibber to access private data on Douban (OAuth):
&lt;a href="http://www.flickr.com/photos/40741608@N08/5053311377/" title="DoubanOAuthAuthorize by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4147/5053311377_43b0a4788e.jpg" width="500" height="394" alt="DoubanOAuthAuthorize" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Display Douban activities on Gwibber timeline:
&lt;a href="http://www.flickr.com/photos/40741608@N08/5053311635/" title="GwibberDouban by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4103/5053311635_ab6716100b.jpg" width="422" height="500" alt="GwibberDouban" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The code has been uploaded to bitbucket:
&lt;a href="https://bitbucket.org/sunng/gwibber-douban/overview"&gt;https://bitbucket.org/sunng/gwibber-douban/overview&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I am currently working on an installation script.&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Redis Data Struct</title><link href="https://sunng.info/blog/redis-data-struct.html" rel="alternate"></link><published>2010-10-01T00:00:00+08:00</published><updated>2010-10-01T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-10-01:/blog/redis-data-struct.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;c&lt;/li&gt;
&lt;li&gt;redis
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://redis.io"&gt;Redis&lt;/a&gt;的几个核心数据结构定义在redis.h和dict.h中。Redis服务器总的数据结构里的根节点是redisServer，下面包含一个redisDb结构数组。数组的大小定义在redis.conf中的databases项，客户端可以通过 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;c&lt;/li&gt;
&lt;li&gt;redis
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://redis.io"&gt;Redis&lt;/a&gt;的几个核心数据结构定义在redis.h和dict.h中。Redis服务器总的数据结构里的根节点是redisServer，下面包含一个redisDb结构数组。数组的大小定义在redis.conf中的databases项，客户端可以通过select命令选择相应的DB。&lt;/p&gt;

&lt;p&gt;一个redisDb定义了redis数据库的基本数据结构，redisDb结构定义如下&lt;br /&gt;
[cc lang="c"]&lt;br /&gt;
typedef struct redisDb {&lt;br /&gt;
    dict *dict;                 /* The keyspace for this DB */&lt;br /&gt;
    dict *expires;              /* Timeout of keys with a timeout set */&lt;br /&gt;
    dict *blocking_keys;        /* Keys with clients waiting for data (BLPOP) */&lt;br /&gt;
    dict *io_keys;              /* Keys with clients waiting for VM I/O */&lt;br /&gt;
    dict *watched_keys;         /* WATCHED keys for MULTI/EXEC CAS */&lt;br /&gt;
    int id;&lt;br /&gt;
} redisDb;&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;其中
&lt;ul&gt;
&lt;li&gt;dict 用于存储键值的哈希表结构&lt;/li&gt;
&lt;li&gt;expires 用于存储键、过期时间的哈希表结构&lt;/li&gt;
&lt;li&gt;blocking_keys 用于存储键、阻塞数据的哈希表结构。目前仅仅在阻塞式的blpop brpop中使用。&lt;/li&gt;
&lt;li&gt;io_keys 存储等待将数据从swap读出的key （vmThreadedIOCompletedJob, dontWaitForSwappedKey, waitForSwappedKey）&lt;/li&gt;
&lt;li&gt;watched_keys 用于支持&lt;a href="http://code.google.com/p/redis/wiki/MultiExecCommand#Check_and_Set_%28CAS%29_transactions_using_WATCH"&gt;WATCH&lt;/a&gt;命令，实现乐观锁功能&lt;/li&gt;
&lt;li&gt;id 数据库编号&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;dict结构定义在dict.h中，主要的定义有：&lt;br /&gt;
[cc lang="c"]&lt;br /&gt;
typedef struct dictEntry {&lt;br /&gt;
    void *key;&lt;br /&gt;
    void *val;&lt;br /&gt;
    struct dictEntry *next;&lt;br /&gt;
} dictEntry;&lt;/p&gt;

&lt;p&gt;typedef struct dictht {&lt;br /&gt;
    dictEntry **table;&lt;br /&gt;
    unsigned long size;&lt;br /&gt;
    unsigned long sizemask;&lt;br /&gt;
    unsigned long used;&lt;br /&gt;
} dictht;&lt;/p&gt;

&lt;p&gt;typedef struct dict {&lt;br /&gt;
    dictType *type;&lt;br /&gt;
    void *privdata;&lt;br /&gt;
    dictht ht[2];&lt;br /&gt;
    int rehashidx; /* rehashing not in progress if rehashidx == -1 */&lt;br /&gt;
    int iterators; /* number of iterators currently running */&lt;br /&gt;
} dict;&lt;/p&gt;

&lt;p&gt;[/cc]&lt;/p&gt;

&lt;p&gt;每个dict中包含两个dictht结构，用来在rehash的时候进行数据交换。rehashidx用于记录rehash的进程，值是当前rehash的table索引。dictht中table二维数组，以hash值为索引。size是哈希表的大小，初始值是4，随着rehash的进行，变成大于size的最小2&lt;sup&gt;n&lt;/sup&gt;。sizemask是size-1，用来与哈希值做&amp;运算。&lt;/p&gt;

&lt;p&gt;redis在引如virtual memory之前，就是以这样的数据结构运行在内存中的。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Visualize call tree of a C function</title><link href="https://sunng.info/blog/visualize-call-tree-of-a-c-function.html" rel="alternate"></link><published>2010-09-24T00:00:00+08:00</published><updated>2010-09-24T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-09-24:/blog/visualize-call-tree-of-a-c-function.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;c&lt;/li&gt;
&lt;li&gt;dot&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;visualization
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;h3&gt;Requirement&lt;/h3&gt;
You want to visualize a call hierarchy of a C function.&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;Solution&lt;/h3&gt;
Utilities you need are listed below:
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.gnu.org/software/cflow/"&gt;GNU cflow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://cflow2vcg.sourceforge.net/"&gt;cflow2vcg&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;graphviz&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;Take 'rdbSaveBackground' (&lt;a href="http://github.com/antirez/redis/blob/master/src/rdb.c"&gt;redis/rdb.c&lt;/a&gt;) for example:&lt;br /&gt;
[cc lang="bash"]&lt;br /&gt;
cflow --format=posix --omit-arguments --level-indent='0 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;c&lt;/li&gt;
&lt;li&gt;dot&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;visualization
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;h3&gt;Requirement&lt;/h3&gt;
You want to visualize a call hierarchy of a C function.&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;Solution&lt;/h3&gt;
Utilities you need are listed below:
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.gnu.org/software/cflow/"&gt;GNU cflow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://cflow2vcg.sourceforge.net/"&gt;cflow2vcg&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;graphviz&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;Take 'rdbSaveBackground' (&lt;a href="http://github.com/antirez/redis/blob/master/src/rdb.c"&gt;redis/rdb.c&lt;/a&gt;) for example:&lt;br /&gt;
[cc lang="bash"]&lt;br /&gt;
cflow --format=posix --omit-arguments --level-indent='0=\t' --level-indent='1=\t' --level-indent=start='\t' -m 'rdbSaveBackground' ~/osprojects/redis/src/rdb.c | cflow2dot | dot -Tjpg -o rdb.jpg&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;Output:
&lt;a href="http://www.flickr.com/photos/40741608@N08/5020142591/" title="visualization of a call tree by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4111/5020142591_7b34a52e3c.jpg" width="500" height="98" alt="visualization of a call tree" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="http://unixdiary.blogspot.com/2006/05/using-cflow.html"&gt;unix diary&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The post is brought to you by &lt;a href="http://fedorahosted.org/lekhonee"&gt;lekhonee&lt;/a&gt; v0.7&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Lua script for Geany to view manpage of functions</title><link href="https://sunng.info/blog/lua-script-for-geany-to-view-manpage-of-functions.html" rel="alternate"></link><published>2010-09-22T00:00:00+08:00</published><updated>2010-09-22T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-09-22:/blog/lua-script-for-geany-to-view-manpage-of-functions.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;c&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;geany&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;lua
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;h3&gt;Requirement&lt;/h3&gt;
When editing when Geany, you need some document of system library functions at hand. So you want to browse manpage at any time.&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;Solution&lt;/h3&gt;
Just two lines:&lt;br /&gt;
[cc lang="lua"]&lt;br /&gt;
sel=geany.selection()&lt;br /&gt;
geany.launch('rxvt-unicode', '-e' …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;c&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;geany&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;lua
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;h3&gt;Requirement&lt;/h3&gt;
When editing when Geany, you need some document of system library functions at hand. So you want to browse manpage at any time.&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;Solution&lt;/h3&gt;
Just two lines:&lt;br /&gt;
[cc lang="lua"]&lt;br /&gt;
sel=geany.selection()&lt;br /&gt;
geany.launch('rxvt-unicode', '-e', 'man', sel)&lt;br /&gt;
[/cc]
&lt;a href="http://gist.github.com/591279"&gt;http://gist.github.com/591279&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;Install&lt;/h3&gt;
Make sure you have geany-plugin-lua installed:
&lt;i&gt;sudo apt-get install geany-plugin-lua&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;Create a lua source file named 'ShowMan.lua' in &lt;i&gt;~/.config/geany/plugins/geanylua/&lt;/i&gt;  . If the direcotry does not exist, you should create it first.&lt;/p&gt;

&lt;p&gt;Copy two lines of lua code into this file, save and close.&lt;/p&gt;

&lt;p&gt;Create a file named hotkeys.cfg in &lt;i&gt;~/.config/geany/plugins/geanylua/&lt;/i&gt;, add following content:
&lt;i&gt;ShowMan.lua&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;Restart geany, then open Preferences-&gt;Keybindings, Lua Script-&gt; ShowMan, bind a key, for example, Alt+m&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;Usage&lt;/h3&gt;
Select the function name, and press Alt+m (or from menu Tools-&gt;Lua Scripts-&gt;ShowMan), then you see the terminal and the manpage.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/5017060556/" title="geany-lua-manpage by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4127/5017060556_ca89e90517.jpg" width="500" height="313" alt="geany-lua-manpage" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The post is brought to you by &lt;a href="http://fedorahosted.org/lekhonee"&gt;lekhonee&lt;/a&gt; v0.7&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>GUI Debugging tools</title><link href="https://sunng.info/blog/gui-debugging-tools.html" rel="alternate"></link><published>2010-09-18T00:00:00+08:00</published><updated>2010-09-18T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-09-18:/blog/gui-debugging-tools.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;c&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;gdb&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;linux
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;想了解一个C程序的运行，打算用gdb来单步看一下流程，发现直接用gdb不太方便，然后模仿偶像用emacs的gdb支持(M-x gdb)。但是境界实在是比不上偶像 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;c&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;gdb&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;linux
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;想了解一个C程序的运行，打算用gdb来单步看一下流程，发现直接用gdb不太方便，然后模仿偶像用emacs的gdb支持(M-x gdb)。但是境界实在是比不上偶像，emacs的操作都还不熟练。最后选择了geany。&lt;a href="http://www.geany.org"&gt;geany&lt;/a&gt;是一个简单的C/C++ IDE，它有一个geanygdb插件可以帮助你在geany的图形界面里调试。&lt;/p&gt;

&lt;p&gt;但是最近安装在仓库里的geanygdb插件有一些问题，会导致很高的CPU占用。不过这个问题被报告在&lt;a href="http://sourceforge.net/tracker/?func=detail&amp;amp;aid=3019827&amp;amp;group_id=222729&amp;amp;atid=1056532"&gt;这里&lt;/a&gt;，现在已经修复了。因为新版本还没有release，所以如果急着用的话，可以从svn签出代码：
&lt;i&gt;svn co https://geany-plugins.svn.sourceforge.net/svnroot/geany-plugins/trunk/geany-plugins&lt;/i&gt;
然后执行：&lt;br /&gt;
[cc lang="bash"]&lt;br /&gt;
cd geany-plugins&lt;br /&gt;
./autogen.sh&lt;br /&gt;
./configure&lt;br /&gt;
cd geanygdb&lt;br /&gt;
make&lt;br /&gt;
sudo make install&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;重启geany，打开plugin manager，看到geanygdb的版本已经变成0.20就可以使用了。&lt;/p&gt;

&lt;p&gt;geany现在还不支持ctags，是因为它内部有一套自己的tags实现。代码导航可以用过上下文菜单 Go to tag Definition 或者 Go to tag Declaration 实现，最好是到Preference中给这两个命令设置一个快捷键，比如Ctrl+]，再对Navigate back 设置一个Ctrl+t，就如同vim+ctags一样了。&lt;/p&gt;

&lt;p&gt;实际上在GNOME桌面还有一个专门的GDB图形界面，叫做&lt;a href="http://projects.gnome.org/nemiver/"&gt;Nemiver&lt;/a&gt;。Nemiver对gdb的常用命令都有快捷键操作，把鼠标移动到代码上还可以查看变量的值，效果和eclipse调试java程序一样，非常强大。&lt;/p&gt;

&lt;p&gt;The post is brought to you by &lt;a href="http://fedorahosted.org/lekhonee"&gt;lekhonee&lt;/a&gt; v0.7&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Artificial301</title><link href="https://sunng.info/blog/artificial301.html" rel="alternate"></link><published>2010-09-16T00:00:00+08:00</published><updated>2010-09-16T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-09-16:/blog/artificial301.html</id><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;firefox&lt;/li&gt;
&lt;li&gt;github&lt;/li&gt;
&lt;li&gt;jetpack&lt;/li&gt;
&lt;li&gt;project
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/4995443949/" title="screenshot_002 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4130/4995443949_fd2f7b8fa4_z.jpg" width="640" height="441" alt="screenshot_002" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/4995474823/" title="screenshot_003 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4107/4995474823_d0cee8e91c.jpg" width="394" height="228" alt="screenshot_003" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Get artificial301 at:
&lt;a href="http://github.com/sunng87/Artificial301/downloads"&gt;http://github.com/sunng87/Artificial301/downloads&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The post is brought to you by &lt;a href="http://fedorahosted.org/lekhonee"&gt;lekhonee&lt;/a&gt; v0.7&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Hudson tips</title><link href="https://sunng.info/blog/hudson-tips.html" rel="alternate"></link><published>2010-09-15T00:00:00+08:00</published><updated>2010-09-15T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-09-15:/blog/hudson-tips.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;hudson&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;maven
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Hudson的项目有三种状态，分别是failed, Success, Unstable。当单元测试未能通过时，Hudson不会fail掉整个build而是设置为ubstable，并且继续执行post build scripts和actions。这就为集成的版本带来了一些 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;hudson&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;maven
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Hudson的项目有三种状态，分别是failed, Success, Unstable。当单元测试未能通过时，Hudson不会fail掉整个build而是设置为ubstable，并且继续执行post build scripts和actions。这就为集成的版本带来了一些不可知因素。取消这个设置，可以通过在maven options中添加一个 &lt;i&gt;-Dmaven.test.failure.ignore=false&lt;/i&gt;。或者在全局设置，manage hudson -&gt; configure hudson -&gt; Global MAVEN_OPTS。这个方法来自：&lt;a href="http://stackoverflow.com/questions/1004540/fail-hudson-build-on-single-unit-test-failure"&gt;http://stackoverflow.com/questions/1004540/fail-hudson-build-on-single-unit-test-failure&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;此外，我们借助hudson来自动完成开发环境的部署。从hudson的插件列表中安装ssh plugin和scp plugin。对打包项目新增一个post build action，使用SCP插件把打包生成的压缩文件上传到开发机上。本想同时配置一个build script，在开发环境机器上执行一个自动化部署脚本，但是使用发现ssh plugin的操作居然先于scp操作，而且这个顺序无法配置！不过不要紧，新建一个freestyle项目，项目只利用ssh插件运行远程脚本，再在打包项目配置中新增一个post build action -&gt; build other project，填写前者的项目名，使之成为打包项目的downstream project即可。&lt;/p&gt;

&lt;p&gt;The post is brought to you by &lt;a href="http://fedorahosted.org/lekhonee"&gt;lekhonee&lt;/a&gt; v0.7&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>迁移pylonsbook chapter 8到pylons1.0</title><link href="https://sunng.info/blog/qian-yi-pylonsbook-chapter-8dao-pylons10.html" rel="alternate"></link><published>2010-09-12T00:00:00+08:00</published><updated>2010-09-12T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-09-12:/blog/qian-yi-pylonsbook-chapter-8dao-pylons10.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;pylons&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;sqlalchemy
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Pylonsbook的&lt;a href="http://pylonsbook.com/en/1.1/starting-the-simplesite-tutorial.html"&gt;第八章&lt;/a&gt;，是一个完整的CRUD程序例子。pylons升级到1.0之后，有一些代码不能正常工作了。&lt;/p&gt;

&lt;p&gt;首先查看你使用的各种库的版本：
&lt;i&gt;paster …&lt;/i&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;pylons&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;sqlalchemy
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Pylonsbook的&lt;a href="http://pylonsbook.com/en/1.1/starting-the-simplesite-tutorial.html"&gt;第八章&lt;/a&gt;，是一个完整的CRUD程序例子。pylons升级到1.0之后，有一些代码不能正常工作了。&lt;/p&gt;

&lt;p&gt;首先查看你使用的各种库的版本：
&lt;i&gt;paster shell development.ini&lt;/i&gt;
[cc lang="python"]&lt;br /&gt;
import pylons&lt;br /&gt;
pylons.__version__&lt;br /&gt;
import sqlalchemy&lt;br /&gt;
sqlalchemy.__version__&lt;br /&gt;
import webhelpers&lt;br /&gt;
webhelpers.__version__&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;Pylons 1.0开始鼓励通过继承Base类的声明式ORM（pylonsbook介绍的是pylons 0.9x，sqlalchemy 0.5x，使用mapper的方式）&lt;br /&gt;
对pylons1.0，mode/__init__.py中声明model的代码如下：&lt;br /&gt;
[cc lang="python"]&lt;br /&gt;
from sqlalchemy import *&lt;br /&gt;
from sqlalchemy.orm import relationship&lt;/p&gt;

&lt;p&gt;class PageTag(Base):&lt;br /&gt;
    __tablename__ = 'pagetag'&lt;br /&gt;
    id = Column('id', Integer, Sequence('pagetag_seq_id', optional=True), primary_key=True)&lt;br /&gt;
    pageid = Column('pageid', Integer, ForeignKey('page.id'))&lt;br /&gt;
    tagid = Column('tagid', Integer, ForeignKey('tag.id'))&lt;/p&gt;

&lt;p&gt;class Page(Base):&lt;br /&gt;
    """docstring for Page"""&lt;br /&gt;
    __tablename__ = 'page'&lt;br /&gt;
    id = Column('id', Integer, Sequence('page_seq_id', optional=True), primary_key=True)&lt;br /&gt;
    content = Column('content', Text, nullable=False)&lt;br /&gt;
    posted = Column('posted', DateTime, default=now())&lt;br /&gt;
    title = Column('title', Unicode(255), default=u'Untitled Page')&lt;br /&gt;
    heading = Column('heading', Unicode(255))&lt;/p&gt;

&lt;p&gt;    tags = relationship(PageTag, backref='page')&lt;br /&gt;
    comments = relationship("Comment", backref='page')&lt;/p&gt;

&lt;p&gt;class Tag(Base):&lt;br /&gt;
    """docstring for Tag"""&lt;br /&gt;
    __tablename__ = 'tag'&lt;br /&gt;
    id = Column('id', Integer, Sequence('tag_seq_id', optional=True), primary_key=True)&lt;br /&gt;
    name = Column('name', Unicode(20), nullable=False, unique=True)&lt;/p&gt;

&lt;p&gt;class Comment(Base):&lt;br /&gt;
    __tablename__ = 'comment'&lt;br /&gt;
    id = Column('id', Integer, Sequence('comment_seq_id', optional=True), primary_key=True)&lt;br /&gt;
    content = Column('content', Text, default=u'')&lt;br /&gt;
    name = Column('name', Unicode(255))&lt;br /&gt;
    email = Column('email', Unicode(255), nullable=False)&lt;br /&gt;
    created = Column('created', TIMESTAMP, default=now())&lt;br /&gt;
    pageid = Column(Integer, ForeignKey('page.id'))&lt;/p&gt;

&lt;p&gt;[/cc]&lt;/p&gt;

&lt;p&gt;&lt;em&gt;init_model&lt;/em&gt; 方法的内容也不相同，但是对Session的配置都是在Session.configure里进行。 如果设置autocommit=True，即使设置了autoflush=True仍然需要Session.flush()一下才能把变更写入数据库。否则，则需要显示调用Session.commit()&lt;/p&gt;

&lt;p&gt;在写controller的部分，pylonsbook大量用了&lt;br /&gt;
[cc lang="python"]&lt;br /&gt;
page_q = model.meta.Session.query(model.Page)&lt;br /&gt;
c.page = page_q.get(int(id))&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;或&lt;br /&gt;
[cc lang="python"]&lt;br /&gt;
page_q = model.meta.Session.query(model.Page)&lt;br /&gt;
c.page = page_q.filter_by(id=id).first()&lt;br /&gt;
[/cc]&lt;br /&gt;
在新版本里只需要&lt;br /&gt;
[cc lang="python" inline="true"]&lt;br /&gt;
c.page=Session.query(model.Page).get(id)&lt;br /&gt;
[/cc]&lt;br /&gt;
即可。&lt;/p&gt;

&lt;p&gt;在模板部分，pylons现在默认不依赖formbuild，所以文中提到的helper方法要额外安装formbuild库。不过这些也可以用过webhelper来实现，在lib/helpers.py里：&lt;br /&gt;
[cc lang="python" inline="true"]&lt;br /&gt;
from webhelpers.html.tags import *&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;在field.html中&lt;br /&gt;
[cc lang="html"]&lt;/p&gt;

&lt;p&gt;&lt;p&gt;Heading: ${h.text(&lt;br /&gt;
    "heading"&lt;br /&gt;
)}
&lt;/p&gt;&lt;/p&gt;

&lt;p&gt;&lt;p&gt;
Title: ${h.text(&lt;br /&gt;
    "title"&lt;br /&gt;
)}
&lt;/p&gt;&lt;/p&gt;

&lt;p&gt;&lt;p&gt;
Content: ${h.textarea(&lt;br /&gt;
    "content", cols=40, rows=10&lt;br /&gt;
)}
&lt;/p&gt;&lt;/p&gt;

&lt;p&gt;[/cc]&lt;/p&gt;

&lt;p&gt;模板中一个非常常用的方法url_for在pylons1.0里被改为pylons.url，需要修改/lib/helpers.py&lt;br /&gt;
[cc lang="python"  inline="true"]&lt;br /&gt;
from pylons import url&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;书中所有的重定向都是直接设置response相关属性实现的，实际可以用redirect，结合url：&lt;br /&gt;
[cc lang="python"  inline="true"]&lt;br /&gt;
redirect(url(controller='page', action='view', id=page.id))&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;分页部分，如果你使用的是sqlalchemy 0.6的话，随pylons 1.0发布的Webhelpers 0.6还不支持 sqlalchemy 0.6的query object，需要手动升级到1.2. (&lt;i&gt;easy_install -U webhelpers&lt;/i&gt;)&lt;/p&gt;

&lt;p&gt;最后关于flash状态，webhelpers里有一个专门的实现，不过还是不太好用：
&lt;a href="http://pylonshq.com/docs/en/1.0/thirdparty/webhelpers/pylonslib/"&gt;http://pylonshq.com/docs/en/1.0/thirdparty/webhelpers/pylonslib/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The post is brought to you by &lt;a href="http://fedorahosted.org/lekhonee"&gt;lekhonee&lt;/a&gt; v0.7&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>9月10日运转记</title><link href="https://sunng.info/blog/9yue-10ri-yun-zhuan-ji.html" rel="alternate"></link><published>2010-09-10T00:00:00+08:00</published><updated>2010-09-10T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-09-10:/blog/9yue-10ri-yun-zhuan-ji.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;Shanghai
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;庆祝教师节，今天请假休息出门放松。&lt;/p&gt;

&lt;p&gt;线路：&lt;br /&gt;
609 -&gt; 其昌栈渡口 -&gt; 秦皇岛路渡口 -&gt; 37 -&gt; 33 -&gt; 金陵西路渡口 -&gt; 东昌路渡口 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;Shanghai
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;庆祝教师节，今天请假休息出门放松。&lt;/p&gt;

&lt;p&gt;线路：&lt;br /&gt;
609 -&gt; 其昌栈渡口 -&gt; 秦皇岛路渡口 -&gt; 37 -&gt; 33 -&gt; 金陵西路渡口 -&gt; 东昌路渡口 -&gt; 东川线&lt;/p&gt;

&lt;p&gt;609从张江路经金桥路出张江，经过碧云路、云山路、羽山路、源深路、福山路等，连接了浦东几个人口稠密的地区。从张江出发做609可以直达世纪大道、张杨路的浦东医保中心，这些地方之前去过但是都不知道，今天途径才恍然大悟。不过609的缺点就是太绕，尤其是在张江这段简直无语。&lt;/p&gt;

&lt;p&gt;其昌栈渡口人不多，极少行人，多数是摩托车电动车。轮渡稍微有点破旧，行人票价0.5元。
&lt;a href="http://www.flickr.com/photos/40741608@N08/4976039507/" title="20100910039.resized by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4130/4976039507_8a43361c11.jpg" width="500" height="375" alt="20100910039.resized" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/4976039365/" title="20100910041.resized by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4085/4976039365_ba35005f9b.jpg" width="500" height="375" alt="20100910041.resized" /&gt;&lt;/a&gt;
没有带相机，手机拍拍玩。&lt;/p&gt;

&lt;p&gt;渡过黄浦江后是浦西的秦皇岛路渡口，附近是4号线大连路车站。从附近坐37路，沿东大名路，经过外白渡铁桥到外滩。
&lt;a href="http://www.flickr.com/photos/40741608@N08/4976650260/" title="20100910046.resized by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4125/4976650260_430b0e1911.jpg" width="500" height="375" alt="20100910046.resized" /&gt;&lt;/a&gt;
像不像这个：&lt;a href="http://music.douban.com/subject/2025422/"&gt;http://music.douban.com/subject/2025422/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;从外滩坐33路到终点站十六铺码头，问路被告知东门路渡口已经关闭了。只能到附近的金陵西路渡口，金陵西路渡口只有行人票，需要2元，对岸是东昌路渡口。东昌路渡口和东昌路地铁站分别在东昌路的两端，这个比较悲剧。更悲剧的是东川线的起点站很不好找，问了世博志愿者，结果被指的路多走了两站。&lt;/p&gt;

&lt;p&gt;东川线经过东方路、浦建路、龙阳路钻进张江，回来比较疲劳，附近的印象就不深了。&lt;/p&gt;

&lt;p&gt;The post is brought to you by &lt;a href="http://fedorahosted.org/lekhonee"&gt;lekhonee&lt;/a&gt; v0.7&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Newly designed front page</title><link href="https://sunng.info/blog/newly-designed-front-page.html" rel="alternate"></link><published>2010-09-07T00:00:00+08:00</published><updated>2010-09-07T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-09-07:/blog/newly-designed-front-page.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;css
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;The last version of my home page was initialized at 2008.9. After two years, the page is completely obsolete,  some features were broken due to different reasons. (fanfou.com has been shutdown and twitter was banned).&lt;/p&gt;

&lt;p&gt;So I just bring you …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;css
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;The last version of my home page was initialized at 2008.9. After two years, the page is completely obsolete,  some features were broken due to different reasons. (fanfou.com has been shutdown and twitter was banned).&lt;/p&gt;

&lt;p&gt;So I just bring you this:
&lt;a href="http://www.flickr.com/photos/40741608@N08/4967665544/" title="screenshot_001 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4124/4967665544_1b607edbf5.jpg" width="500" height="294" alt="screenshot_001" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The page is extremely simple, without any JavaScript or image. Some CSS3 features have been applied to it, including gradient background, css border radius (also web font will be added when I find a suitable font).&lt;/p&gt;

&lt;p&gt;The photo is taken at Expo, outside the  Holland Pavilion.&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Upgraded to Maverick Meerkat</title><link href="https://sunng.info/blog/upgraded-to-maverick-meerkat.html" rel="alternate"></link><published>2010-09-06T00:00:00+08:00</published><updated>2010-09-06T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-09-06:/blog/upgraded-to-maverick-meerkat.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;上周Ubuntu 10.10 Maverick Meerkat beta发布了，本人向来是在Beta版发布的时候更新，然后经历一个月的黑白颠倒最终走向光明走向stable。因为我安装 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;上周Ubuntu 10.10 Maverick Meerkat beta发布了，本人向来是在Beta版发布的时候更新，然后经历一个月的黑白颠倒最终走向光明走向stable。因为我安装的规模有点大，这次升级总共要下载两千八百多个包，花了我两天的时间。&lt;/p&gt;

&lt;p&gt;升级到Meerkat后，由于内核版本变成了2.6.35，原先ath5k网卡的驱动madwifi不能用了。这点一定要有心理准备，好在madwifi已经有了针对2.6.35的分支。可以从madwifi-project网站上下载：
&lt;a href="http://snapshots.madwifi-project.org/madwifi-0.9.4-current.tar.gz"&gt;http://snapshots.madwifi-project.org/madwifi-0.9.4-current.tar.gz&lt;/a&gt;
然后您可以根据UserGuide上的步骤进行操作：
&lt;a href="http://madwifi-project.org/wiki/UserDocs/FirstTimeHowTo"&gt;http://madwifi-project.org/wiki/UserDocs/FirstTimeHowTo&lt;/a&gt;
话说我升级了网卡驱动之后无线网路稳定性、速度都有了很明显的提升，对一个饱受无线掉线折磨的人来说，这个太重要了。（我先前使用的驱动是 madwifi-hal-0.10.5.6-r4119）&lt;/p&gt;

&lt;p&gt;其他变化包括：
&lt;ul&gt;
    &lt;li&gt;主题多了橙色元素&lt;/li&gt;
&lt;li&gt;indicator applet增加了几个新的图标&lt;/li&gt;
&lt;li&gt;tracker applet可以显示出来了 -_-|||&lt;/li&gt;
&lt;li&gt;Evolution 2.30 工具栏有点变化&lt;/li&gt;
&lt;li&gt;UpdateManager UI有点变化&lt;/li&gt;
&lt;li&gt;多了一个Input Method Switch的图形界面设置&lt;/li&gt;
&lt;li&gt;Indicator多了network manager&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;其他更新还要慢慢体会，不过总得来说这次10.10更新不多，恐怕也和GNOME3跳票有关吧。&lt;/p&gt;

&lt;p&gt;The post is brought to you by &lt;a href="http://fedorahosted.org/lekhonee"&gt;lekhonee&lt;/a&gt; v0.7&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>A day in Expo</title><link href="https://sunng.info/blog/a-day-in-expo.html" rel="alternate"></link><published>2010-09-05T00:00:00+08:00</published><updated>2010-09-05T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-09-05:/blog/a-day-in-expo.html</id><content type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;D60&lt;/li&gt;
&lt;li&gt;Shanghai
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;A day in Expo, Shanghai.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/4957383122/" title="DSC_0015 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4133/4957383122_818b9af46c.jpg" width="500" height="335" alt="DSC_0015" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/4957392860/" title="DSC_0017 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4076/4957392860_4249a043bc.jpg" width="500" height="335" alt="DSC_0017" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/4956812599/" title="DSC_0023 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4110/4956812599_fdd0e0b035.jpg" width="335" height="500" alt="DSC_0023" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/4957416828/" title="DSC_0025 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4096/4957416828_7d74399e72.jpg" width="500" height="335" alt="DSC_0025" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/4957437892/" title="DSC_0026 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4108/4957437892_5b92d804fd.jpg" width="500" height="335" alt="DSC_0026" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/4957449690/" title="DSC_0028 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4091/4957449690_22357b0ffc.jpg" width="500" height="335" alt="DSC_0028" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/4958948524/" title="DSC_0032 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4090/4958948524_08095c93cc.jpg" width="335" height="500" alt="DSC_0032" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/4958953244/" title="DSC_0035 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4086/4958953244_9d9dfa3fc3.jpg" width="335" height="500" alt="DSC_0035" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/4958964376/" title="DSC_0053 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4083/4958964376_b9054d2a3c.jpg" width="500" height="335" alt="DSC_0053" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/4958390957/" title="DSC_0057 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4086/4958390957_d62179b219.jpg" width="335" height="500" alt="DSC_0057" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/4958405807/" title="DSC_0060 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4094/4958405807_8a0dda2557.jpg" width="500" height="335" alt="DSC_0060" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/4959010738/" title="DSC_0063 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4151/4959010738_c5b6da5fe1.jpg" width="500" height="335" alt="DSC_0063" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/4958468151/" title="DSC_0067 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4142/4958468151_cf0f270d04.jpg" width="500" height="335" alt="DSC_0067" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/4958477457/" title="DSC_0069 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4126/4958477457_3437cbe143.jpg" width="500" height="335" alt="DSC_0069" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;早晨6点开始阳光就非常剧烈，不适合拍照，而且大部分时间都用来排队了，只留下这么一点点能看的而已。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>ls my python packages</title><link href="https://sunng.info/blog/ls-my-python-packages.html" rel="alternate"></link><published>2010-09-02T00:00:00+08:00</published><updated>2010-09-02T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-09-02:/blog/ls-my-python-packages.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;在豆瓣上看到一个无聊的&lt;a href="http://www.douban.com/online/10544092/"&gt;秀gems的活动&lt;/a&gt;，我也无聊一下，秀一下eggs。&lt;/p&gt;

&lt;p&gt;&lt;i&gt;ls /usr/local/lib/python2.6/dist-packages/ | grep .egg | sed 's/\(.*\)/&amp;lt;li&amp;gt;\1&amp;lt;\/li&amp;gt;/'&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;&lt;ul&gt;
&lt;li&gt;amqplib-0.6.1-py2.6.egg …&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;在豆瓣上看到一个无聊的&lt;a href="http://www.douban.com/online/10544092/"&gt;秀gems的活动&lt;/a&gt;，我也无聊一下，秀一下eggs。&lt;/p&gt;

&lt;p&gt;&lt;i&gt;ls /usr/local/lib/python2.6/dist-packages/ | grep .egg | sed 's/\(.*\)/&amp;lt;li&amp;gt;\1&amp;lt;\/li&amp;gt;/'&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;&lt;ul&gt;
&lt;li&gt;amqplib-0.6.1-py2.6.egg&lt;/li&gt;
&lt;li&gt;argparse-1.1-py2.6.egg-info&lt;/li&gt;
&lt;li&gt;AuthKit-0.4.5-py2.6.egg&lt;/li&gt;
&lt;li&gt;avro-1.3.3-py2.6.egg&lt;/li&gt;
&lt;li&gt;Beaker-1.3-py2.6.egg&lt;/li&gt;
&lt;li&gt;Beaker-1.5.4-py2.6.egg&lt;/li&gt;
&lt;li&gt;beanstalkc-0.2.0-py2.6.egg&lt;/li&gt;
&lt;li&gt;bottle-0.8.1-py2.6.egg&lt;/li&gt;
&lt;li&gt;bottle-0.8.3-py2.6.egg&lt;/li&gt;
&lt;li&gt;bpython-0.9.6.2-py2.6.egg&lt;/li&gt;
&lt;li&gt;bson-0.1.2-py2.6.egg&lt;/li&gt;
&lt;li&gt;CherryPy-3.1.2-py2.6.egg&lt;/li&gt;
&lt;li&gt;CouchDB-0.6-py2.6.egg&lt;/li&gt;
&lt;li&gt;coverage-3.2-py2.6-linux-i686.egg&lt;/li&gt;
&lt;li&gt;Cython-0.12.1-py2.6-linux-i686.egg&lt;/li&gt;
&lt;li&gt;decorator-3.0.0-py2.6.egg&lt;/li&gt;
&lt;li&gt;distribute-0.6.14-py2.6.egg&lt;/li&gt;
&lt;li&gt;Django-1.0.2_final-py2.6.egg&lt;/li&gt;
&lt;li&gt;Django-1.1-py2.6.egg&lt;/li&gt;
&lt;li&gt;Django-1.2-py2.6.egg&lt;/li&gt;
&lt;li&gt;django_sphinx-2.2.3-py2.6.egg&lt;/li&gt;
&lt;li&gt;docutils-0.6-py2.6.egg&lt;/li&gt;
&lt;li&gt;Editra-0.4.95-py2.6.egg&lt;/li&gt;
&lt;li&gt;Editra-0.5.05-py2.6.egg&lt;/li&gt;
&lt;li&gt;Editra-0.5.72-py2.6.egg&lt;/li&gt;
&lt;li&gt;elementtree-1.2.7_20070827_preview-py2.6.egg&lt;/li&gt;
&lt;li&gt;eventlet-0.9.9-py2.6.egg&lt;/li&gt;
&lt;li&gt;Fabric-0.9.1-py2.6.egg&lt;/li&gt;
&lt;li&gt;fapws3-0.5.dev-py2.6-linux-i686.egg&lt;/li&gt;
&lt;li&gt;FormEncode-1.2.1-py2.6.egg&lt;/li&gt;
&lt;li&gt;GeoAlchemy-0.1-py2.6.egg&lt;/li&gt;
&lt;li&gt;Geohash-1.0rc1-py2.6.egg&lt;/li&gt;
&lt;li&gt;gevent-0.13.0.egg-info&lt;/li&gt;
&lt;li&gt;greenlet-0.3.1-py2.6-linux-i686.egg&lt;/li&gt;
&lt;li&gt;gunicorn-0.10.0-py2.6.egg&lt;/li&gt;
&lt;li&gt;gunicorn-0.11.0-py2.6.egg&lt;/li&gt;
&lt;li&gt;gunicorn-0.8.1-py2.6.egg&lt;/li&gt;
&lt;li&gt;httplib2-0.5.0-py2.6.egg&lt;/li&gt;
&lt;li&gt;ipython-0.10-py2.6.egg&lt;/li&gt;
&lt;li&gt;ipython-0.9.1-py2.6.egg&lt;/li&gt;
&lt;li&gt;Jinja2-2.5-py2.6.egg&lt;/li&gt;
&lt;li&gt;Mako-0.2.4-py2.6.egg&lt;/li&gt;
&lt;li&gt;Mako-0.3.4-py2.6.egg&lt;/li&gt;
&lt;li&gt;Markdoc-0.6.4-py2.6.egg-info&lt;/li&gt;
&lt;li&gt;Markdown-2.0.3-py2.6.egg&lt;/li&gt;
&lt;li&gt;Markdown-2.0.3-py2.6.egg-info&lt;/li&gt;
&lt;li&gt;MarkupSafe-0.9.2-py2.6-linux-i686.egg&lt;/li&gt;
&lt;li&gt;meinheld-0.3.1-py2.6-linux-i686.egg&lt;/li&gt;
&lt;li&gt;Ming-0.2-py2.6.egg&lt;/li&gt;
&lt;li&gt;mock-0.6.0-py2.6.egg&lt;/li&gt;
&lt;li&gt;mocker-0.10.1-py2.6.egg&lt;/li&gt;
&lt;li&gt;MySQL_python-1.2.3c1-py2.6-linux-i686.egg&lt;/li&gt;
&lt;li&gt;networkx-1.0rc1-py2.6.egg&lt;/li&gt;
&lt;li&gt;nose-0.10.4-py2.6.egg&lt;/li&gt;
&lt;li&gt;numpy-1.4.1-py2.6-linux-i686.egg&lt;/li&gt;
&lt;li&gt;Paste-1.7.2-py2.6.egg&lt;/li&gt;
&lt;li&gt;PasteDeploy-1.3.3-py2.6.egg&lt;/li&gt;
&lt;li&gt;PasteScript-1.7.3-py2.6.egg&lt;/li&gt;
&lt;li&gt;pip-0.4-py2.6.egg&lt;/li&gt;
&lt;li&gt;pip-0.8-py2.6.egg&lt;/li&gt;
&lt;li&gt;progressbar-2.2-py2.6.egg&lt;/li&gt;
&lt;li&gt;psycopg2-2.2.2-py2.6-linux-i686.egg&lt;/li&gt;
&lt;li&gt;pycurl-7.19.0-py2.6-linux-i686.egg&lt;/li&gt;
&lt;li&gt;pyev-0.5.3_3.8-py2.6-linux-i686.egg&lt;/li&gt;
&lt;li&gt;Pygments-1.0-py2.6.egg&lt;/li&gt;
&lt;li&gt;Pygments-1.3.1-py2.6.egg&lt;/li&gt;
&lt;li&gt;pygooglechart-0.2.1-py2.6.egg&lt;/li&gt;
&lt;li&gt;PyGreSQL-4.0-py2.6-linux-i686.egg&lt;/li&gt;
&lt;li&gt;Pylons-0.9.7-py2.6.egg&lt;/li&gt;
&lt;li&gt;Pylons-1.0-py2.6.egg&lt;/li&gt;
&lt;li&gt;pymongo-1.6-py2.6-linux-i686.egg&lt;/li&gt;
&lt;li&gt;pymongo-1.7-py2.6-linux-i686.egg&lt;/li&gt;
&lt;li&gt;pymongo-1.8-py2.6-linux-i686.egg&lt;/li&gt;
&lt;li&gt;pysal-1.0.0.egg-info&lt;/li&gt;
&lt;li&gt;python_cjson-1.0.5-py2.6-linux-i686.egg&lt;/li&gt;
&lt;li&gt;python_memcached-1.45-py2.6.egg&lt;/li&gt;
&lt;li&gt;python_openid-2.2.5-py2.6.egg&lt;/li&gt;
&lt;li&gt;PyXML-0.8.4-py2.6-linux-i686.egg&lt;/li&gt;
&lt;li&gt;PyYAML-3.09-py2.6.egg-info&lt;/li&gt;
&lt;li&gt;RDFobject-0.1.4-py2.6.egg&lt;/li&gt;
&lt;li&gt;Rocket-1.1.1-py2.6.egg&lt;/li&gt;
&lt;li&gt;Routes-1.10.3-py2.6.egg&lt;/li&gt;
&lt;li&gt;Routes-1.12.3-py2.6.egg&lt;/li&gt;
&lt;li&gt;Rtree-0.6.0-py2.6.egg&lt;/li&gt;
&lt;li&gt;ruffus-2.2-py2.6.egg&lt;/li&gt;
&lt;li&gt;scipy-0.8.0-py2.6-linux-i686.egg&lt;/li&gt;
&lt;li&gt;setuptools-0.6c11-py2.6.egg-info&lt;/li&gt;
&lt;li&gt;setuptools-0.6c9-py2.6.egg&lt;/li&gt;
&lt;li&gt;setuptools-0.6c9-py2.6.egg.OLD.1283177768.67&lt;/li&gt;
&lt;li&gt;Shapely-1.0.12-py2.6.egg&lt;/li&gt;
&lt;li&gt;Shapely-1.2-py2.6.egg&lt;/li&gt;
&lt;li&gt;simplejson-2.0.8-py2.6.egg&lt;/li&gt;
&lt;li&gt;Spawning-0.9.3rc2-py2.6.egg&lt;/li&gt;
&lt;li&gt;Sphinx-1.0b2-py2.6.egg&lt;/li&gt;
&lt;li&gt;SQLAlchemy-0.6.3-py2.6.egg&lt;/li&gt;
&lt;li&gt;Tempita-0.2-py2.6.egg&lt;/li&gt;
&lt;li&gt;threadpool-1.2.7-py2.6.egg&lt;/li&gt;
&lt;li&gt;tornado-0.2-py2.6.egg&lt;/li&gt;
&lt;li&gt;twill-0.9-py2.6.egg&lt;/li&gt;
&lt;li&gt;Twisted-10.1.0-py2.6-linux-i686.egg&lt;/li&gt;
&lt;li&gt;vimpdb-0.3.8-py2.6.egg&lt;/li&gt;
&lt;li&gt;virtualenv-1.3.3-py2.6.egg&lt;/li&gt;
&lt;li&gt;WebError-0.10.1-py2.6.egg&lt;/li&gt;
&lt;li&gt;WebHelpers-0.6.4-py2.6.egg&lt;/li&gt;
&lt;li&gt;WebOb-0.9.6.1-py2.6.egg&lt;/li&gt;
&lt;li&gt;web.py-0.32-py2.6.egg-info&lt;/li&gt;
&lt;li&gt;web.py-0.34-py2.6.egg&lt;/li&gt;
&lt;li&gt;WebTest-1.1-py2.6.egg&lt;/li&gt;
&lt;li&gt;Werkzeug-0.6.2-py2.6.egg&lt;/li&gt;
&lt;li&gt;whizzer-0.2.egg-info&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;话说用sed能不能让开头重复的行不输出呢？或者什么命令能实现？&lt;/p&gt;

&lt;p&gt;The post is brought to you by &lt;a href="http://fedorahosted.org/lekhonee"&gt;lekhonee&lt;/a&gt; v0.7&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Open for a new job</title><link href="https://sunng.info/blog/open-for-a-new-job.html" rel="alternate"></link><published>2010-08-31T00:00:00+08:00</published><updated>2010-08-31T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-08-31:/blog/open-for-a-new-job.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;job
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;I am about to leave current company and looking for a new challenge for future. If you are interested, and also looking for a Java or Python developer, please check the hot baked resume:
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://sunng.info/resume_en.html"&gt;en&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://sunng.info/resume_cn.html"&gt;zh&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;Actually, I am not good at documenting …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;job
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;I am about to leave current company and looking for a new challenge for future. If you are interested, and also looking for a Java or Python developer, please check the hot baked resume:
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://sunng.info/resume_en.html"&gt;en&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://sunng.info/resume_cn.html"&gt;zh&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;Actually, I am not good at documenting myself. So if you need more details, feel free to get connected with email: classicning[at]gmail.com&lt;/p&gt;

&lt;p&gt;The post is brought to you by &lt;a href="http://fedorahosted.org/lekhonee"&gt;lekhonee&lt;/a&gt; v0.7&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>周记</title><link href="https://sunng.info/blog/zhou-ji.html" rel="alternate"></link><published>2010-08-28T00:00:00+08:00</published><updated>2010-08-28T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-08-28:/blog/zhou-ji.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;bason&lt;/li&gt;
&lt;li&gt;flash&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;先讲个&lt;a href="http://www.douban.com/group/topic/13651452/"&gt;笑话&lt;/a&gt;，然后回顾一下这个星期。&lt;/p&gt;

&lt;p&gt;这个星期从周四晚上开始，和一位外国朋友一位台湾女士聊 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;bason&lt;/li&gt;
&lt;li&gt;flash&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;先讲个&lt;a href="http://www.douban.com/group/topic/13651452/"&gt;笑话&lt;/a&gt;，然后回顾一下这个星期。&lt;/p&gt;

&lt;p&gt;这个星期从周四晚上开始，和一位外国朋友一位台湾女士聊天。第一次经历这样的场面，忐忑得很，起初还尝试和大家说英文的，奈何我的口语和听力实在是难以支撑，最后不知不觉间大家都平滑过渡到中文了。后来我想着听力还可以通过看InfoQ上的会议录像什么的练习一下，口语就鲜有机会和场合提升了。进而又想到长期在一个小环境里做重复的事情，有些能力的退化你甚至都感觉不到，更不要说原先不具备的能力了，你甚至都不能觉察到自己缺乏某一方面的训练。&lt;/p&gt;

&lt;p&gt;今天下午又和原先在IBM实习的同事聚会，正好在五角场附近转转。比起张江这种近乎边缘的生活环境，五角场实在是强太多了，更难能可贵的是如果你在附近转一转，询问一下房租，那里的生活成本实际上并不比张江高，生活质量实在是没法比。这是为什么呢？不理解。另外今天看到了&lt;a href="http://blog.samsonis.me/2010/08/update-41-%E6%9C%89%E4%BA%9B%E4%BA%8B%E6%83%85%E6%88%91%E8%BF%98%E7%9C%9F%E4%B8%8D%E7%90%86%E8%A7%A3/"&gt;samson理解不了的沃尔玛&lt;/a&gt;，还有传说中那一片Oracle / EMC / eBao / 百度。上海真是大，地铁走几站就是一个完全不同的世界。&lt;/p&gt;

&lt;p&gt;从同事那里了解到了团购网站的一些事情，这年头，好像不做个团购网站都不好意思跟人打招呼似的。于是有了&lt;a href="http://www.zuitu.com/"&gt;这种东西&lt;/a&gt;，又是一窝蜂，这就是我们的互联网，你有什么意见呢。&lt;/p&gt;

&lt;p&gt;昨天本想写一个对 Continuous Deployment 的理解来着，结果不料电脑过热死机了。什么原因呢，原来是浏览器里开着一个崔永元的网站，一个基于flash的网站，你懂的，CPU一直占满。这么明显的、严重的、高能耗的问题，这么多年了，Adobe一点动作都没有。另外我发现今天禁用了Adobe的Flashplayer居然Firefox的Ctrl+C/Ctrl+T等等几个快捷键突然恢复了，对于这种情况还是不说什么吧。最近听说开源的实现&lt;a href="http://digitizor.com/2010/08/23/gnash-0-88-released-100-youtube-compatible-now/"&gt;gnash已经可以播放Youtube的视频&lt;/a&gt;了（当然严格地说这事跟咱们没什么关系），一会写完blog我决定配置一下试试。&lt;/p&gt;

&lt;p&gt;这周另一个重要的事情是Kindle 3上市了，第一次对一个手持设备产生这么强烈的兴趣。而且尤其是发现你没有那么容易的渠道能够买到它的时候，这种兴趣膨胀得更厉害了。半年前电子墨水的电子书都还在2000人民币以上，这次6寸不带3G的Kindle 3只要139美元，折合人民币连950都不到，这个领域很快又要炸开锅了。&lt;/p&gt;

&lt;p&gt;最后，我把Bason放到Sonatype的&lt;a href="https://oss.sonatype.org/content/repositories/snapshots/info/sunng/bason/"&gt;&lt;/a&gt;OSS Repository上了。Sonatype提供了一个免费的Maven仓库，你可以把自己的开源项目deploy到这个仓库中方便分发。详情您可以去看&lt;a href="https://docs.sonatype.org/display/repository/sonatype+oss+maven+repository+usage+guide"&gt;sonatype提供的文档&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;The post is brought to you by &lt;a href="http://fedorahosted.org/lekhonee"&gt;lekhonee&lt;/a&gt; v0.7&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>'Bason: A BSON Serialization Code Generator'</title><link href="https://sunng.info/blog/bason-a-bson-serialization-code-generator.html" rel="alternate"></link><published>2010-08-23T00:00:00+08:00</published><updated>2010-08-23T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-08-23:/blog/bason-a-bson-serialization-code-generator.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;maven&lt;/li&gt;
&lt;li&gt;opensource
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Bason is a code generator for object to bson serialization and deserialization. Different from tranditional reflection way, bason uses an annotation processor to generate serialization manager at compile time. You just add Bason as compilation dependency and drop it in the …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;maven&lt;/li&gt;
&lt;li&gt;opensource
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Bason is a code generator for object to bson serialization and deserialization. Different from tranditional reflection way, bason uses an annotation processor to generate serialization manager at compile time. You just add Bason as compilation dependency and drop it in the runtime.&lt;/p&gt;

&lt;p&gt;To use Bason, you simply add annotation to JavaBeans:&lt;br /&gt;
[cc lang="java"]&lt;br /&gt;
/**&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
package info.sunng.bason.example;&lt;/p&gt;

&lt;p&gt;import java.util.Date;&lt;/p&gt;

&lt;p&gt;import info.sunng.bason.annotations.BsonAlias;&lt;br /&gt;
import info.sunng.bason.annotations.BsonDocument;&lt;br /&gt;
import info.sunng.bason.annotations.BsonIgnore;&lt;/p&gt;

&lt;p&gt;/**&lt;br /&gt;
 * @author SunNing&lt;br /&gt;
 *&lt;br /&gt;
 * @since Aug 18, 2010&lt;br /&gt;
 */&lt;br /&gt;
@BsonDocument&lt;br /&gt;
public class Passenger {&lt;/p&gt;

&lt;p&gt; private double packageWeight;&lt;/p&gt;

&lt;p&gt; private long ticketId;&lt;/p&gt;

&lt;p&gt; private String name;&lt;/p&gt;

&lt;p&gt; private Date createdDate;&lt;/p&gt;

&lt;p&gt; private Flight flight;&lt;/p&gt;

&lt;p&gt; /**&lt;br /&gt;
     * @return the packageWeight&lt;br /&gt;
     */&lt;br /&gt;
    @BsonIgnore&lt;br /&gt;
    public double getPackageWeight() {&lt;br /&gt;
        return packageWeight;&lt;br /&gt;
    }&lt;/p&gt;

&lt;p&gt; /**&lt;br /&gt;
     * @param packageWeight the packageWeight to set&lt;br /&gt;
     */&lt;br /&gt;
    public void setPackageWeight(double packageWeight) {&lt;br /&gt;
        this.packageWeight = packageWeight;&lt;br /&gt;
    }&lt;/p&gt;

&lt;p&gt; /**&lt;br /&gt;
     * @return the ticketId&lt;br /&gt;
     */&lt;br /&gt;
    @BsonAlias("ticket")&lt;br /&gt;
    public long getTicketId() {&lt;br /&gt;
        return ticketId;&lt;br /&gt;
    }&lt;/p&gt;

&lt;p&gt; /**&lt;br /&gt;
     * @param ticketId the ticketId to set&lt;br /&gt;
     */&lt;br /&gt;
    public void setTicketId(long ticketId) {&lt;br /&gt;
        this.ticketId = ticketId;&lt;br /&gt;
    }&lt;/p&gt;

&lt;p&gt; /**&lt;br /&gt;
     * @return the name&lt;br /&gt;
     */&lt;br /&gt;
    public String getName() {&lt;br /&gt;
        return name;&lt;br /&gt;
    }&lt;/p&gt;

&lt;p&gt; /**&lt;br /&gt;
     * @param name the name to set&lt;br /&gt;
     */&lt;br /&gt;
    public void setName(String name) {&lt;br /&gt;
        this.name = name;&lt;br /&gt;
    }&lt;/p&gt;

&lt;p&gt; /**&lt;br /&gt;
     * @param createdDate the createdDate to set&lt;br /&gt;
     */&lt;br /&gt;
    public void setCreatedDate(Date createdDate) {&lt;br /&gt;
        this.createdDate = createdDate;&lt;br /&gt;
    }&lt;/p&gt;

&lt;p&gt; /**&lt;br /&gt;
     * @return the createdDate&lt;br /&gt;
     */&lt;br /&gt;
    public Date getCreatedDate() {&lt;br /&gt;
        return createdDate;&lt;br /&gt;
    }&lt;/p&gt;

&lt;p&gt; /**&lt;br /&gt;
     * @param flight the flight to set&lt;br /&gt;
     */&lt;br /&gt;
    public void setFlight(Flight flight) {&lt;br /&gt;
        this.flight = flight;&lt;br /&gt;
    }&lt;/p&gt;

&lt;p&gt; /**&lt;br /&gt;
     * @return the flight&lt;br /&gt;
     */&lt;br /&gt;
    public Flight getFlight() {&lt;br /&gt;
        return flight;&lt;br /&gt;
    }&lt;/p&gt;

&lt;p&gt;}&lt;/p&gt;

&lt;p&gt;[/cc]
&lt;ul&gt;
&lt;li&gt;@BsonDocument marks this bean to be processed by bason processor. Serialization and deserialization support for this bean will be added to the manager. The bean must follow the Java Bean specification that has a getter and a setter for each property.&lt;/li&gt;
&lt;li&gt;@BsonAlias on the getter allows user the specify a name for bson document instead of the default java bean property name.&lt;/li&gt;
&lt;li&gt;@BsonIgnore on the getter marks a property to be transient when serialization and deserialization.&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;Then you need a &lt;i&gt;bason.properties&lt;/i&gt; at the root of classpath which looks like&lt;br /&gt;
[cc lang="properties"]&lt;br /&gt;
bason.managerClassName=info.sunng.bason.BasonManager&lt;br /&gt;
[/cc]&lt;br /&gt;
You specify the manager class name here. This name can not be duplicated if you use Bason in multiple modules.&lt;/p&gt;

&lt;p&gt;Take maven configuration as an example:&lt;br /&gt;
[cc lang="xml"]&lt;br /&gt;
    &lt;dependencies&gt;
        &lt;dependency&gt;
            &lt;groupid&gt;info.sunng.bason&lt;/groupid&gt;
            &lt;artifactid&gt;bason-annotation&lt;/artifactid&gt;
            &lt;scope&gt;compile&lt;/scope&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupid&gt;info.sunng.bason&lt;/groupid&gt;
            &lt;artifactid&gt;bason-internal&lt;/artifactid&gt;
            &lt;scope&gt;compile&lt;/scope&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupid&gt;org.mongodb&lt;/groupid&gt;
            &lt;artifactid&gt;mongo-java-driver&lt;/artifactid&gt;
        &lt;/dependency&gt;
    &lt;/dependencies&gt;&lt;/p&gt;

&lt;p&gt; &lt;build&gt;
        &lt;finalname&gt;${project.artifactId}&lt;/finalname&gt;&lt;/build&gt;&lt;/p&gt;

&lt;p&gt;     &lt;plugins&gt;
            &lt;plugin&gt;
                &lt;groupid&gt;org.apache.maven.plugins&lt;/groupid&gt;
                &lt;artifactid&gt;maven-compiler-plugin&lt;/artifactid&gt;&lt;/plugin&gt;&lt;/plugins&gt;&lt;/p&gt;

&lt;p&gt;             &lt;configuration&gt;
                    &lt;source&gt;1.6&lt;/source&gt;
                    &lt;target&gt;1.6&lt;/target&gt;&lt;/configuration&gt;&lt;/p&gt;

&lt;p&gt;                 &lt;compilerarguments&gt;
                        &lt;processor&gt;info.sunng.bason.internal.BasonProcessor&lt;/processor&gt;
                    &lt;/compilerarguments&gt;




[/cc]&lt;/p&gt;

&lt;p&gt; when everything is ready, run &lt;i&gt;mvn compile&lt;/i&gt; to generate the manager source file. By default, in a standard maven project, the generated file will be placed at:
&lt;i&gt;/bason-example/target/generated-sources/annotations/&lt;/i&gt;
[cc lang="java"]&lt;br /&gt;
package info.sunng.bason;&lt;br /&gt;
import org.bson.*;&lt;br /&gt;
import javax.annotation.Generated;&lt;br /&gt;
@Generated({"info.sunng.bason.BasonManager"})&lt;br /&gt;
public final class BasonManager{&lt;br /&gt;
    public static final BSONObject toBson(info.sunng.bason.example.Passenger o){&lt;br /&gt;
        if (o == null) {&lt;br /&gt;
            throw new NullPointerException();&lt;br /&gt;
        }&lt;br /&gt;
        BSONObject bson = new BasicBSONObject();&lt;br /&gt;
        bson.put("ticket",o.getTicketId());&lt;br /&gt;
        bson.put("name",o.getName());&lt;br /&gt;
        bson.put("createdDate",o.getCreatedDate());&lt;br /&gt;
        bson.put("flight",toBson(o.getFlight()));&lt;br /&gt;
        return bson;&lt;br /&gt;
    }&lt;br /&gt;
    public static final info.sunng.bason.example.Passenger fromBson(info.sunng.bason.example.Passenger o, BSONObject bson){&lt;br /&gt;
        if (o == null || bson == null) {&lt;br /&gt;
            throw new NullPointerException();&lt;br /&gt;
        }&lt;br /&gt;
        o.setTicketId((java.lang.Long)bson.get("ticket"));&lt;br /&gt;
        o.setName((java.lang.String)bson.get("name"));&lt;br /&gt;
        o.setCreatedDate((java.util.Date)bson.get("createdDate"));&lt;br /&gt;
        o.setFlight(fromBson(new info.sunng.bason.example.Flight(), (BSONObject)bson.get("flight")));&lt;br /&gt;
        return o;&lt;br /&gt;
    }&lt;br /&gt;
    public static final BSONObject toBson(info.sunng.bason.example.Flight o){&lt;br /&gt;
        if (o == null) {&lt;br /&gt;
            throw new NullPointerException();&lt;br /&gt;
        }&lt;br /&gt;
        BSONObject bson = new BasicBSONObject();&lt;br /&gt;
        bson.put("company",o.getCompany());&lt;br /&gt;
        bson.put("flightId",o.getFlightId());&lt;br /&gt;
        return bson;&lt;br /&gt;
    }&lt;br /&gt;
    public static final info.sunng.bason.example.Flight fromBson(info.sunng.bason.example.Flight o, BSONObject bson){&lt;br /&gt;
        if (o == null || bson == null) {&lt;br /&gt;
            throw new NullPointerException();&lt;br /&gt;
        }&lt;br /&gt;
        o.setCompany((java.lang.String)bson.get("company"));&lt;br /&gt;
        o.setFlightId((java.lang.String)bson.get("flightId"));&lt;br /&gt;
        return o;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;/p&gt;

&lt;p&gt;[/cc]&lt;/p&gt;

&lt;p&gt;The project is hosted at
&lt;a href="http://github.com/sunng87/bason"&gt;http://github.com/sunng87/bason&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you have any ideas, just let me know.&lt;/p&gt;

&lt;p&gt;The post is brought to you by &lt;a href="http://fedorahosted.org/lekhonee"&gt;lekhonee&lt;/a&gt; v0.7&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Introduction to Amoeba</title><link href="https://sunng.info/blog/introduction-to-amoeba.html" rel="alternate"></link><published>2010-08-19T00:00:00+08:00</published><updated>2010-08-19T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-08-19:/blog/introduction-to-amoeba.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;amoeba&lt;/li&gt;
&lt;li&gt;mysql
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Amoeba is a distributed database middleware works as mysql proxy, provides sharding and high availability support for large scale applications using multiple mysql servers as backend.&lt;/p&gt;

&lt;p&gt;Compatible with mysql protocol, Amoeba is fully transparent to any client using standard mysql drivers, which …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;amoeba&lt;/li&gt;
&lt;li&gt;mysql
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Amoeba is a distributed database middleware works as mysql proxy, provides sharding and high availability support for large scale applications using multiple mysql servers as backend.&lt;/p&gt;

&lt;p&gt;Compatible with mysql protocol, Amoeba is fully transparent to any client using standard mysql drivers, which means, to use Amoeba you don't have to modify any code of database connector. You just configure rules for amoeba, then the client request will automatically route to certain mysql instance. Amoeba has a flexible set of configuration rules that can satisfy your requirements.&lt;/p&gt;

&lt;p&gt;Amoeba is written in Java, and deployed on Linux server in most cases. The IO module is built on the top of Java nonblocking IO, which keeps communication between client, Amoeba and mysql at a high performance.&lt;/p&gt;

&lt;p&gt;The project is initialized in 2008. Stable release 1.2.1-GA have been available since July, 2010 . It is now serving on the production environment of the social networks &lt;a href="http://t.sdo.com/"&gt;http://t.sdo.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Project home:
&lt;a href="http://code.google.com/p/amoeba/"&gt;http://code.google.com/p/amoeba/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Development logs:
&lt;a href="http://amoeba.meidusa.com/wordpress/"&gt;http://amoeba.meidusa.com/wordpress/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The post is brought to you by &lt;a href="http://fedorahosted.org/lekhonee"&gt;lekhonee&lt;/a&gt; v0.7&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>geb for browser functional testing</title><link href="https://sunng.info/blog/geb-for-browser-functional-testing.html" rel="alternate"></link><published>2010-08-17T00:00:00+08:00</published><updated>2010-08-17T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-08-17:/blog/geb-for-browser-functional-testing.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;groovy&lt;/li&gt;
&lt;li&gt;Testing&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;虽然现在不做前段了，但是发现好的工具还是很兴奋。今天在twitter上看到Grails in Action的作者 @pledbrook 转了一个geb 0.4的消息，顺带看了 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;groovy&lt;/li&gt;
&lt;li&gt;Testing&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;虽然现在不做前段了，但是发现好的工具还是很兴奋。今天在twitter上看到Grails in Action的作者 @pledbrook 转了一个geb 0.4的消息，顺带看了一下这个工具&lt;br /&gt;
http://geb.codehaus.org&lt;/p&gt;

&lt;p&gt;geb项目旨在创造一套groovy dsl帮助人们进行webapp的functional test。它是对selenium的封装，举例：&lt;br /&gt;
[cc lang="groovy"]&lt;br /&gt;
@Grapes([&lt;br /&gt;
    @Grab('org.seleniumhq.selenium:selenium-firefox-driver:latest.release'),&lt;br /&gt;
    @Grab('org.codehaus.geb:geb-core:latest.release')&lt;br /&gt;
])&lt;br /&gt;
import geb.*&lt;/p&gt;

&lt;p&gt;println "Dependencies downloaded, ready for testing"&lt;br /&gt;
Browser.drive('http://sunng.info:8000/Pacajus'){&lt;br /&gt;
    assert title== 'Pacajus'&lt;/p&gt;

&lt;p&gt;    assert $("p", 3).text() == 'Population: 41558'&lt;br /&gt;
}
println "Tested, bye"&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;打开页面，执行断言。如果断言失败，driver方法会报null：&lt;br /&gt;
Caught: geb.error.DriveException: null&lt;/p&gt;

&lt;p&gt;只要在命令行用groovy执行即可，grapes会搞定依赖关系。&lt;/p&gt;

&lt;p&gt;很方便吧，文档上说还可以跟grails / junit等等集成，快去看看吧
&lt;a href="http://geb.codehaus.org/manual/latest/index.html"&gt;http://geb.codehaus.org/manual/latest/index.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The post is brought to you by &lt;a href="http://fedorahosted.org/lekhonee"&gt;lekhonee&lt;/a&gt; v0.7&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Next to Richard Stallman</title><link href="https://sunng.info/blog/next-to-richard-stallman.html" rel="alternate"></link><published>2010-08-13T00:00:00+08:00</published><updated>2010-08-13T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-08-13:/blog/next-to-richard-stallman.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;linux
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;p&gt;我这个人一般不八卦，你知道的。但是个别时候实在是情难以堪。&lt;/p&gt;
&lt;p&gt;Bug 626593 - Gnome ate my boyfriend! Help!&lt;br /&gt;
&lt;a href="https://bugzilla.gnome.org/show_bug.cgi?id=626593"&gt;https://bugzilla.gnome.org/show_bug.cgi?id=626593 …&lt;/a&gt;&lt;/p&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;linux
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;p&gt;我这个人一般不八卦，你知道的。但是个别时候实在是情难以堪。&lt;/p&gt;
&lt;p&gt;Bug 626593 - Gnome ate my boyfriend! Help!&lt;br /&gt;
&lt;a href="https://bugzilla.gnome.org/show_bug.cgi?id=626593"&gt;https://bugzilla.gnome.org/show_bug.cgi?id=626593&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;这位女士在GNOME bugzilla上提交bug，bug提交在empathy项目下。（注 dict empathy: 心意相通）&lt;/p&gt;
&lt;p&gt;先不看正文，备注信息就极具幽默感：&lt;br /&gt;
Status:      RESOLVED INVALID&lt;br /&gt;
Product:    empathy&lt;br /&gt;
Component:  User Guide&lt;br /&gt;
Version:    unspecified&lt;br /&gt;
OS:     Windows&lt;br /&gt;
Importance:     Normal critical &lt;/p&gt;
&lt;p&gt;是不是真的RESOLVED就不知道了。&lt;/p&gt;
&lt;p&gt;事情的直接原因是上周海牙GUADEC，此男cancel了周末和这位reporter的约会。此女不堪长期忍受此男、Linux、Maemo、C等等，彻底爆发了。&lt;/p&gt;
&lt;p&gt;He even tried to put Linux on my computer and I simply could not take it.  I came home from work one day and my computer said "UNIX" all over it!&lt;br /&gt;
这句有力的证明了我曾经看到的一句箴言，给女生装linux，对她们、对电脑、对linux都是一种折磨。&lt;/p&gt;
&lt;p&gt;接下来搞笑开始了。一楼就是一位极缺乏幽默感的大叔：&lt;br /&gt;
Akhil Laddha      2010-08-11 06:45:46 UTC&lt;br /&gt;
This is a GNOME bug tracking system, not any family consultancy.&lt;/p&gt;
&lt;p&gt;二楼是位好心人，提出了一个Linux week计划，还一厢情愿整了个交易计划：&lt;br /&gt;
for each bug you report, he has to spend one night with you without the computer.&lt;/p&gt;
&lt;p&gt;八楼精华：&lt;br /&gt;
 David Liang      2010-08-12 08:53:16 UTC&lt;br /&gt;
不得不顶&lt;/p&gt;
&lt;p&gt;九楼亮出一个家属俱乐部的邮件列表：&lt;br /&gt;
gnome-women-list@gnome.org，说你要是在家属圈子里麻将打得无敌手，自然你的boyfriend就崇拜你了，到时你就是爷了&lt;/p&gt;
&lt;p&gt;高潮在十楼出现了，“你不说这男的是谁我们怎么帮你？”&lt;/p&gt;
&lt;p&gt;紧接着十一楼一位神秘人士笑而不语。&lt;/p&gt;
&lt;p&gt;十四楼比较直接&lt;br /&gt;
#apt-get remove boyfriend --purge&lt;/p&gt;
&lt;p&gt;十九楼知道宁拆十座庙的道理，建议先查看一下情况再说：&lt;br /&gt;
cat /dev/boyfriend | grep love&lt;/p&gt;
&lt;p&gt;最冷的是十六楼说咱们有这么个项目的：&lt;br /&gt;
&lt;a href="http://projects.gnome.org/outreach/women/"&gt;http://projects.gnome.org/outreach/women/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;你以为故事就这么结束了，你错了。&lt;br /&gt;
没想到男主人公的网站被我不经意间人肉出来了：&lt;br /&gt;
&lt;a href="http://zachgoldberg.com"&gt;http://zachgoldberg.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;一上来第一篇就说这事：&lt;br /&gt;
&lt;a href="http://zachgoldberg.com/2010/08/12/help-my-girlfriend-learned-how-to-use-a-bug-tracker/"&gt;HELP! My Girlfriend Learned How To Use A Bug Tracker&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I never thought it would happen.&lt;/p&gt;
&lt;p&gt;此男字字珠玑：&lt;br /&gt;
It all started one night when I got home very late from work (where I get to play with Linux all day… who would ever go home?).  I got the usual “you need to pay more attention to me” and “Linux will never have sex with you!”.  I sat through it all and when it was over she went to sleep and I…. opened up my Laptop (running Ubuntu Linux, of course) and started hacking.  All is right with the world.&lt;/p&gt;
&lt;p&gt;于是他发现自己上了LWN的Quote of the Week，和RMS的新闻并列，然后又被转载到hacker news, geek.com, slashdot。他感叹道 I never imagined my entry into the “slashdot number of zero” (think Erdos or Kevin Bacon) club would happen in this way.&lt;/p&gt;
&lt;p&gt;最后他说他要开一个bugs.zachgoldberg.com专门解决这类的问题，If you want your own component in the bugtracker to help you and your loved one vent your problems all you need to do is ask!&lt;/p&gt;
&lt;p&gt;哈哈哈哈&lt;/p&gt;
&lt;p&gt;补充个精华链接：http://www.reddit.com/r/linux/comments/d02j6/bug_626593_gnome_ate_my_boyfriend/&lt;/p&gt;
&lt;p&gt;最后daf同学总结性地指出，她不会学习使用bug tracker。&lt;/p&gt;
&lt;p&gt;The post is brought to you by &lt;a href="http://fedorahosted.org/lekhonee"&gt;lekhonee&lt;/a&gt; v0.7&lt;/p&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>beanstalkd</title><link href="https://sunng.info/blog/beanstalkd.html" rel="alternate"></link><published>2010-08-11T00:00:00+08:00</published><updated>2010-08-11T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-08-11:/blog/beanstalkd.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;opensource
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://kr.github.com/beanstalkd/"&gt;beanstalkd&lt;/a&gt;是一个极轻量级的消息队列服务，作者的说法叫做Work Queue。&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;概念&lt;/h3&gt;&lt;/p&gt;

&lt;p&gt;Beanstalkd里包含以下几个概念：
&lt;ul&gt;
&lt;li&gt;Producer&lt;/li&gt;
&lt;li&gt;Worker&lt;/li&gt;
&lt;li&gt;Job&lt;/li&gt;
&lt;li&gt;Tube&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;Producer 与传统的消息队列服 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;opensource
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://kr.github.com/beanstalkd/"&gt;beanstalkd&lt;/a&gt;是一个极轻量级的消息队列服务，作者的说法叫做Work Queue。&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;概念&lt;/h3&gt;&lt;/p&gt;

&lt;p&gt;Beanstalkd里包含以下几个概念：
&lt;ul&gt;
&lt;li&gt;Producer&lt;/li&gt;
&lt;li&gt;Worker&lt;/li&gt;
&lt;li&gt;Job&lt;/li&gt;
&lt;li&gt;Tube&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;Producer 与传统的消息队列服务中的概念类似，是Job的生产者。这个角色通过put命令来创建Job。&lt;/p&gt;

&lt;p&gt;Worker 即消费者，worker通过reserve / release / bury / delete 等命令操作job的生命周期。&lt;/p&gt;

&lt;p&gt;Job 是beanstalkd中的基本单元，一个job包含id和body，从属于一个tube。Job的生命周期是beanstalkd中的核心概念，它包含DELAYED / READY / RESERVED / BURIED / DELETED等状态。beanstalkd文档中的这个图对Job的声明周期进行了说明：
&lt;a href="http://github.com/kr/beanstalkd/blob/master/doc/protocol.txt#L81"&gt;http://github.com/kr/beanstalkd/blob/master/doc/protocol.txt#L81&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tube 是类似namespace的概念，Producer在生产Job前通过use指定相应的Tube。而Worker通过watch / Ignore命令来决定从哪些Tube中获得Job。&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;安装&lt;/h3&gt;&lt;/p&gt;

&lt;p&gt;Beanstalkd 可以通过很多Linux发行版的包管理器直接安装。依赖libevent 1.4.1 以上版本。&lt;br /&gt;
通过以下命令启动即可&lt;/p&gt;

&lt;p&gt;&lt;i&gt;beanstalkd -d -p &amp;lt;PORT&amp;gt;&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;协议&lt;/h3&gt;
Beanstalkd在各方面都继承memcached的风格，协议也与memcached类似，同样是基于文本的：&lt;/p&gt;

&lt;p&gt;命令 参数 [参数...] [命令体字节数]\r\n&lt;br /&gt;
[命令体]\r\n&lt;/p&gt;

&lt;p&gt;beanstalkd很多命令的返回是yaml格式，但是系统对命令体的格式并没有限制。&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;客户端&lt;/h3&gt;&lt;/p&gt;

&lt;p&gt;Beanstalkd协议见简单，有各种语言的客户端实现。python有一个非常简单的beanstalkc，可以通过easy_install安装。不过，这个客户端缺少断线重连机制，正式发布的0.2.0版本也有一些严重的bug。可以从github上下载源代码安装，并在使用时控制重连。&lt;/p&gt;

&lt;p&gt;The post is brought to you by &lt;a href="http://fedorahosted.org/lekhonee"&gt;lekhonee&lt;/a&gt; v0.7&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>近况1</title><link href="https://sunng.info/blog/jin-kuang-1.html" rel="alternate"></link><published>2010-08-09T00:00:00+08:00</published><updated>2010-08-09T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-08-09:/blog/jin-kuang-1.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags: []
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;ol&gt;
&lt;li&gt;Exaile豆瓣电台插件发布一个多月，github和googlecode的下载量加载一起大约有190次左右。考虑的这两个用户群的交集有限，这个数字应该算是超乎期望了，呵呵。等下载量超过500的时候，我。。。&lt;/li&gt;
&lt;li&gt;本来计划 …&lt;/li&gt;&lt;/ol&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags: []
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;ol&gt;
&lt;li&gt;Exaile豆瓣电台插件发布一个多月，github和googlecode的下载量加载一起大约有190次左右。考虑的这两个用户群的交集有限，这个数字应该算是超乎期望了，呵呵。等下载量超过500的时候，我。。。&lt;/li&gt;
&lt;li&gt;本来计划三周做完的项目，后台服务调用的中间件，做了刚一周整个流程刚跑通就被砍了。最近工作得很不顺心，公司各种产品之间结构松散，模型各不相同，想要all in one谈何容易。不愉快就容易萌生去意，可是看了JavaEye robbin的&lt;a href="http://robbin.javaeye.com/blog/730223"&gt;这篇&lt;/a&gt;，就彷徨了。&lt;/li&gt;
&lt;li&gt;上面一节原本写了不少，但考虑到说出来不太合适，就删了。你懂的。&lt;/li&gt;
&lt;li&gt;最近听豆瓣电台非常喜欢苏阳、野孩子这些西北味的歌，我毕竟还是干旱地区的人，听着就有共鸣。苏阳的&lt;a href="http://www.douban.com/artist/suyang/download?song_id=10027"&gt;长在银川&lt;/a&gt;我很喜欢，我喜欢这种带着地名的歌。有西北的味道，能让我想起小时候。“我的家住在，&lt;a href="http://j.map.baidu.com/8n8f"&gt;同心路&lt;/a&gt;边上” ，如果有机会，能去考察一下就太他妈好了。&lt;/li&gt;
&lt;li&gt;今天图灵把&lt;a href="http://www.turingbook.com/Data/Newswire/d3aaad02-1af3-47df-8e87-d06e0a88672e/Asset/Newsletter_201008.html"&gt;八月份的新书&lt;/a&gt;列出来，发现这么一本《我编程，我快乐》。本来评价不错的书，起这么个名字，你说谁还敢买，买了还不被同事鄙视死。编辑同志您做到了！&lt;/li&gt;
&lt;li&gt;最近某公司的电子书宣传力度很大，连&lt;a href="http://www.cnbeta.com/articles/118806.htm"&gt;CEO都请自上阵&lt;/a&gt;的（请注意右侧第二条）。我突然想买个Kindle，上周五在Amazon.com上订了到了最后一步，结果是地址不能邮寄。看来还得想其他办法，今天貌似关于国际网购邮寄有了新的&lt;a href="http://finance.ce.cn/rolling/201008/09/t20100809_16064224.shtml"&gt;规定&lt;/a&gt;。&lt;/li&gt;
&lt;/ol&gt;&lt;/p&gt;

&lt;p&gt;The post is brought to you by &lt;a href="http://fedorahosted.org/lekhonee"&gt;lekhonee&lt;/a&gt; v0.7&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Exaile-doubanfm-plugin for exaile 0.3.2</title><link href="https://sunng.info/blog/exaile-doubanfm-plugin-for-exaile-032.html" rel="alternate"></link><published>2010-08-03T00:00:00+08:00</published><updated>2010-08-03T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-08-03:/blog/exaile-doubanfm-plugin-for-exaile-032.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;Douban&lt;/li&gt;
&lt;li&gt;exaile
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Ubuntu 10.04的Exaile还停留在0.3.1，而Fedora 13上已经升级到0.3.2了。为此我更新了豆瓣电台插件，现在这个版本可以在0.3.2上正常工作。&lt;/p&gt;

&lt;p&gt;可以到github的下载页面获取 doubanfm-0.0.3a-exaile032.tar …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;Douban&lt;/li&gt;
&lt;li&gt;exaile
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Ubuntu 10.04的Exaile还停留在0.3.1，而Fedora 13上已经升级到0.3.2了。为此我更新了豆瓣电台插件，现在这个版本可以在0.3.2上正常工作。&lt;/p&gt;

&lt;p&gt;可以到github的下载页面获取 doubanfm-0.0.3a-exaile032.tar.gz
&lt;a href="http://github.com/sunng87/exaile-doubanfm-plugin/downloads"&gt;http://github.com/sunng87/exaile-doubanfm-plugin/downloads&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;此外for exaile 0.3.1的版本也有一个优化和bugfix release, Ubuntu用户可以获取&lt;br /&gt;
doubanfm-0.0.3-exaile031.tar.gz&lt;/p&gt;

&lt;p&gt;接下来三周又有项目了，难度比较大，估计豆瓣插件近期不会有什么新的惊喜了。&lt;/p&gt;

&lt;p&gt;The post is brought to you by &lt;a href="http://fedorahosted.org/lekhonee"&gt;lekhonee&lt;/a&gt; v0.7&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>sunng's pastebin</title><link href="https://sunng.info/blog/sunngs-pastebin.html" rel="alternate"></link><published>2010-07-31T00:00:00+08:00</published><updated>2010-07-31T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-07-31:/blog/sunngs-pastebin.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;gae&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;写了一个基本的pastebin放在appengine上：
&lt;a href="http://sunoffline.appspot.com/pb/"&gt;http://sunoffline.appspot.com/pb/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;支持纯文本、Markdown和代码高亮。数据永久保留，推荐大家收藏以备不时之需。&lt;/p&gt;

&lt;p&gt;The post is brought to …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;gae&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;写了一个基本的pastebin放在appengine上：
&lt;a href="http://sunoffline.appspot.com/pb/"&gt;http://sunoffline.appspot.com/pb/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;支持纯文本、Markdown和代码高亮。数据永久保留，推荐大家收藏以备不时之需。&lt;/p&gt;

&lt;p&gt;The post is brought to you by &lt;a href="http://fedorahosted.org/lekhonee"&gt;lekhonee&lt;/a&gt; v0.7&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>用markdown书写文档</title><link href="https://sunng.info/blog/yong-markdownshu-xie-wen-dang.html" rel="alternate"></link><published>2010-07-26T00:00:00+08:00</published><updated>2010-07-26T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-07-26:/blog/yong-markdownshu-xie-wen-dang.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;linux
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;写文档是头疼事，没人愿意写文档。在word里写文档，时间长了都怀疑自己是不是搞技术的。我阅历有限，在我的印 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;linux
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;写文档是头疼事，没人愿意写文档。在word里写文档，时间长了都怀疑自己是不是搞技术的。我阅历有限，在我的印象里就没有用word格式看过什么有价值的东西。只要一打开word想到的就是连篇累牍的废话、码字。word最重要的功能是什么？保存。其次呢？字数统计。我所写过的word文档绝大多数都是“只写”的，通常作为流程里的一个附件，没有人真正去看。&lt;/p&gt;

&lt;p&gt;以上是人身攻击，接下来比较实际的问题，word文档不是符合unix哲学的东西。格式不开放，你就没有办法进行diff操作，把word文档放到svn里，只能使用最基本的版本控制，没法查看changeset，只知道改了，不知道改了什么。&lt;/p&gt;

&lt;p&gt;为了改变这种情况，我试过用&lt;a href="http://www.docbook.org/"&gt;docbook&lt;/a&gt;格式。docbook用xml书写，定义了一套复杂的Schema，详细到作者的email都有定义。docbook还有丰富的工具集，可以通过xsl把docbook转换成所有你知道的文档格式。&lt;a href="http://www.sonatype.com/products/maven/documentation/book-defguide"&gt;Maven: the Definitive Guide&lt;/a&gt;就是用docbook写成的。不过用docbook也存在一些问题，docbook太复杂了，用纯文本编辑器很难处理，作者的学习曲线也比较高，需要所见即所得的编辑器支持。与docbook相类似的DITA，也存在这样的问题，它们是重量级的格式。&lt;/p&gt;

&lt;p&gt;轻量级的Wiki格式不错，但是Wiki格式很让人头疼就是没有统一的规范。举例，dokuwiki里顶级标题是6个=，而moinmoin里顶级标题恰恰相反是一个=，不portable，文档维护起来就非常麻烦。&lt;/p&gt;

&lt;p&gt;铺垫了这么多，委屈以上格式了，该&lt;a href="http://daringfireball.net/projects/markdown/"&gt;markdown&lt;/a&gt;出场了。markdown是简单的html原型，用来生成html，它的设计目标就是为了KISS，兼容html。看看一些必要的格式吧：（或者直接看&lt;a href="http://en.wikipedia.org/wiki/Markdown"&gt;Wikipedia&lt;/a&gt;）&lt;br /&gt;
标题&lt;br /&gt;
# 标题&lt;br /&gt;
## 二级标题&lt;br /&gt;
...&lt;br /&gt;
###### 六级标题&lt;/p&gt;

&lt;p&gt;对于一级标题还可以这么写&lt;br /&gt;
headline&lt;br /&gt;
========&lt;br /&gt;
二级标题可以&lt;br /&gt;
headline&lt;br /&gt;
--------&lt;br /&gt;
这个怎么输入呢，我想起来前几天看vim hacks里的一组快捷键&lt;br /&gt;
yypoVr=&lt;br /&gt;
yypoVr-&lt;br /&gt;
谁用谁知道&lt;/p&gt;

&lt;p&gt;段落：&lt;br /&gt;
一段文本以两个换行结束。&lt;br /&gt;
换行：&lt;br /&gt;
一行文本行末两个空格。&lt;/p&gt;

&lt;p&gt;图片&lt;br /&gt;
![alternative text](image-url "image-title")&lt;br /&gt;
用markdown，图片的alt你不写都不行。&lt;/p&gt;

&lt;p&gt;链接&lt;br /&gt;
[Linktext](link "linktitle")&lt;/p&gt;

&lt;p&gt;列表&lt;br /&gt;
ul 无序列表&lt;br /&gt;
*
*&lt;br /&gt;
ol 有序列表&lt;br /&gt;
1.&lt;br /&gt;
2.&lt;/p&gt;

&lt;p&gt;以上就是主要的格式支持。用标题来划分文档层次，没有多余的格式，没有机会让你五颜六色。&lt;/p&gt;

&lt;p&gt;在linux上可以安装markdown的处理脚本：&lt;br /&gt;
apt-get install markdown&lt;br /&gt;
安装vim的语法文件：&lt;br /&gt;
http://www.vim.org/scripts/script.php?script_id=1242&lt;br /&gt;
这里是一个简单的例子：&lt;br /&gt;
http://github.com/sunng87/exaile-doubanfm-plugin/blob/master/README.mkd&lt;/p&gt;

&lt;p&gt;The post is brought to you by &lt;a href="http://fedorahosted.org/lekhonee"&gt;lekhonee&lt;/a&gt; v0.7&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>bottle &amp; fapws3</title><link href="https://sunng.info/blog/bottle-fapws3.html" rel="alternate"></link><published>2010-07-21T00:00:00+08:00</published><updated>2010-07-21T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-07-21:/blog/bottle-fapws3.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;bottle&lt;/li&gt;
&lt;li&gt;fapws3&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;wsgi
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://github.com/defnull/bottle"&gt;Bottle&lt;/a&gt;是一个Python web框架，兼容&lt;a href="http://www.python.org/dev/peps/pep-0333"&gt;wsgi&lt;/a&gt;标准，lightweight，self-contained。&lt;/p&gt;

&lt;p&gt;提到web框架，自然要和相类似的python框架相比。&lt;/p&gt;

&lt;p&gt;Django是大型框架，包含ORM、Controller、Templating全套，这也是Django的缺点，使用Django意味着必须使用关系型数据库进行存储（尽 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;bottle&lt;/li&gt;
&lt;li&gt;fapws3&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;wsgi
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://github.com/defnull/bottle"&gt;Bottle&lt;/a&gt;是一个Python web框架，兼容&lt;a href="http://www.python.org/dev/peps/pep-0333"&gt;wsgi&lt;/a&gt;标准，lightweight，self-contained。&lt;/p&gt;

&lt;p&gt;提到web框架，自然要和相类似的python框架相比。&lt;/p&gt;

&lt;p&gt;Django是大型框架，包含ORM、Controller、Templating全套，这也是Django的缺点，使用Django意味着必须使用关系型数据库进行存储（尽管有一些Model层的其他实现，但绝大多数都是Hack的方式实现），必须使用Django并不非常出色的Template系统。Pylons针对Django的这些问题，采用了松散的方式，数据层可选择由SQLAlchemy实现，模板系统可以选择mako / jinja等。Pylons用paster来管理项目、创建代码模板。借鉴了rails的哲学，目录结构也相类似。可是pylons仍然显得重量级，把注意力放到web.py。只要定义一个router，定义相应的handler就可以处理web请求，handler对象的GET POST等方法分别对应相应的HTTP请求。看起来不错了，不过与bottle相比，webpy仍然显得繁琐、功能有限，而且它本身的db模块就更加鸡肋了。&lt;/p&gt;

&lt;p&gt;看一个实例便知：&lt;br /&gt;
定义一个简单的HTTP页面：&lt;br /&gt;
[cc lang="python"]&lt;br /&gt;
from bottle import Bottle, run, mako_view, request&lt;br /&gt;
from bottle import FapwsServer&lt;/p&gt;

&lt;p&gt;myapp = Bottle()&lt;/p&gt;

&lt;p&gt;@myapp.route('/nihao/:name/:count#\\d+#')&lt;br /&gt;
@mako_view('nihao')&lt;br /&gt;
def nihao(name, count):&lt;br /&gt;
    return dict(n=name, c=int(count), ip=request.environ.get('REMOTE_ADDR'))&lt;/p&gt;

&lt;p&gt;run(app=myapp, server=FapwsServer)&lt;/p&gt;

&lt;p&gt;[/cc]&lt;br /&gt;
对应的nihao.tpl模板，用mako引擎实现：&lt;br /&gt;
[cc lang="html"]
&lt;html&gt;
&lt;head&gt;
    &lt;meta http-equiv="content-type" content="text/html; charset=utf-8" /&gt;&lt;/head&gt;&lt;/html&gt;&lt;/p&gt;

&lt;p&gt;    &lt;title&gt;Nihao&lt;/title&gt;&lt;/p&gt;

&lt;p&gt;
&lt;body&gt;
    &lt;div id="ip"&gt;
        &lt;h1 id="heading"&gt;Request From ${ip}&lt;/h1&gt;
    &lt;/div&gt;
    &lt;%def name="greeting(n)"&gt;
        &lt;div id="name"&gt;
            Nihao ${n}&lt;br /&gt;
        &lt;/div&gt;
    &lt;!--%def--&gt;
    % for i in range(c):&lt;br /&gt;
        ${greeting(n)}&lt;br /&gt;
    % endfor
&lt;/body&gt;

[/cc]&lt;br /&gt;
仅仅是一些简单的内容，接着只需要：
&lt;i&gt;python bottle-test.py&lt;/i&gt;
即可运行服务器。&lt;/p&gt;

&lt;p&gt;bottle通过decorator定义route规则，还支持url提取参数。通过decorator指定模板、模板引擎。可以说近乎简化到了极致。&lt;/p&gt;

&lt;p&gt;bottle遵循单一职责原则，不提供数据层的实现，由用户自己指定，bottle没有任何限制。模板引擎，bottle支持mako / jinja / cheetah，本身还内建一个默认SimpleTemplate引擎。bottle还支持多种wsgi服务器，包括flup / wsgiref / cherrypy / paste / twisted / tornado / fapws3 等等。&lt;/p&gt;

&lt;p&gt;最后提一个wsgi的实现&lt;a href="http://github.com/william-os4y/fapws3"&gt;fapws3&lt;/a&gt;，号称是目前最快的wsgi服务器。fapws3用libev实现，在不同的操作系统上采用不同的多路IO模型以达到高性能。&lt;/p&gt;

&lt;p&gt;bottle的作者做过一个关于不同实现的性能比较：
&lt;a href="http://bottle.paws.de/page/2009-12-19_Comparing_HelloWorld_Performance"&gt;http://bottle.paws.de/page/2009-12-19_Comparing_HelloWorld_Performance&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The post is brought to you by &lt;a href="http://fedorahosted.org/lekhonee"&gt;lekhonee&lt;/a&gt; v0.7&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Bayeux Protocol</title><link href="https://sunng.info/blog/bayeux-protocol.html" rel="alternate"></link><published>2010-07-17T00:00:00+08:00</published><updated>2010-07-17T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-07-17:/blog/bayeux-protocol.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;bayuex&lt;/li&gt;
&lt;li&gt;cometd&lt;/li&gt;
&lt;li&gt;http&lt;/li&gt;
&lt;li&gt;websocket
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;运行一个CometD Demo非常简单，只要创建一个Maven项目即可（&lt;a href="http://cometd.org/documentation/howtos/primer"&gt;CometD Howtos&lt;/a&gt;）：
&lt;i&gt;$ mvn archetype:generate -DarchetypeCatalog=http://cometd.org&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;maven会提示用户选择archetype，包括cometd的版本1、版本2，jetty6、jetty7的实现，以及客户端dojo或jquery的实现。这里 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;bayuex&lt;/li&gt;
&lt;li&gt;cometd&lt;/li&gt;
&lt;li&gt;http&lt;/li&gt;
&lt;li&gt;websocket
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;运行一个CometD Demo非常简单，只要创建一个Maven项目即可（&lt;a href="http://cometd.org/documentation/howtos/primer"&gt;CometD Howtos&lt;/a&gt;）：
&lt;i&gt;$ mvn archetype:generate -DarchetypeCatalog=http://cometd.org&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;maven会提示用户选择archetype，包括cometd的版本1、版本2，jetty6、jetty7的实现，以及客户端dojo或jquery的实现。这里可以选择最新的：
&lt;i&gt;http://cometd.org -&gt; cometd-archetype-dojo-jetty7 (2.0.0 - CometD archetype for creating a server-side event-driven web application)&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;项目创建完成后执行mvn jetty:run即可，打开http://127.0.0.1:8080/{artifactId}即可。&lt;/p&gt;

&lt;p&gt;CometD的协议包容了各种主要的浏览器，比如在Chromium 5上，dojo采用WebSocket实现；而在不支持WebSocket的Firefox 3上，通过long-polling实现。Bayuex是一个应用协议，CometD是Bayuex的实现，类似鸡与蛋的关系。&lt;/p&gt;

&lt;p&gt;有了昨天在Chromium上看WebSocket协议的经验，先看一下CometD的WebSocket实现：&lt;br /&gt;
握手。客户端请求/{artifactId}/cometd/handshake&lt;br /&gt;
包含Header
&lt;blockquote&gt;GET /cometd-jetty/cometd/handshake HTTP/1.1&lt;br /&gt;
Upgrade: WebSocket&lt;br /&gt;
Connection: Upgrade&lt;br /&gt;
Host: 127.0.0.1:8080&lt;br /&gt;
Origin: http://127.0.0.1:8080&lt;br /&gt;
Cookie: JSESSIONID=12jqq6hbsfkfic8vzqpevxtrw
&lt;/blockquote&gt;&lt;/p&gt;

&lt;p&gt;这是标准的WebSocket握手协议，服务端返回：&lt;/p&gt;

&lt;p&gt;&lt;blockquote&gt;HTTP/1.1 101 Web Socket Protocol Handshake&lt;br /&gt;
Upgrade: WebSocket&lt;br /&gt;
Connection: Upgrade&lt;br /&gt;
WebSocket-Origin: http://127.0.0.1:8080&lt;br /&gt;
WebSocket-Location: ws://127.0.0.1:8080/cometd-jetty/cometd/handshake&lt;/blockquote&gt;&lt;/p&gt;

&lt;p&gt;双方完成WebSocket连接的建立。客户端通过websocket发送JSON，进行bayuex的握手：&lt;/p&gt;

&lt;p&gt;&lt;blockquote&gt;[{"version":"1.0","minimumVersion":"0.9","channel":"/meta/handshake","supportedConnectionTypes":["websocket","long-polling","callback-polling"],"advice":{"timeout":60000,"interval":0},"id":"1"}]&lt;/blockquote&gt;&lt;/p&gt;

&lt;p&gt;服务端返回JSON，下发clientId完成握手：&lt;/p&gt;

&lt;p&gt;&lt;blockquote&gt;[{"channel":"/meta/handshake","clientId":"8g6dbnlqr2k6jfo1tdpaeb7iw","version":"1.0","successful":true,"minimumVersion":"1.0","id":"1","supportedConnectionTypes":["websocket","long-polling","callback-polling"]}]&lt;/blockquote&gt;&lt;/p&gt;

&lt;p&gt;握手完成，bayuex连接建立。&lt;/p&gt;

&lt;p&gt;在Demo中，客户端添加了一个handshake的listerner&lt;br /&gt;
[cc lang="javascript"]&lt;br /&gt;
    function _metaHandshake(handshake)&lt;br /&gt;
    {&lt;br /&gt;
        if (handshake.successful === true)&lt;br /&gt;
        {&lt;br /&gt;
            cometd.batch(function()&lt;br /&gt;
            {&lt;br /&gt;
                cometd.subscribe('/hello', function(message)&lt;br /&gt;
                {&lt;br /&gt;
                    dojo.byId('body').innerHTML += '&lt;div&gt;Server Says: ' + message.data.greeting + '&lt;/div&gt;';&lt;br /&gt;
                });&lt;br /&gt;
                // Publish on a service channel since the message is for the server only&lt;br /&gt;
                cometd.publish('/service/hello', { name: 'World' });&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
[/cc]&lt;br /&gt;
所以在完成握手后，客户端发送一个批量请求，subscribe /hello频道，并且向/service/hello发送json格式的消息。向/service channel发送的信息&lt;a href="http://svn.cometd.com/trunk/bayeux/bayeux.html#toc_81"&gt;表示客户端与服务端的单独通信&lt;/a&gt;，不会被转发给其他客户端。&lt;br /&gt;
id用于区分每个请求，&lt;a href="http://svn.cometd.com/trunk/bayeux/bayeux.html#toc_38"&gt;bayuex spec规定&lt;/a&gt;向/meta和/service发送的请求必须包含id字段，用于标示请求响应。&lt;br /&gt;
请求的内容最终聚合为一个Json&lt;/p&gt;

&lt;p&gt;&lt;blockquote&gt;[{"channel":"/meta/subscribe","subscription":"/hello","id":"2","clientId":"8g6dbnlqr2k6jfo1tdpaeb7iw"},{"channel":"/service/hello","data":{"name":"World"},"id":"3","clientId":"8g6dbnlqr2k6jfo1tdpaeb7iw"}]&lt;/blockquote&gt;&lt;/p&gt;

&lt;p&gt;服务端发回响应，id=2的请求成功，订阅/hello频道成功&lt;/p&gt;

&lt;p&gt;&lt;blockquote&gt;[{"channel":"/meta/subscribe","successful":true,"id":"2","subscription":"/hello"}]&lt;/blockquote&gt;&lt;/p&gt;

&lt;p&gt;之后，服务端发回/hello channel的消息&lt;/p&gt;

&lt;p&gt;&lt;blockquote&gt;[{"channel":"/hello","data":{"greeting":"Hello, World"}},{"channel":"/service/hello","successful":true,"id":"3"}]&lt;/blockquote&gt;&lt;/p&gt;

&lt;p&gt;客户端还要定期发送连接请求保持连接&lt;/p&gt;

&lt;p&gt;&lt;blockquote&gt;[{"channel":"/meta/connect","connectionType":"websocket","advice":{"timeout":0},"id":"4","clientId":"8g6dbnlqr2k6jfo1tdpaeb7iw"}]&lt;/blockquote&gt;&lt;/p&gt;

&lt;p&gt;服务端返回，连接成功&lt;/p&gt;

&lt;p&gt;&lt;blockquote&gt;[{"channel":"/meta/connect","advice":{"reconnect":"retry","interval":2500,"timeout":15000},"successful":true,"id":"4"}]&lt;/blockquote&gt;&lt;/p&gt;

&lt;p&gt;connect请求是用于在客户端和服务端维持连接， Bayeux标准中提到(&lt;a href="http://svn.cometd.com/trunk/bayeux/bayeux.html#toc_52"&gt;1&lt;/a&gt;, &lt;a href="http://svn.cometd.com/trunk/bayeux/bayeux.html#toc_53"&gt;2&lt;/a&gt;)：&lt;/p&gt;

&lt;p&gt;&lt;blockquote&gt;A transport MUST maintain one and only one outstanding connect message. When a HTTP response that contains a /meta/connect response terminates, the client MUST wait at least the interval specified in the last received advice before following the advice to reestablish the connection &lt;/blockquote&gt;&lt;/p&gt;

&lt;p&gt;&lt;blockquote&gt;
The client MUST maintain only a single outstanding connect message. If the server does not have a current outstanding connect and a connect is not received within a configured timeout, then the server SHOULD act as if a disconnect message has been received. &lt;/blockquote&gt;&lt;/p&gt;

&lt;p&gt;至此，cometd客户端就可以在/hello频道上订阅、发布消息了。&lt;br /&gt;
在Chromium上，所有的操作都在一个WebSocket连接上完成。&lt;/p&gt;

&lt;p&gt;而当断开连接时，客户端向服务端发送&lt;/p&gt;

&lt;p&gt;&lt;blockquote&gt;[{"channel":"/meta/disconnect","id":"188","clientId":"a8iutjvfp7dtwhzrfujeonk5q"}]&lt;/blockquote&gt;&lt;/p&gt;

&lt;p&gt;服务端响应&lt;/p&gt;

&lt;p&gt;&lt;blockquote&gt;[{"channel":"/meta/disconnect","successful":true,"id":"188"}]&lt;/blockquote&gt;&lt;/p&gt;

&lt;p&gt;Bayuex基本上就可以理解为一个websocket上的应用协议了。&lt;/p&gt;

&lt;p&gt;再看看Firefox 3.6上的实现。Firefox 3.6不支持WebSocket，所有的通信只能通过XHR来实现。&lt;br /&gt;
握手，通过一个xhr post请求实现：&lt;/p&gt;

&lt;p&gt;&lt;blockquote&gt;POST /{artifactId}/cometd/handshake HTTP/1.1&lt;br /&gt;
Host: 127.0.0.1:8080&lt;br /&gt;
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.6) Gecko/20100628 Ubuntu/10.04 (lucid) Firefox/3.6.6&lt;br /&gt;
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8&lt;br /&gt;
Accept-Language: en-us,en;q=0.5&lt;br /&gt;
Accept-Encoding: gzip,deflate&lt;br /&gt;
Accept-Charset: UTF-8,*&lt;br /&gt;
Keep-Alive: 115&lt;br /&gt;
Connection: keep-alive&lt;br /&gt;
Content-Type: application/json;charset=UTF-8&lt;br /&gt;
X-Requested-With: XMLHttpRequest&lt;br /&gt;
Referer: http://127.0.0.1:8080/{artifactId}/&lt;br /&gt;
Content-Length: 182&lt;br /&gt;
Cookie: JSESSIONID=fjnyxb28raih1cnaljrijl1ic&lt;br /&gt;
Pragma: no-cache&lt;br /&gt;
Cache-Control: no-cache
&lt;/blockquote&gt;&lt;/p&gt;

&lt;p&gt;服务器端响应：&lt;/p&gt;

&lt;p&gt;&lt;blockquote&gt;HTTP/1.1 200 OK&lt;br /&gt;
Content-Type: application/json;charset=UTF-8&lt;br /&gt;
Set-Cookie: BAYEUX_BROWSER=df92-h8q89f416mutgbpxrwb8185u;Path=/&lt;br /&gt;
Content-Length: 213&lt;br /&gt;
Server: Jetty(7.1.5.v20100705)&lt;/blockquote&gt;&lt;/p&gt;

&lt;p&gt;[{"channel":"/meta/handshake","clientId":"9185k23lo482oq1po3ivxup2cj","version":"1.0","successful":true,"minimumVersion":"1.0","id":"1","supportedConnectionTypes":["websocket","long-polling","callback-polling"]}]&lt;/p&gt;

&lt;p&gt;握手完成，执行客户端定义的回调。发送bayeux请求，通过一个新的XHR上&lt;br /&gt;
[{"channel":"/meta/subscribe","subscription":"/hello","id":"2","clientId":"9185k23lo482oq1po3ivxup2cj"},{"channel":"/service/hello","data":{"name":"World"},"id":"3","clientId":"9185k23lo482oq1po3ivxup2cj"}]&lt;/p&gt;

&lt;p&gt;服务端同时返回三个bayuex的请求响应&lt;/p&gt;

&lt;p&gt;&lt;blockquote&gt;[{"channel":"/meta/subscribe","successful":true,"id":"2","subscription":"/hello"},{"channel":"/hello","data":{"greeting":"Hello, World"}},{"channel":"/service/hello","successful":true,"id":"3"}]&lt;/blockquote&gt;&lt;/p&gt;

&lt;p&gt;客户端开始发送连接请求&lt;/p&gt;

&lt;p&gt;&lt;blockquote&gt;[{"channel":"/meta/connect","connectionType":"long-polling","advice":{"timeout":0},"id":"4","clientId":"9185k23lo482oq1po3ivxup2cj"}]&lt;/blockquote&gt;&lt;/p&gt;

&lt;p&gt;注意这里使用的是&lt;a href="http://svn.cometd.com/trunk/bayeux/bayeux.html#toc_69"&gt;long-polling&lt;/a&gt;方式，这是由dojo针对浏览器特性决定的。&lt;/p&gt;

&lt;p&gt;&lt;blockquote&gt;Long-polling server implementations attempt to hold open each request until there are events to deliver; the goal is to always have a pending request available to use for delivering events as they occur, thereby minimizing the latency in message delivery.&lt;/blockquote&gt;&lt;/p&gt;

&lt;p&gt;如果没有新消息，服务端阻塞十秒后返回&lt;/p&gt;

&lt;p&gt;&lt;blockquote&gt;[{"channel":"/meta/connect","successful":true,"id":"7"}]&lt;/blockquote&gt;&lt;/p&gt;

&lt;p&gt;客户端接收到返回立刻发起新的connect请求&lt;/p&gt;

&lt;p&gt;当有新消息时，阻塞在服务器端的connect请求会立即返回，同时带回新的消息，如
&lt;blockquote&gt;[{"channel":"/hello","data":{"name":"555"},"id":"6"},{"channel":"/meta/connect","successful":true,"id":"619"}]&lt;/blockquote&gt;
而如果是本客户端publish的新消息，会在请求成功的响应中返回，不会影响connect连接，如：
&lt;blockquote&gt;[{"channel":"/hello","data":{"name":"nihao"},"id":"715"},{"channel":"/hello","successful":true,"id":"715"}]&lt;/blockquote&gt;&lt;/p&gt;

&lt;p&gt;断开时，仍然是通过xhr post一条bayuex命令到服务端
&lt;blockquote&gt;[{"channel":"/meta/disconnect","id":"750","clientId":"9185k23lo482oq1po3ivxup2cj"}]&lt;/blockquote&gt;
服务端响应：
&lt;blockquote&gt;[{"channel":"/meta/disconnect","successful":true,"id":"750"}]&lt;/blockquote&gt;&lt;/p&gt;

&lt;p&gt;至此，通过long polling方式实现bayuex的cometd客户端也描述清楚了。long-polling仍然是通过connect请求来实现pull的方式准实时，与websocket真正push的方式还是存在区别的。&lt;/p&gt;

&lt;p&gt;The post is brought to you by &lt;a href="http://fedorahosted.org/lekhonee"&gt;lekhonee&lt;/a&gt; v0.7&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Websocket Protocol</title><link href="https://sunng.info/blog/websocket-protocol.html" rel="alternate"></link><published>2010-07-16T00:00:00+08:00</published><updated>2010-07-16T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-07-16:/blog/websocket-protocol.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;html5&lt;/li&gt;
&lt;li&gt;http&lt;/li&gt;
&lt;li&gt;jetty&lt;/li&gt;
&lt;li&gt;websocket
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;下午用jetty的WekSocketServlet写了一个简单的WebIM程序，正好第一次瞥见WebSocket的狰容。&lt;/p&gt;

&lt;p&gt;服务器端&lt;br /&gt;
jetty 7.1.5&lt;br /&gt;
客户端&lt;br /&gt;
Chromium 5.0.375.86&lt;/p&gt;

&lt;p&gt;通过wireshark抓包获得这样一些数据：
&lt;em&gt;var _ws = new WebSocket("ws://127.0.0 …&lt;/em&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;html5&lt;/li&gt;
&lt;li&gt;http&lt;/li&gt;
&lt;li&gt;jetty&lt;/li&gt;
&lt;li&gt;websocket
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;下午用jetty的WekSocketServlet写了一个简单的WebIM程序，正好第一次瞥见WebSocket的狰容。&lt;/p&gt;

&lt;p&gt;服务器端&lt;br /&gt;
jetty 7.1.5&lt;br /&gt;
客户端&lt;br /&gt;
Chromium 5.0.375.86&lt;/p&gt;

&lt;p&gt;通过wireshark抓包获得这样一些数据：
&lt;em&gt;var _ws = new WebSocket("ws://127.0.0.1:8080/nothing")&lt;/em&gt;
这个环节创建WebSocket，浏览器与服务器端进行handshake，发送请求&lt;/p&gt;

&lt;p&gt;&lt;blockquote&gt;GET /nothing HTTP/1.1&lt;br /&gt;
Upgrade: WebSocket&lt;br /&gt;
Connection: Upgrade&lt;br /&gt;
Host: 127.0.0.1:8080&lt;br /&gt;
Origin: http://127.0.0.1:8080&lt;/blockquote&gt;&lt;/p&gt;

&lt;p&gt;客户端发出一个Upgrade头，upgrade头在&lt;a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.42"&gt;RFC2616 14.42&lt;/a&gt;定义&lt;/p&gt;

&lt;p&gt;&lt;blockquote&gt;The Upgrade general-header allows the client to specify what additional communication protocols it supports and would like to use if the server finds it appropriate to switch protocols. &lt;/blockquote&gt;&lt;/p&gt;

&lt;p&gt;Upgrade必须被放入Connection头中标示这是一个Upgrade请求&lt;br /&gt;
Connection定义在&lt;a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.10"&gt;RFC2616 14.10&lt;/a&gt;中：&lt;/p&gt;

&lt;p&gt;&lt;blockquote&gt;The Connection general-header field allows the sender to specify options that are desired for that particular connection and MUST NOT be communicated by proxies over further connections. &lt;/blockquote&gt;&lt;/p&gt;

&lt;p&gt;Origin头还没有进入RFC，他的标准草案可以在&lt;a href="http://tools.ietf.org/html/draft-abarth-origin-07"&gt;这里&lt;/a&gt;找到，W3C的标准草案&lt;a href="http://dev.w3.org/2006/waf/access-control/"&gt;Cross-Origin Resource Sharing&lt;/a&gt;里&lt;a href="http://dev.w3.org/2006/waf/access-control/#origin-request-header"&gt;定义&lt;/a&gt;Origin Header：&lt;/p&gt;

&lt;p&gt;&lt;blockquote&gt;The Origin  header indicates where the cross-origin request or preflight request  originates from. &lt;/blockquote&gt;&lt;/p&gt;

&lt;p&gt;Origin头的提出是为了解决&lt;a href="http://en.wikipedia.org/wiki/Cross_Site_Request_Forgery"&gt;CSRF&lt;/a&gt;的潜在危险，通过Origin服务器端可以获知请求的来源，进而判断其合法性。也就是说将跨域安全性检查的责任交给了服务器端，浏览器端采取信任的策略，避免了原先对跨域一棍子打死的做法。&lt;br /&gt;
Jetty 7的org.eclipse.jetty.servlets.CrossOriginFilter对这个头进行了处理。&lt;/p&gt;

&lt;p&gt;此外，handshake请求的header中还允许一个Sec-WebSocket-Protocol，用于对服务器端指定一个子协议（应用协议）。&lt;/p&gt;

&lt;p&gt;服务器端应答&lt;/p&gt;

&lt;p&gt;&lt;blockquote&gt;HTTP/1.1 101 Web Socket Protocol Handshake&lt;br /&gt;
Upgrade: WebSocket&lt;br /&gt;
Connection: Upgrade&lt;br /&gt;
WebSocket-Origin: http://127.0.0.1:8080&lt;br /&gt;
WebSocket-Location: ws://127.0.0.1:8080/nothing&lt;/blockquote&gt;&lt;/p&gt;

&lt;p&gt;Websocket连接建立。此后，服务器端和客户端可以实现bidirectional的通信，消息体即websocket.send(msg)中的纯文本。要实现这样的机制，浏览器和服务器间需要建立至少两个连接。目前，&lt;a href="http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76#4"&gt;WebSocket协议&lt;/a&gt;中还没有规定客户端对服务器端的连接数限制。不过关于这个限制，RFC2616（HTTP1.1）中&lt;a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html#sec8.1.4"&gt;规定&lt;/a&gt;：&lt;/p&gt;

&lt;p&gt;&lt;blockquote&gt;Clients that use persistent connections SHOULD limit the number of simultaneous connections that they maintain to a given server. A single-user client SHOULD NOT maintain more than 2 connections with any server or proxy. &lt;/blockquote&gt;&lt;/p&gt;

&lt;p&gt;对此，另一个Bayeux协议倒是已经有了明确的限制：&lt;/p&gt;

&lt;p&gt;&lt;blockquote&gt;the Bayeux protocol MUST NOT require any more than 2 HTTP requests to be simultaneously handled by a server in order to handle all application (Bayeux based or otherwise) requests from a client. &lt;/blockquote&gt;&lt;/p&gt;

&lt;p&gt;到此，客户端和服务器端已经可以建立双工的通信，这也是浏览器级别实现WebSocket协议的最大优点。而对于Firefox 3.x， IE x.x等等，只能在现有的HTTP连接机制上实现WebSocket，如通过long polling和callback polling的方式，但终归无法实现真正双工的通信。&lt;/p&gt;

&lt;p&gt;The post is brought to you by &lt;a href="http://fedorahosted.org/lekhonee"&gt;lekhonee&lt;/a&gt; v0.7&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>clojure recur</title><link href="https://sunng.info/blog/clojure-recur.html" rel="alternate"></link><published>2010-07-15T00:00:00+08:00</published><updated>2010-07-15T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-07-15:/blog/clojure-recur.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;clojure
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;clojure是函数是编程语言，本身没有循环的语法。要实现类似循环的效果，需要用递归来实现。比如计算从1加到n的和这样一个函数：&lt;/p&gt;

&lt;p&gt;[cc lang="lisp"]&lt;br /&gt;
(defn addup&lt;br /&gt;
    ([limit] (addup limit …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;clojure
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;clojure是函数是编程语言，本身没有循环的语法。要实现类似循环的效果，需要用递归来实现。比如计算从1加到n的和这样一个函数：&lt;/p&gt;

&lt;p&gt;[cc lang="lisp"]&lt;br /&gt;
(defn addup&lt;br /&gt;
    ([limit] (addup limit 0 0))&lt;br /&gt;
    ([limit current sum]&lt;br /&gt;
        (if (&lt; limit current)&lt;br /&gt;
            sum&lt;br /&gt;
            (addup limit (+ 1 current) (+ current sum)))))&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;调用&lt;/p&gt;

&lt;p&gt;(addup 10)&lt;/p&gt;

&lt;p&gt;可以得到55&lt;/p&gt;

&lt;p&gt;但是调用&lt;/p&gt;

&lt;p&gt;(addup 50000)&lt;/p&gt;

&lt;p&gt;不出意外，出现了StackOverflowException， 这是递归典型的错误。&lt;/p&gt;

&lt;p&gt;为此，需要进行优化。尾递归优化是指如果递归满足一定条件，编译器可以将递归展开，避免出现栈溢出的问题。这里的条件，就是递归的最后一次调用是否是其本身，比如上面的代码，最后一次操作仍然是addup，即满足尾递归的条件，而如果同样的代码写成：&lt;/p&gt;

&lt;p&gt;[cc lang="lisp"]&lt;br /&gt;
(defn addup2&lt;br /&gt;
    ([limit] (addup2 limit 0))&lt;br /&gt;
    ([limit current]&lt;br /&gt;
        (if (&lt; limit current)&lt;br /&gt;
            0&lt;br /&gt;
            (+ current (addup2 limit (+ current 1))))))[/cc]&lt;/p&gt;

&lt;p&gt;最后一个操作是+，不满足尾递归循环，无法进行优化。&lt;/p&gt;

&lt;p&gt;clojure默认不进行尾递归优化，需要使用关键字recur来保证优化，把第一段代码写成：&lt;/p&gt;

&lt;p&gt;[cc lang="lisp"]&lt;br /&gt;
(defn addup&lt;br /&gt;
    ([limit] (addup limit 0 0))&lt;br /&gt;
    ([limit current sum]&lt;br /&gt;
        (if (&lt; limit current)&lt;br /&gt;
            sum&lt;br /&gt;
            (recur limit (+ 1 current) (+ current sum)))))&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;社区的解释是，如果程序员在无法进行尾递归优化的代码中使用recur会发生报错，这样有助于他们发现代码中潜在的缺陷。&lt;/p&gt;

&lt;p&gt;在第二段代码中使用recur，会得到异常：&lt;/p&gt;

&lt;p&gt;java.lang.UnsupportedOperationException: Can only recur from tail position&lt;/p&gt;

&lt;p&gt;此外，clojure提供loop函数简化书写：&lt;/p&gt;

&lt;p&gt;[cc lang="lisp"]&lt;br /&gt;
(defn addup3&lt;br /&gt;
    ([limit]&lt;br /&gt;
    (loop [current 0 sum 0]&lt;br /&gt;
        (if (&lt; limit current)&lt;br /&gt;
            sum&lt;br /&gt;
            (recur (+ current 1) (+ current sum))))))&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;这种写法同样可以应用尾递归优化而不会导致栈溢出。&lt;/p&gt;

&lt;p&gt;The post is brought to you by &lt;a href="http://fedorahosted.org/lekhonee"&gt;lekhonee&lt;/a&gt; v0&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>fixing "libmozjs" missing when using mongodb on Ubuntu lucid</title><link href="https://sunng.info/blog/fixing-libmozjs-missing-when-using-mongodb-on-ubuntu-lucid.html" rel="alternate"></link><published>2010-07-13T00:00:00+08:00</published><updated>2010-07-13T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-07-13:/blog/fixing-libmozjs-missing-when-using-mongodb-on-ubuntu-lucid.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;mongodb&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;
When running mongod/mongo/mongos, you got message like this:
&lt;i&gt;mongod: error while loading shared libraries: libmozjs.so: cannot open shared object file: No such file or directory&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;
Make sure you have xulrunner-dev installed:
&lt;em&gt;sudo apt-get install xulrunner-dev&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;then …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;mongodb&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;
When running mongod/mongo/mongos, you got message like this:
&lt;i&gt;mongod: error while loading shared libraries: libmozjs.so: cannot open shared object file: No such file or directory&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;
Make sure you have xulrunner-dev installed:
&lt;em&gt;sudo apt-get install xulrunner-dev&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;then find libmozjs on your filesystem:
&lt;em&gt;sudo locate libmozjs&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;in lucid, it's supposed to locate at:
&lt;em&gt;/usr/lib/xulrunner-1.9.2.6/libmozjs.so&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;(and some other directories, such as firefox / thunderbird / seamonkey)&lt;/p&gt;

&lt;p&gt;Just create a symbol link:
&lt;em&gt;sudo ln -s /usr/lib/xulrunner-1.9.2.6/libmozjs.so /usr/lib/&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;try to restart mongodb:
&lt;em&gt;sudo service mongodb start&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;take a look at process list:
&lt;em&gt;ps aux | grep mongo&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;it works.&lt;/p&gt;

&lt;p&gt;The post is brought to you by &lt;a href="http://fedorahosted.org/lekhonee"&gt;lekhonee&lt;/a&gt; v0.7&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Update1</title><link href="https://sunng.info/blog/update1.html" rel="alternate"></link><published>2010-07-10T00:00:00+08:00</published><updated>2010-07-10T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-07-10:/blog/update1.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;Life&lt;/li&gt;
&lt;li&gt;work
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;ol&gt;
&lt;li&gt;搬家了，搬了一条街，从马路这边搬到马路那边。从二楼搬到四楼，告别了潮湿发霉的小屋，换了新环境 …&lt;/li&gt;&lt;/ol&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;Life&lt;/li&gt;
&lt;li&gt;work
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;ol&gt;
&lt;li&gt;搬家了，搬了一条街，从马路这边搬到马路那边。从二楼搬到四楼，告别了潮湿发霉的小屋，换了新环境迎接工作第二年。本来想搬出这个玉兰香苑，但是看了一圈不是太远就是太贵。都说现在是不炒房价改炒房租了，别的不知道，反正我原来那屋房租就涨了200，而且我搬出去那天就租出去了。&lt;/li&gt;
&lt;li&gt;妈妈来上海，晚上可以一起玩桌游。妈妈对卡卡松还挺感兴趣的，我们一开始玩基础版，然后加河流扩展、旅馆教堂扩展。卡卡松绝对是家庭娱乐的好工具，各位常年在外的童鞋休假回家可以带一个和父母一起玩，调节气氛，增进和谐，居家旅行，必备良方。&lt;/li&gt;
&lt;li&gt;周五盛大创新院，人间网介绍mongodb实践。人间网的用户不多，数据量不大，mongodb用来存储timeline里的status id，至今有不到900M的数据和900M的索引。交流活动持续一个小时，最火爆的环节是Q&amp;A。人间网用mongodb的经验也不是非常丰富，很多问题也解释不清楚，最后多亏了我们组&lt;a href="http://www.ccdaisy.com/"&gt;一位上海程序员界的新星&lt;/a&gt;救场，除了解决了四面八方有意义无意义的问题以外，也引来了全场的叹为观止，以至于都有人说这是SDO的人嘛。其实不完全是，其实人家才大三。&lt;/li&gt;
&lt;li&gt;工作一年了，下周安静下来总结一下吧。&lt;/li&gt;
&lt;/ol&gt;&lt;/p&gt;

&lt;p&gt;The post is brought to you by &lt;a href="http://fedorahosted.org/lekhonee"&gt;lekhonee&lt;/a&gt; v0.7&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Exaile-doubanfm-plugin 0.0.2</title><link href="https://sunng.info/blog/exaile-doubanfm-plugin-002.html" rel="alternate"></link><published>2010-07-04T00:00:00+08:00</published><updated>2010-07-04T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-07-04:/blog/exaile-doubanfm-plugin-002.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;Douban&lt;/li&gt;
&lt;li&gt;exaile&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;gtk&lt;/li&gt;
&lt;li&gt;opensource&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Exaile doubanfm plugin 0.0.2 预览版，目标是在Linux桌面提供豆瓣电台的完整体验。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;目前插件只能运行在Exaile 0.3.1版本上。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;0.0.2增加了豆瓣电台专用的视图：
&lt;a href="http://www.flickr.com/photos/40741608@N08/4760493886/" title="screenshot_001 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4075/4760493886_5e334726cf.jpg" width="408" height="156" alt="screenshot_001" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;相比第一版通过rating来实现豆瓣电台喜欢 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;Douban&lt;/li&gt;
&lt;li&gt;exaile&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;gtk&lt;/li&gt;
&lt;li&gt;opensource&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Exaile doubanfm plugin 0.0.2 预览版，目标是在Linux桌面提供豆瓣电台的完整体验。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;目前插件只能运行在Exaile 0.3.1版本上。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;0.0.2增加了豆瓣电台专用的视图：
&lt;a href="http://www.flickr.com/photos/40741608@N08/4760493886/" title="screenshot_001 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4075/4760493886_5e334726cf.jpg" width="408" height="156" alt="screenshot_001" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;相比第一版通过rating来实现豆瓣电台喜欢、跳过和删除的功能，在专用视图上有专门的按钮来操作。&lt;/p&gt;

&lt;p&gt;其他细节更新：
&lt;ul&gt;
    &lt;li&gt;当剩余曲目超过15首时不再增加播放列表&lt;/li&gt;
    &lt;li&gt;当播放到最后一首歌曲取新播放列表时增加重试机制&lt;/li&gt;
    &lt;li&gt;修正libdbfm跳过曲目bug一个&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;安装：&lt;br /&gt;
打开Exaile Preference，Plugin页，点击按钮Install Plugin，选择doubanfm.exz即可。如果存在问题，可以执行以下命令：&lt;br /&gt;
mv doubanfm.exz douban.tar.gz&lt;br /&gt;
tar xf doubanfm.tar.gz&lt;br /&gt;
mv doubanfm ~/.local/share/exaile/plugins/&lt;/p&gt;

&lt;p&gt;转到doubanfm设置页面，填写用户名密码重启Exaile。
&lt;a href="http://www.flickr.com/photos/40741608@N08/4754666940/" title="screenshot_002 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4141/4754666940_b998d99c4b.jpg" width="500" height="412" alt="screenshot_002" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;打开File菜单，选择豆瓣电台频道
&lt;a href="http://www.flickr.com/photos/40741608@N08/4754619030/" title="screenshot_001 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4094/4754619030_933e00fcfa.jpg" width="500" height="359" alt="screenshot_001" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;选择曲目就可以开始播放了，选择视图中豆瓣电台视图可以切换到豆瓣电台视图。&lt;/p&gt;

&lt;p&gt;项目地址：
&lt;a href="http://github.com/sunng87/exaile-doubanfm-plugin"&gt;http://github.com/sunng87/exaile-doubanfm-plugin&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;下载：
&lt;a href="http://github.com/sunng87/exaile-doubanfm-plugin/downloads"&gt;http://github.com/sunng87/exaile-doubanfm-plugin/downloads&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;另外，doubancovers插件也有一个针对豆瓣电台的更新可以快速获取豆瓣电台音乐的封面：
&lt;a href="http://bitbucket.org/sunng/exailedoubancovers/downloads?highlight=9265"&gt;http://bitbucket.org/sunng/exailedoubancovers/downloads?highlight=9265&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The post is brought to you by &lt;a href="http://fedorahosted.org/lekhonee"&gt;lekhonee&lt;/a&gt; v0.7&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>exaile豆瓣电台插件(exaile-doubanfm-plugin)</title><link href="https://sunng.info/blog/exailedou-ban-dian-tai-cha-jian-exaile-doubanfm-plugin.html" rel="alternate"></link><published>2010-07-02T00:00:00+08:00</published><updated>2010-07-02T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-07-02:/blog/exailedou-ban-dian-tai-cha-jian-exaile-doubanfm-plugin.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;Douban&lt;/li&gt;
&lt;li&gt;exaile&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;opensource&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;豆瓣电台一直是flash客户端，嵌入浏览器，出于对flash的厌恶，本人开发了这个exaile插件，让广大linux同学在exaile里享受豆瓣电台一目了然桌面集成和全部豆瓣电台功能。&lt;/p&gt;

&lt;p&gt;exaile-doubanfm-plugin features:
&lt;ul&gt;
    &lt;li&gt;登录豆瓣，获取豆瓣 …&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;Douban&lt;/li&gt;
&lt;li&gt;exaile&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;opensource&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;豆瓣电台一直是flash客户端，嵌入浏览器，出于对flash的厌恶，本人开发了这个exaile插件，让广大linux同学在exaile里享受豆瓣电台一目了然桌面集成和全部豆瓣电台功能。&lt;/p&gt;

&lt;p&gt;exaile-doubanfm-plugin features:
&lt;ul&gt;
    &lt;li&gt;登录豆瓣，获取豆瓣电台播放列表&lt;/li&gt;
    &lt;li&gt;持续下载电台播放列表，实现不重复的持续播放&lt;/li&gt;
    &lt;li&gt;标记/取消“喜爱”、标记“回收站”、跳过&lt;/li&gt;
    &lt;li&gt;显示歌曲信息&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/4754619030/" title="screenshot_001 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4094/4754619030_933e00fcfa.jpg" width="500" height="359" alt="screenshot_001" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/4754615592/" title="screenshot_002 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4079/4754615592_0c1486c2df.jpg" width="500" height="359" alt="screenshot_002" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/4754666940/" title="screenshot_002 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4141/4754666940_b998d99c4b.jpg" width="500" height="412" alt="screenshot_002" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;项目地址：
&lt;a href="http://github.com/sunng87/exaile-doubanfm-plugin"&gt;http://github.com/sunng87/exaile-doubanfm-plugin&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;下载页：
&lt;a href="http://github.com/sunng87/exaile-doubanfm-plugin/downloads"&gt;http://github.com/sunng87/exaile-doubanfm-plugin/downloads&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;使用：
&lt;ol&gt;
&lt;li&gt;激活插件，填写用户名密码&lt;/li&gt;
&lt;li&gt;重启exaile&lt;/li&gt;
&lt;li&gt;文件菜单，open douban.fm，选择频道&lt;/li&gt;
&lt;li&gt;点击播放开始&lt;/li&gt;
&lt;li&gt;将rating设置为5 标记喜欢&lt;/li&gt;
&lt;li&gt;rating设置2 豆瓣标准跳过&lt;/li&gt;
&lt;li&gt;rating设置1 豆瓣删除&lt;/li&gt;
&lt;/ol&gt;&lt;/p&gt;

&lt;p&gt;The post is brought to you by &lt;a href="http://fedorahosted.org/lekhonee"&gt;lekhonee&lt;/a&gt; v0.7&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Exaile-Douban-Covers 0.0.3 Released</title><link href="https://sunng.info/blog/exaile-douban-covers-003-released.html" rel="alternate"></link><published>2010-06-30T00:00:00+08:00</published><updated>2010-06-30T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-06-30:/blog/exaile-douban-covers-003-released.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;exaile&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;opensource
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;一眨眼有大半年没有维护这个插件了，Exaile从0.3.0升级到了0.3.2。严重的是这三个版本的插件接口各不相同，0.3 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;exaile&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;opensource
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;一眨眼有大半年没有维护这个插件了，Exaile从0.3.0升级到了0.3.2。严重的是这三个版本的插件接口各不相同，0.3.1新增了cover providers管理， 修改了track获得相应字段的api，简化了CoverSearchMethod的职责（去掉了缓存管理，只负责find_convers和get_conver_data两个功能）。而0.3.2至少也修改了配置管理界面的API，所以可以在0.3.1上工作的版本也不能在0.3.2上工作。&lt;/p&gt;

&lt;p&gt;这次升级维护把插件升级到0.0.3，兼容目前Ubuntu仓库里的Exaile 0.3.1.1。0.3.1.1新提供了一个cover provider的配置界面，可以调整cover provider的优先级，如果你听中文歌比较多，建议可以把douban cover放在第一位。&lt;/p&gt;

&lt;p&gt;下载页面（doubancovers-0.0.3.exz ）：
&lt;a href="http://bitbucket.org/sunng/exailedoubancovers/downloads"&gt;http://bitbucket.org/sunng/exailedoubancovers/downloads&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;新版本和0.3.0已经无法兼容，0.3.0的用户可以继续使用0.0.2 。而兼容0.3.2的版本预计要等到10月份之后推出了，用Archlinux和其他自己安装的用户要稍等一阵了。&lt;/p&gt;

&lt;p&gt;The post is brought to you by &lt;a href="http://fedorahosted.org/lekhonee"&gt;lekhonee&lt;/a&gt; v0.7&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Twitter talks about the LBS feature and implementation</title><link href="https://sunng.info/blog/twitter-talks-about-the-lbs-feature-and-implementation.html" rel="alternate"></link><published>2010-06-27T00:00:00+08:00</published><updated>2010-06-27T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-06-27:/blog/twitter-talks-about-the-lbs-feature-and-implementation.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;未分类
tags: []
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;这是3月份OReilly Where 2.0会议上Twitter介绍自己LBS功能和实现的幻灯片
&lt;a href="http://www.slideshare.net/raffikrikorian/handling-realtime-geostreams-3597425"&gt;http://www.slideshare.net/raffikrikorian/handling-realtime-geostreams-3597425&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;div style="width:425px" id="__ss_3597425"&gt;&lt;strong style="display:block;margin:12px 0 4px"&gt;&lt;a href="http://www.slideshare.net/raffikrikorian/handling-realtime-geostreams-3597425" title="Handling Real-time Geostreams"&gt;Handling Real-time Geostreams&lt;/a&gt;&lt;/strong&gt;&lt;object id="__sse3597425" width="425" height="355"&gt;&lt;param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=real-time-geostreams-100330161823-phpapp02&amp;stripped_title=handling-realtime-geostreams-3597425" /&gt;&lt;param name="allowFullScreen" value="true" /&gt;&lt;param name="allowScriptAccess" value="always" /&gt;&lt;embed name="__sse3597425" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=real-time-geostreams-100330161823-phpapp02&amp;stripped_title=handling-realtime-geostreams-3597425" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div style="padding:5px 0 12px"&gt;View more &lt;a href="http://www.slideshare.net/"&gt;presentations&lt;/a&gt; from &lt;a href="http://www.slideshare.net/raffikrikorian"&gt;Raffi Krikorian&lt;/a&gt;.&lt;/div&gt;&lt;/div&gt;&lt;/p&gt;

&lt;p&gt;都封了，越封越傻逼，封封更傻逼。&lt;/p&gt;

&lt;p&gt;The post is brought to you by &lt;a href="http://fedorahosted.org/lekhonee"&gt;lekhonee …&lt;/a&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;未分类
tags: []
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;这是3月份OReilly Where 2.0会议上Twitter介绍自己LBS功能和实现的幻灯片
&lt;a href="http://www.slideshare.net/raffikrikorian/handling-realtime-geostreams-3597425"&gt;http://www.slideshare.net/raffikrikorian/handling-realtime-geostreams-3597425&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;div style="width:425px" id="__ss_3597425"&gt;&lt;strong style="display:block;margin:12px 0 4px"&gt;&lt;a href="http://www.slideshare.net/raffikrikorian/handling-realtime-geostreams-3597425" title="Handling Real-time Geostreams"&gt;Handling Real-time Geostreams&lt;/a&gt;&lt;/strong&gt;&lt;object id="__sse3597425" width="425" height="355"&gt;&lt;param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=real-time-geostreams-100330161823-phpapp02&amp;stripped_title=handling-realtime-geostreams-3597425" /&gt;&lt;param name="allowFullScreen" value="true" /&gt;&lt;param name="allowScriptAccess" value="always" /&gt;&lt;embed name="__sse3597425" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=real-time-geostreams-100330161823-phpapp02&amp;stripped_title=handling-realtime-geostreams-3597425" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div style="padding:5px 0 12px"&gt;View more &lt;a href="http://www.slideshare.net/"&gt;presentations&lt;/a&gt; from &lt;a href="http://www.slideshare.net/raffikrikorian"&gt;Raffi Krikorian&lt;/a&gt;.&lt;/div&gt;&lt;/div&gt;&lt;/p&gt;

&lt;p&gt;都封了，越封越傻逼，封封更傻逼。&lt;/p&gt;

&lt;p&gt;The post is brought to you by &lt;a href="http://fedorahosted.org/lekhonee"&gt;lekhonee&lt;/a&gt; v0.7&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Redis</title><link href="https://sunng.info/blog/redis.html" rel="alternate"></link><published>2010-06-24T00:00:00+08:00</published><updated>2010-06-24T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-06-24:/blog/redis.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;kvstore&lt;/li&gt;
&lt;li&gt;redis
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://code.google.com/p/redis"&gt;Redis&lt;/a&gt;是一个高性能Key-Value store&lt;/p&gt;

&lt;p&gt;&lt;h2&gt;Installation&lt;/h2&gt;
下载解压，make之后就可以直接启动./redis-server 默认读取当前目录下的redis.conf作为配置，亦可参数指定。默认监听6379端口。&lt;/p&gt;

&lt;p&gt;&lt;h2&gt;Features&lt;/h2&gt;
&lt;h3&gt;支 …&lt;/h3&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;kvstore&lt;/li&gt;
&lt;li&gt;redis
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://code.google.com/p/redis"&gt;Redis&lt;/a&gt;是一个高性能Key-Value store&lt;/p&gt;

&lt;p&gt;&lt;h2&gt;Installation&lt;/h2&gt;
下载解压，make之后就可以直接启动./redis-server 默认读取当前目录下的redis.conf作为配置，亦可参数指定。默认监听6379端口。&lt;/p&gt;

&lt;p&gt;&lt;h2&gt;Features&lt;/h2&gt;
&lt;h3&gt;支持多种数据结构&lt;/h3&gt;
相较memcached，redis支持多种数据结构，包括
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://code.google.com/p/redis/wiki/CommandReference#Commands_operating_on_string_values"&gt;string&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://code.google.com/p/redis/wiki/CommandReference#Commands_operating_on_lists"&gt;list&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://code.google.com/p/redis/wiki/CommandReference#Commands_operating_on_sets"&gt;set&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://code.google.com/p/redis/wiki/CommandReference#Commands_operating_on_sorted_sets_(zsets,_Redis_version_%3E=_1."&gt;sorted set&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://code.google.com/p/redis/wiki/CommandReference#Commands_operating_on_hashes"&gt;hash&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;针对每种数据结构，redis都提供比较完整的操作支持。&lt;/p&gt;

&lt;p&gt;&lt;h3&gt;高性能&lt;/h3&gt;
据Wiki上说可以达到110,000 SETs/second, 81,000 GETs/second，数据根据一定的策略存在内存和磁盘上（支持持久化）。从1.1开始支持&lt;a href="http://code.google.com/p/redis/wiki/AppendOnlyFileHowto"&gt;append-only持久化方式&lt;/a&gt;，数据被加到文件中，并进行异步的维护，保证文件的大小。从2.0（即1.3）开始支持virtual memory&lt;a href="http://code.google.com/p/redis/wiki/VirtualMemoryUserGuide"&gt;&lt;/a&gt;，当内存占用超过配置文件中vm-max-memory时，数据被写入virtual memory。&lt;/p&gt;

&lt;p&gt;&lt;h2&gt;Replication&lt;/h2&gt;
redis支持主从复制，从而提供多台实例进行读操作，可以在slave的配置文件里配置master的地址。&lt;/p&gt;

&lt;p&gt;&lt;h2&gt;Protocol&lt;/h2&gt;
redis目前只有基于TCP的文本协议，与memcache类似，有一些改进。&lt;br /&gt;
客户端通常发送
&lt;i&gt;命令 参数... 值字节数\r\n&lt;/i&gt;
&lt;i&gt;值\r\n&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;服务端的返回，根据第一个字节，可以判断：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;-&lt;/strong&gt; 错误信息&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;+&lt;/strong&gt; 普通文本信息&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$&lt;/strong&gt; 变长字节数，$6表示CRLF之后有6个字节的字符&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;:&lt;/strong&gt; 返回一个整数&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;*&lt;/strong&gt; 返回组数，即*6表示CRLF之后将返回6组变长字符&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;&lt;h2&gt;其他&lt;/h2&gt;
项目地址
&lt;a href="http://code.google.com/p/redis/"&gt;http://code.google.com/p/redis/&lt;/a&gt;
&lt;a href="http://github.com/antirez/redis/"&gt;http://github.com/antirez/redis/&lt;/a&gt;
开发者twitter
&lt;a href="http://twitter.com/antirez"&gt;http://twitter.com/antirez&lt;/a&gt;
&lt;a href="http://twitter.com/pnoordhuis"&gt;http://twitter.com/pnoordhuis&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Redis功能简单精悍，很符合Unix哲学，核心redis.c只有一万多行，实在是个让人爱不释手的东西。如果可能，争取把他用到生产环境来取代现在的memcached。&lt;/p&gt;

&lt;p&gt;The post is brought to you by &lt;a href="http://fedorahosted.org/lekhonee"&gt;lekhonee&lt;/a&gt; v0.7&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>下班了</title><link href="https://sunng.info/blog/xia-ban-liao.html" rel="alternate"></link><published>2010-06-22T00:00:00+08:00</published><updated>2010-06-22T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-06-22:/blog/xia-ban-liao.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;snda&lt;/li&gt;
&lt;li&gt;work
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;先汇报一下时间，我这个WP主题默认不显示具体时间，现在是2010年6月22日凌晨4点整，西班牙2：0领先洪都拉斯，比赛进行到75分钟。&lt;/p&gt;

&lt;p&gt;我刚刚下班，晚上我们部署了t.sdo.com的新版本 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;snda&lt;/li&gt;
&lt;li&gt;work
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;先汇报一下时间，我这个WP主题默认不显示具体时间，现在是2010年6月22日凌晨4点整，西班牙2：0领先洪都拉斯，比赛进行到75分钟。&lt;/p&gt;

&lt;p&gt;我刚刚下班，晚上我们部署了t.sdo.com的新版本，性能有了一定的改善。后半夜发现一台memcached行为诡异，暂且先禁用了那台，踉踉跄跄回家准备睡觉。&lt;/p&gt;

&lt;p&gt;今天&lt;a href="http://t.sdo.com"&gt;t.sdo.com&lt;/a&gt;正式发布了，现在不需要鸡和马就可以注册登录。产品本身不谈了，如果你不感兴趣说明你不是目标人群，缺乏90后的气质。前两天twitter上有一篇新浪工程师的&lt;a href="http://liruqi.cn/aboutleavesina"&gt;离职感言&lt;/a&gt;，里面透露新浪要做的“游戏平台”，t.sdo.com就是典型的这种东西。至于里面说的新浪那种气氛，，恐怕在国内的互联网公司里，这种现象都比较普遍吧。&lt;/p&gt;

&lt;p&gt;邀请链接，大家点这个就可以了
&lt;a href="http://isdo.cn/10CM"&gt;http://isdo.cn/10CM&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;这就睡觉去，今天不用上班了。&lt;/p&gt;

&lt;p&gt;The post is brought to you by &lt;a href="http://fedorahosted.org/lekhonee"&gt;lekhonee&lt;/a&gt; v0.7&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>近日琐事</title><link href="https://sunng.info/blog/jin-ri-suo-shi.html" rel="alternate"></link><published>2010-06-18T00:00:00+08:00</published><updated>2010-06-18T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-06-18:/blog/jin-ri-suo-shi.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;最近创作激情很是强烈，又赶上大家在博客上踊跃留言，我继续无聊闲扯吧。&lt;/p&gt;

&lt;p&gt;做现在这个项目以来，我总 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;最近创作激情很是强烈，又赶上大家在博客上踊跃留言，我继续无聊闲扯吧。&lt;/p&gt;

&lt;p&gt;做现在这个项目以来，我总是被需要同时处理多件事情的状况困扰。多任务处理，今天终于发现自己是LIFO的方式，后进先出，总是在做最近发生的事情，之前的就压栈，栈满了没准栈底就自动丢失了（策略就是java.util.concurrent.ThreadPoolExecutor.DiscardOldestPolicy）。用这种方式，耗时较长的任务往往花掉的时间非常长而且不断被打断。&lt;/p&gt;

&lt;p&gt;今天公司新鲜事，我们的工牌的带子终于换掉了。这以前带子的颜色表明了森严的等级制度，员工深蓝色，中层红色，高层黄色，访客绿色，外包白色，物业粉色，平时走在公司里花花绿绿的实在是他妈的一道景观。这事还在twitter上被冯大辉retweet了一下搞得地球人都知道了，今天通通换成了统一的红色。对于一个员工来说，今天看见自己领子上挂着一个鲜艳的中层颜色的带子，时常还是有那么一点错觉。&lt;/p&gt;

&lt;p&gt;还得说说世界杯，这是我看得第四届世界杯了，已经说不上自己支持哪个队了，为此我都怀疑自己已经是伪球迷了。眼看着当初看球时候的球星现在陆陆续续当了教练，那种沧桑感啊。98年的时候还在收集方便面里的球星卡，那时候法国的5号布兰科，人家今年世界杯以后就要上任了。而像比埃尔霍夫这样96年欧洲杯一战成名的，现在都是德国队领队了。大伙再瞧瞧踢了四届世界杯的球员：
&lt;ul&gt;
&lt;li&gt;亨利      法国      1998/2002/2006/2010&lt;/li&gt;
&lt;li&gt;布冯  意大利     1998/2002/2006/2010&lt;/li&gt;
&lt;li&gt;卡纳瓦罗    意大利     1998/2002/2006/2010&lt;/li&gt;
&lt;li&gt;李云在     韩国  1998/2002/2006/2010&lt;/li&gt;
&lt;li&gt;楢崎正刚    日本  1998/2002/2006/2010&lt;/li&gt;
&lt;li&gt;川口能活    日本  1998/2002/2006/2010&lt;/li&gt;
&lt;li&gt;里戈贝特-宋  喀麦隆     1994/1998/2002/2010&lt;/li&gt;
&lt;/ul&gt;
98年大家都说法国队锋线不行，其实那是是亨利和特雷泽盖的组合，不过这二位确实从2000年欧洲杯才开始成熟。布冯98年的时候还是打酱油吧，卡纳瓦罗那时倒是已经有点名气了。李云在，包括十多年来轮着换的楢崎正刚和川口能活现在都打不上主力了，不过一个个都算是现象。&lt;/p&gt;

&lt;p&gt;昨天收到了新买的桌游“全球传染病”(PANDEMIC，名字是恶心了点)，这是一个合作性质的游戏，参与的人扮演不同的角色拥有不同的技能为达到相同目标。昨晚自个玩了一局，难度不小，一颗CPU是搞不定了，下回小聚的时候和大伙一起玩吧。话说不知不觉现在我有卡卡松（加四个扩展），三国杀，UNO，矮人金矿和全球传染病，这发展下去要开店了。。。&lt;/p&gt;

&lt;p&gt;都是玩的事情了，正经事都没提。最近准备看&lt;a href="http://www.apress.com/book/view/9781430272311"&gt;Practical Clojure&lt;/a&gt;，看看Clojure如何Practical吧。&lt;/p&gt;

&lt;p&gt;The post is brought to you by &lt;a href="http://fedorahosted.org/lekhonee"&gt;lekhonee&lt;/a&gt; v0.7&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>我在那里吃拉面</title><link href="https://sunng.info/blog/wo-zai-na-li-chi-la-mian.html" rel="alternate"></link><published>2010-06-16T00:00:00+08:00</published><updated>2010-06-16T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-06-16:/blog/wo-zai-na-li-chi-la-mian.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;Beijing&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;“终于找到一家和我身份地位品味收入都相符的拉面馆。” 这条本来要发到twitter上的，结果不料哪哪出了问题 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;Beijing&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;“终于找到一家和我身份地位品味收入都相符的拉面馆。” 这条本来要发到twitter上的，结果不料哪哪出了问题，没发出去，却被我吃面条的时候记下来了。自从有了饭否twitter，只言片语多了，真正坐下来搞创作的动力却没有了，咱虽然比不上&lt;a href="http://www.google.com.hk/search?hl=zh-CN&amp;newwindow=1&amp;safe=strict&amp;q=%E6%9E%81%E5%B0%91%E9%83%A8%E5%88%86%E6%90%9E%E8%89%BA%E6%9C%AF%E7%9A%84%E7%94%B7%E9%9D%92%E5%B9%B4&amp;aq=f&amp;aqi=&amp;aql=&amp;oq=&amp;gs_rfai="&gt;极少部分搞艺术的男青年&lt;/a&gt;，但追求上进也不该含糊是吧。&lt;/p&gt;

&lt;p&gt;纵然再有追求，也比不上清真拉面馆热卖鱼香肉丝面。上海郊区天高真主远，远亲不如近邻，再不&lt;a href="http://it.21cn.com/itnews/hygc/2010/06/13/7588720.shtml"&gt;创个新&lt;/a&gt;，小小的拉面馆恐怕连在这里匹配我的身份地位和品味的可能性也没有了。但是在首都的时候就不一样，同样是少数民族兄弟开的面馆，人家可不含糊，别说是买鱼香肉丝了，门都不让进。&lt;/p&gt;

&lt;p&gt;在&lt;a href="http://osm.org/go/50je@Ocrn-"&gt;宝盛里&lt;/a&gt;的半年，我发现最贴近我们民工生活的就要数西北拉面和成都小吃，碗大量足（当然也不会有拿着筷子在碗里一粒一粒拣肉末吃的男士）。就为了第一次在成都小吃发现盖浇饭里鸡丁可以比花生多，我在那家店里活活吃过了夏秋冬，后来我每每想起这事都难免情绪激动。而之所以没能熬到冬去春来，也是因为老板时间长了实在看不下去，问我要不要换点别的。我是受宠若惊，才不得不转战拉面店。那家叫不上名字店，名字不重要你知道它在哪就成，墙上还挂着清真寺的画，就像很多湘菜馆会挂毛主席像一样。你可以想象这样一家店是绝对做不出卖鱼香肉丝的勾当的。十三块一个的烤羊腿是他们冬天的热门；端菜的小孩最多也就初中年纪，解析几何他大概是不会清楚了，但是你不催他他也还是不会给你上菜。&lt;/p&gt;

&lt;p&gt;另一家印象深刻的面馆是&lt;a href="http://osm.org/go/50jfFm40@-"&gt;中关村软件园北门&lt;/a&gt;对面的，名字好像叫老马拉面，店的规模还不小，在那个鸟不那啥的地方算是鹤立鸡群了，是软件园民工，西北旺、西二旗一代租住户，应届大学毕业生理想的消费场所。我就在那吃过一次，印象深刻仅仅是因为一位服务员长得好看。那是北门最美好的两件事物之一，另一件是那里成排的杨树，我每次在那里等642路等会被这两件事物陶醉。&lt;/p&gt;

&lt;p&gt;思绪万千了又。其实我就是感叹能找到一家10块钱以内打发晚饭晚上十点还不会饿的地方，抑制不住内心的喜悦，又回忆起以前了。&lt;/p&gt;

&lt;p&gt;最后标题是借名记唐师曾的书名。&lt;/p&gt;

&lt;p&gt;The post is brought to you by &lt;a href="http://fedorahosted.org/lekhonee"&gt;lekhonee&lt;/a&gt; v0.7&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Perfomed my first commit to OpenStreetMap.org</title><link href="https://sunng.info/blog/perfomed-my-first-commit-to-openstreetmaporg.html" rel="alternate"></link><published>2010-06-13T00:00:00+08:00</published><updated>2010-06-13T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-06-13:/blog/perfomed-my-first-commit-to-openstreetmaporg.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;GIS&lt;/li&gt;
&lt;li&gt;map&lt;/li&gt;
&lt;li&gt;opensource&lt;/li&gt;
&lt;li&gt;OpenStreetMap
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;每每看到OSM上&lt;a href="http://osm.org/go/5nFq9bhH-"&gt;南京的地图&lt;/a&gt;都一阵唏嘘，这么多年了几乎没有任何变化，仅有的一些数据也都是错 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;GIS&lt;/li&gt;
&lt;li&gt;map&lt;/li&gt;
&lt;li&gt;opensource&lt;/li&gt;
&lt;li&gt;OpenStreetMap
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;每每看到OSM上&lt;a href="http://osm.org/go/5nFq9bhH-"&gt;南京的地图&lt;/a&gt;都一阵唏嘘，这么多年了几乎没有任何变化，仅有的一些数据也都是错误百出，身为一个南京人，又是一个GIS专业毕业的，深感压力巨大。&lt;/p&gt;

&lt;p&gt;实际上编辑OSM没有大家想象的那么复杂。任何人都可以注册登录网站，用默认的Potlatch工具进行编辑，即使你手上没有数据也可以编辑一些基本的属性，比如把错误的中山路纠正为北京东路。我从MapMyTracks上导出GPX文件上传到OSM，GPS数据会在OSM上被显示出来，根据这些数据稍作加工就是道路信息了。浏览器里的编辑器功能并不强大，KDE桌面的Merkaator的编辑能力要强大得多，可以对道路进行split/join操作，这些操作对之前南京地图上各种错误的数据非常必要。&lt;/p&gt;

&lt;p&gt;其实OpenStreetMap可以借助一些背景图进行数字化，但是在国内，目前只有分辨率非常低Yahoo卫星地图可以用。至于Google的卫星地图，&lt;a href="http://wiki.openstreetmap.org/wiki/FAQ#What_images_and_maps_may_I_use_to_make_maps_from.3F"&gt;由于License的原因&lt;/a&gt;，你是不能在编辑自由地图时使用的。&lt;/p&gt;

&lt;p&gt;上海的OSM已经非常完善，数据完善的程度、渲染出地图的壮观已经让你难以想象，连去年开通的张江电车已经在地图上标出，还有各个站名。注册后可以发现，仅仅在浦东就有十几位OSM的贡献者。相比之下，南京在这方面一片空白，甚至还没有苏州的地图完善。考虑到南京有这么多学校这么多GIS专业学生，每年做那么多无用的、自以为是的数字化作业，真正有意义的工作却无人问津，并非没有人愿意去做，而恐怕是大部分人对OSM都闻所未闻。&lt;/p&gt;

&lt;p&gt;今后每周我回抽出一定的时间来编辑南京的地图，也算是为家乡做一些贡献吧。&lt;/p&gt;

&lt;p&gt;The post is brought to you by &lt;a href="http://fedorahosted.org/lekhonee"&gt;lekhonee&lt;/a&gt; v0.7&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>RPC, Serialization and Schema</title><link href="https://sunng.info/blog/rpc-serialization-and-schema.html" rel="alternate"></link><published>2010-06-12T00:00:00+08:00</published><updated>2010-06-12T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-06-12:/blog/rpc-serialization-and-schema.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;sergent&lt;/li&gt;
&lt;li&gt;work
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;The post is brought to you by &lt;a href="http://fedorahosted.org/lekhonee"&gt;lekhonee&lt;/a&gt; v0.7&lt;/p&gt;

&lt;p&gt;糖果项目的后端用Java编写，我负责service gateway的开发（暂且叫sergent），服务以Java接口+Annotation的形式声明，与Spring集成使用，Java对象被序列化为JSON和XML（通过jackson和castor）与 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;sergent&lt;/li&gt;
&lt;li&gt;work
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;The post is brought to you by &lt;a href="http://fedorahosted.org/lekhonee"&gt;lekhonee&lt;/a&gt; v0.7&lt;/p&gt;

&lt;p&gt;糖果项目的后端用Java编写，我负责service gateway的开发（暂且叫sergent），服务以Java接口+Annotation的形式声明，与Spring集成使用，Java对象被序列化为JSON和XML（通过jackson和castor）与外部系统交互。专门的JSON Schema和XML Schema是可选的，系统交互通过简明的文档和人工确认。&lt;/p&gt;

&lt;p&gt;RPC框架是跨进程、跨系统交互的重要工具，RPC框架中又包括远程调用、网络传输和序列化反序列化等等部分。流行的工具包括Facebook的&lt;a href="http://incubator.apache.org/thrift/"&gt;thrift&lt;/a&gt;，Google的&lt;a href="http://code.google.com/p/protobuf/"&gt;Protobuf&lt;/a&gt;和原先Hadoop项目下的&lt;a href="http://avro.apache.org/"&gt;avro&lt;/a&gt;。其中thrift包含远程调用、反序列化、网络等等全部的功能。Protobuf本身是一个序列化反序列化库，另有很多&lt;a href="http://code.google.com/p/protobuf/wiki/ThirdPartyAddOns#RPC_Implementations"&gt;第三方RPC实现&lt;/a&gt;，avro目前除了序列化和反序列化的功能，也包含了ipc的HTTP Server和SocketServer等实现。在序列化的格式方面，Thrift支持JSON和二进制协议，Protobuf本身&lt;a href="http://code.google.com/p/protobuf/issues/detail?id=82"&gt;仅有二进制支持&lt;/a&gt;，但已经存在&lt;a href="http://code.google.com/p/protobuf-java-format/"&gt;第三方的其他格式实现&lt;/a&gt;。 avro原生支持二进制和JSON格式。&lt;/p&gt;

&lt;p&gt;从效率上来说，二进制方式的序列化要比文本方式的快。Google Code上（最近迁往了github）有一个&lt;a href="http://wiki.github.com/eishay/jvm-serializers/"&gt;tpc项目&lt;/a&gt;（thrift-protobuf-compare），根据这个项目的最新的比较结果（与原先不同）：
&lt;img src="http://chart.apis.google.com/chart?chtt=total&amp;chf=c||lg||0||FFFFFF||1||76A4FB||0|bg||s||EFEFEF&amp;chs=700x426&amp;chd=t:3234,4375,4583,5094,5712,6949,7199,7817,9228,9532,11160,11265,12575,12698,13233,14117,14950,16244,17187,23282,28258,31018,37012,43989,61663,74099,113707,141689,218143,225905,227399,227943,228237&amp;chds=0,251061.7483&amp;chxt=y&amp;chxl=0:|woodstox-xstream|fastinfo-xstream|xml%2Fxstream|aalto-xstream|json%2Fgoogle-gson|json%2Fprotobuf|scala%2Fjava-built-in|java-built-in|xml%2Fxstream%2Bc|fastinfo-xstream%2Bc|woodstox-xstream%2Bc|xml%2Fmanual-fastinfo|aalto-xstream%2Bc|hessian|json%2Fjackson-databind|xml%2Fmanual-woodstox|xml%2Fjavolution|json%2Fprotostuff-runtime|avro-generic|json%2Fprotostuff-core|avro|json%2Fjackson-manual|xml%2Fmanual-aalto|thrift-compact|thrift|scala%2Fsbinary|protobuf%2Factivemq%2Balt|protobuf|protobuf%2Fprotostuff-runtime|protobuf%2Fprotostuff-core|kryo|kryo-opt|java-manual&amp;chm=N%20*f*,000000,0,-1,10&amp;lklk&amp;chdlp=t&amp;chco=660000|660033|660066|660099|6600CC|6600FF|663300|663333|663366|663399|6633CC|6633FF|666600|666633|666666&amp;cht=bhg&amp;chbh=6,0,6&amp;nonsense=aaa.png" title="" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;protobuf成为了三者中耗时最少的框架，之后是thrift和avro，这次avro的耗时甚至超过了文本方式的jackson（主要在反序列化上）。&lt;/p&gt;

&lt;p&gt;但是二进制协议通常都需要定义Schema，thrift / protobuf / avro三者各自定义了Schema的格式，没有类似XSD和Json Schema的统一标准，也就是说，当你需要传输一个对象，就要为它编写一个Schema文件。按照通常的习惯，都是先编写Schema，然后通过命令行工具或者自动构建工具来生成Java source。对于新系统还好说，对旧系统这个改造就比较麻烦了。另外，二进制协议不便于调试，所以各个thrift/protobuf/avro先后也都有JSON的实现，在文本的序列化格式上，JSON对XML的优势是全方位的。&lt;/p&gt;

&lt;p&gt;所以综合起来，很难说有一种完美的解决方案。二进制协议的效率高，但是改造、编写Schema的代价并不小，还要面对核心Model被绑架到具体框架的风险。文本协议开发简便，不需要Schema，直接POJO就可以序列化和反序列化，但是在时间和空间上都不如二进制的方式。&lt;/p&gt;

&lt;p&gt;补充&lt;br /&gt;
从tpc项目的结果上看，kryo在时间、空间上都击败了所有对手，而且，kryo的API非常简洁，不需要Schema文件就可以序列化POJO，听起来太完美了，看来以后sergent要借鉴一下的。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;补充 2010-06-14&lt;/strong&gt;
发现avro现在也有ReflectDatumReader和ReflectDatumWriter，可以通过反射内部自动映射生成Schema，可以尝试一下。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>ibatis infinite loop when getFirstResultSet</title><link href="https://sunng.info/blog/ibatis-infinite-loop-when-getfirstresultset.html" rel="alternate"></link><published>2010-06-08T00:00:00+08:00</published><updated>2010-06-08T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-06-08:/blog/ibatis-infinite-loop-when-getfirstresultset.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;ibatis&lt;/li&gt;
&lt;li&gt;java
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;前几天上线后老大发现几台负载非常高，dump线程状态后发现多个线程死循环在同一处，于是发现了ibatis的这个bug：&lt;br /&gt;
https://issues.apache.org/jira/browse/IBATIS-384&lt;br /&gt;
https://issues.apache.org/jira/browse/IBATIS-587&lt;/p&gt;

&lt;p&gt;在mysql数据库上，没 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;ibatis&lt;/li&gt;
&lt;li&gt;java
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;前几天上线后老大发现几台负载非常高，dump线程状态后发现多个线程死循环在同一处，于是发现了ibatis的这个bug：&lt;br /&gt;
https://issues.apache.org/jira/browse/IBATIS-384&lt;br /&gt;
https://issues.apache.org/jira/browse/IBATIS-587&lt;/p&gt;

&lt;p&gt;在mysql数据库上，没有结果集时，&lt;em&gt;stmt.getUpdateCount()&lt;/em&gt;会返回0，而非-1.&lt;br /&gt;
ibatis 2.4.3&lt;br /&gt;
[cc lang="java"]&lt;br /&gt;
  private ResultSet getFirstResultSet(StatementScope scope, Statement stmt) throws SQLException {&lt;br /&gt;
    ResultSet rs = null;&lt;br /&gt;
    boolean hasMoreResults = true;&lt;br /&gt;
    while (hasMoreResults) {&lt;br /&gt;
      rs = stmt.getResultSet();&lt;br /&gt;
      if (rs != null) {&lt;br /&gt;
        break;&lt;br /&gt;
      }&lt;br /&gt;
      hasMoreResults = moveToNextResultsIfPresent(scope, stmt);&lt;br /&gt;
    }&lt;br /&gt;
    return rs;&lt;br /&gt;
  }&lt;/p&gt;

&lt;p&gt;  private boolean moveToNextResultsIfPresent(StatementScope scope, Statement stmt) throws SQLException {&lt;br /&gt;
    boolean moreResults;&lt;br /&gt;
    // This is the messed up JDBC approach for determining if there are more results&lt;br /&gt;
    moreResults = !(((moveToNextResultsSafely(scope, stmt) == false) &amp;&amp; (stmt.getUpdateCount() == -1)));&lt;br /&gt;
    return moreResults;&lt;br /&gt;
  }&lt;/p&gt;

&lt;p&gt;  private boolean moveToNextResultsSafely(StatementScope scope, Statement stmt) throws SQLException {&lt;br /&gt;
    if (forceMultipleResultSetSupport(scope) || stmt.getConnection().getMetaData().supportsMultipleResultSets()) {&lt;br /&gt;
      return stmt.getMoreResults();&lt;br /&gt;
    }&lt;br /&gt;
    return false;&lt;br /&gt;
  }&lt;/p&gt;

&lt;p&gt;[/cc]&lt;br /&gt;
moreResults恒为真，程序出现死循环。&lt;/p&gt;

&lt;p&gt;在mybatis 2.5的代码里，这部分已经修改为：&lt;br /&gt;
[cc lang="java"]&lt;br /&gt;
  private boolean moveToNextResultsIfPresent(StatementScope scope, Statement stmt) throws SQLException {&lt;br /&gt;
    boolean moreResults;&lt;br /&gt;
    // This is the messed up JDBC approach for determining if there are more results&lt;br /&gt;
    boolean movedToNextResultsSafely = moveToNextResultsSafely(scope, stmt);&lt;br /&gt;
    int updateCount = stmt.getUpdateCount();&lt;/p&gt;

&lt;p&gt;    moreResults = !(!movedToNextResultsSafely &amp;&amp; (updateCount == -1));&lt;/p&gt;

&lt;p&gt;    //ibatis-384: workaround for mysql not returning -1 for stmt.getUpdateCount()&lt;br /&gt;
    if (moreResults == true){&lt;br /&gt;
        moreResults = !(!movedToNextResultsSafely &amp;&amp; !isMultipleResultSetSupportPresent(scope, stmt));&lt;br /&gt;
    }&lt;/p&gt;

&lt;p&gt;    return moreResults;&lt;br /&gt;
  }&lt;/p&gt;

&lt;p&gt;  private boolean moveToNextResultsSafely(StatementScope scope, Statement stmt) throws SQLException {&lt;br /&gt;
    if (isMultipleResultSetSupportPresent(scope, stmt)) {&lt;br /&gt;
      return stmt.getMoreResults();&lt;br /&gt;
    }&lt;br /&gt;
    return false;&lt;br /&gt;
  }&lt;/p&gt;

&lt;p&gt;  /**&lt;br /&gt;
   * checks whether multiple result set support is present - either by direct support of the database driver or by forcing it&lt;br /&gt;
   */&lt;br /&gt;
  private boolean isMultipleResultSetSupportPresent(StatementScope scope,&lt;br /&gt;
          Statement stmt) throws SQLException {&lt;br /&gt;
      return forceMultipleResultSetSupport(scope) || stmt.getConnection().getMetaData().supportsMultipleResultSets();&lt;br /&gt;
  }&lt;br /&gt;
[/cc]&lt;br /&gt;
这部分条件判断实在很极致了。&lt;/p&gt;

&lt;p&gt;新的实现当getUpdateResult是0时，moreResults恒为真，这时再进行一个判断，如果是由于isMultipleResultSetSupportPresent为false导致了moveToNextResultsSafely为false，那么实际moreResults应是false&lt;/p&gt;

&lt;p&gt;MyBatis 2.5还有一个issue没有解决，离发布还有一些时间，这个问题只好签出新版本代码自己build了&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>t.sdo.com上线</title><link href="https://sunng.info/blog/tsdocomshang-xian.html" rel="alternate"></link><published>2010-06-08T00:00:00+08:00</published><updated>2010-06-08T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-06-08:/blog/tsdocomshang-xian.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;snda&lt;/li&gt;
&lt;li&gt;work
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;昨天搞到3点多，盛大糖果&lt;a href="http://t.sdo.com/"&gt;http://t.sdo.com/&lt;/a&gt;，昨天晚上终于开放IP给大家访问了。&lt;/p&gt;

&lt;p&gt;留一些邀请码给大家
&lt;ul&gt;
&lt;li&gt;2HlXyYKYz2&lt;/li&gt;
&lt;li&gt;2dKoCcQZYV&lt;/li&gt;
&lt;li&gt;oHBYhO1JPw&lt;/li&gt;
&lt;li&gt;-~YTMDYmN+&lt;/li&gt;
&lt;li&gt;IXKYMq0kE@&lt;/li&gt;
&lt;li&gt;Ub-C@xk8~r&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;我们后 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;snda&lt;/li&gt;
&lt;li&gt;work
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;昨天搞到3点多，盛大糖果&lt;a href="http://t.sdo.com/"&gt;http://t.sdo.com/&lt;/a&gt;，昨天晚上终于开放IP给大家访问了。&lt;/p&gt;

&lt;p&gt;留一些邀请码给大家
&lt;ul&gt;
&lt;li&gt;2HlXyYKYz2&lt;/li&gt;
&lt;li&gt;2dKoCcQZYV&lt;/li&gt;
&lt;li&gt;oHBYhO1JPw&lt;/li&gt;
&lt;li&gt;-~YTMDYmN+&lt;/li&gt;
&lt;li&gt;IXKYMq0kE@&lt;/li&gt;
&lt;li&gt;Ub-C@xk8~r&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;我们后端服务从3月初开始正式开发，11个成员，加了三个多月的班。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Python获取本机IP</title><link href="https://sunng.info/blog/pythonhuo-qu-ben-ji-ip.html" rel="alternate"></link><published>2010-06-05T00:00:00+08:00</published><updated>2010-06-05T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-06-05:/blog/pythonhuo-qu-ben-ji-ip.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;除了用&lt;a href="http://alastairs-place.net/netifaces/"&gt;inetface&lt;/a&gt;库和分析ifconfig输出以外，一个比较简单的获取本机IP的方法：&lt;br /&gt;
[cc lang="python"]&lt;br /&gt;
import socket&lt;br /&gt;
def get_local_ip():&lt;br /&gt;
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)&lt;br /&gt;
    s.connect(("sdo.com",80))&lt;br /&gt;
    return …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;除了用&lt;a href="http://alastairs-place.net/netifaces/"&gt;inetface&lt;/a&gt;库和分析ifconfig输出以外，一个比较简单的获取本机IP的方法：&lt;br /&gt;
[cc lang="python"]&lt;br /&gt;
import socket&lt;br /&gt;
def get_local_ip():&lt;br /&gt;
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)&lt;br /&gt;
    s.connect(("sdo.com",80))&lt;br /&gt;
    return s.getsockname()[0]&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;参考 http://stackoverflow.com/questions/166506/finding-local-ip-addresses-in-python&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Alexandria with Douban support</title><link href="https://sunng.info/blog/alexandria-with-douban-support.html" rel="alternate"></link><published>2010-05-25T00:00:00+08:00</published><updated>2010-05-25T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-05-25:/blog/alexandria-with-douban-support.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;opensource
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;迟到的新闻了，5月8号，GNOME桌面的书籍管理程序Alexandria发布了0.6.6 Beta2。上个月底我提交的豆瓣支持已经被加入了标准发布的打包里，这里可以看 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;opensource
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;迟到的新闻了，5月8号，GNOME桌面的书籍管理程序Alexandria发布了0.6.6 Beta2。上个月底我提交的豆瓣支持已经被加入了标准发布的打包里，这里可以看到Release note
&lt;a href="http://alexandria.rubyforge.org/news/2010-05-08--0.6.6beta2-released.html"&gt;http://alexandria.rubyforge.org/news/2010-05-08--0.6.6beta2-released.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;最近半年忙里偷闲给开源软件做一点贡献，上次&lt;a href="http://live.gnome.org/DeskbarApplet/Extending"&gt;gnome deskbar wiki&lt;/a&gt;名字是自己加上去的，这次在alexandria里终于算是得到了一些认可，再接再厉，嗯！&lt;/p&gt;

&lt;p&gt;各位用开源桌面的读者是不是看在这个份上也装一下这个软件啊。。。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>我的卡卡颂</title><link href="https://sunng.info/blog/wo-de-qia-qia-song.html" rel="alternate"></link><published>2010-05-24T00:00:00+08:00</published><updated>2010-05-24T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-05-24:/blog/wo-de-qia-qia-song.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;boardgame&lt;/li&gt;
&lt;li&gt;carcassonne
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;口水了一年多的入门桌游，在山寨高仿版本泛滥的时候，我终于拥有了属于自己的。
&lt;a href="http://www.flickr.com/photos/40741608@N08/4635772206/" title="carcassonne by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4062/4635772206_044925a923.jpg" width="500" height="335" alt="carcassonne" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;卡卡颂规则简单 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;boardgame&lt;/li&gt;
&lt;li&gt;carcassonne
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;口水了一年多的入门桌游，在山寨高仿版本泛滥的时候，我终于拥有了属于自己的。
&lt;a href="http://www.flickr.com/photos/40741608@N08/4635772206/" title="carcassonne by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4062/4635772206_044925a923.jpg" width="500" height="335" alt="carcassonne" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;卡卡颂规则简单，号称是最佳入门桌游。除了基本版以外，后来又陆陆续续除了很多扩展，更有传闻说还有一款投石机扩展，提供一个木制的简易投石机让卡卡松变成了动作类游戏。我最喜欢卡卡颂的地方除了本身的趣味性之外，还要数最后游戏结束时的场面，让人看了真有成就感。（本来这里有幅图的，鉴于背景是床单，就不展示给大家了）&lt;/p&gt;

&lt;p&gt;原版的&lt;a href="http://www.amazon.com/Rio-Grande-Games-Carcassonne-Big/dp/B001UH9FHQ/ref=sr_1_2?ie=UTF8&amp;s=toys-and-games&amp;qid=1274712191&amp;sr=8-2"&gt;卡卡颂big box 2&lt;/a&gt;（所谓全集），在亚马逊上要卖到64美元。而淘宝上山寨的原版加四合一扩展大概也就不到90元（big box比这个多了一个修道院与市长扩展），关于这个我也不说什么了。&lt;/p&gt;

&lt;p&gt;第一次玩卡卡颂是全年三月底左右和工作室的上海帮在桌游店，那是第一次玩桌游，印象那叫一个深刻。第二次是毕业前和原来寝室同学一起玩他自己买的Bigbox（那叫一个阔气）。嗯，要提一下，第一次组织游戏的童鞋后来去了伦敦，第二次组织游戏童鞋后来去了米国。等过几天回南京了，我再找那位要去倭国的童鞋来两局，进一步提升一下国际影响。&lt;/p&gt;

&lt;p&gt;我还是个桌游新手，最近盯上了冷战背景的&lt;a href="http://www.amazon.com/Robinson-Gamestore-Twilight-Struggle/dp/B000TQ17FC/ref=sr_1_1?ie=UTF8&amp;s=toys-and-games&amp;qid=1274712752&amp;sr=1-1"&gt;Twilight Struggle&lt;/a&gt;， 心里有痒痒的，希望能有一笔横财，让我好不心痛地拿下它。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>'Maven recipe #1: Test Java code with groovy'</title><link href="https://sunng.info/blog/maven-recipe-1-test-java-code-with-groovy.html" rel="alternate"></link><published>2010-05-19T00:00:00+08:00</published><updated>2010-05-19T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-05-19:/blog/maven-recipe-1-test-java-code-with-groovy.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;groovy&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;maven
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;问题： 受够了，不想写Java了，写个含有数据的map还要new出来一个一个put进去，想用groovy解决单元测试&lt;br /&gt;
解决： gmaven+groovy eclipse插件可以解决这个需求&lt;br /&gt;
在pom.xml中添加gmaven的依赖，注意，仅用来测试。老大不让生 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;groovy&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;maven
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;问题： 受够了，不想写Java了，写个含有数据的map还要new出来一个一个put进去，想用groovy解决单元测试&lt;br /&gt;
解决： gmaven+groovy eclipse插件可以解决这个需求&lt;br /&gt;
在pom.xml中添加gmaven的依赖，注意，仅用来测试。老大不让生产代码里有不可靠的东西。&lt;br /&gt;
[cc lang="xml"]&lt;br /&gt;
...&lt;br /&gt;
        &lt;dependency&gt;
            &lt;groupid&gt;org.codehaus.groovy.maven.runtime&lt;/groupid&gt;
            &lt;artifactid&gt;gmaven-runtime-default&lt;/artifactid&gt;
            &lt;version&gt;1.0-rc-3&lt;/version&gt;
            &lt;scope&gt;test&lt;/scope&gt;
        &lt;/dependency&gt;
...&lt;br /&gt;
            &lt;plugin&gt;
                &lt;groupid&gt;org.codehaus.groovy.maven&lt;/groupid&gt;
                &lt;artifactid&gt;gmaven-plugin&lt;/artifactid&gt;
                &lt;version&gt;1.0-rc-3&lt;/version&gt;
                &lt;executions&gt;
                    &lt;execution&gt;
                        &lt;goals&gt;
                            &lt;goal&gt;generateStubs&lt;/goal&gt;
                            &lt;goal&gt;compile&lt;/goal&gt;
                            &lt;goal&gt;generateTestStubs&lt;/goal&gt;
                            &lt;goal&gt;testCompile&lt;/goal&gt;
                        &lt;/goals&gt;
                    &lt;/execution&gt;
                &lt;/executions&gt;
            &lt;/plugin&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;在eclipse中新建source folder, &lt;em&gt;src/test/groovy&lt;/em&gt;，在其中创建groovy test case即可&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>'Maven recipe #0'</title><link href="https://sunng.info/blog/maven-recipe-0.html" rel="alternate"></link><published>2010-05-16T00:00:00+08:00</published><updated>2010-05-16T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-05-16:/blog/maven-recipe-0.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;maven&lt;/li&gt;
&lt;li&gt;recipe
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;问题： 多个root pom的dependencyManagement有重复的内容，希望统一管理。&lt;br /&gt;
解决：&lt;br /&gt;
新建一个空pom.xml，在dependencyManagement中指定这些依赖，如&lt;br /&gt;
[cc lang="xml"]
&lt;project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&lt;br /&gt;
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;maven&lt;/li&gt;
&lt;li&gt;recipe
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;问题： 多个root pom的dependencyManagement有重复的内容，希望统一管理。&lt;br /&gt;
解决：&lt;br /&gt;
新建一个空pom.xml，在dependencyManagement中指定这些依赖，如&lt;br /&gt;
[cc lang="xml"]
&lt;project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&lt;br /&gt;
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"&gt;&lt;/p&gt;

&lt;p&gt; &lt;modelversion&gt;4.0.0&lt;/modelversion&gt;
    &lt;groupid&gt;info.sunng&lt;/groupid&gt;
    &lt;artifactid&gt;root&lt;/artifactid&gt;
    &lt;packaging&gt;pom&lt;/packaging&gt;
    &lt;version&gt;0.0.1-SNAPSHOT&lt;/version&gt;
    &lt;dependencymanagement /&gt;&lt;/p&gt;

&lt;p&gt;     &lt;dependencies&gt;
            &lt;dependency&gt;
                &lt;groupid&gt;info.sunng&lt;/groupid&gt;
                &lt;artifactid&gt;X&lt;/artifactid&gt;
                &lt;version&gt;0.0.1-SNAPSHOT&lt;/version&gt;
            &lt;/dependency&gt;&lt;/dependencies&gt;&lt;/p&gt;

&lt;p&gt;     
    &lt;/p&gt;

&lt;p&gt;
[/cc]&lt;br /&gt;
将这个pom.xml部署到你的Maven私服。&lt;/p&gt;

&lt;p&gt;在其他root pom中添加这样的dependencyManagement&lt;br /&gt;
[cc lang="xml"]&lt;br /&gt;
....&lt;br /&gt;
            &lt;dependency&gt;
                &lt;groupid&gt;info.sunng&lt;/groupid&gt;
                &lt;artifactid&gt;root&lt;/artifactid&gt;
                &lt;version&gt;0.0.1-SNAPSHOT&lt;/version&gt;
                &lt;type&gt;pom&lt;/type&gt;
                &lt;scope&gt;import&lt;/scope&gt;
            &lt;/dependency&gt;
....&lt;br /&gt;
[/cc]&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>开始实践Poromodo Technique</title><link href="https://sunng.info/blog/kai-shi-shi-jian-poromodo-technique.html" rel="alternate"></link><published>2010-05-12T00:00:00+08:00</published><updated>2010-05-12T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-05-12:/blog/kai-shi-shi-jian-poromodo-technique.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;work
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;看了@juvenxv的博客介绍&lt;a href="http://book.douban.com/subject/4199701/"&gt;Poromodo Technique Illustrated&lt;/a&gt;一书，结合我现在低下的工作效率，我决定实践一下这个方法。
&lt;ul&gt;
    &lt;li&gt;一张TODO表记录今天的计划&lt;/li&gt;
    &lt;li&gt;一张Activity Inventory表用于记录长期的任务，标记已完 …&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;work
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;看了@juvenxv的博客介绍&lt;a href="http://book.douban.com/subject/4199701/"&gt;Poromodo Technique Illustrated&lt;/a&gt;一书，结合我现在低下的工作效率，我决定实践一下这个方法。
&lt;ul&gt;
    &lt;li&gt;一张TODO表记录今天的计划&lt;/li&gt;
    &lt;li&gt;一张Activity Inventory表用于记录长期的任务，标记已完成的任务&lt;/li&gt;
    &lt;li&gt;一张Record表统计每天完成的任务数量&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;一个Poromodo持续25分钟，在这25分钟内只专注当前任务。Poromodo间隙用来给大脑做缓冲，清空前一个任务的影响，以便下一个Poromodo开始时快速切换到相应任务。&lt;/p&gt;

&lt;p&gt;应对Interruption，对internal的accept, record &amp; continue，一个poromodo作为一个原子单元不可被打断，一旦被打断应该重新开始。&lt;br /&gt;
对external的，评估重要性，分别拒绝、延后、安排时间、安排回调。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>交通游戏OpenTTD</title><link href="https://sunng.info/blog/jiao-tong-you-xi-openttd.html" rel="alternate"></link><published>2010-05-02T00:00:00+08:00</published><updated>2010-05-02T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-05-02:/blog/jiao-tong-you-xi-openttd.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;opensource
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;我总是对这种城市建设、交通规划一类的游戏欲罢不能，以前在Linux上一直只是玩&lt;a href="http://lincity-ng.berlios.de/"&gt;Lincity&lt;/a&gt;，后来发现了纯交通类的&lt;a href="http://www.simutrans.com/"&gt;simutrans&lt;/a&gt;，最近 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;opensource
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;我总是对这种城市建设、交通规划一类的游戏欲罢不能，以前在Linux上一直只是玩&lt;a href="http://lincity-ng.berlios.de/"&gt;Lincity&lt;/a&gt;，后来发现了纯交通类的&lt;a href="http://www.simutrans.com/"&gt;simutrans&lt;/a&gt;，最近升级到Ubuntu10.04以后，终于可以玩这个六年磨一剑，精雕细琢的&lt;a href="http://openttd.org"&gt;OpenTTD&lt;/a&gt;了。&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/4571371260/" title="Chindhattan Springs Transport, 19th Dec 1953 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm4.static.flickr.com/3521/4571371260_ce28e57779.jpg" width="500" height="284" alt="Chindhattan Springs Transport, 19th Dec 1953" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;可以建造各种客运货运的汽车站、火车站，机场，码头等等，可以建设道路、铁路，可以购买公交车、卡车、火车头、火车车厢等等。可惜我玩了大半天还不知道怎么盈利。。。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>五月一号</title><link href="https://sunng.info/blog/wu-yue-yi-hao.html" rel="alternate"></link><published>2010-05-01T00:00:00+08:00</published><updated>2010-05-01T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-05-01:/blog/wu-yue-yi-hao.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;Life&lt;/li&gt;
&lt;li&gt;ruby
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;今天值得说的事情挺多的。首先，Ubuntu 10.04发布以后，Sun JDK 被放到了partner 仓库里，要使用Sun JDK，添加这个仓库：
&lt;em&gt;sudo apt-add-repository "deb http …&lt;/em&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;Life&lt;/li&gt;
&lt;li&gt;ruby
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;今天值得说的事情挺多的。首先，Ubuntu 10.04发布以后，Sun JDK 被放到了partner 仓库里，要使用Sun JDK，添加这个仓库：
&lt;em&gt;sudo apt-add-repository "deb http://archive.canonical.com/ lucid partner"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;这是悲剧的开始，早晨七点四十起床，添加了这个仓库，把机器上的Sun JDK升级到6u20，为此到了八点三十五才出门。现在没有自行车不比从前了，等公交加上下来走到公司最后九点零六分打上卡。进了办公室一看大家整整齐齐地坐着了，这才听到悲剧：三薪工作日不能迟到，否则只算半天。就为了6分钟，一天半的工资没了。这事现在想起来我都郁闷，发自内心的郁闷啊。&lt;/p&gt;

&lt;p&gt;在上班的公交车上恰好收到邮件，是rubyforge的更新，昨天提交的patch已经被接受了，放进了svn仓库，会随Alexandria的0.6.6版本发布。项目的负责人修改了代码，把我那些\t都换成了空格。另外，豆瓣的API接口是json格式的，解析需要依赖libjson-ruby1.8这个库，这段代码被负责人修改了，真的很神奇，原来json可以就这样被转换成yaml&lt;br /&gt;
[cc lang="ruby"]&lt;br /&gt;
def json2yaml(json)&lt;br /&gt;
    # insert spaces after : and , except within strings&lt;br /&gt;
    yaml = json.gsub(/(\:|\,)([^A-Za-z\/\\])/) do |match|&lt;br /&gt;
        "#{$1} #{$2}"&lt;br /&gt;
    end&lt;br /&gt;
    yaml.gsub!(/\\\//, '/') # unescape forward slashes&lt;br /&gt;
    yaml&lt;br /&gt;
end&lt;br /&gt;
[/cc]&lt;br /&gt;
震惊了，不过我之前也太了解yaml，也许这个技巧在ruby世界是众所周知的吧。这下json模块的依赖就可以去除了，真没想到会这么简单。&lt;/p&gt;

&lt;p&gt;想起来我给另一个开源软件fantasdic提交的google translate的patch，作者已经认可了，但是至今还没有被加进仓库里。这位作者在日本读书不亦乐乎看来心思已经完全不在这个项目上了。&lt;/p&gt;

&lt;p&gt;下午下班回家又是坐公交车，越想越忿忿，晚上又去买了一辆新自行车，于是今天基本上就算是白干了。之前的自行车被偷，多半的原因是后来买的锁强度太弱。先前的锁呢又因为生锈最后没法用了，归根结底，还是我保养地不好。其实保养锁很简单的，买个肉包，把里面的肉吃掉之后，那钥匙蘸蘸剩下的油，放到锁眼里捅捅就成。&lt;/p&gt;

&lt;p&gt;五一了，世博了。和奥运会一样，还是和我没有什么直接关系。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Douban provider for Alexandria</title><link href="https://sunng.info/blog/douban-provider-for-alexandria.html" rel="alternate"></link><published>2010-04-30T00:00:00+08:00</published><updated>2010-04-30T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-04-30:/blog/douban-provider-for-alexandria.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;Douban&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;ruby
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://alexandria.rubyforge.org/"&gt;Alexandra&lt;/a&gt; is a desktop book collection manager on gnome written in ruby. With an extensible architecture, Alexandria uses different sources to retrieve book's data, including Amazon and many local sites.&lt;/p&gt;

&lt;p&gt;Douban.com is considered to be most applicable data source …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;Douban&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;ruby
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://alexandria.rubyforge.org/"&gt;Alexandra&lt;/a&gt; is a desktop book collection manager on gnome written in ruby. With an extensible architecture, Alexandria uses different sources to retrieve book's data, including Amazon and many local sites.&lt;/p&gt;

&lt;p&gt;Douban.com is considered to be most applicable data source for books published in Chinese. So I write this provider according to Alexandria's SPI. Now it's possible to add Chinese books and manage reading lists.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/4564768299/" title="alexandria by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4046/4564768299_f424dc2be9.jpg" width="500" height="388" alt="alexandria" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now the code can be found in Alexandria's bug tracker:
&lt;a href="http://rubyforge.org/tracker/index.php?func=detail&amp;aid=28160&amp;group_id=205&amp;atid=865"&gt;http://rubyforge.org/tracker/index.php?func=detail&amp;aid=28160&amp;group_id=205&amp;atid=865&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, the patch file of book_providers.rb in that list is for svn trunk head version only. To use it with currently stable version of Ubuntu, first, make sure you have Alexandria version &lt;strong&gt;0.6.5-0ubuntu1&lt;/strong&gt; and &lt;strong&gt;libjson-ruby1.8&lt;/strong&gt; installed.&lt;/p&gt;

&lt;p&gt;Download douban.rb from rubyforge:
&lt;a href="http://rubyforge.org/tracker/download.php/205/865/28160/4923/douban.rb"&gt;http://rubyforge.org/tracker/download.php/205/865/28160/4923/douban.rb&lt;/a&gt;
Copy the file to /usr/lib/ruby/1.8/alexandria/book_providers/ with super user privileges.&lt;br /&gt;
Use this patch to /usr/lib/ruby/1.8/alexandria/book_providers.rb&lt;br /&gt;
[cc lang="ruby"]&lt;br /&gt;
310a311,323
&lt;     begin
&lt;       begin
&lt;         require 'json'
&lt;       rescue LoadError
&lt;         require 'rubygems'
&lt;         require 'json'
&lt;       end
&lt;       require 'alexandria/book_providers/douban'
&lt;     rescue LoadError =&lt; ex
&lt;       log.error{ex}
&lt;       log.warn {'Fail to load douban as provider'}
&lt;     end
&lt;&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;Feel free to report issue here.&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>在苏州河</title><link href="https://sunng.info/blog/zai-su-zhou-he.html" rel="alternate"></link><published>2010-04-25T00:00:00+08:00</published><updated>2010-04-25T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-04-25:/blog/zai-su-zhou-he.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;D60
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;今天又到苏州河附近拍，发现在四川路桥对面的邮政博物馆楼顶拍浦东效果甚佳，闹中取静。另外在河 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;D60
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;今天又到苏州河附近拍，发现在四川路桥对面的邮政博物馆楼顶拍浦东效果甚佳，闹中取静。另外在河南中路和宁波路路口有一家小杨生煎店，人相对其他地方比较少。&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/4550135617/" title="DSC_0015 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm1.static.flickr.com/130/4550135617_8a002f5d83.jpg" width="500" height="335" alt="DSC_0015" /&gt;&lt;/a&gt;
&lt;a href="http://www.flickr.com/photos/40741608@N08/4550774618/" title="DSC_0021 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4057/4550774618_72de9e945c.jpg" width="500" height="335" alt="DSC_0021" /&gt;&lt;/a&gt;
&lt;a href="http://www.flickr.com/photos/40741608@N08/4550775202/" title="DSC_0022 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4033/4550775202_41d92fc9d4.jpg" width="500" height="335" alt="DSC_0022" /&gt;&lt;/a&gt;
&lt;a href="http://www.flickr.com/photos/40741608@N08/4550775526/" title="DSC_0023 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4019/4550775526_020cba3039.jpg" width="500" height="335" alt="DSC_0023" /&gt;&lt;/a&gt;
&lt;a href="http://www.flickr.com/photos/40741608@N08/4550137615/" title="DSC_0055 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4069/4550137615_dfddeef846.jpg" width="335" height="500" alt="DSC_0055" /&gt;&lt;/a&gt;
&lt;a href="http://www.flickr.com/photos/40741608@N08/4550776288/" title="DSC_0067 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4049/4550776288_89fa9919db.jpg" width="335" height="500" alt="DSC_0067" /&gt;&lt;/a&gt;
&lt;a href="http://www.flickr.com/photos/40741608@N08/4550781092/" title="DSC_0078 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4022/4550781092_1c48fe79d2.jpg" width="335" height="500" alt="DSC_0078" /&gt;&lt;/a&gt;
&lt;a href="http://www.flickr.com/photos/40741608@N08/4550781268/" title="DSC_0079 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4016/4550781268_cb2dc15328.jpg" width="335" height="500" alt="DSC_0079" /&gt;&lt;/a&gt;
&lt;a href="http://www.flickr.com/photos/40741608@N08/4550781990/" title="DSC_0082 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4058/4550781990_97aa9e4ebe.jpg" width="335" height="500" alt="DSC_0082" /&gt;&lt;/a&gt;
&lt;a href="http://www.flickr.com/photos/40741608@N08/4550782612/" title="DSC_0084 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4044/4550782612_f18eac8bda.jpg" width="335" height="500" alt="DSC_0084" /&gt;&lt;/a&gt;
&lt;a href="http://www.flickr.com/photos/40741608@N08/4550783120/" title="DSC_0085 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4047/4550783120_f10395f159.jpg" width="335" height="500" alt="DSC_0085" /&gt;&lt;/a&gt;
&lt;a href="http://www.flickr.com/photos/40741608@N08/4550145171/" title="DSC_0091 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4031/4550145171_64c65a42a0.jpg" width="500" height="335" alt="DSC_0091" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;最后一张景不错，不过拍得曝光过度了，处理了一下
&lt;a href="http://www.flickr.com/photos/40741608@N08/4550776010/" title="DSC_0054 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4056/4550776010_4073d1a16f.jpg" width="500" height="335" alt="DSC_0054" /&gt;&lt;/a&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>今天在朱家角了</title><link href="https://sunng.info/blog/jin-tian-zai-zhu-jia-jiao-liao.html" rel="alternate"></link><published>2010-04-24T00:00:00+08:00</published><updated>2010-04-24T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-04-24:/blog/jin-tian-zai-zhu-jia-jiao-liao.html</id><content type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;D60
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/4548344670/" title="DSC_0026 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4046/4548344670_336a34ea42.jpg" width="335" height="500" alt="DSC_0026" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/4548345184/" title="DSC_0032 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4051/4548345184_427f8faf9c.jpg" width="335" height="500" alt="DSC_0032" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/4547709891/" title="DSC_0038 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4057/4547709891_9679476751.jpg" width="335" height="500" alt="DSC_0038" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/4548345712/" title="DSC_0088 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4034/4548345712_8ebd36013f.jpg" width="332" height="500" alt="DSC_0088" /&gt;&lt;/a&gt;
这张手持着暴了两秒，拿到电脑上一看还是抖了。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>My First Hello World Web App Using Compojure</title><link href="https://sunng.info/blog/my-first-hello-world-web-app-using-compojure.html" rel="alternate"></link><published>2010-04-24T00:00:00+08:00</published><updated>2010-04-24T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-04-24:/blog/my-first-hello-world-web-app-using-compojure.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;compojure&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;lein
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://github.com/weavejester/compojure"&gt;Compojure&lt;/a&gt;是一个用&lt;a href="http://clojure.org/"&gt;Clojure&lt;/a&gt;写成的类似&lt;a href="http://www.sinatrarb.com/"&gt;Sinatra&lt;/a&gt;的Web框架。&lt;a href="http://github.com/technomancy/leiningen"&gt;Leiningen&lt;/a&gt;是一个新的Clojure构建工具，它用Maven来处理依赖管理，而通过封装&lt;a href="http://github.com/stuarthalloway/lancet"&gt;Lancet&lt;/a&gt;（基于Ant）来实现build-in的task。&lt;/p&gt;

&lt;p&gt;以上 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;clojure&lt;/li&gt;
&lt;li&gt;compojure&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;lein
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://github.com/weavejester/compojure"&gt;Compojure&lt;/a&gt;是一个用&lt;a href="http://clojure.org/"&gt;Clojure&lt;/a&gt;写成的类似&lt;a href="http://www.sinatrarb.com/"&gt;Sinatra&lt;/a&gt;的Web框架。&lt;a href="http://github.com/technomancy/leiningen"&gt;Leiningen&lt;/a&gt;是一个新的Clojure构建工具，它用Maven来处理依赖管理，而通过封装&lt;a href="http://github.com/stuarthalloway/lancet"&gt;Lancet&lt;/a&gt;（基于Ant）来实现build-in的task。&lt;/p&gt;

&lt;p&gt;以上是背景介绍。以下是HelloWorld。&lt;/p&gt;

&lt;p&gt;创建一个目录作为工程目录&lt;br /&gt;
[cc lang="bash"]&lt;br /&gt;
mkdir compojure-app&lt;br /&gt;
cd compojure-app&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;像创建build.xml和pom.xml一样创建project.clj&lt;br /&gt;
[cc lang="lisp"]&lt;br /&gt;
(defproject info.sunng/compojure-app "0.0.1"&lt;br /&gt;
        :description "A demo app running on compojure framework"&lt;br /&gt;
        :dependencies [[org.clojure/clojure "1.1.0"]&lt;br /&gt;
                        [org.clojure/clojure-contrib "1.1.0"]&lt;br /&gt;
                        [compojure "0.3.2"]]&lt;br /&gt;
        :dev-dependencies [[leiningen/lein-swank "1.1.0"]]&lt;br /&gt;
        :main info.sunng.compojureapp.helloworld)&lt;br /&gt;
[/cc]&lt;br /&gt;
首行定义了项目的groupId, artifactId和version，其后的是maven风格的依赖定义，最后我们还定义了程序的主类。&lt;/p&gt;

&lt;p&gt;这个helloworld只有一个文件，被放在src/info/sunng/compojureapp/目录下&lt;br /&gt;
[cc lang="lisp"]&lt;br /&gt;
(ns info.sunng.compojureapp.helloworld (:gen-class) (:use compojure))&lt;/p&gt;

&lt;p&gt;(defroutes example-routes&lt;br /&gt;
    (GET "/" "Hello W0rld")&lt;br /&gt;
    (ANY "*" [404 "Page Not Found"]))&lt;/p&gt;

&lt;p&gt;(defn -main []&lt;br /&gt;
    (run-server {:port 8080} "/*" (servlet example-routes)))&lt;br /&gt;
[/cc]&lt;br /&gt;
中间就是Sinatra风格的URL映射定义，最后在主类中通过一个run-server方法以嵌入式的方式运行一个jetty&lt;/p&gt;

&lt;p&gt;回到工程目录，执行构建&lt;br /&gt;
[cc lang="bash"]&lt;br /&gt;
lein deps&lt;br /&gt;
lein compile&lt;br /&gt;
lein uberjar&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;通过uberjar可以将依赖通通打入一个jar包中，接着就可以通过
&lt;em&gt;java -jar compojure-app-standalone.jar &lt;/em&gt;
启动你的Web程序了，很酷吧&lt;/p&gt;

&lt;p&gt;除了这种方式，还可以通过
&lt;em&gt;lein swank&lt;/em&gt;
启动一个&lt;a href="http://github.com/technomancy/leiningen/tree/master/lein-swank"&gt;swank&lt;/a&gt; server（project.clj中定义了dev-dependency），再用Emacs的&lt;a href="http://common-lisp.net/project/slime/"&gt;SLIME&lt;/a&gt;交互式地运行程序。&lt;/p&gt;

&lt;p&gt;参考
&lt;ul&gt;
    &lt;li&gt;&lt;a href="http://kzar.co.uk/blog/view/simple-compojure-project"&gt;http://kzar.co.uk/blog/view/simple-compojure-project&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href="http://schipplock.de/2010/01/31/clojure-web-framework-compojure/"&gt;http://schipplock.de/2010/01/31/clojure-web-framework-compojure/&lt;/a&gt;&lt;/li&gt;
        &lt;li&gt;&lt;a href="http://zef.me/2470/building-clojure-projects-with-leiningen"&gt;http://zef.me/2470/building-clojure-projects-with-leiningen&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>XAuth.org &amp; OpenAddresses.org</title><link href="https://sunng.info/blog/xauthorg-openaddressesorg.html" rel="alternate"></link><published>2010-04-20T00:00:00+08:00</published><updated>2010-04-20T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-04-20:/blog/xauthorg-openaddressesorg.html</id><content type="html">&lt;ul&gt;
&lt;li&gt;他山
tags:&lt;/li&gt;
&lt;li&gt;opensource&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;XAuth.org 提供一个新的轻量级认证机制，他的机制可以参考：
&lt;a href="http://xauth.org/spec/"&gt;http://xauth.org/spec/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;OpenAddresses.org是上个月O'Reilly Where 2.0上发布的开放的Geocoding服务，类似OpenStreetMaps
&lt;a href="http://openaddresses.org/"&gt;http://openaddresses.org/&lt;/a&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>今天在植物园</title><link href="https://sunng.info/blog/jin-tian-zai-zhi-wu-yuan.html" rel="alternate"></link><published>2010-04-18T00:00:00+08:00</published><updated>2010-04-18T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-04-18:/blog/jin-tian-zai-zhi-wu-yuan.html</id><content type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;Shanghai
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/4531445458/" title="DSC_0033 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4008/4531445458_8b08c7df45.jpg" width="500" height="335" alt="DSC_0033" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/4530818599/" title="DSC_0038 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4010/4530818599_dfc527d063.jpg" width="500" height="335" alt="DSC_0038" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/4530825965/" title="DSC_0042 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4026/4530825965_dc3c25b47e.jpg" width="500" height="335" alt="DSC_0042" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/4530831935/" title="DSC_0043 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4054/4530831935_2c05fda26f.jpg" width="500" height="335" alt="DSC_0043" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/4530838699/" title="DSC_0060 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4006/4530838699_28b1558b4e.jpg" width="500" height="335" alt="DSC_0060" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/4530846071/" title="DSC_0062 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4039/4530846071_828351bc5d.jpg" width="500" height="335" alt="DSC_0062" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/4530854713/" title="DSC_0065 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4065/4530854713_71a97eec7b.jpg" width="500" height="335" alt="DSC_0065" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/4530876879/" title="DSC_0048 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4059/4530876879_b105722b03.jpg" width="500" height="335" alt="DSC_0048" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/4531496808/" title="DSC_0080 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4021/4531496808_c34b38910d.jpg" width="500" height="335" alt="DSC_0080" /&gt;&lt;/a&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>悲剧不止你一个</title><link href="https://sunng.info/blog/bei-ju-bu-zhi-ni-yi-ge.html" rel="alternate"></link><published>2010-04-14T00:00:00+08:00</published><updated>2010-04-14T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-04-14:/blog/bei-ju-bu-zhi-ni-yi-ge.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;apache
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;近日连连悲剧，今天一早收到root@apache.org发送的邮件：
&lt;strong&gt;issues.apache.org compromised: please update your password&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;事情发生在4月6日，大意是apache的jira被黑，怀疑内部的用户资料 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;apache
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;近日连连悲剧，今天一早收到root@apache.org发送的邮件：
&lt;strong&gt;issues.apache.org compromised: please update your password&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;事情发生在4月6日，大意是apache的jira被黑，怀疑内部的用户资料包括SHA-512加密的密码被黑客拷贝，请大家更新所有相关的密码，以免造成更严重的损失。&lt;/p&gt;

&lt;p&gt;详细的情况在这里：
&lt;a href="https://blogs.apache.org/infra/entry/apache_org_04_09_2010"&gt;https://blogs.apache.org/infra/entry/apache_org_04_09_2010&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;终于有个契机，把我的gmail密码更新一下了。这世上的悲剧啊。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>事与愿违</title><link href="https://sunng.info/blog/shi-yu-yuan-wei.html" rel="alternate"></link><published>2010-04-08T00:00:00+08:00</published><updated>2010-04-08T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-04-08:/blog/shi-yu-yuan-wei.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;hudson&lt;/li&gt;
&lt;li&gt;snda
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;本以为hudson to rescue，结果no silver bullet。本想着大家都遵守定时构建，一个小时一次，结果事实却是每个人都想一 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;hudson&lt;/li&gt;
&lt;li&gt;snda
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;本以为hudson to rescue，结果no silver bullet。本想着大家都遵守定时构建，一个小时一次，结果事实却是每个人都想一提交就构建自己的项目，然后立刻集成、部署。每个人都认为自己的事非常urgent，结果定时任务成了聋子的耳朵。逃避了每天手动构建，现在却还是频繁地要到hudson上去点点。工具再好，架不住违背工具哲学的使用。&lt;/p&gt;

&lt;p&gt;今年北京QCon大家是看不到我了。从1月份就跟老大提要参加，折腾到昨天公司终于开始报名了，结果就他娘的报了四个人。四个人实在是轮不到我了。老大还专门向高层替我争取一个名额，结果得到一个诡异的眼神。那高层啊，你要是以后不出人头地都对不起他那小眼神。&lt;/p&gt;

&lt;p&gt;也好，终于有个周末可以陪dearaprilfool出去玩了，最近太他娘的忙了。&lt;/p&gt;

&lt;p&gt;我真是羡慕goodtiger啊。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>We are using Hudson for continuous integration</title><link href="https://sunng.info/blog/we-are-using-hudson-for-continuous-integration.html" rel="alternate"></link><published>2010-04-06T00:00:00+08:00</published><updated>2010-04-06T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-04-06:/blog/we-are-using-hudson-for-continuous-integration.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;CI&lt;/li&gt;
&lt;li&gt;dev&lt;/li&gt;
&lt;li&gt;hudson&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;maven&lt;/li&gt;
&lt;li&gt;software&lt;/li&gt;
&lt;li&gt;tool
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;经历了持续两周的人肉集成，今天上午抢得一台Linux机器，终于尝试用hudson来替代人肉构建。&lt;/p&gt;

&lt;p&gt;Hudson的安装和配置远比想象的简单，只要下载发布的war包 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;CI&lt;/li&gt;
&lt;li&gt;dev&lt;/li&gt;
&lt;li&gt;hudson&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;maven&lt;/li&gt;
&lt;li&gt;software&lt;/li&gt;
&lt;li&gt;tool
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;经历了持续两周的人肉集成，今天上午抢得一台Linux机器，终于尝试用hudson来替代人肉构建。&lt;/p&gt;

&lt;p&gt;Hudson的安装和配置远比想象的简单，只要下载发布的war包，在相应的目录执行&lt;br /&gt;
nohup java -jar hudson.war &gt; hudson.log 2&gt;&amp;1 &amp;&lt;br /&gt;
即可启动到后台&lt;/p&gt;

&lt;p&gt;hudson的web图形界面可以胜任几乎全部工作。我们主要使用maven来构建项目，hudson提供了非常强大的功能：邮件提醒（通过插件支持twitter/jabber/irc提醒）；自动构建，除了基本的定时构建以外，hudson还会自动解析其管理的项目之间的依赖关系，从而实现级联的构建，这个功能非常震撼。&lt;/p&gt;

&lt;p&gt;刚刚上手以后我还安装了两个插件。一个是build-timeout插件，可以之间一次构建的超时时间：我们的项目中有老大写的交互式的maven配置插件，一旦这个插件在自动构建时运行会阻塞构建的流程。另一个是scp发布插件，可以自动scp一个文件到远程服务器上，我用这个插件来把构建版本发布到运行环境中，只要在适当的时候重启一下运行环境的服务器就可以实现部署了。不过scp插件由于上游依赖的问题貌似不支持putty生成的privatekey，这是暂时的美中不足。&lt;/p&gt;

&lt;p&gt;配置了整整一天，终于有了CI工具，我就彻底解放出来可以做其他事了。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Reset default cursor theme and web browser after installing kubuntu</title><link href="https://sunng.info/blog/reset-default-cursor-theme-and-web-browser-after-installing-kubuntu.html" rel="alternate"></link><published>2010-04-03T00:00:00+08:00</published><updated>2010-04-03T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-04-03:/blog/reset-default-cursor-theme-and-web-browser-after-installing-kubuntu.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;If you installed kubuntu parallel to ubuntu, you might find that the default browser is set to konqueror and cursor theme is oxygen which cannot modified by "gnome-default-applications-properties" and "gnome-appearance-properties".&lt;/p&gt;

&lt;p&gt;To fix this, use Debian update-alternatives tool:
&lt;em&gt;update-alternatives --config x-www-browser&lt;/em&gt;
&lt;em&gt;update-alternatives …&lt;/em&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;If you installed kubuntu parallel to ubuntu, you might find that the default browser is set to konqueror and cursor theme is oxygen which cannot modified by "gnome-default-applications-properties" and "gnome-appearance-properties".&lt;/p&gt;

&lt;p&gt;To fix this, use Debian update-alternatives tool:
&lt;em&gt;update-alternatives --config x-www-browser&lt;/em&gt;
&lt;em&gt;update-alternatives --config x-cursor-theme&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;All configurable options are listed at &lt;em&gt;/var/lib/dkpg/alternatives&lt;/em&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Update0</title><link href="https://sunng.info/blog/update0.html" rel="alternate"></link><published>2010-04-02T00:00:00+08:00</published><updated>2010-04-02T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-04-02:/blog/update0.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;snda&lt;/li&gt;
&lt;li&gt;spring
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;太忙了，真的太忙了，每天重复无数遍 mvn clean install -Dmaven.test.skip， 一不小心再遇上"cidaemon.exe"锁死文件无法删除，或者 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;snda&lt;/li&gt;
&lt;li&gt;spring
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;太忙了，真的太忙了，每天重复无数遍 mvn clean install -Dmaven.test.skip， 一不小心再遇上"cidaemon.exe"锁死文件无法删除，或者SVN报点Tree conflict，无法见证奇迹就只剩下悲剧了。都是重复的工作，但是在Windows下想做automation还是一筹莫展，比如要构建多个maven项目，也不知道怎么自动化地判断maven构建成功还是失败；再者在Windows里也不知道怎么自动化的scp文件到服务器上去，还要ssh执行重启服务的命令；手足无措啊。&lt;/p&gt;

&lt;p&gt;最严重的问题是每天忙的要命，但是并没有感觉有应有的收获或者长进什么的，折腾了半天做的事情还是自己熟悉的小方面，只是做得相对变态而已。每天新的情况让你根本来不及重构，而且严重的问题是，重构好了又有什么用呢，谁能想到后面又有什么变化把你自己引以为豪的设计通通推翻。程序员要考虑的事情太多了，常常要揣摩一些需求方的心思，偷偷摸摸地提前做一些准备。&lt;/p&gt;

&lt;p&gt;我现在已经有了一个annotation driven的自动暴露Java接口的框架。之前在CRL实习时，在Jazz上开发也是用一个类似的机制，当时是使用接口的命名规范来确定暴露，比如说有以get和post开头的接口都暴露为HTTP服务，就有很多类似postUpdate、postAdd之类的名字，昨天同事说这个名字太诡异。后来参考了淘宝Open平台的实现，通通的XML描述接口，服务名、方法名、参数名、约束通通写XML配置，我懒，也放弃这个方案了。最后选择用annotation，通过反射解析Java接口，实现的效果和前两者差不多，自认为相对名称的约定要灵活一些，相对无数XML描述符要简洁一些。&lt;/p&gt;

&lt;p&gt;后来还为方法的每一步调用都加上监听器，每一个环节都可以注册外部自定义的监听器，所有的核心功能之外的需求都放到自定义的监听器里处理，和框架核心剥离。&lt;/p&gt;

&lt;p&gt;再者，框架原本直接序列化接口返回的对象，后来需求有变，要加各种各样的包装。OK，弄一个WrapperFacotry的接口出去，想怎么搞也放到框架核心之外了。Spring的@Authwire(required=false)真是解决了许多问题，在实现InitializingBean初始化一个默认实现就可以了，这样在applicationContext里可以轻松控制外部功能。&lt;/p&gt;

&lt;p&gt;还有的发现，序列化的框架分别用的是castor和jackson，结果做profile的时候一比吓一跳，jackson序列话的时间占整个调用的20%，castor的就占到50%了，太可怕了，恐怕要试试其他实现了。&lt;/p&gt;

&lt;p&gt;细节扯多了，其实框架的核心都是一天写成的，后面加各种各样的功能花了很大精力。&lt;/p&gt;

&lt;p&gt;最近升级到UBuntu10.04已经可以见证奇迹了，眼看着Bug越来越少，之前启动打在tty1上的ureadahead错误日志现在也隐藏了。一切趋于完美，新indicator菜单很好用。&lt;/p&gt;

&lt;p&gt;清明回家，下午的火车。因为回家周一不能加班了，少了3倍的工资，悲凉啊。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>封闭开发2</title><link href="https://sunng.info/blog/feng-bi-kai-fa-2.html" rel="alternate"></link><published>2010-03-26T00:00:00+08:00</published><updated>2010-03-26T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-03-26:/blog/feng-bi-kai-fa-2.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;snda&lt;/li&gt;
&lt;li&gt;work
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;明天不用加班了，给大家汇报一下封闭开发的情况。这周最大的变化在于我们搬上了二楼专门又会 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;snda&lt;/li&gt;
&lt;li&gt;work
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;明天不用加班了，给大家汇报一下封闭开发的情况。这周最大的变化在于我们搬上了二楼专门又会议室改装的封闭开发室，终于可以在安静、宽敞的环境里做事了。以前巨大的办公室放眼望去他娘的全是人，现在在楼上平均一个人可以用两个桌子，台式机笔记本一边一个舒舒服服，实在是享受啊。而且这两天公司又批钱让我们买了很多零食（两大箱），不夸张地说我今天活活吃了一整天。&lt;/p&gt;

&lt;p&gt;项目进度还算可以，最近产品策划比较忙，大家可以趁着这间隙写实现。我主要负责为实现业务的童鞋们写一些节约时间的框架，所以和业务本身关联不大，这也是最值得庆幸的事情。&lt;/p&gt;

&lt;p&gt;我们组11个人，很有意思。我开玩笑说我们是433阵型，四个正式，3个外包，3个实习，剩下一个老大守门。虽说是工作了，可是这种感觉时长让我想起在工作室的时候，大家的关系和在学校里没什么两样。每天上班虽然很幸苦（这周一天都是14个小时），挣得也不多，不过倒是开心得很。&lt;/p&gt;

&lt;p&gt;我们组的项目4月9号要内部上线，真正的产品内测是5月18号，大家请期待。&lt;/p&gt;

&lt;p&gt;4月23号的北京QCon，今天看到&lt;a href="http://blog.goodtiger.net/"&gt;Goodtiger&lt;/a&gt;说他要去了，我在这里严重嫉妒一下。哎，我一月份的时候就跟老大说了今年QCon牛人很多，上次老大还说公司在准备订，结果最近忙成这个样子，估计是泡汤了。去也轮不到咱，悲凉啊。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>搞定gwibber</title><link href="https://sunng.info/blog/gao-ding-gwibber.html" rel="alternate"></link><published>2010-03-22T00:00:00+08:00</published><updated>2010-03-22T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-03-22:/blog/gao-ding-gwibber.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;gwibber&lt;/li&gt;
&lt;li&gt;pycurl&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;升级10.04之后，gwibber2.29开始使用pycurl来处理IO。结果一条更新都拿不下来，为此搜到很多相关、不相关的bug。最后终于自己找到问 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;gwibber&lt;/li&gt;
&lt;li&gt;pycurl&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;升级10.04之后，gwibber2.29开始使用pycurl来处理IO。结果一条更新都拿不下来，为此搜到很多相关、不相关的bug。最后终于自己找到问题，&lt;strong&gt;原来是pycurl.setopt方法传入字符串如果是unicode就会报typeerror&lt;/strong&gt;。而通过gwibber配置界面输入的所有字符都是unicode编码，于是问题也就简单了。做个处理，在 &lt;em&gt;/usr/lib/python2.6/dist-packages/gwibber/microblog/network.py&lt;/em&gt; 第18到21行：&lt;br /&gt;
[cc lang="python"]&lt;br /&gt;
    self.curl.setopt(pycurl.URL, str(url).encode('ascii'))&lt;/p&gt;

&lt;p&gt;    if username and password:&lt;br /&gt;
      self.curl.setopt(pycurl.USERPWD, ("%s:%s" % (username, password)).encode('ascii'))&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;另外，使用StatusNet的Twitter兼容API来做Twitter API代理，需要把代理的目录名设置为api，因为/api这个路径是写死在gwibber代码里的。通过界面输入的，只是代理的domain。&lt;/p&gt;

&lt;p&gt;说实在的gwibber还是挺烂的。&lt;br /&gt;
https://bugs.launchpad.net/bugs/542501&lt;br /&gt;
https://bugs.launchpad.net/bugs/543860&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>将！将！将！</title><link href="https://sunng.info/blog/jiang-jiang-jiang.html" rel="alternate"></link><published>2010-03-20T00:00:00+08:00</published><updated>2010-03-20T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-03-20:/blog/jiang-jiang-jiang.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;Life&lt;/li&gt;
&lt;li&gt;snda&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;ol&gt;
    &lt;li&gt;汇报一下近况，最近仍然是6*12小时的工作，这是第二个礼拜了。我们的进度还算是顺利，不过架不住产品那里 …&lt;/li&gt;&lt;/ol&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;Life&lt;/li&gt;
&lt;li&gt;snda&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;ol&gt;
    &lt;li&gt;汇报一下近况，最近仍然是6*12小时的工作，这是第二个礼拜了。我们的进度还算是顺利，不过架不住产品那里经常会有另他们自己拍案的新点子，这可苦了开发。可怜我们开发在产品面前基本没有什么话语权，连老大都只能说我们尽量支持。因为我负责的部分跟业务关系不是特别紧密，之前也算是有先见之明，坚决解耦，所以策划的变化对我影响不大，只是听见两旁的同事不断地重复“我悲剧了”。&lt;/li&gt;
    &lt;li&gt;10.04来了，迫不及待了又。结果升级的过程中遇到了无数依赖问题和冲突，apt几乎都无能为力，前天搞到1点多才睡，早晨7点起床又继续折腾。到了昨天中午终于可以进入桌面了，累得够呛，晚上又发烧了。&lt;/li&gt;
    &lt;li&gt;早晨起床忽然想起winter，现在的4square这么火，昨天又看到了比较mood的微博产品。哎，我们做的时候可是07年啊，也许是太超前了吧。&lt;/li&gt;
    &lt;li&gt;加班路上哼张楚的《将将将》，我真想再见张楚一回。&lt;/li&gt;
    &lt;li&gt;看见@dearaprilfool同学抄的《致橡树》，很有共鸣，女生们应该读一下。小时候读不算，这个年纪读才好。&lt;/li&gt;
    &lt;li&gt;新的一天开始了。&lt;/li&gt;&lt;/ol&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Firefox addon for wms development</title><link href="https://sunng.info/blog/firefox-addon-for-wms-development.html" rel="alternate"></link><published>2010-03-12T00:00:00+08:00</published><updated>2010-03-12T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-03-12:/blog/firefox-addon-for-wms-development.html</id><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;firefox&lt;/li&gt;
&lt;li&gt;GIS&lt;/li&gt;
&lt;li&gt;wms
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;今天一个叫做&lt;a href="http://wiki.github.com/amercader/WMS-Inspector/"&gt;WMS Inspector&lt;/a&gt;的Firefox扩展发布了。这个扩展用来查看页面上的WMS请求，还可以生成GetCapabilities的Report。&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/4427008096/" title="Screenshot by 贝小塔, on Flickr"&gt;&lt;img src="http://farm3.static.flickr.com/2678/4427008096_1e27a3caa8.jpg" width="500" height="116" alt="Screenshot" /&gt;&lt;/a&gt;
&lt;a href="http://www.flickr.com/photos/40741608@N08/4427007996/" title="Screenshot-GetCapabilities request by 贝小塔, on Flickr"&gt;&lt;img src="http://farm3.static.flickr.com/2715/4427007996_30f5b42791_o.png" width="394" height="314" alt="Screenshot-GetCapabilities request" /&gt;&lt;/a&gt;
&lt;a href="http://www.flickr.com/photos/40741608@N08/4427007932/" title="Screenshot-Capabilities report - WMS Inspector - Mozilla Firefox by 贝小塔, on Flickr"&gt;&lt;img src="http://farm3.static.flickr.com/2734/4427007932_8c3260710f.jpg" width="500" height="286" alt="Screenshot-Capabilities report - WMS Inspector - Mozilla Firefox" /&gt;&lt;/a&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>封闭近况</title><link href="https://sunng.info/blog/feng-bi-jin-kuang.html" rel="alternate"></link><published>2010-03-11T00:00:00+08:00</published><updated>2010-03-11T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-03-11:/blog/feng-bi-jin-kuang.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;Life&lt;/li&gt;
&lt;li&gt;snda
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;其实哪是什么封闭啊，不过是在公司大厅中间辟出一块相对独立的区域，大家坐在一起开发。别提了 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;Life&lt;/li&gt;
&lt;li&gt;snda
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;其实哪是什么封闭啊，不过是在公司大厅中间辟出一块相对独立的区域，大家坐在一起开发。别提了，我们原来都是分布式开发，也就是最近才坐在一起。除了老大偶尔自掏腰包大伙一起吃饭以外，“有人给你洗衣服”就不要想了。晚上依然是各回各家，老大的老大昨天对老大说，要注意身体，不要天天干到12点，到11点就行了。&lt;/p&gt;

&lt;p&gt;最近一周每天工作12小时以上，身体居然真的撑不住了。昨天下午开始头痛无力，按时下了班，结果晚上就发烧了。不过咱底子好，睡了一觉功力又恢复了，今天继续干。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Sunng.info in Mosaic</title><link href="https://sunng.info/blog/sunnginfo-in-mosaic.html" rel="alternate"></link><published>2010-03-10T00:00:00+08:00</published><updated>2010-03-10T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-03-10:/blog/sunnginfo-in-mosaic.html</id><content type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Mosaic:
&lt;a href="http://en.wikipedia.org/wiki/Mosaic_browser"&gt;http://en.wikipedia.org/wiki/Mosaic_browser&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/4420674473/" title="mosaic by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4058/4420674473_e14d48020e.jpg" width="457" height="500" alt="mosaic" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;这么一看，什么标准通通扯淡，还是图片管用，什么浏览器都可以显示。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>New features in JTS 1.11</title><link href="https://sunng.info/blog/new-features-in-jts-111.html" rel="alternate"></link><published>2010-03-03T00:00:00+08:00</published><updated>2010-03-03T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-03-03:/blog/new-features-in-jts-111.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;GIS&lt;/li&gt;
&lt;li&gt;java
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://lin-ear-th-inking.blogspot.com/2010/03/jts-version-111-released.html"&gt;JTS最近发布了1.11版本&lt;/a&gt;，新增了：
&lt;ul&gt;
&lt;li&gt;对&lt;a href="http://en.wikipedia.org/wiki/Delaunay_triangulation"&gt;Delaunay三角网&lt;/a&gt;、&lt;a href="http://en.wikipedia.org/wiki/Voronoi"&gt;Voronoi多边形&lt;/a&gt;的支持；&lt;/li&gt;
&lt;li&gt;把Geometry对象转换为AWT的Shape对象的功能&lt;/li&gt;
&lt;li&gt;对几何对象进行densify的操作（增加结点）；&lt;/li&gt;
&lt;li&gt;计算&lt;a href="http://en.wikipedia.org/wiki/Hausdorff_distance"&gt;Hausdorff相似度&lt;/a&gt;和Area相似度的支持&lt;/li&gt;
&lt;/ul&gt;
计算Delaunay三角网和Voronoi多边形：&lt;br /&gt;
{% raw %}

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;java.util.ArrayList&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;java.util …&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;GIS&lt;/li&gt;
&lt;li&gt;java
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://lin-ear-th-inking.blogspot.com/2010/03/jts-version-111-released.html"&gt;JTS最近发布了1.11版本&lt;/a&gt;，新增了：
&lt;ul&gt;
&lt;li&gt;对&lt;a href="http://en.wikipedia.org/wiki/Delaunay_triangulation"&gt;Delaunay三角网&lt;/a&gt;、&lt;a href="http://en.wikipedia.org/wiki/Voronoi"&gt;Voronoi多边形&lt;/a&gt;的支持；&lt;/li&gt;
&lt;li&gt;把Geometry对象转换为AWT的Shape对象的功能&lt;/li&gt;
&lt;li&gt;对几何对象进行densify的操作（增加结点）；&lt;/li&gt;
&lt;li&gt;计算&lt;a href="http://en.wikipedia.org/wiki/Hausdorff_distance"&gt;Hausdorff相似度&lt;/a&gt;和Area相似度的支持&lt;/li&gt;
&lt;/ul&gt;
计算Delaunay三角网和Voronoi多边形：&lt;br /&gt;
{% raw %}

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;java.util.ArrayList&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;java.util.Collection&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;com.vividsolutions.jts.geom.Coordinate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;com.vividsolutions.jts.geom.Geometry&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;com.vividsolutions.jts.geom.GeometryCollection&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;com.vividsolutions.jts.geom.GeometryFactory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;com.vividsolutions.jts.triangulate.DelaunayTriangulationBuilder&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;com.vividsolutions.jts.triangulate.VoronoiDiagramBuilder&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/**&lt;/span&gt;
&lt;span class="cm"&gt; *&lt;/span&gt;
&lt;span class="cm"&gt; * @author Sun Ning/SNDA&lt;/span&gt;
&lt;span class="cm"&gt; * @since 2010-3-3&lt;/span&gt;
&lt;span class="cm"&gt; */&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DelaunayAndVoronoiApp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cm"&gt;/**&lt;/span&gt;
&lt;span class="cm"&gt;     * create some predefined sites&lt;/span&gt;
&lt;span class="cm"&gt;     * @return&lt;/span&gt;
&lt;span class="cm"&gt;     */&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Collection&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;coordinate&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;getPredefinedSites&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;[][]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;coords&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="p"&gt;},{&lt;/span&gt;&lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;},{&lt;/span&gt;&lt;span class="mi"&gt;29&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;},{&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;26&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}};&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;coordinate&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;coordinates&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;coordinate&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;coords&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;coordinate&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;coordinate&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;coordinate&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nf"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;coords&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;coordinates&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Coordinate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;coords&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;coords&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;coordinates&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cm"&gt;/**&lt;/span&gt;
&lt;span class="cm"&gt;     *&lt;/span&gt;
&lt;span class="cm"&gt;     * @param coords&lt;/span&gt;
&lt;span class="cm"&gt;     * @return a geometry collection of triangulations&lt;/span&gt;
&lt;span class="cm"&gt;     */&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Geometry&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;buildDelaunayTriangulation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Collection&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;coordinate&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;coords&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;DelaunayTriangulationBuilder&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DelaunayTriangulationBuilder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setSites&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;coords&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTriangles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GeometryFactory&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cm"&gt;/**&lt;/span&gt;
&lt;span class="cm"&gt;     *&lt;/span&gt;
&lt;span class="cm"&gt;     * @param coords&lt;/span&gt;
&lt;span class="cm"&gt;     * @return a collection of polygons&lt;/span&gt;
&lt;span class="cm"&gt;     */&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Geometry&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;buildVoronoiDiagram&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Collection&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;coordinate&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;coords&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;VoronoiDiagramBuilder&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;VoronoiDiagramBuilder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setSites&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;coords&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getDiagram&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GeometryFactory&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;coordinate&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;coordinate&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cm"&gt;/**&lt;/span&gt;
&lt;span class="cm"&gt;     *&lt;/span&gt;
&lt;span class="cm"&gt;     * @param args&lt;/span&gt;
&lt;span class="cm"&gt;     */&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;Collection&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;coordinate&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;coordinates&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;getPredefinedSites&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;coordinate&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="cm"&gt;/**&lt;/span&gt;
&lt;span class="cm"&gt;         * Delauny&lt;/span&gt;
&lt;span class="cm"&gt;         */&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;GeometryCollection&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;triangulations&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GeometryCollection&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;buildDelaunayTriangulation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;coordinates&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;triangulations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getNumGeometries&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Total triangulations: %d\n&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;Geometry&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;triangulations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getGeometryN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;Coordinate&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;coords&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCoordinates&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Triangulation %d: &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Coordinate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;coords&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;(%.3f, %.3f) &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="cm"&gt;/**&lt;/span&gt;
&lt;span class="cm"&gt;         * Voronoi&lt;/span&gt;
&lt;span class="cm"&gt;         */&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;GeometryCollection&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;diagram&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GeometryCollection&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;buildVoronoiDiagram&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;coordinates&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;totalDia&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;diagram&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getNumGeometries&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;totalDia&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;Geometry&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;diagram&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getGeometryN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;Coordinate&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;coords&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCoordinates&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Diagram %d: &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Coordinate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;coords&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;(%.3f, %.3f) &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


{% endraw %}
输出：&lt;br /&gt;
[cc lang="text"]&lt;br /&gt;
Total triangulations: 5&lt;br /&gt;
Triangulation 0: (32.000, 90.000) (12.000, 26.000) (28.000, 50.000) (32.000, 90.000)&lt;br /&gt;
Triangulation 1: (32.000, 90.000) (28.000, 50.000) (100.000, 27.000) (32.000, 90.000)&lt;br /&gt;
Triangulation 2: (100.000, 27.000) (28.000, 50.000) (29.000, 40.000) (100.000, 27.000)&lt;br /&gt;
Triangulation 3: (100.000, 27.000) (29.000, 40.000) (12.000, 26.000) (100.000, 27.000)&lt;br /&gt;
Triangulation 4: (12.000, 26.000) (29.000, 40.000) (28.000, 50.000) (12.000, 26.000)&lt;br /&gt;
Diagram 0: (-76.000, 88.625) (-76.000, 178.000) (176.713, 178.000) (72.699, 65.730) (-38.235, 76.824) (-76.000, 88.625)&lt;br /&gt;
Diagram 1: (-76.000, -62.000) (-76.000, 88.625) (-38.235, 76.824) (11.978, 43.348) (56.422, -10.619) (57.006, -62.000) (-76.000, -62.000)&lt;br /&gt;
Diagram 2: (11.978, 43.348) (-38.235, 76.824) (72.699, 65.730) (67.316, 48.882) (11.978, 43.348)&lt;br /&gt;
Diagram 3: (176.713, 178.000) (188.000, 178.000) (188.000, -62.000) (57.006, -62.000) (56.422, -10.619) (67.316, 48.882) (72.699, 65.730) (176.713, 178.000)&lt;br /&gt;
Diagram 4: (11.978, 43.348) (67.316, 48.882) (56.422, -10.619) (11.978, 43.348)&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;将Geometry对象转换成Shape对象，绘制在JPanel上：&lt;br /&gt;
[cc lang="java"]&lt;br /&gt;
import java.awt.Color;&lt;br /&gt;
import java.awt.Dimension;&lt;br /&gt;
import java.awt.Graphics;&lt;br /&gt;
import java.awt.Graphics2D;&lt;br /&gt;
import java.awt.Shape;&lt;br /&gt;
import java.util.Collection;&lt;br /&gt;
import javax.swing.JFrame;&lt;br /&gt;
import javax.swing.JPanel;&lt;/p&gt;

&lt;p&gt;import com.vividsolutions.jts.awt.ShapeWriter;&lt;br /&gt;
import com.vividsolutions.jts.geom.Coordinate;&lt;br /&gt;
import com.vividsolutions.jts.geom.Geometry;&lt;br /&gt;
import java.util.Random;&lt;/p&gt;

&lt;p&gt;/**&lt;br /&gt;
 *&lt;br /&gt;
 * @author Sun Ning/SNDA&lt;br /&gt;
 * @since 2010-3-3&lt;br /&gt;
 */&lt;br /&gt;
public class JTS2Awt {&lt;/p&gt;

&lt;p&gt; public static void showUI(final Shape... shape){&lt;br /&gt;
        JFrame jframe = new JFrame("JTS Geometry to AWT Shape");&lt;/p&gt;

&lt;p&gt;     JPanel jp = new JPanel(){&lt;br /&gt;
            @Override&lt;br /&gt;
            public void paint(Graphics g){&lt;br /&gt;
                super.paint(g);&lt;br /&gt;
                Graphics2D g2d = (Graphics2D)g;&lt;br /&gt;
                if(shape != null){&lt;br /&gt;
                    for(Shape s: shape){&lt;br /&gt;
                        g2d.setColor(new Color(Color.HSBtoRGB(new Random().nextFloat(), 1f, 0.6f)));&lt;br /&gt;
                        g2d.draw(s);&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        };&lt;br /&gt;
        jp.setPreferredSize(new Dimension(150, 150));&lt;/p&gt;

&lt;p&gt;     jframe.getContentPane().add(jp);&lt;br /&gt;
        jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);&lt;br /&gt;
        jframe.pack();&lt;br /&gt;
        jframe.setVisible(true);&lt;br /&gt;
    }&lt;/p&gt;

&lt;p&gt; public static Shape toShape(Geometry geom){&lt;br /&gt;
        ShapeWriter writer = new ShapeWriter();&lt;br /&gt;
        return writer.toShape(geom);&lt;br /&gt;
    }&lt;/p&gt;

&lt;p&gt; public static void main(String[] args){&lt;br /&gt;
        Collection&lt;coordinate&gt; coords = DelaunayAndVoronoiApp.getPredefinedSites();&lt;br /&gt;
        Geometry geomT = DelaunayAndVoronoiApp.buildDelaunayTriangulation(coords);&lt;br /&gt;
        Geometry geomD = DelaunayAndVoronoiApp.buildVoronoiDiagram(coords);&lt;/coordinate&gt;&lt;/p&gt;

&lt;p&gt;     showUI(toShape(geomT), toShape(geomD));&lt;br /&gt;
    }&lt;/p&gt;

&lt;p&gt;}&lt;/p&gt;

&lt;p&gt;[/cc]
&lt;a href="http://www.flickr.com/photos/40741608@N08/4403349474/" title="jts by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4010/4403349474_cc3db44369_o.png" width="158" height="177" alt="jts" /&gt;&lt;/a&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>封闭开发</title><link href="https://sunng.info/blog/feng-bi-kai-fa.html" rel="alternate"></link><published>2010-03-02T00:00:00+08:00</published><updated>2010-03-02T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-03-02:/blog/feng-bi-kai-fa.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;从后天起，我就要被封闭开发了。地点在公司对面的酒店里，问具体的情况据&lt;a href="http://amoeba.meidusa.com/wordpress/"&gt;老大&lt;/a&gt;说，是要去一个“有人给 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;从后天起，我就要被封闭开发了。地点在公司对面的酒店里，问具体的情况据&lt;a href="http://amoeba.meidusa.com/wordpress/"&gt;老大&lt;/a&gt;说，是要去一个“有人给你做饭，有人给你洗衣服”的地方，说得跟人间天堂似的。去年刚到公司实习的时候就赶上有组封闭开发，印象中好像封了有一个多月。老实说封闭开发也是没办法的事情，我们这个菜场式的办公环境，一个人手上又有多个项目，不断地有人来找你问各种各样的事情，就算不问你，问你旁边的人，声音大到耳机挡不住，这种环境下怎么可能开发出有质量的产品。参考一下&lt;a href="http://www.infoq.com/cn/news/2010/02/agile-team-spaces"&gt;敏捷团队空间注意事项&lt;/a&gt;，那想想，封就封了吧。&lt;/p&gt;

&lt;p&gt;这次项目到四月中旬截止，还不太清楚会是什么情况，能不能上网，每天要干多长时间，具体情况等我从小黑屋发来现场报道吧。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Get your conky location aware</title><link href="https://sunng.info/blog/get-your-conky-location-aware.html" rel="alternate"></link><published>2010-02-19T00:00:00+08:00</published><updated>2010-02-19T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-02-19:/blog/get-your-conky-location-aware.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;conky&lt;/li&gt;
&lt;li&gt;GIS&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;location
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/4370628754/" title="2010-02-19-225826_188x44_scrot by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4014/4370628754_5a22522b80_o.png" width="188" height="44" alt="2010-02-19-225826_188x44_scrot" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add this in your conkyrc&lt;br /&gt;
[cc lang="text" nowrap="0"]&lt;br /&gt;
${exec curl -s "http://api.hostip.info" | xpath -e "//gml:featureMember/Hostip/gml:name/text()" -q} ${exec curl -s "http://api.hostip.info" | xpath -e "//gml:featureMember/Hostip//gml …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;conky&lt;/li&gt;
&lt;li&gt;GIS&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;location
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://www.flickr.com/photos/40741608@N08/4370628754/" title="2010-02-19-225826_188x44_scrot by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4014/4370628754_5a22522b80_o.png" width="188" height="44" alt="2010-02-19-225826_188x44_scrot" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add this in your conkyrc&lt;br /&gt;
[cc lang="text" nowrap="0"]&lt;br /&gt;
${exec curl -s "http://api.hostip.info" | xpath -e "//gml:featureMember/Hostip/gml:name/text()" -q} ${exec curl -s "http://api.hostip.info" | xpath -e "//gml:featureMember/Hostip//gml:coordinates/text()" -q}&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;Hostip is well known as a service provider of the geoclue framework. It translates IP address to geolocation information. The API we use will return a GML document like&lt;br /&gt;
[cc lang="xml" nowrap="0"]
&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;
&lt;hostiplookupresultset version="1.0.1" xmlns:gml="http://www.opengis.net/gml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nonamespaceschemalocation="http://www.hostip.info/api/hostip-1.0.1.xsd"&gt;
 &lt;gml:description&gt;This is the Hostip Lookup Service&lt;/gml:description&gt;
 &lt;gml:name&gt;hostip&lt;/gml:name&gt;
 &lt;gml:boundedby&gt;
  &lt;gml:null&gt;inapplicable&lt;/gml:null&gt;
 &lt;/gml:boundedby&gt;
 &lt;gml:featuremember&gt;
  &lt;hostip&gt;
   &lt;ip&gt;58.212.88.212&lt;/ip&gt;
   &lt;gml:name&gt;Nanjing&lt;/gml:name&gt;
   &lt;countryname&gt;CHINA&lt;/countryname&gt;
   &lt;countryabbrev&gt;CN&lt;/countryabbrev&gt;
   &lt;!-- Co-ordinates are available as lng,lat --&gt;
   &lt;iplocation&gt;
    &lt;gml:pointproperty&gt;
     &lt;gml:point srsname="http://www.opengis.net/gml/srs/epsg.xml#4326"&gt;
      &lt;gml:coordinates&gt;118.883,32.05&lt;/gml:coordinates&gt;
     &lt;/gml:point&gt;
    &lt;/gml:pointproperty&gt;
   &lt;/iplocation&gt;
  &lt;/hostip&gt;
 &lt;/gml:featuremember&gt;
&lt;/hostiplookupresultset&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;Because it uses ip to lookup your address, you cannot expect higher resolution and precision currently.&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Using weather</title><link href="https://sunng.info/blog/using-weather.html" rel="alternate"></link><published>2010-02-18T00:00:00+08:00</published><updated>2010-02-18T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-02-18:/blog/using-weather.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;location&lt;/li&gt;
&lt;li&gt;util
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Ubuntu仓库里有个weather-util包，可以用来查看天气信息。weather工具从weather.noaa.gov网站获得天气信息，对美国的城市可以直接用名字查询，其他国家只能使用id查询。id的规则和weather.com不太相同 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;location&lt;/li&gt;
&lt;li&gt;util
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Ubuntu仓库里有个weather-util包，可以用来查看天气信息。weather工具从weather.noaa.gov网站获得天气信息，对美国的城市可以直接用名字查询，其他国家只能使用id查询。id的规则和weather.com不太相同。&lt;/p&gt;

&lt;p&gt;中国的天气站点id可以在&lt;a href="http://weather.noaa.gov/weather/CN_cc.html"&gt;这个页面&lt;/a&gt;上查到。比如南京ZSNJ，上海浦东ZSPD。使用weather工具查询：
&lt;em&gt;weather -i ZSNJ&lt;/em&gt;
&lt;blockquote&gt;Current conditions at China (ZSNJ) 32-00N 118-48E 12M (ZSNJ)&lt;br /&gt;
Last updated Feb 18, 2010 - 08:00 AM EST / 2010.02.18 1300 UTC&lt;br /&gt;
   Temperature: 35 F (2 C)&lt;br /&gt;
   Relative Humidity: 59%&lt;br /&gt;
   Wind: from the SSW (210 degrees) at 4 MPH (4 KT)&lt;/blockquote&gt;&lt;/p&gt;

&lt;p&gt;简化操作，可以在$HOME下创建.weatherrc文件，形如&lt;br /&gt;
[cc lang="ini"]&lt;br /&gt;
[default]&lt;br /&gt;
ID = ZSPD&lt;br /&gt;
[nj]&lt;br /&gt;
ID = ZSNJ&lt;br /&gt;
[/cc]&lt;/p&gt;

&lt;p&gt;就可以直接使用
&lt;em&gt;weather&lt;/em&gt;
&lt;blockquote&gt;
Current conditions at China (ZSPD) 31-07N 121-46E (ZSPD)&lt;br /&gt;
Last updated Feb 18, 2010 - 08:00 AM EST / 2010.02.18 1300 UTC&lt;br /&gt;
   Temperature: 35 F (2 C)&lt;br /&gt;
   Relative Humidity: 47%&lt;br /&gt;
   Wind: from the NE (050 degrees) at 7 MPH (6 KT) (direction variable)
&lt;/blockquote&gt;
和
&lt;em&gt;weather nj&lt;/em&gt;
&lt;blockquote&gt;Current conditions at China (ZSNJ) 32-00N 118-48E 12M (ZSNJ)&lt;br /&gt;
Last updated Feb 18, 2010 - 08:00 AM EST / 2010.02.18 1300 UTC&lt;br /&gt;
   Temperature: 35 F (2 C)&lt;br /&gt;
   Relative Humidity: 59%&lt;br /&gt;
   Wind: from the SSW (210 degrees) at 4 MPH (4 KT)&lt;/blockquote&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>I ♥ Free and Open Source Software</title><link href="https://sunng.info/blog/i-free-and-open-source-software.html" rel="alternate"></link><published>2010-02-14T00:00:00+08:00</published><updated>2010-02-14T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-02-14:/blog/i-free-and-open-source-software.html</id><content type="html">&lt;ul&gt;
&lt;li&gt;未分类
tags:&lt;/li&gt;
&lt;li&gt;foss
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://fsfe.org/campaigns/valentine-2010/valentine-2010.html"&gt;&lt;img src="http://fsfe.org/campaigns/valentine-2010/vd-i-love-fs-358x60.png" style="border: 0 !important;" alt="I love Free Software!" /&gt;&lt;/a&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>老物什 / Old Stuff</title><link href="https://sunng.info/blog/lao-wu-shi-old-stuff.html" rel="alternate"></link><published>2010-02-14T00:00:00+08:00</published><updated>2010-02-14T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-02-14:/blog/lao-wu-shi-old-stuff.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;photo
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;大年初一翻旧物，找到一些让人颇有感触的旧物件。2004年买的RH9，2005年夏天买的Fedora 4。前者至今还安装在我家最旧的电脑里，虽 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;photo
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;大年初一翻旧物，找到一些让人颇有感触的旧物件。2004年买的RH9，2005年夏天买的Fedora 4。前者至今还安装在我家最旧的电脑里，虽然我都忘了它的样子了。&lt;/p&gt;

&lt;p&gt;包装
&lt;a href="http://www.flickr.com/photos/40741608@N08/4355891380/" title="00011 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4044/4355891380_b32fe9d173.jpg" width="500" height="335" alt="00011" /&gt;&lt;/a&gt;
&lt;a href="http://www.flickr.com/photos/40741608@N08/4355892466/" title="00012 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4008/4355892466_04bca6512d.jpg" width="500" height="335" alt="00012" /&gt;&lt;/a&gt;
&lt;a href="http://www.flickr.com/photos/40741608@N08/4355894700/" title="00014 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4061/4355894700_a83995eaca.jpg" width="500" height="335" alt="00014" /&gt;&lt;/a&gt;
&lt;a href="http://www.flickr.com/photos/40741608@N08/4355150633/" title="00013 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4049/4355150633_106e97e909.jpg" width="500" height="335" alt="00013" /&gt;&lt;/a&gt;
&lt;a href="http://www.flickr.com/photos/40741608@N08/4355155529/" title="00018 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4057/4355155529_d0397b83d3.jpg" width="500" height="335" alt="00018" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;手册
&lt;a href="http://www.flickr.com/photos/40741608@N08/4355153647/" title="00016 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4010/4355153647_7877938213.jpg" width="500" height="335" alt="00016" /&gt;&lt;/a&gt;
&lt;a href="http://www.flickr.com/photos/40741608@N08/4355171643/" title="00022 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm3.static.flickr.com/2708/4355171643_d7c67e9d3a.jpg" width="500" height="335" alt="00022" /&gt;&lt;/a&gt;
&lt;a href="http://www.flickr.com/photos/40741608@N08/4355170579/" title="00021 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm3.static.flickr.com/2744/4355170579_18ee80953a.jpg" width="500" height="335" alt="00021" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;CDs
&lt;a href="http://www.flickr.com/photos/40741608@N08/4355152825/" title="00015 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4042/4355152825_d7b6d44328.jpg" width="500" height="335" alt="00015" /&gt;&lt;/a&gt;
&lt;a href="http://www.flickr.com/photos/40741608@N08/4355897540/" title="00017 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm3.static.flickr.com/2802/4355897540_6ff0e17b46.jpg" width="500" height="335" alt="00017" /&gt;&lt;/a&gt;
&lt;a href="http://www.flickr.com/photos/40741608@N08/4355899486/" title="00019 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4064/4355899486_a9a618f68f.jpg" width="500" height="335" alt="00019" /&gt;&lt;/a&gt;
&lt;a href="http://www.flickr.com/photos/40741608@N08/4355900468/" title="00020 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4004/4355900468_ec4c180d79.jpg" width="500" height="335" alt="00020" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;这个代理商里仁软件似乎已经倒闭了。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>List your installed virtualbox virtual marchines using vbox python xpcom api</title><link href="https://sunng.info/blog/list-your-installed-virtualbox-virtual-marchines-using-vbox-python-xpcom-api.html" rel="alternate"></link><published>2010-02-12T00:00:00+08:00</published><updated>2010-02-12T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-02-12:/blog/list-your-installed-virtualbox-virtual-marchines-using-vbox-python-xpcom-api.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;VirtualBox
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;I'm sorry for the long title. Due to lack of documentation, it's not easy to use python xpcom api from virtualbox sdk. The code below is just a sample that lists your installed virtual machines. It works on linux with …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;VirtualBox
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;I'm sorry for the long title. Due to lack of documentation, it's not easy to use python xpcom api from virtualbox sdk. The code below is just a sample that lists your installed virtual machines. It works on linux with VirtualBox OSE 2.0.8. Hope useful to you.&lt;br /&gt;
[cc lang="python"]&lt;br /&gt;
import vboxapi&lt;br /&gt;
vmsg = vboxapi.VirtualBoxManager(None, None)&lt;br /&gt;
vbox = vmsg.vbox&lt;br /&gt;
vmsg.mgr.getSessionObject(vbox)&lt;br /&gt;
machs = vbox.getMachines()&lt;br /&gt;
names = map(lambda x: (x.name,x.id), machs)&lt;br /&gt;
print names&lt;br /&gt;
[/cc]&lt;br /&gt;
Output:&lt;br /&gt;
[cc lang="python" nowrap="false" line_numbers="false"]&lt;br /&gt;
[(u'Ubuntu', u'aac3fd46-4f1e-4f6e-8abe-b1a5516abf8c'), (u'Windows', u'fa73952e-f0a6-4bf0-af20-259513d73bc1'), (u'OpenSolaris', u'a951542c-3178-4a6b-8087-f1b0314e283a'), (u'LinuxMint', u'8191f256-1b76-4ce7-a202-93730dae1b33'), (u'Fedora', u'ca250f45-d752-4baf-9281-fb0847dc8287'), (u'Freebsd', u'8322b62a-aed5-4f04-9815-9fadfeba9fe1'), (u'ArchLinux', u'efc1167a-4e06-4c14-aeb0-bfb303c8f5f7'), (u'openSUSE', u'02e18816-311d-420b-99b3-6b4b4cccf7b7'), (u'CentOS', u'ccf0421c-85c4-4123-8722-57abc494c633')]&lt;br /&gt;
[/cc]&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Virtual Machine Searcher for Gnome Deskbar Applet</title><link href="https://sunng.info/blog/virtual-machine-searcher-for-gnome-deskbar-applet.html" rel="alternate"></link><published>2010-02-12T00:00:00+08:00</published><updated>2010-02-12T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-02-12:/blog/virtual-machine-searcher-for-gnome-deskbar-applet.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Very glad to announce another daily-coding work: an extension for gnome deskbar applet to search and launch virtual machine. There is a plugin for gnome-do that does the same job. That's what I create the the plugin for. I switched …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Very glad to announce another daily-coding work: an extension for gnome deskbar applet to search and launch virtual machine. There is a plugin for gnome-do that does the same job. That's what I create the the plugin for. I switched to deskbar because gnome-do's Do.exe reminds me nightmares when I was a M$ Windows user. The deskbar applet has been a great replacement, however, the virtualbox plugin in Do is really impressive while deskbar doesn't provide me the same functionality.&lt;/p&gt;

&lt;p&gt;You can ignore words above and just take a look at the screenshot:
&lt;a title="deskbar-virtualbox by 贝小塔, on Flickr" href="http://www.flickr.com/photos/40741608@N08/4350951202/"&gt;&lt;img src="http://farm5.static.flickr.com/4062/4350951202_9ede9c4318_o.png" alt="deskbar-virtualbox" width="560" height="400" /&gt;&lt;/a&gt;
&lt;h3&gt;Download&lt;/h3&gt;
Download the extension from (the highlighted one):
&lt;a href="http://bitbucket.org/sunng/daily-coding/downloads/?highlight=8792"&gt;http://bitbucket.org/sunng/daily-coding/downloads/?highlight=5137&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also trace the development at bitbucket project. However, the repository is mixed with other small code snippets. Currently, mercurial doesn't support subdirectory pull. So there is no way to grab the deskbar-applet individually.
&lt;h3&gt;Installation&lt;/h3&gt;
copy this file to &lt;em&gt;/usr/lib/deskbar-applet/deskbar-applet/modules-2.20-compatible/&lt;/em&gt; (Ubuntu installation for example) with super user privilege. Right click desktbar applet, select Preference, Searchers tab, hit "Reload" button, then check the Virtualbox Deskbar Module.
&lt;a title="deskbar-preference by 贝小塔, on Flickr" href="http://www.flickr.com/photos/40741608@N08/4350219097/"&gt;&lt;img src="http://farm5.static.flickr.com/4006/4350219097_126cdb3c8d.jpg" alt="deskbar-preference" width="500" height="346" /&gt;&lt;/a&gt;
&lt;h3&gt;Issue&lt;/h3&gt;
Feel free the report issues on bitbucket:
&lt;a href="http://bitbucket.org/sunng/daily-coding/issues/"&gt;http://bitbucket.org/sunng/daily-coding/issues/&lt;/a&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>解决Ubuntu Openbox无法启动的问题</title><link href="https://sunng.info/blog/jie-jue-ubuntu-openboxwu-fa-qi-dong-de-wen-ti.html" rel="alternate"></link><published>2010-02-11T00:00:00+08:00</published><updated>2010-02-11T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-02-11:/blog/jie-jue-ubuntu-openboxwu-fa-qi-dong-de-wen-ti.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;openbox&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;今天在工作用的ArchLinux上安装了用Openbox取代了GNOME桌面，感觉良好，回来尝试一下在Ubuntu上也做同样的事情。不料遇到问题，在完成基本的配置之后，Openbox Session无法启动，总是 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;openbox&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;今天在工作用的ArchLinux上安装了用Openbox取代了GNOME桌面，感觉良好，回来尝试一下在Ubuntu上也做同样的事情。不料遇到问题，在完成基本的配置之后，Openbox Session无法启动，总是自动跳回gdm。查看.xsession-errors，是gnome-setting-daemon报错：&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(gnome-settings-daemon:2519): GLib-CRITICAL **: g_propagate_error: assertion `src != NULL' failed&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;既然是gnome-settings-daemon报错，就在.config/openbox/autostart.sh中注视掉和gnome-settings&lt;em&gt;-&lt;/em&gt;daemon相关的部分：
&lt;pre class="brush:bash"&gt;# Make GTK apps look and behave how they were set up in the gnome config tools
#if test -x /usr/libexec/gnome-settings-daemon &amp;gt;/dev/null; then
#  /usr/libexec/gnome-settings-daemon &amp;amp;
#elif which gnome-settings-daemon &amp;gt;/dev/null; then
#  gnome-settings-daemon &amp;amp;
# Make GTK apps look and behave how they were set up in the XFCE config tools
#elif which xfce-mcs-manager &amp;gt;/dev/null; then
#  xfce-mcs-manager n &amp;amp;
#fi
&lt;/pre&gt;
进而可以启动Openbox Session了，但是发现Conky仍然无法启动，经过搜索是sleep的时间不够长导致的。而根据launchpad上上的讨论，gnome-settings-daemon也可以在Openbox Session启动后正常运行，于是可以这样设置autostart.sh
&lt;pre class="brush:bash"&gt;(sleep 20 &amp;amp;&amp;amp; conky 1&amp;gt;/dev/null 2&amp;gt;/dev/null) &amp;amp;
(sleep 2 &amp;amp;&amp;amp; tint) &amp;amp;
(sleep 5 &amp;amp;&amp;amp; tilda) &amp;amp;
(sleep 20 &amp;amp;&amp;amp; gnome-settings-daemon 1&amp;gt;/dev/null 2&amp;gt;/dev/null) &amp;amp;
&lt;/pre&gt;
tint和tilda对启动顺序没有明确的要求，conky和gnome-settings-daemon需要设置一个较长的等待时间。&lt;/p&gt;

&lt;p&gt;这是Ubuntu中Openbox的一个bug，可以在此跟踪：&lt;br /&gt;
https://bugs.launchpad.net/ubuntu/+source/openbox/+bug/459005&lt;/p&gt;

&lt;p&gt;顺手展示一下我的Openbox桌面
&lt;a href="http://www.flickr.com/photos/40741608@N08/4346625590/" title="myopenbox by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4067/4346625590_2230b8b3d8.jpg" width="500" height="313" alt="myopenbox" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;openbox / tint2 / conky / tilda 还没来得及认真配置，呵呵。&lt;/p&gt;

&lt;p&gt;再有，Ubuntu源里的的tint2版本很低，问题不少，不支持宽度的百分数配置，不支持systray等配置。建议安装开发版本：&lt;br /&gt;
http://code.google.com/p/tint2/wiki/Install#For_Ubuntu_9.10_%28Karmic%29&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Sunng's Canvas based Heatmap API</title><link href="https://sunng.info/blog/sunngs-canvas-based-heatmap-api.html" rel="alternate"></link><published>2010-02-11T00:00:00+08:00</published><updated>2010-02-11T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-02-11:/blog/sunngs-canvas-based-heatmap-api.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN&lt;/li&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;canvas&lt;/li&gt;
&lt;li&gt;html&lt;/li&gt;
&lt;li&gt;javascript&lt;/li&gt;
&lt;li&gt;visualization&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Glad to announce my works this morning: A simple heatmap API based on HTML5 canvas.
&lt;a href="http://www.flickr.com/photos/40741608@N08/4347604245/" title="canvas by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4071/4347604245_486bd3a12e_o.jpg" width="300" height="215" alt="canvas" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The programming interface is rather simple now. To create such a heatmap, you just new a heatmap object by:
&lt;pre class="brush:html"&gt;
&amp;lt;canvas width="300 …&lt;/pre&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN&lt;/li&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;canvas&lt;/li&gt;
&lt;li&gt;html&lt;/li&gt;
&lt;li&gt;javascript&lt;/li&gt;
&lt;li&gt;visualization&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Glad to announce my works this morning: A simple heatmap API based on HTML5 canvas.
&lt;a href="http://www.flickr.com/photos/40741608@N08/4347604245/" title="canvas by 贝小塔, on Flickr"&gt;&lt;img src="http://farm5.static.flickr.com/4071/4347604245_486bd3a12e_o.jpg" width="300" height="215" alt="canvas" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The programming interface is rather simple now. To create such a heatmap, you just new a heatmap object by:
&lt;pre class="brush:html"&gt;
&amp;lt;canvas width="300" height="215" id="canv"&amp;gt;&amp;lt;/canvas&amp;gt;
&lt;/pre&gt;
&lt;pre class="brush:javascript"&gt;
heatmap = new HeatMap("canv");
&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;Then read your dataset and push data into the heatmap:
&lt;pre class="brush:javascript"&gt;
heatmap.push(x, y, value);
&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;At last, spread the data and get the canvas rendered:
&lt;pre class="brush:javascript"&gt;
heatmap.spread();
heatmap.render();
&lt;/pre&gt;
Now you got it.&lt;/p&gt;

&lt;p&gt;For advanced usage, you can specify the resolution of heatmap by px:
&lt;pre class="brush:javascript"&gt;
heatmap = new HeatMap("canv", 2);
&lt;/pre&gt;
Large value will gain performance with low image quality.&lt;/p&gt;

&lt;p&gt;Also, you can specify a value to define the attenuation value of each px.
&lt;pre class="brush:javascript"&gt;
heatmap.spread(5);
&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;Finally, there is an option to use your own color schema for heatmap generation.
&lt;pre class="brush:javascript"&gt;
heatmap.render(function(value, maxValue){
    var light = value / maxValue * 100;
    return "hsl(20, 75%, "+light+"%)";
});
&lt;/pre&gt;
The parameters passed in are current pixel value and max value in whole context.&lt;/p&gt;

&lt;p&gt;That's all the toolkit. More functionality and options might be added in future. Grab it from my bitbucket page:
&lt;a href="http://bitbucket.org/sunng/daily-coding/src/tip/canvas-heatmap/"&gt;http://bitbucket.org/sunng/daily-coding/src/tip/canvas-heatmap/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is a demo page which you can test the api by clicking canvas:
&lt;a href="http://www.flickr.com/photos/40741608@N08/4347650163/" title="canvas-raw by 贝小塔, on Flickr"&gt;&lt;img src="http://farm3.static.flickr.com/2733/4347650163_ea740356e4_o.jpg" width="326" height="284" alt="canvas-raw" /&gt;&lt;/a&gt;
then click heatmap button!
&lt;a href="http://www.flickr.com/photos/40741608@N08/4348397874/" title="canvas2 by 贝小塔, on Flickr"&gt;&lt;img src="http://farm3.static.flickr.com/2796/4348397874_8ac80b0756_o.jpg" width="314" height="275" alt="canvas2" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the new year gift for my readers and my dear friends !&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>无题</title><link href="https://sunng.info/blog/wu-ti.html" rel="alternate"></link><published>2010-02-10T00:00:00+08:00</published><updated>2010-02-10T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-02-10:/blog/wu-ti.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;football
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;我草，这是真的吗，同志们是3:0吗？？！！&lt;/p&gt;

&lt;p&gt;是不是电视显示错了？？！&lt;/p&gt;

&lt;p&gt;我有生之年居然看到这一幕了！！！&lt;/p&gt;

&lt;p&gt;人生啊，这是人生 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;football
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;我草，这是真的吗，同志们是3:0吗？？！！&lt;/p&gt;

&lt;p&gt;是不是电视显示错了？？！&lt;/p&gt;

&lt;p&gt;我有生之年居然看到这一幕了！！！&lt;/p&gt;

&lt;p&gt;人生啊，这是人生吗&lt;/p&gt;

&lt;p&gt;见证历史了！！！&lt;/p&gt;

&lt;p&gt;牛逼！！这是真的吗，这是他妈真的吗&lt;/p&gt;

&lt;p&gt;邓卓翔！！&lt;/p&gt;

&lt;p&gt;牛逼！！！&lt;/p&gt;

&lt;p&gt;娘的，要下载录像&lt;/p&gt;

&lt;p&gt;这他妈是真的吗？？！！&lt;/p&gt;

&lt;p&gt;去你妈的CCTV&lt;/p&gt;

&lt;p&gt;去你妈的江和平&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Suppressing simple-xml's class attribute</title><link href="https://sunng.info/blog/suppressing-simple-xmls-class-attribute.html" rel="alternate"></link><published>2010-02-08T00:00:00+08:00</published><updated>2010-02-08T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-02-08:/blog/suppressing-simple-xmls-class-attribute.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;xml
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://simple.sourceforge.net" target="_blank"&gt;Simple-xml&lt;/a&gt; is an object-xml serialization and de-serialization framework. It's featured by annotation-driven, light-weight and self-contained.&lt;/p&gt;

&lt;p&gt;In simple-xml, pojos that will be serialized are annotated with @Root, @Element, @Attribute or @Text. The problem is, when handling with inheritance, if the actual type …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;xml
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://simple.sourceforge.net" target="_blank"&gt;Simple-xml&lt;/a&gt; is an object-xml serialization and de-serialization framework. It's featured by annotation-driven, light-weight and self-contained.&lt;/p&gt;

&lt;p&gt;In simple-xml, pojos that will be serialized are annotated with @Root, @Element, @Attribute or @Text. The problem is, when handling with inheritance, if the actual type is different from declared type, simple-xml will add a "class" attribute for de-serialization consideration. That is, the parser will use this attribute to determine the schema of the xml element. However, this is of no useful when we do not parse xml by simple-xml. And there is also risk that the class attribute exposes our program's internal structure.&lt;/p&gt;

&lt;p&gt;Take following code as example:
&lt;pre class="brush:java"&gt;@Root
public class Response{
    @Element
    private Object entry;&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;    // getter and setter ...&lt;br /&gt;
}&lt;/p&gt;

&lt;p&gt;public class OrderItem{&lt;br /&gt;
    @Element&lt;br /&gt;
    private String name;&lt;/p&gt;

&lt;p&gt;    // getter and setter ...&lt;br /&gt;
}
When you generate xml from classes above, you will have the entry element of response with an attribute "class", which value is the qualified class name, like "package.OrderItem".&lt;/p&gt;

&lt;p&gt;This is done by simple-xml's Strategy interface. By default, the Persister uses TreeStrategy which has an implementation of setElement like:
&lt;pre class="brush:java"&gt;/**
 * This is used to attach a attribute to the provided element
 * that is used to identify the class. The attribute name is
 * "class" and has the value of the fully qualified class
 * name for the object provided. This will only be invoked
 * if the object class is different from the field class.
 *
 * @param type this is the declared class for the field used
 * @param value this is the instance variable being serialized
 * @param node this is the element used to represent the value
 * @param map this is used to maintain contextual information
 *
 * @return this returns true if serialization is complete
 */
public boolean setElement(Type type, Object value, NodeMap node, Map map){
    Class actual = value.getClass();
    Class expect = type.getType();
    Class real = actual;
    if(actual.isArray()) {
        real = setArray(expect, value, node);
    }
    if(actual != expect) {
        node.put(label, real.getName());
    }
    return false;
}&lt;/pre&gt;
This is where class attributed. So to suppress the attribute, we simply override this method by inherit TreeStrategy. I take a inline class for convenience here.
&lt;pre class="brush:java"&gt;Serializer s = new Persister(new TreeStrategy(){
    @Override
    public boolean setElement(Type type, Object value, NodeMap node, Map map){
        return false;
    }
});&lt;/pre&gt;
Now it works, however, simple-xml won't be able to deserialize xml generated by this modified strategy.&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>使用gnome-dictionary和dictd</title><link href="https://sunng.info/blog/shi-yong-gnome-dictionaryhe-dictd.html" rel="alternate"></link><published>2010-02-07T00:00:00+08:00</published><updated>2010-02-07T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-02-07:/blog/shi-yong-gnome-dictionaryhe-dictd.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;dictd&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;最近琢磨字典多一些，以前一直不知道gnome自带的gnome-dictionary如何使用，因为默认的字典源里都没有中文字典，而且离线更 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;dictd&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;最近琢磨字典多一些，以前一直不知道gnome自带的gnome-dictionary如何使用，因为默认的字典源里都没有中文字典，而且离线更是无法使用。其实不然，字典服务可以自架，并且可以使用自己的字典。&lt;/p&gt;

&lt;p&gt;在自己的机器上架设dictionary server，以ubuntu为例：&lt;/p&gt;

&lt;p&gt;安装dictd / dict
&lt;em&gt;sudo apt-get install dictd dict&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;前者是字典服务器，后者是字典服务客户端。还可以从仓库里安装已经打包好的字典，如
&lt;em&gt;sudo apt-get install dict-stardic&lt;/em&gt;
是一个英汉字典&lt;/p&gt;

&lt;p&gt;dictd安装好后会默认自启动，也可以通过
&lt;em&gt;sudo service dictd start&lt;/em&gt;
来控制dictd的启动、重启和关闭。&lt;/p&gt;

&lt;p&gt;通过dict测试服务的状况
&lt;em&gt;dict -D&lt;/em&gt;
命令会列出可用的数据库（即字典）&lt;/p&gt;

&lt;p&gt;在gnome-dictionary中添加字典源 Edit-&amp;gt;Preference-&amp;gt;Source-&amp;gt;Add&lt;br /&gt;
Name: 自定义的字典名&lt;br /&gt;
Transport: Dictionary Source&lt;br /&gt;
Hostname: 127.0.0.1&lt;br /&gt;
Port: 2628&lt;/p&gt;

&lt;p&gt;Close之后可以到主界面打开侧边栏，在Dictionary Source中双击选定此字典，在Aailable Dictionaries里双击选中目标字典，于是就可以在gnome-dictionary里使用本地的字典服务了。&lt;/p&gt;

&lt;p&gt;接下来安装自己的字典。stardict的&lt;a href="http://stardict.sourceforge.net/Dictionaries_zh_CN.php"&gt;网站&lt;/a&gt;上有很多中文字典，上面也都标示了授权协议。要在dictd里使用这些字典，需要进行一个简单的格式转换。安装一个工具
&lt;em&gt;sudo apt-get install dictconv&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;它的使用方法：&lt;br /&gt;
dictconv -o OUTPUT_FILE INPUT_FILE&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;在input_file指定stardict的.ifo文件，在output_file指定要生成的dictd的.index文件。这个转换非常耗时，需要有足够的耐心。这个操作将会生成.index和.dict文件。对.dict文件进行压缩：
&lt;em&gt;dictzip OUT.dict&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;转换完成后，编辑&lt;em&gt;/var/lib/dictd/db.list&lt;/em&gt;文件，在其中添加一个database定义
&lt;em&gt;database YOUR_DB_NAME{&lt;br /&gt;
data PATH_TO_DICT_DZ&lt;br /&gt;
index PATH_TO_INDEX&lt;br /&gt;
}&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;其中PATH_TO_DICT_DZ是上一步的.dz文件，PATH_TO_INDEX是到上一步生成的.index文件的路径。编辑完成后，重启dictd服务
&lt;em&gt;sudo service dictd restart&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;使用dict -D可以查看启用的字典。&lt;/p&gt;

&lt;p&gt;建议备份&lt;em&gt;/var/lib/dictd/db.list&lt;/em&gt;文件，因为每次从apt-get安装字典后，这个文件都会被覆盖。&lt;/p&gt;

&lt;p&gt;dictd服务也可以在fantasdic里访问。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>很山寨</title><link href="https://sunng.info/blog/hen-shan-zhai.html" rel="alternate"></link><published>2010-02-03T00:00:00+08:00</published><updated>2010-02-03T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-02-03:/blog/hen-shan-zhai.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;firefox&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;gnu&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;经常访问&lt;a href="http://www.gnu.org" target="_blank"&gt;GNU&lt;/a&gt;的网站你会发现一些很神奇的东西，比如&lt;a href="http://http://www.gnewsense.org/" target="_blank"&gt;gNewSense&lt;/a&gt;，又比如最新的：
&lt;a title="IceCat by 贝小塔, on Flickr" href="http://www.flickr.com/photos/40741608@N08/4326991719/"&gt;&lt;img src="http://farm5.static.flickr.com/4009/4326991719_a5253581dc_o.png" alt="IceCat" width="306" height="482" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;怎么样，够震撼啊，&lt;a href="http://www.gnu.org/software/gnuzilla/" target="_blank"&gt;GNUzilla&lt;/a&gt;套件 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;firefox&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;gnu&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;经常访问&lt;a href="http://www.gnu.org" target="_blank"&gt;GNU&lt;/a&gt;的网站你会发现一些很神奇的东西，比如&lt;a href="http://http://www.gnewsense.org/" target="_blank"&gt;gNewSense&lt;/a&gt;，又比如最新的：
&lt;a title="IceCat by 贝小塔, on Flickr" href="http://www.flickr.com/photos/40741608@N08/4326991719/"&gt;&lt;img src="http://farm5.static.flickr.com/4009/4326991719_a5253581dc_o.png" alt="IceCat" width="306" height="482" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;怎么样，够震撼啊，&lt;a href="http://www.gnu.org/software/gnuzilla/" target="_blank"&gt;GNUzilla&lt;/a&gt;套件里的第一个发布，IceCat。IceCat是嘛？你知道Firefox吧，那拉开冰箱门，把Firefox塞进去，带上冰箱门，嗯，那就是IceCat了。当然是开玩笑了，GNU IceCat is the GNU version of the Firefox browser.  Its main advantage is an ethical one: it is entirely &lt;a href="http://www.gnu.org/philosophy/free-sw.html"&gt;free software&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;现在已经可以在Ubuntu中使用这个病猫，哦不，冰猫了：&lt;/p&gt;

&lt;p&gt;&lt;em&gt;sudo add-apt-repository ppa:gnuzilla-team/ppa&lt;br /&gt;
sudo apt-get update&lt;br /&gt;
sudo apt-get install icecat&lt;/em&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Patch for Fantasdic Google Source</title><link href="https://sunng.info/blog/patch-for-fantasdic-google-source.html" rel="alternate"></link><published>2010-02-02T00:00:00+08:00</published><updated>2010-02-02T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-02-02:/blog/patch-for-fantasdic-google-source.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;ruby
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;I found fantasdic a great powerful dictionary tool with friendly UI and supports multiply sources. Unfortunately, this project seems to be no longer maintained. The latest version is 1.0-beta7.
&lt;a title="fantasdic-google by 贝小塔, on Flickr" href="http://www.flickr.com/photos/40741608@N08/4324570353/"&gt;&lt;img src="http://farm5.static.flickr.com/4049/4324570353_c1f79ac6dc.jpg" alt="fantasdic-google" width="500" height="362" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The google translate source was created in 2007. As the google …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;ruby
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;I found fantasdic a great powerful dictionary tool with friendly UI and supports multiply sources. Unfortunately, this project seems to be no longer maintained. The latest version is 1.0-beta7.
&lt;a title="fantasdic-google by 贝小塔, on Flickr" href="http://www.flickr.com/photos/40741608@N08/4324570353/"&gt;&lt;img src="http://farm5.static.flickr.com/4049/4324570353_c1f79ac6dc.jpg" alt="fantasdic-google" width="500" height="362" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The google translate source was created in 2007. As the google translate service page changed, this module doesn't work any more, so I just picked up my vim then created this patch for those still want to use it.&lt;/p&gt;

&lt;p&gt;This patch has dependency with ruby's json module, so before you apply it, run:
&lt;em&gt;sudo gem install json&lt;/em&gt;
or
&lt;em&gt;sudo apt-get install libjson-ruby1.8&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Download the patch at:
&lt;a href="https://bugzilla.gnome.org/attachment.cgi?id=152835"&gt;https://bugzilla.gnome.org/attachment.cgi?id=152835&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Patch it (Ubuntu installation as example):
&lt;em&gt;cd /usr/lib/ruby/1.8/fantasdic/sources/&lt;br /&gt;
sudo patch google_translate.rb /home/sun/google_translate.rb.diff&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Restart fantasdic, then your get it works! And this bug is tracked at:
&lt;a href="https://bugzilla.gnome.org/show_bug.cgi?id=605641"&gt;https://bugzilla.gnome.org/show_bug.cgi?id=605641&lt;/a&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>翻译po</title><link href="https://sunng.info/blog/fan-yi-po.html" rel="alternate"></link><published>2010-01-31T00:00:00+08:00</published><updated>2010-01-31T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-01-31:/blog/fan-yi-po.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;linux
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;翻译一个po文件。确认安装了gettext工具包。&lt;/p&gt;

&lt;p&gt;cd到po目录，创建pot
&lt;em&gt;intltool-update --pot&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;创建相应的语言文件，例如zh_CN
&lt;em&gt;msginit -l zh_CN.utf8&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;程序会提示选择 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;linux
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;翻译一个po文件。确认安装了gettext工具包。&lt;/p&gt;

&lt;p&gt;cd到po目录，创建pot
&lt;em&gt;intltool-update --pot&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;创建相应的语言文件，例如zh_CN
&lt;em&gt;msginit -l zh_CN.utf8&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;程序会提示选择或输入邮箱，之后就会生成zh_CN.po文件，打开文件，逐条编辑msgstr即可。&lt;/p&gt;

&lt;p&gt;手动编辑LINGUAS文件，按顺序将zh_CN添加到其中。&lt;/p&gt;

&lt;p&gt;可以通过这个命令查看翻译的进度
&lt;em&gt;msgfmt -cv zh_CN.po&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;对于更新pot更新的情况，可以使用命令更新po文件
&lt;em&gt;intltool-update zh_CN&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;参考：
&lt;ul&gt;
    &lt;li&gt;&lt;a href="http://live.gnome.org/TranslationProject/LocalisationGuidezh"&gt;http://live.gnome.org/TranslationProject/LocalisationGuidezh&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="http://blog.chinaunix.net/u1/37261/showart_476642.html"&gt;http://blog.chinaunix.net/u1/37261/showart_476642.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>'Emerillon: map viewer for gnome desktop'</title><link href="https://sunng.info/blog/emerillon-map-viewer-for-gnome-desktop.html" rel="alternate"></link><published>2010-01-30T00:00:00+08:00</published><updated>2010-01-30T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-01-30:/blog/emerillon-map-viewer-for-gnome-desktop.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;GIS&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;map&lt;/li&gt;
&lt;li&gt;OpenStreetMap
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;自从libchamplain / geoclue等库发布之后，gnome桌面的地理信息工具和支持发展很迅速：例如之前提到过的eye-of-gnome的地理信息插件，根据EXIF信息在地图上显示。现在gnome桌面上终于有一个专门的地图查看器了，仍然是基于libchamplain，名字叫做&lt;a href="http://www.novopia.com/emerillon/" target="_blank"&gt;emerillon&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a title="Emerillon Map Viewer by 贝小塔, on Flickr" href="http://www.flickr.com/photos/40741608@N08/4315412541/"&gt;&lt;img src="http://farm5.static.flickr.com/4021/4315412541_d34e08d02b.jpg" alt="Emerillon Map Viewer" width="500" height="286" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;仍然是使用open street maps …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;GIS&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;map&lt;/li&gt;
&lt;li&gt;OpenStreetMap
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;自从libchamplain / geoclue等库发布之后，gnome桌面的地理信息工具和支持发展很迅速：例如之前提到过的eye-of-gnome的地理信息插件，根据EXIF信息在地图上显示。现在gnome桌面上终于有一个专门的地图查看器了，仍然是基于libchamplain，名字叫做&lt;a href="http://www.novopia.com/emerillon/" target="_blank"&gt;emerillon&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a title="Emerillon Map Viewer by 贝小塔, on Flickr" href="http://www.flickr.com/photos/40741608@N08/4315412541/"&gt;&lt;img src="http://farm5.static.flickr.com/4021/4315412541_d34e08d02b.jpg" alt="Emerillon Map Viewer" width="500" height="286" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;仍然是使用open street maps，这两年上海的地图发展的非常不错，连最新的二号线东延都已经被标注出来了。相比之下，南京的地图就还是一片空白。&lt;/p&gt;

&lt;p&gt;在Ubuntu上安装emerillon，可以从其网站上下载源码编译安装：
&lt;a href="http://www.novopia.com/emerillon/download.html"&gt;http://www.novopia.com/emerillon/download.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;emerillon的几个主要依赖：
&lt;ul&gt;
    &lt;li&gt;libchamplain&lt;/li&gt;
    &lt;li&gt;librest&lt;/li&gt;
    &lt;li&gt;ethos&lt;/li&gt;
&lt;/ul&gt;
libchamplain在ubuntu 9.10的仓库已经包含&lt;/p&gt;

&lt;p&gt;librest也在软件仓库中，不过需要注意的是ubuntu将librest安装在pkg-config里时的名字叫做rest.pc，而emerillon查找的是rest-0.6.pc，所以需要手动建立一个软连接：
&lt;em&gt;sudo ln -s /usr/lib/pkgconfig/rest.pc /usr/lib/pkgconfig/rest-0.6.pc&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;ethos是一个Gtk的插件框架，目前还不在软件仓库中，需要从网站下载代码编译：
&lt;a href="http://git.dronelabs.com/ethos/" target="_blank"&gt;http://git.dronelabs.com/ethos/&lt;/a&gt;
ethos网站上提到的PPA源中的版本偏旧，不建议使用。&lt;/p&gt;

&lt;p&gt;&lt;em&gt;实际上也可以直接添加emerillon的PPA源：&lt;br /&gt;
deb http://ppa.launchpad.net/mathieu-tl/emerillon/ubuntu karmic main&lt;br /&gt;
deb-src http://ppa.launchpad.net/mathieu-tl/emerillon/ubuntu karmic main&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;另外，也可以通过Ubuntu Tweak安装。&lt;/p&gt;

&lt;p&gt;作者Blog：
&lt;a href="http://blog.pierlux.com/en/"&gt;http://blog.pierlux.com/en/&lt;/a&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>All new SUNNG.INFO</title><link href="https://sunng.info/blog/all-new-sunnginfo.html" rel="alternate"></link><published>2010-01-28T00:00:00+08:00</published><updated>2010-01-28T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-01-28:/blog/all-new-sunnginfo.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;blog&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;今早短路，在godaddy上注册了我的新域名sunng.info。更换域名主要出于几方面的考虑：
&lt;ol&gt;
    &lt;li&gt;新域名1年的注册费用相当于原先的一半；&lt;/li&gt;
    &lt;li&gt;众所周知的原因，域名放在国内存在一 …&lt;/li&gt;&lt;/ol&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;blog&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;今早短路，在godaddy上注册了我的新域名sunng.info。更换域名主要出于几方面的考虑：
&lt;ol&gt;
    &lt;li&gt;新域名1年的注册费用相当于原先的一半；&lt;/li&gt;
    &lt;li&gt;众所周知的原因，域名放在国内存在一定的风险；&lt;/li&gt;
    &lt;li&gt;原先域名过长不方便记忆和交流；&lt;/li&gt;
    &lt;li&gt;.com后缀和我一穷二白地状况不符&lt;/li&gt;
    &lt;li&gt;classicning.com曾被加入stopbadware.org，至今没有从twitter的malcious数据库里移除&lt;/li&gt;
&lt;/ol&gt;
原先的classicning.com会在今年6月到期，如果没有意外情况我不准备续费了，届时这个使用了四年的域名就要废弃了（当时客服大嫂还在电话里娇嗔地问我为啥不续十年呢，你说为啥呢）。&lt;/p&gt;

&lt;p&gt;麻烦大家利用这半年的时间逐步改变习惯，逐步用sunng.info了。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>New composite based captcha image</title><link href="https://sunng.info/blog/new-composite-based-captcha-image.html" rel="alternate"></link><published>2010-01-27T00:00:00+08:00</published><updated>2010-01-27T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-01-27:/blog/new-composite-based-captcha-image.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;captcha&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;Yan
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;recaptcha的验证码新增了alpha composite的新机制取代干扰线，今天用了一些时间在&lt;a href="http://bitbucket.org/sunng/yan/" target="_blank"&gt;YAN&lt;/a&gt;上也实现了这种绘图机制。&lt;/p&gt;

&lt;p&gt;&lt;a title="30a2512d899641a8ab79a7c86946ff71 by 贝小塔, on Flickr" href="http://www.flickr.com/photos/40741608@N08/4308533479/"&gt;&lt;img src="http://farm3.static.flickr.com/2752/4308533479_42337f4525_o.png" alt="30a2512d899641a8ab79a7c86946ff71" width="300" height="100" /&gt;&lt;/a&gt;
&lt;a title="f03ec596b91b4ce985c5b5af4a79e961 by 贝小塔, on Flickr" href="http://www.flickr.com/photos/40741608@N08/4308533483/"&gt;&lt;img src="http://farm5.static.flickr.com/4068/4308533483_a9e687b74c_o.png" alt="f03ec596b91b4ce985c5b5af4a79e961" width="300" height="100" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;使用Java2D的AlphaComposite实现，选用的Rule为alpha 1.0的SrcOut，即通过公式&lt;/p&gt;

&lt;p&gt;Ar = As * (1 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;captcha&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;Yan
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;recaptcha的验证码新增了alpha composite的新机制取代干扰线，今天用了一些时间在&lt;a href="http://bitbucket.org/sunng/yan/" target="_blank"&gt;YAN&lt;/a&gt;上也实现了这种绘图机制。&lt;/p&gt;

&lt;p&gt;&lt;a title="30a2512d899641a8ab79a7c86946ff71 by 贝小塔, on Flickr" href="http://www.flickr.com/photos/40741608@N08/4308533479/"&gt;&lt;img src="http://farm3.static.flickr.com/2752/4308533479_42337f4525_o.png" alt="30a2512d899641a8ab79a7c86946ff71" width="300" height="100" /&gt;&lt;/a&gt;
&lt;a title="f03ec596b91b4ce985c5b5af4a79e961 by 贝小塔, on Flickr" href="http://www.flickr.com/photos/40741608@N08/4308533483/"&gt;&lt;img src="http://farm5.static.flickr.com/4068/4308533483_a9e687b74c_o.png" alt="f03ec596b91b4ce985c5b5af4a79e961" width="300" height="100" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;使用Java2D的AlphaComposite实现，选用的Rule为alpha 1.0的SrcOut，即通过公式&lt;/p&gt;

&lt;p&gt;Ar = As * (1 – Ad )&lt;br /&gt;
Cr = Cs * (1 – Ad )&lt;br /&gt;
用语言描述就是叠加区域的透明度为0. 使用这种机制必须采用BufferedImage.TYPE_INT_ARGB的图像，并且输出支持alpha通道的格式。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>近况0</title><link href="https://sunng.info/blog/jin-kuang-0.html" rel="alternate"></link><published>2010-01-19T00:00:00+08:00</published><updated>2010-01-19T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-01-19:/blog/jin-kuang-0.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags: []
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;ol&gt;
    &lt;li&gt;这是经历了又一次长时间的unavailable回来。&lt;/li&gt;
    &lt;li&gt;最近挺忙，新项目启动了，旧项目月底要上线，早先的项目deadline也要到了，还不见动静不 …&lt;/li&gt;&lt;/ol&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags: []
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;ol&gt;
    &lt;li&gt;这是经历了又一次长时间的unavailable回来。&lt;/li&gt;
    &lt;li&gt;最近挺忙，新项目启动了，旧项目月底要上线，早先的项目deadline也要到了，还不见动静不知道什么情况，今天又被叫去说数据部门的老大又有新想法了，娘勒，四个，四个啊。&lt;/li&gt;
    &lt;li&gt;即使有四个，我发现我在twitter上还是那么神情自若，rabr自动取新消息的功能太害人了，忍不住就想点点看看，看了就想点，很影响注意力的集中。于是最近改用lynx / w3m这样的命令行浏览器上rabr。rabr做的还真是非常不错，在没有Javascript支持的情况下依然能用。诚心推荐大家一起来复古，w3m很powerful，支持vim和lynx的快捷键切换，居然还能显示图片，当时我就shock了。&lt;/li&gt;
    &lt;li&gt;YAN有了200次commit，接下来的主要工作是把它拆成多个maven module，以便未来开发的方便。尽管之前做了一些准备了，但是这个过程预计不会太轻松。最近精力有限，暂时先不全砸在上面吧。&lt;/li&gt;
    &lt;li&gt;业余的项目还要做下去，分为multimodule之后YAN的开发也就差不多了；之前写的Exaile的豆瓣Cover插件也没有后续开发的必要了，大家有什么点子欢迎分享。&lt;/li&gt;
    &lt;li&gt;上周去上图发现楼上的阅览室资源很多，我决定以后每个月至少花一天泡在图书馆。&lt;/li&gt;
    &lt;li&gt;有时候想想我真凄凉啊，哎&lt;/li&gt;
&lt;/ol&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Which University</title><link href="https://sunng.info/blog/which-university.html" rel="alternate"></link><published>2010-01-10T00:00:00+08:00</published><updated>2010-01-10T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-01-10:/blog/which-university.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;joke&lt;/li&gt;
&lt;li&gt;Life&lt;/li&gt;
&lt;li&gt;Nanjing
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;这年头怪事真叫多。今天在珠江路地铁站，突然过来一位深度大叔，弱弱地问我这个方向是不是去 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;joke&lt;/li&gt;
&lt;li&gt;Life&lt;/li&gt;
&lt;li&gt;Nanjing
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;这年头怪事真叫多。今天在珠江路地铁站，突然过来一位深度大叔，弱弱地问我这个方向是不是去火车站，我说这大叔也真不容易，牌子都不认识怎么进来的，赶紧说是的。&lt;br /&gt;
大叔继续问你也去火车站吗，我想这大叔估计第一次来，人生地不熟的，也说是。&lt;br /&gt;
这就罢了吧，您跟着我就行了。但是大叔不罢休，继续，“你是学生吗”。我本能地把手装进口袋握住钱包，说了一个我平生最凄凉的谎话，是的。以为会话到此结束的朋友你们错了，大叔继续不依不饶，问你是哪个学校的啊，我刚要如实撒谎，大叔蹦出一句which university（请相信我的听力，我的听力和大叔的口语水平还是相当的），这句没让我当场摔倒完全是多亏中午吃得多有劲。&lt;br /&gt;
奇妙的对话从他娘的which university开始了，大叔滔滔不绝&lt;br /&gt;
“英语怎么样？四级过了吗？”&lt;br /&gt;
“你是学什么专业的？which major？”&lt;br /&gt;
“What about your speaking English？口语怎么样啊？”&lt;br /&gt;
“你知道向日葵怎么说吗？桑福拉沃”&lt;br /&gt;
“月季呢？”&lt;br /&gt;
“肉丝你知道吧？玫瑰。加上中国，China rose就是月季”&lt;br /&gt;
（地铁来了，我不堪忍受，从旁边的门进去，大叔跟过来继续）&lt;br /&gt;
"toilet你知道吧？toilet water你猜是什么？"&lt;br /&gt;
这个时候终于有个座位，大叔当然不让坐下，还不忘循循善诱，“是花露水”。&lt;/p&gt;

&lt;p&gt;好在这课也上到花露水为止。临下车摸了一下手机和钱包，嗯，这课是免费试听的，也罢也罢。原来我长得这么像是有英语培训市场价值的啊。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>OAuth Step by Step</title><link href="https://sunng.info/blog/oauth-step-by-step.html" rel="alternate"></link><published>2010-01-04T00:00:00+08:00</published><updated>2010-01-04T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-01-04:/blog/oauth-step-by-step.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;mashup&lt;/li&gt;
&lt;li&gt;oauth&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;最近琢磨OAuth认证方式。OAuth的优点主要在于﻿
&lt;ul&gt;
    &lt;li&gt;用户不需要直接提供用户名密码给第三方应用，就可以让第三方应用访问受限 …&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;mashup&lt;/li&gt;
&lt;li&gt;oauth&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;最近琢磨OAuth认证方式。OAuth的优点主要在于﻿
&lt;ul&gt;
    &lt;li&gt;用户不需要直接提供用户名密码给第三方应用，就可以让第三方应用访问受限资源；&lt;/li&gt;
    &lt;li&gt;资源提供方对第三方应用有更细粒度的控制。&lt;/li&gt;
&lt;/ul&gt;
在整个OAuth协议里，生成signature的base string是最容易出错的部分。它由HTTP方法名、URL编码的请求路径和请求的参数表组成。&lt;br /&gt;
请求的参数表是除去oauth_signature以外的所有参数，按参数名排序，并进行url转义
&lt;pre class="brush:python"&gt;def to_signature_key(method, url, data):
    keys = list(data.keys())
    keys.sort()
    encoded = urllib.quote("&amp;amp;".join([key+"="+data[key] for key in keys]))
    return "&amp;amp;".join([method, urllib.quote(url, safe="~"), encoded])
&lt;/pre&gt;
有了这个通用的生成signature base string的方法，以后就可以根据OAuth协议规范按步骤进行。&lt;/p&gt;

&lt;p&gt;首先获取Request Token。这一步通常使用资源提供方注册的API Key和API Key Secret
&lt;pre class="brush:python"&gt;def request_token_params(consumer_key, consumer_secret, path, method='GET'):
    data={}
    data['oauth_consumer_key']=consumer_key
    data['oauth_signature_method']='HMAC-SHA1'
    data['oauth_timestamp']=str(int(time.time()))
    data['oauth_nonce']=''.join([str(random.randint(0,9)) for i in range(10)])
    print data&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt; msg = to_signature_key(method, path, data)&lt;br /&gt;
    print msg&lt;/p&gt;

&lt;p&gt; signed = base64.b64encode(hmac.new(consumer_secret+"&amp;amp;", msg, hashlib.sha1).digest())&lt;br /&gt;
    print signed&lt;br /&gt;
    data['oauth_signature']=signed&lt;br /&gt;
    return data&lt;/p&gt;

&lt;p&gt;def result2dict(result_string):&lt;br /&gt;
    d = {}&lt;br /&gt;
    params = res.split('&amp;amp;')&lt;br /&gt;
    for p in params:&lt;br /&gt;
        d[p.split('=')[0]] = p.split('=')[1]&lt;br /&gt;
    return d&lt;/p&gt;

&lt;p&gt;conn = httplib.HTTPConnection("www.douban.com", 80)&lt;/p&gt;

&lt;p&gt;params = request_token_params(consumer_key, consumer_secret, request_token_path)&lt;br /&gt;
conn.request('GET', request_token_path+"?"+urllib.urlencode(params))&lt;br /&gt;
res = conn.getresponse().read()&lt;br /&gt;
print res&lt;br /&gt;
request_token = result2dict(res)

这一步可以获得未经认证的Request Token和Request Token Secret。需要注意的细节是在计算hmac签名的时候，即使只有一个Token Secret，仍然需要加上"&amp;amp;"&lt;/p&gt;

&lt;p&gt;第二步要求用户授权该Request Token，打开浏览器，将用户定向到相应的授权页面，参数为上一步获得的Request Token&lt;/p&gt;

&lt;p&gt;第三步，用授权过的Request Token换取Access Token。这一步类似第一步，只是用于签名的token包括API Key Secret和Request Token
&lt;pre class="brush:python"&gt;def access_token_params(consumer_key, consumer_secret, oauth_token, oauth_secret, path, method='GET'):
    data={}
    data['oauth_consumer_key']=consumer_key
    data['oauth_signature_method']='HMAC-SHA1'
    data['oauth_timestamp']=str(int(time.time()))
    data['oauth_nonce']=''.join([str(random.randint(0,9)) for i in range(10)])
    data['oauth_token'] = oauth_token&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt; msg = to_signature_key(method, path, data)&lt;br /&gt;
    print msg&lt;/p&gt;

&lt;p&gt; signed = base64.b64encode(hmac.new(consumer_secret+"&amp;amp;"+oauth_secret, msg, hashlib.sha1).digest())&lt;br /&gt;
    print signed&lt;br /&gt;
    data['oauth_signature']=signed&lt;br /&gt;
    return data&lt;/p&gt;

&lt;p&gt;params = access_token_params(consumer_key, consumer_secret, request_token['oauth_token'],&lt;br /&gt;
    request_token['oauth_token_secret'], access_token_path)&lt;br /&gt;
conn.request('GET', access_token_path+"?"+urllib.urlencode(params))&lt;br /&gt;
res = conn.getresponse().read()&lt;br /&gt;
print res&lt;br /&gt;
access_token = result2dict(res)

这一步将至少返回Access Token和Access Token Secret，是最终用于访问受限资源的Token。以豆瓣的实现为例，OAuth的相关参数应放在HTTP头里随请求进行发送。
&lt;pre class="brush:python"&gt;def oauth_header(consumer_key, consumer_secret, oauth_token, oauth_secret, path, realm):
    data = access_token_params(consumer_key, consumer_secret, oauth_token, oauth_secret, path, method="POST")
    header_string = ','.join([key+'="'+data[key]+'"' for key in data.keys()])
    return 'OAuth realm="'+realm+'",'+header_string&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;posturl = 'http://api.douban.com/miniblog/saying'&lt;/p&gt;

&lt;p&gt;content = """&amp;lt;?xml version='1.0' encoding='UTF-8'?&amp;gt;&lt;br /&gt;
&amp;lt;entry xmlns:ns0="http://www.w3.org/2005/Atom" xmlns:db="http://www.douban.com/xmlns/"&amp;gt;&lt;br /&gt;
&amp;lt;content&amp;gt;li lei ju le han mei mei&amp;lt;/content&amp;gt;&lt;br /&gt;
&amp;lt;/entry&amp;gt;&lt;br /&gt;
"""&lt;/p&gt;

&lt;p&gt;header = {}&lt;br /&gt;
header['Authorization'] = oauth_header(consumer_key, consumer_secret,&lt;br /&gt;
        access_token['oauth_token'], access_token['oauth_token_secret'],&lt;br /&gt;
        posturl, "http://api.douban.com")&lt;br /&gt;
header['Content-Type'] = 'application/atom+xml'&lt;br /&gt;
print header&lt;/p&gt;

&lt;p&gt;conn.request('POST', posturl, content, header)&lt;br /&gt;
res = conn.getresponse().read()&lt;/p&gt;

&lt;p&gt;print res&lt;/p&gt;

&lt;p&gt;conn.close()

在这一步中，用于生成signature base string的url是要访问的受限资源地址，而签名的参数表依然是oauth相关的参数。&lt;br /&gt;
生成的Authorization头如下
&lt;pre class="brush:text"&gt;Authorization: OAuth realm="http://api.douban.com",
    oauth_nonce="8735717688",
    oauth_timestamp="1262613619",
    oauth_consumer_key="0bc081a01168b263234184e0343a1729",
    oauth_signature_method="HMAC-SHA1",
    oauth_token="5fb836c37543ad691f28a44a5fcb083b",
    oauth_signature="jk6p5qaXVPrGQctSzpO5jjYHfDk="
&lt;/pre&gt;
用这个头就可以在一定的时间内访问所有授权范围内的受限资源。&lt;/p&gt;

&lt;p&gt;代码是ugly了一些，不过应该相对易于理解吧。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Happy new year with Yan 0.4</title><link href="https://sunng.info/blog/happy-new-year-with-yan-04.html" rel="alternate"></link><published>2010-01-01T00:00:00+08:00</published><updated>2010-01-01T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2010-01-01:/blog/happy-new-year-with-yan-04.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;hg&lt;/li&gt;
&lt;li&gt;Yan
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;I am glad to release the Yan 0.4 at the last night of 2009. It's an important release which witnesses the improvement on both code and the project itself. We have great changes in all modules.  Packages have been rearranged and …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;hg&lt;/li&gt;
&lt;li&gt;Yan
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;I am glad to release the Yan 0.4 at the last night of 2009. It's an important release which witnesses the improvement on both code and the project itself. We have great changes in all modules.  Packages have been rearranged and renamed to be more comprehensible. Let's dive into the changeset:
&lt;h3&gt;Changeset&lt;/h3&gt;
&lt;ul&gt;
    &lt;li&gt;ApiKey database derby / h2 support, still use hsql as default because it's fastest (#9)&lt;/li&gt;
    &lt;li&gt;Captcha provider classes now loaded dynamically on runtime. They are not managed by guice container any longer. (#15)&lt;/li&gt;
    &lt;li&gt;The useless cache module was completely removed (#10)&lt;/li&gt;
    &lt;li&gt;ApiKey CR&lt;span style="text-decoration: line-through;"&gt;U&lt;/span&gt;D UI has been replaced by RESTful GET/PUT/DELETE interfaces&lt;/li&gt;
    &lt;li&gt;For text-based captcha, just return the question in ticket, no need for (also impossible to)  retrieve it from browser (#8)&lt;/li&gt;
    &lt;li&gt;Resource has been separated from the project file (#14 #18)&lt;/li&gt;
    &lt;li&gt;Add test to make sure the /ticket and /validate request are sent by the same application (#17)&lt;/li&gt;
    &lt;li&gt;Standardized error output: use HTTP error for client(browser), a selected error object for application (#24 #26)&lt;/li&gt;
    &lt;li&gt;/captcha/ request (invoked by client directly) now accepts configuration parameters&lt;/li&gt;
    &lt;li&gt;ApiKey is now binded to specified domain, it will check the /captcha request's referer (#27)&lt;/li&gt;
    &lt;li&gt;NEW captcha provider introduced in: Tiled Image Captcha (#12)&lt;/li&gt;
    &lt;li&gt;JMX monitoring support on EhCache (#33)&lt;/li&gt;
    &lt;li&gt;a great deal of code improvements and bug-fixes&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Interface changes&lt;/h3&gt;
The object return by /ticket has its attribute "url" renamed to "data".
&lt;h3&gt;Screenshots&lt;/h3&gt;
Use visualvm or jconsole the monitor ehcache status and statistics (enabled in 0.4 by default):&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" src="http://farm5.static.flickr.com/4034/4231348850_db1b877ba8.jpg" alt="" width="500" height="313" /&gt;&lt;/p&gt;

&lt;p&gt;The new captcha provider in ruby and python sample application (&lt;a href="http://bitbucket.org/sunng/yan/wiki/SampleCode" target="_blank"&gt;Sample Code&lt;/a&gt;):&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" src="http://farm3.static.flickr.com/2645/4230582459_bf63ce6677.jpg" alt="" width="500" height="313" /&gt;
&lt;h3&gt;Download &amp;amp; Deploy&lt;/h3&gt;
Since 0.4, yan package are available for download directly. Please refer to the  &lt;a href="http://bitbucket.org/sunng/yan/downloads/" target="_blank"&gt;download page&lt;/a&gt;, grab both yan-0.4.war and yan-resource.tar.bz2. Just throw the war package to your servlet container.  Then extract the resource package to your disk. Don't forget to set the environment variable:
&lt;em&gt;export YAN_RESOURCE=/path/to/your/resource&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Start the servlet container in the same context, browser &lt;em&gt;http://localhost:8080/yan-0.4/&lt;/em&gt; to see the test page and emulate the captcha process.
&lt;h3&gt;Retrieve the code&lt;/h3&gt;
Clone the mercurial repository from bitbucket.org&lt;/p&gt;

&lt;p&gt;&lt;em&gt;$ hg clone https://sunng@bitbucket.org/sunng/yan/&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You will get a copy of whole code repository (because mercurial is a distributed version control system). You are on the default branch at the beginning. The default branch maintains the code of 0.4, if you want to see latest things on Yan, switch to the development branch by&lt;/p&gt;

&lt;p&gt;&lt;em&gt;$ hg update 0.5-dev&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Yan 0.5 is already on the way.
&lt;h3&gt;Reporting Issue&lt;/h3&gt;
Issue reporting and patch submitting are always welcomed. Check the &lt;a href="http://bitbucket.org/sunng/yan/issues/" target="_blank"&gt;issue tracker on bitbucket&lt;/a&gt;, you will find &lt;a href="http://bitbucket.org/sunng/yan/issues/?kind=proposal&amp;amp;version=0.5" target="_blank"&gt;new features&lt;/a&gt; in 0.5&lt;/p&gt;

&lt;p&gt;Thanks for your support and &lt;img class="alignnone" src="http://farm5.static.flickr.com/4030/4231554900_935d453250_o.png" alt="" width="250" height="40" /&gt;!&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Uncle Wang 他去年退了休</title><link href="https://sunng.info/blog/uncle-wang-ta-qu-nian-tui-liao-xiu.html" rel="alternate"></link><published>2009-12-31T00:00:00+08:00</published><updated>2009-12-31T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-12-31:/blog/uncle-wang-ta-qu-nian-tui-liao-xiu.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;好久没有用这么不着边际的标题了，其实这种做法是我的偏好。你要是觉着它顺口的话它确确实实是 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;好久没有用这么不着边际的标题了，其实这种做法是我的偏好。你要是觉着它顺口的话它确确实实是一句歌词，WG的&lt;a href="http://blog.williamgates.biz/2009/11/li-lei-han-mei-mei-2/" target="_blank"&gt;这篇文章&lt;/a&gt;不推荐实在太可惜了。&lt;/p&gt;

&lt;p&gt;这本来应该是一篇2009年的总结来着，早上上班的路上我一直在过滤哪些要说哪些值得说哪些能说。不用我说，认识我的人都知道我今年可说的事情太多了。鉴于太多太碎，就不一件一件地说了。另一方面，也正好是过去的已经远去，开始的才刚刚开始，不是作总结的时候。这个年关是客观上的一个坎，于个人的生活和进度，他还不是什么milestone，远不及9号要交房租的重要。&lt;/p&gt;

&lt;p&gt;紧接着是老生常谈感叹时光的话，&lt;a href="http://lightory.net/" target="_blank"&gt;Lightory&lt;/a&gt;在twitter上说的这句我很有共鸣——“马上 2010 年了，总觉得这个数字没现实感，是只属于科幻小说里的年代。” 分明是快要到了何勇唱的“宇宙飞船没找到天堂”的时候了。不过前几天鲜果联播上看到有人发的一句话很好地解释了日子越过越快的原因：“对于一个10岁的人来说，每长一岁，意味着他复制新增了自己十分之一；而对一个30岁的人来说，每长一岁，意味着他只复制新增了自己过往经历的三十分之一”。他明白了，你呢？&lt;/p&gt;

&lt;p&gt;说总结就要定计划（说这句话的时候我猛然想起这周项目周报又忘填了），不过&lt;a href="http://timyang.net/" target="_blank"&gt;Tim&lt;/a&gt;大牛半开玩笑地说，计划定得越多，洋相出得越大。需求变化那么快，闹不好项目中途都被cut了，所以细节上还是短周期迭代。大局上，还是给自己一直以来的要求，不要随波逐流，注意自己的言行，努力学习技术，认真工作积累经验。&lt;/p&gt;

&lt;p&gt;今天晚上找一找Yan的bug，没有什么问题的话，0.4的tag就可以打了。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Setting Environment Variables for NetBeans, Continued</title><link href="https://sunng.info/blog/setting-environment-variables-for-netbeans-continued.html" rel="alternate"></link><published>2009-12-30T00:00:00+08:00</published><updated>2009-12-30T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-12-30:/blog/setting-environment-variables-for-netbeans-continued.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;netbeans
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;This is a feature introduced in NetBeans 6.7.&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" src="http://farm5.static.flickr.com/4072/4227520447_bccc557571.jpg" alt="" width="500" height="287" /&gt;&lt;/p&gt;

&lt;p&gt;Set environment variables for specified life-cycle phases: Open project properties dialog, select action category. Select actions which depend on the variable, click add button to add such an environment variable.&lt;/p&gt;

&lt;p&gt;However, this …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;netbeans
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;This is a feature introduced in NetBeans 6.7.&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" src="http://farm5.static.flickr.com/4072/4227520447_bccc557571.jpg" alt="" width="500" height="287" /&gt;&lt;/p&gt;

&lt;p&gt;Set environment variables for specified life-cycle phases: Open project properties dialog, select action category. Select actions which depend on the variable, click add button to add such an environment variable.&lt;/p&gt;

&lt;p&gt;However, this method doesn't work with Glassfish spawned the IDE. If you want use environment variables in the server spawned by NetBeans IDE, you still have to export the variables in netbeans.conf&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>我是一盏小灯</title><link href="https://sunng.info/blog/wo-shi-yi-zhan-xiao-deng.html" rel="alternate"></link><published>2009-12-30T00:00:00+08:00</published><updated>2009-12-30T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-12-30:/blog/wo-shi-yi-zhan-xiao-deng.html</id><content type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;img class="alignnone" src="http://farm5.static.flickr.com/4053/4228168348_1ff34e05e0.jpg" alt="" width="500" height="335" /&gt;&lt;/p&gt;

&lt;p&gt;长期苦于桌上没有照明设备，上周末买了一盏灯。之所以选择Esc版的，因为咱是VI用户。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Setting Environment Variables for Glassfish v3</title><link href="https://sunng.info/blog/setting-environment-variables-for-glassfish-v3.html" rel="alternate"></link><published>2009-12-29T00:00:00+08:00</published><updated>2009-12-29T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-12-29:/blog/setting-environment-variables-for-glassfish-v3.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;Glassfish&lt;/li&gt;
&lt;li&gt;java
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;After you test and build your application in NetBeans, you deploy it to Glassfish, but the environment variables are invalid again. In such situation, you should set these variable in glassfish configuration file which is supposed to located at:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;$GLASSFISH_HOME/glassfish …&lt;/em&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;Glassfish&lt;/li&gt;
&lt;li&gt;java
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;After you test and build your application in NetBeans, you deploy it to Glassfish, but the environment variables are invalid again. In such situation, you should set these variable in glassfish configuration file which is supposed to located at:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;$GLASSFISH_HOME/glassfish/config/asenv.conf&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Append your variables to the file, and restart the server. It works.&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Setting Environment Variables for NetBeans</title><link href="https://sunng.info/blog/setting-environment-variables-for-netbeans.html" rel="alternate"></link><published>2009-12-28T00:00:00+08:00</published><updated>2009-12-28T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-12-28:/blog/setting-environment-variables-for-netbeans.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;netbeans
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;If you use NetBeans IDE to build an application which reads environment variable, you will have to do these settings in your IDE. There is no GUI to set such variables in NetBeans while Eclipse provides you a "Run Configuration Dialog …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;netbeans
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;If you use NetBeans IDE to build an application which reads environment variable, you will have to do these settings in your IDE. There is no GUI to set such variables in NetBeans while Eclipse provides you a "Run Configuration Dialog". So just add the declaration in your netbeans.conf.&lt;/p&gt;

&lt;p&gt;It is highly recommended to copy the configuration file to your home directory before you edit it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;cp -r /usr/local/netbeans-6.8/etc ~/.netbeans/6.8/&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And append this line:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;export YAN_RESOURCE=/home/sun/work/yan-resource&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Then restart your IDE, it works. You can test/build/run your application with the environment variable &lt;em&gt;$YAN_RESOURCE&lt;/em&gt; inside NetBeans IDE,&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Hush!</title><link href="https://sunng.info/blog/hush.html" rel="alternate"></link><published>2009-12-26T00:00:00+08:00</published><updated>2009-12-26T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-12-26:/blog/hush.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN&lt;/li&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;captcha&lt;/li&gt;
&lt;li&gt;Yan
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;img class="alignnone" src="http://farm3.static.flickr.com/2577/4215800886_9e33ac4830_o.png" alt="" width="329" height="492" /&gt;&lt;/p&gt;

&lt;p&gt;今天新增的拼图验证码的可配置性非常强，你只要替换资源文件，在配置文件中修改提问的模版 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN&lt;/li&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;captcha&lt;/li&gt;
&lt;li&gt;Yan
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;img class="alignnone" src="http://farm3.static.flickr.com/2577/4215800886_9e33ac4830_o.png" alt="" width="329" height="492" /&gt;&lt;/p&gt;

&lt;p&gt;今天新增的拼图验证码的可配置性非常强，你只要替换资源文件，在配置文件中修改提问的模版，指定图片的大小、行数、列数，就可以创造一套全新的验证码。他的简单程度实在超出你的想象。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Web 2.0 Icon Captcha</title><link href="https://sunng.info/blog/web-20-icon-captcha.html" rel="alternate"></link><published>2009-12-26T00:00:00+08:00</published><updated>2009-12-26T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-12-26:/blog/web-20-icon-captcha.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;captcha&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;Yan
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Yan 新增了一种验证码类型，Web 2.0 图标验证码。用户根据图标的内容和提示的信息，提交验证码。验证 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;captcha&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;Yan
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Yan 新增了一种验证码类型，Web 2.0 图标验证码。用户根据图标的内容和提示的信息，提交验证码。验证码图片如下：&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" src="http://farm3.static.flickr.com/2556/4215434144_f589085acd_o.png" alt="" width="256" height="192" /&gt;&lt;/p&gt;

&lt;p&gt;提示文字： Please figure out twitter icons.&lt;/p&gt;

&lt;p&gt;用户输入Twitter图标左上角上的字母，即可进行验证。在Yan的测试界面上使用如图：&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" src="http://farm3.static.flickr.com/2565/4214663971_cdc2168f69_o.png" alt="" width="394" height="503" /&gt;&lt;/p&gt;

&lt;p&gt;Web2.0 Icon实际上是Yan中新增的拼图验证码的一个实例，利用拼图验证码可以生成相似的更有创意的验证码。在我的开发环境中生成这样一张图片大约需要80ms。&lt;/p&gt;

&lt;p&gt;项目中使用的图标均从互联网收集，遵循CC等协议或经作者授权，详情参考项目中README文件。&lt;/p&gt;

&lt;p&gt;祝DAF同学生日快乐。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Load Test on Yan</title><link href="https://sunng.info/blog/load-test-on-yan.html" rel="alternate"></link><published>2009-12-25T00:00:00+08:00</published><updated>2009-12-25T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-12-25:/blog/load-test-on-yan.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;captcha&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;Yan
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;给Yan的验证码图片服务做了压力测试。测试环境：
&lt;ul&gt;
    &lt;li&gt;Intel Xeon 3.00GHz 4核&lt;/li&gt;
    &lt;li&gt;内存2G&lt;/li&gt;
    &lt;li&gt;Red Hat Enterprise Linux AS release 4 (Nahant Update 7)&lt;/li&gt;
    &lt;li&gt;Jetty 6 / JDK 6&lt;/li&gt;
&lt;/ul&gt;
Jetty采用默认配置 maxThreads 200。&lt;/p&gt;

&lt;p&gt;测试工具：ab (Apache Bench)&lt;/p&gt;

&lt;p&gt;分别用 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;captcha&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;Yan
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;给Yan的验证码图片服务做了压力测试。测试环境：
&lt;ul&gt;
    &lt;li&gt;Intel Xeon 3.00GHz 4核&lt;/li&gt;
    &lt;li&gt;内存2G&lt;/li&gt;
    &lt;li&gt;Red Hat Enterprise Linux AS release 4 (Nahant Update 7)&lt;/li&gt;
    &lt;li&gt;Jetty 6 / JDK 6&lt;/li&gt;
&lt;/ul&gt;
Jetty采用默认配置 maxThreads 200。&lt;/p&gt;

&lt;p&gt;测试工具：ab (Apache Bench)&lt;/p&gt;

&lt;p&gt;分别用10/50/100/200/500/1000并发用户，每个用户请求100次进行测试。结果如下：&lt;/p&gt;

&lt;p&gt;&lt;!--         BODY,DIV,TABLE,THEAD,TBODY,TFOOT,TR,TH,TD,P { font-family:"宋体"; font-size:x-small } --&gt;&lt;/p&gt;

&lt;p&gt;&lt;!--         BODY,DIV,TABLE,THEAD,TBODY,TFOOT,TR,TH,TD,P { font-family:"宋体"; font-size:x-small } --&gt;
&lt;table border="0" cellspacing="0" frame="VOID" rules="NONE"&gt;&lt;colgroup&gt; &lt;col width="161" /&gt; &lt;col width="159" /&gt; &lt;col width="167" /&gt; &lt;col width="169" /&gt; &lt;col width="160" /&gt; &lt;col width="145" /&gt; &lt;col width="136" /&gt; &lt;/colgroup&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align="LEFT"&gt;&lt;/td&gt;
&lt;td align="RIGHT"&gt;&lt;span style="font-family: Arial;"&gt;10&lt;/span&gt;&lt;/td&gt;
&lt;td align="RIGHT"&gt;&lt;span style="font-family: Arial;"&gt;50&lt;/span&gt;&lt;/td&gt;
&lt;td align="RIGHT"&gt;&lt;span style="font-family: Arial;"&gt;100&lt;/span&gt;&lt;/td&gt;
&lt;td align="RIGHT"&gt;&lt;span style="font-family: Arial;"&gt;200&lt;/span&gt;&lt;/td&gt;
&lt;td align="RIGHT"&gt;&lt;span style="font-family: Arial;"&gt;500&lt;/span&gt;&lt;/td&gt;
&lt;td align="RIGHT"&gt;&lt;span style="font-family: Arial;"&gt;1000&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="LEFT"&gt;&lt;span style="font-family: Arial;"&gt;Requests per second&lt;/span&gt;&lt;/td&gt;
&lt;td align="RIGHT"&gt;&lt;span style="font-family: Arial;"&gt;487.11&lt;/span&gt;&lt;/td&gt;
&lt;td align="RIGHT"&gt;&lt;span style="font-family: Arial;"&gt;472.09&lt;/span&gt;&lt;/td&gt;
&lt;td align="RIGHT"&gt;&lt;span style="font-family: Arial;"&gt;442.74&lt;/span&gt;&lt;/td&gt;
&lt;td align="RIGHT"&gt;&lt;span style="font-family: Arial;"&gt;421.63&lt;/span&gt;&lt;/td&gt;
&lt;td align="RIGHT"&gt;&lt;span style="font-family: Arial;"&gt;408.11&lt;/span&gt;&lt;/td&gt;
&lt;td align="RIGHT"&gt;&lt;span style="font-family: Arial;"&gt;326.12&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="LEFT"&gt;&lt;span style="font-family: Arial;"&gt;Time per request&lt;/span&gt;&lt;/td&gt;
&lt;td align="RIGHT"&gt;&lt;span style="font-family: Arial;"&gt;2.05&lt;/span&gt;&lt;/td&gt;
&lt;td align="RIGHT"&gt;&lt;span style="font-family: Arial;"&gt;2.12&lt;/span&gt;&lt;/td&gt;
&lt;td align="RIGHT"&gt;&lt;span style="font-family: Arial;"&gt;2.26&lt;/span&gt;&lt;/td&gt;
&lt;td align="RIGHT"&gt;&lt;span style="font-family: Arial;"&gt;2.37&lt;/span&gt;&lt;/td&gt;
&lt;td align="RIGHT"&gt;&lt;span style="font-family: Arial;"&gt;2.45&lt;/span&gt;&lt;/td&gt;
&lt;td align="RIGHT"&gt;&lt;span style="font-family: Arial;"&gt;3.07&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="LEFT"&gt;&lt;span style="font-family: Arial;"&gt;Transfer rate&lt;/span&gt;&lt;/td&gt;
&lt;td align="RIGHT"&gt;&lt;span style="font-family: Arial;"&gt;987.91&lt;/span&gt;&lt;/td&gt;
&lt;td align="RIGHT"&gt;&lt;span style="font-family: Arial;"&gt;955.54&lt;/span&gt;&lt;/td&gt;
&lt;td align="RIGHT"&gt;&lt;span style="font-family: Arial;"&gt;896.85&lt;/span&gt;&lt;/td&gt;
&lt;td align="RIGHT"&gt;&lt;span style="font-family: Arial;"&gt;854.31&lt;/span&gt;&lt;/td&gt;
&lt;td align="RIGHT"&gt;&lt;span style="font-family: Arial;"&gt;826.25&lt;/span&gt;&lt;/td&gt;
&lt;td align="RIGHT"&gt;&lt;span style="font-family: Arial;"&gt;660.45&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
﻿&lt;img class="alignnone" src="http://farm3.static.flickr.com/2677/4212622846_f6023f9b40_o.png" alt="" width="358" height="334" /&gt;&lt;/p&gt;

&lt;p&gt;目前对每个请求独立使用JDK的awt实时绘图，吞吐量可以达到400以上，如果稍稍优化一下Jetty的配置，性能还有一定的提升空间。这个结果还是不错的。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Be with you</title><link href="https://sunng.info/blog/be-with-you.html" rel="alternate"></link><published>2009-12-24T00:00:00+08:00</published><updated>2009-12-24T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-12-24:/blog/be-with-you.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;今天下班骑车到高斯路时，突然路边奔出一只小狗跟着我的自行车一起跑。小狗挺干净，出现在这种工 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;今天下班骑车到高斯路时，突然路边奔出一只小狗跟着我的自行车一起跑。小狗挺干净，出现在这种工业区人不多的路上倒是挺让人诧异。我开始放慢速度，让它跟着我不至于太辛苦。可是它还足够顽皮，一会跑在左边，一会在右边，好像是知道今天过节一样，欢实得很。不过我对路中间不时经过的汽车有所顾虑，有意得向路边靠，打算把它挤到路涯边，这样安全一些。&lt;/p&gt;

&lt;p&gt;大约是嫌我速度太慢了，一会后面又上来一个骑自行车的人，小狗顿时起了劲，蹦达蹦达地追了上去。那速度大概是它向往的吧，我在后面看着，直到他们一前一后，过了马路，我从另一个方向回家了。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Java嵌入式数据库的速度对比</title><link href="https://sunng.info/blog/javaqian-ru-shi-shu-ju-ku-de-su-du-dui-bi.html" rel="alternate"></link><published>2009-12-24T00:00:00+08:00</published><updated>2009-12-24T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-12-24:/blog/javaqian-ru-shi-shu-ju-ku-de-su-du-dui-bi.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;db&lt;/li&gt;
&lt;li&gt;java
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Yan的APIKEY一直是用嵌入式的数据库存储的，最初使用的是hsqldb，最近又添加了H2和Derby的支持，基本上囊括了所有开源的Java嵌入式数据库。实现多了自然需要挑选、比较一下 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;db&lt;/li&gt;
&lt;li&gt;java
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Yan的APIKEY一直是用嵌入式的数据库存储的，最初使用的是hsqldb，最近又添加了H2和Derby的支持，基本上囊括了所有开源的Java嵌入式数据库。实现多了自然需要挑选、比较一下。&lt;/p&gt;

&lt;p&gt;数据库特性的比较，H2的网站上有很好的Matrix，一目了然&lt;br /&gt;
http://www.h2database.com/html/features.html#comparison&lt;/p&gt;

&lt;p&gt;关于速度的比较，今天做了一个简单的测试。&lt;/p&gt;

&lt;p&gt;分别从derby / H2 / hsqldb中取出10 、100、1000条数据，循环100000次，比较耗时，如下：&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" src="http://farm3.static.flickr.com/2665/4210754380_5cb4d7b0a8_o.png" alt="" width="376" height="394" /&gt;&lt;/p&gt;

&lt;p&gt;三者的速度差距非常明显，hsqldb远快于其他两个。&lt;/p&gt;

&lt;p&gt;而在10、100、1000条记录的索引上查询，并取出指定记录呢，同样是100000次，如下：&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" src="http://farm3.static.flickr.com/2716/4210754396_e52acc7e69_o.png" alt="" width="376" height="394" /&gt;&lt;/p&gt;

&lt;p&gt;再索引上查询，速度受记录数量的影响非常微弱了。但是hsqldb还是远快于其他二者，有趣的是derby的速度要略微快于H2.&lt;/p&gt;

&lt;p&gt;根据这样的结果，在Yan的应用中，hsqldb还是最理想的实现。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>那年榕树下</title><link href="https://sunng.info/blog/na-nian-rong-shu-xia.html" rel="alternate"></link><published>2009-12-23T00:00:00+08:00</published><updated>2009-12-23T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-12-23:/blog/na-nian-rong-shu-xia.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;internet&lt;/li&gt;
&lt;li&gt;snda
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;有的朋友看到这标题，肯定感叹，哎呀妈呀，这哥们也堕落了，开始写软文了。好吧，就算是软文吧，我决定 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;internet&lt;/li&gt;
&lt;li&gt;snda
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;有的朋友看到这标题，肯定感叹，哎呀妈呀，这哥们也堕落了，开始写软文了。好吧，就算是软文吧，我决定东拉西扯地码一些字，不同于上周五的工作指标，这次完全是自觉自愿的。&lt;/p&gt;

&lt;p&gt;榕树下，25号，重新上线。&lt;/p&gt;

&lt;p&gt;我接触互联网稍微晚了一些，2002年冬天周五的一个下午（不出意外是2002年12月20号），家里终于接上了网线。我打开的第一个网站是搜狐，第二个是华军，第三个不记得了。那时已经听说了榕树下，途径大约是每季度一期的贝塔斯曼广告和天天都有的广播小说节目。&lt;/p&gt;

&lt;p&gt;贝塔斯曼当时似乎和榕树下还是合作关系，后来收购了榕树下，这也是榕树的不幸。每一期的目录都会有热门的网络小说，那时还都是以榕树下的为主。我记得曾经买过陆幼青的书，红极一时的。&lt;/p&gt;

&lt;p&gt;广播每晚十点左右多会有读小说的节目，安妮宝贝的文字常常是DJ们的偏好。到现在具体的内容早就不记得，但是暖暖的感觉还是值得常常温故。&lt;/p&gt;

&lt;p&gt;这是我对互联网最初的认识，虽然我那时也不看网络小说，但在印象里，网络小说是互联网生活很重要的一部分。现在要是问我知名的作者我可能几乎都说不上来，但是看到今天网站上列出来那些名字，诸如李寻欢、安妮、宁财神、慕容雪村这样的，还是有一分陌生的熟悉。&lt;/p&gt;

&lt;p&gt;有一天，daf同学说她看小说，我说我只知道榕树下。于是顺手搜到已经忘记了的网址，漫长的载入过程还是以一个空白的窗口告终。这些年这种场面也见得多了，没有什么大惊小怪的：大概是太久远了吧。&lt;/p&gt;

&lt;p&gt;今天上午看路金波的博客，突然发现榕树下被收购的新闻，转而打开网站，看到了这个让人想流眼泪的&lt;a href="http://www.rongshu.com" target="_blank"&gt;页面&lt;/a&gt;和这个让人想擦眼泪的&lt;a href="http://58.215.39.134/" target="_blank"&gt;留言板&lt;/a&gt;。人们对于榕树下的情感，绝不是现在充斥着肾上腺素的起点之流可以相比的。我说这是我加入以来公司做得最好的一件事情，也希望从此榕树下对大家不再只是一些回忆而已。&lt;/p&gt;

&lt;p&gt;&lt;blockquote&gt;&lt;h3&gt;那年榕树下&lt;/h3&gt;
词:叶风  曲:马条  演唱:马条&lt;br /&gt;
那年榕树下   我们肩头披着细碎阳光&lt;br /&gt;
总会有个枝头  将青春安放&lt;br /&gt;
那年榕树下  我们亲手种下快乐忧伤&lt;br /&gt;
可以要风穿过发梢的那一种飞扬&lt;br /&gt;
榕树下  那年萤火虫一样飞舞盘旋&lt;br /&gt;
每道光的背后是怎样的世界&lt;br /&gt;
榕树下  那年谁其实都在谁的身边&lt;br /&gt;
闭上眼的云端 却是秋千&lt;br /&gt;
......&lt;/blockquote&gt;&lt;/p&gt;

&lt;p&gt;那年榕树下   我们肩头披着细碎阳光&lt;br /&gt;
总会有个枝头  将青春安放&lt;br /&gt;
那年榕树下  我们亲手种下快乐忧伤&lt;br /&gt;
可以要风穿过发梢的那一种飞扬&lt;br /&gt;
榕树下  那年萤火虫一样飞舞盘旋&lt;br /&gt;
每道光的背后是怎样的世界&lt;br /&gt;
榕树下  那年谁其实都在谁的身边&lt;br /&gt;
闭上眼的云端 却是秋千&lt;br /&gt;
......&lt;br /&gt;
......&lt;br /&gt;
榕树下  那年萤火虫一样飞舞盘旋&lt;br /&gt;
每道光的背后是怎样的世界&lt;br /&gt;
榕树下  那年谁其实都在谁的身边&lt;br /&gt;
闭上眼的云端 却是秋千&lt;br /&gt;
......&lt;br /&gt;
也许只字片语   一个不经意的标点  都能送给曾经些许温暖&lt;br /&gt;
......&lt;br /&gt;
......&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>上海图书馆</title><link href="https://sunng.info/blog/shang-hai-tu-shu-guan.html" rel="alternate"></link><published>2009-12-22T00:00:00+08:00</published><updated>2009-12-22T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-12-22:/blog/shang-hai-tu-shu-guan.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;Books&lt;/li&gt;
&lt;li&gt;Life&lt;/li&gt;
&lt;li&gt;Shanghai
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;发现南图的资源很不错之后就想着到了上海以后要办一张借书证，眼看住这半年了，险些忘了这 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;Books&lt;/li&gt;
&lt;li&gt;Life&lt;/li&gt;
&lt;li&gt;Shanghai
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;发现南图的资源很不错之后就想着到了上海以后要办一张借书证，眼看住这半年了，险些忘了这件事。上上周六去办证不料比截止时间晚了三分钟，白白跑了一趟。上周六正好约samson去，终于一切顺利。&lt;/p&gt;

&lt;p&gt;办证的系统很强大，我第一次见识了二代身份证的高级功能。读卡器可以直接读取身份证里的相应信息，简化填申请表的过程。普通外借功能只需要一年100元押金即可，没有额外的收费。一次可以借5本书，还书的期限是4周。&lt;/p&gt;

&lt;p&gt;比较遗憾的是发现在衡山路附近的上图的外借馆非常小，可选的书相对也少。计算机类的书架大约有两个，这个数量和南图是差不多的，但是长度远远没有南图的长，就是说比起南图的外借馆这个规模实在是很可怜。除去一些初级的、质量较低的书，想要刨出理想的计算机图书还是很难的，考虑到这图书馆serve着如此大的一个城市，这也是很正常的，恐怕真要看准时机才能找到目标。&lt;/p&gt;

&lt;p&gt;幸运的是这次借到了一直没舍得买的《Unix编程艺术》，当时看到它躺在书架上时几乎是直接抢过来抓在手里的，生怕被别人捷足先登。另外找到一本Jolt获奖的《Code Quality》的译本，也是没翻先拿在手上再说。还有一本《空间数据库》是早先想买但是各大网站纷纷缺货的书，现在拿到有点物是人非的感觉。而另外一本CSS的书是拿回来用来放松的。&lt;/p&gt;

&lt;p&gt;临走的时候还拿了一本IBM郭士纳的自传，想来咱也曾经蓝过｜｜｜&lt;/p&gt;

&lt;p&gt;有意思的是借书的时候还被搭讪要名片。我就想起年初在南图借书的时候也有意无意地看周围的人手上拿的书，借此推断一个人的技术和背景。&lt;/p&gt;

&lt;p&gt;从上图坐1号线一站到常熟路换新开的7号线回张江还是非常方便的，可以避开人多的人民广场站。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Using Yan in Ruby Web Application</title><link href="https://sunng.info/blog/using-yan-in-ruby-web-application.html" rel="alternate"></link><published>2009-12-21T00:00:00+08:00</published><updated>2009-12-21T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-12-21:/blog/using-yan-in-ruby-web-application.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;captcha&lt;/li&gt;
&lt;li&gt;ruby&lt;/li&gt;
&lt;li&gt;web&lt;/li&gt;
&lt;li&gt;Yan
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;I will show you the usage of Yan captcha service. In this tutorial, it's based on a simple ruby web application of the Sinatra web framework.&lt;/p&gt;

&lt;p&gt;Before we start to use the service, it is necesary to get Yan …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;captcha&lt;/li&gt;
&lt;li&gt;ruby&lt;/li&gt;
&lt;li&gt;web&lt;/li&gt;
&lt;li&gt;Yan
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;I will show you the usage of Yan captcha service. In this tutorial, it's based on a simple ruby web application of the Sinatra web framework.&lt;/p&gt;

&lt;p&gt;Before we start to use the service, it is necesary to get Yan running. Download the code from &lt;a href="http://bitbucket.org/sunng/yan/" target="_blank"&gt;the project page&lt;/a&gt;, then build and run it with maven:
&lt;em&gt;mvn jetty:run&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To enable the application to use Yan, we have to register our application to get an API Key. If you use Yan 0.3, there is a secret registration page at &lt;em&gt;http://localhost:8080/yan/reg.jsp&lt;/em&gt; The page is protected by HTTP Basic Authentication, the username and password are store in 'realm.properties' which is considered to locate in the root directory. Open the file you can see the plain text username and password. If you are running the latest development version, there is no long any UI for API Key creation, but restful interface. This won't be hard to you, pickup your tools such as curl or poster (a firefox extension) to send a HTTP request. Take curl as example, do it like this:
&lt;em&gt;curl -X PUT "http://localhost:8080/yan/apikey/" -d "SinatraTestApp" -u "username:password"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If it works, you will get a line of json:
&lt;em&gt;{"apikey":"b251b0dc2eed31cac38555b61d4fa6a453923bfd","appName":"SinatraTestApp"}&lt;/em&gt;
Save this apikey.&lt;/p&gt;

&lt;p&gt;Sinatra is generally considered to be the world's lightest and smallest web framework. And our application is rather simple. Just check the code:
&lt;pre class="brush:ruby"&gt;require "rubygems"
require "sinatra"
require "net/http"
require "yaml"&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;apikey='b251b0dc2eed31cac38555b61d4fa6a453923bfd'&lt;/p&gt;

&lt;p&gt;get '/' do&lt;br /&gt;
    conn = Net::HTTP.new('localhost', 8080)&lt;br /&gt;
    q = "ip=#{@env['REMOTE_ADDR']}&amp;amp;apikey=#{apikey}&amp;amp;alt=yaml&amp;amp;mode=0"&lt;br /&gt;
    resp, data = conn.get("/yan/ticket?#{q}")&lt;br /&gt;
    @ticket = YAML::load(data)&lt;br /&gt;
    haml :sinatra_captcha&lt;br /&gt;
end&lt;/p&gt;

&lt;p&gt;post '/' do&lt;br /&gt;
    conn = Net::HTTP.new('localhost', 8080)&lt;br /&gt;
    q = "ip=#{@env['REMOTE_ADDR']}&amp;amp;apikey=#{apikey}&amp;amp;key=#{params['key']}&amp;amp;code=#{params['captcha']}"&lt;br /&gt;
    resp, data = conn.get("/yan/validate?#{q}")&lt;br /&gt;
    data&lt;br /&gt;
end&lt;/p&gt;

&lt;p&gt;use_in_file_templates!&lt;br /&gt;
__END__&lt;/p&gt;

&lt;p&gt;@@ sinatra_captcha&lt;br /&gt;
%html&lt;br /&gt;
    %head&lt;br /&gt;
        %title Yan Captcha on Sinatra&lt;br /&gt;
    %body&lt;br /&gt;
        %form{:action=&amp;gt;"/", :method=&amp;gt;"post"}&lt;br /&gt;
            %p&lt;br /&gt;
                Username:&lt;br /&gt;
                %input{:name=&amp;gt;"username", :type=&amp;gt;"text"}&lt;br /&gt;
            %p&lt;br /&gt;
                Password:&lt;br /&gt;
                %input{:name=&amp;gt;"password", :type=&amp;gt;"password"}&lt;br /&gt;
            %p&lt;br /&gt;
                Captcha:&lt;br /&gt;
                %img{:src=&amp;gt;@ticket['url']}&lt;br /&gt;
                %br&lt;br /&gt;
                %input{:name=&amp;gt;"captcha", :type=&amp;gt;"text"}&lt;br /&gt;
                %input{:name=&amp;gt;"key", :type=&amp;gt;"hidden", :value=&amp;gt;@ticket['key']}&lt;br /&gt;
                %input{:type=&amp;gt;'submit'}

There are two parts of this application: ruby code and haml. I just use in-file-template for convenience. We define a get handler and a post handler on the path '/'. The get handler will request a ticket from Yan which contains captcha image url and ticket key. The post handler will extract user input and submit the Yan's validator and return user the result. And the HAML code is template for page rendering after GET request.&lt;/p&gt;

&lt;p&gt;Maybe you need to install sinatra and some dependency:
&lt;em&gt;sudo gem install sinatra haml&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Run the code with a build-in WEBrick
&lt;em&gt;ruby sinatra-yan.rb&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Browse to the default url, test it:&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" src="http://farm3.static.flickr.com/2766/4203612734_3237ed066c_o.png" alt="" width="459" height="268" /&gt;&lt;/p&gt;

&lt;p&gt;For another similar tutorial using python, check Yan's wiki page:
&lt;a href="http://bitbucket.org/sunng/yan/wiki/SampleCode" target="_blank"&gt;http://bitbucket.org/sunng/yan/wiki/SampleCode&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thank you for your support. btw, today is my dear girl friend's birthday, I just wish her happy everyday.&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Yan 0.3</title><link href="https://sunng.info/blog/yan-03.html" rel="alternate"></link><published>2009-12-20T00:00:00+08:00</published><updated>2009-12-20T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-12-20:/blog/yan-03.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;captcha&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;Yan
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;经过一周的重构和开发，我的开源项目，验证码服务，打上了0.3的tag，算是一个release吧。&lt;/p&gt;

&lt;p&gt;This release has been focusing on support for different types …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;captcha&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;Yan
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;经过一周的重构和开发，我的开源项目，验证码服务，打上了0.3的tag，算是一个release吧。&lt;/p&gt;

&lt;p&gt;This release has been focusing on support for different types captcha generators. Now Yan is not only able to provide image/jpeg captcha, but also text/plain and any others.
&lt;h3&gt;Changeset:&lt;/h3&gt;
&lt;ul&gt;
    &lt;li&gt;Internal API Changes:
&lt;ul&gt;
    &lt;li&gt;Cache API improved: new CacheItemIdentity and CacheIdStrategy were introduced in to provide convertion between cache item name and captcha info model;&lt;/li&gt;
    &lt;li&gt;Captcha Generator API improved: Add CaptchaGeneratorInfo to define some meta information on captcha providers (such as mode code, captcha type);&lt;/li&gt;
    &lt;li&gt;Captcha Data Model(CaptchaInfo) and Ticket Data Model improved: configuration parameters are separated from required parameters and has been more generic for different types of generator algorisms;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
    &lt;li&gt;External API Changes:
&lt;ul&gt;
    &lt;li&gt;Rename /image url to /captcha for better literal accuracy;&lt;/li&gt;
    &lt;li&gt;/ticket now supports multiple types of applicable format (plain text / json / xml / yaml);&lt;/li&gt;
    &lt;li&gt;/ticket now returns the mime type of the captcha generator;&lt;/li&gt;
    &lt;li&gt;Add a Simple-Plain-Text generator as a sample for those whose mime type is other than image/jpeg;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
    &lt;li&gt;Other changes:
&lt;ul&gt;
    &lt;li&gt;I created a new branch for next version of Yan. So the code in repository now has multiply branches, you can use 'hg update branch-name' to switch between difference branches;&lt;/li&gt;
    &lt;li&gt;The sample test page for the service (index.jsp) has been adopt to the new protocol and totally restyled.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;img class="alignnone" src="http://farm3.static.flickr.com/2503/4200328968_b3dd1fee5f.jpg" alt="" width="500" height="391" /&gt;&lt;/p&gt;

&lt;p&gt;Again, grab code from the development repository:
&lt;em&gt;hg clone https://sunng@bitbucket.org/sunng/yan/&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you don't use mercurial/hg, you can also download the tagged version from the page:
&lt;a href="http://bitbucket.org/sunng/yan/downloads/" target="_blank"&gt;&lt;em&gt;http://bitbucket.org/sunng/yan/downloads/&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just use maven to resolve dependency, build and run the project:
&lt;em&gt;mvn jetty:run&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Feel free to report issue :)&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>A button by GIMP</title><link href="https://sunng.info/blog/a-button-by-gimp.html" rel="alternate"></link><published>2009-12-09T00:00:00+08:00</published><updated>2009-12-09T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-12-09:/blog/a-button-by-gimp.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;gimp
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Inspired by a &lt;a href="http://sixrevisions.com/tutorials/web-development-tutorials/create-an-animated-call-to-action-button/" target="_blank"&gt;"Call to action" tutorial of Photoshop&lt;/a&gt;, I'd like to do the same thing with free and open source software, GIMP.&lt;/p&gt;

&lt;p&gt;Before we start the journey, it's better to get well prepared. Take a look at the powerful plugin …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;gimp
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Inspired by a &lt;a href="http://sixrevisions.com/tutorials/web-development-tutorials/create-an-animated-call-to-action-button/" target="_blank"&gt;"Call to action" tutorial of Photoshop&lt;/a&gt;, I'd like to do the same thing with free and open source software, GIMP.&lt;/p&gt;

&lt;p&gt;Before we start the journey, it's better to get well prepared. Take a look at the powerful plugin "&lt;a href="http://registry.gimp.org/node/186" target="_blank"&gt;Layer Effects&lt;/a&gt;". It provides you complete functionality as "Layer Style" in Photoshop does which is of critical importance in such kind of image manipulation. Follow the installation guide and I won't spend many words about this step.&lt;/p&gt;

&lt;p&gt;Open your gimp, create a new canvas with custom size (which makes you feel comfortable)&lt;/p&gt;

&lt;p&gt;New a layer, use selection tool to create a rounded rectangle selection area.&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" src="http://farm3.static.flickr.com/2744/4171189572_a6c70c8b00.jpg" alt="" width="500" height="375" /&gt;&lt;/p&gt;

&lt;p&gt;Fill the selection with gradient color.&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" src="http://farm3.static.flickr.com/2653/4170430801_ee9b8e973d.jpg" alt="" width="500" height="375" /&gt;&lt;/p&gt;

&lt;p&gt;Resize the canvas. Glow the selection with 3px by menu command "selection-&amp;gt;glow". The crop the canvas by selection using "image-&amp;gt;crop by selection".&lt;/p&gt;

&lt;p&gt;Then we apply some layer effects to beauty the button.
&lt;ul&gt;
    &lt;li&gt;layer effects: inner glow, white 75% opacity, size 5&lt;/li&gt;
    &lt;li&gt;layer effects: stroke 1px&lt;/li&gt;
&lt;/ul&gt;
&lt;img src="http://farm3.static.flickr.com/2592/4171189712_2b48af7d78.jpg" alt="" width="500" height="375" /&gt;&lt;/p&gt;

&lt;p&gt;Place some text on the button such as "Sign Up". Bold sans font(DejaVu sans or Helvetica bold) is recommended here.&lt;/p&gt;

&lt;p&gt;Use gradient overlay effect on the text. As the layer effects plugin doesn't support gradient overlay on text, we have to do it in a different way. First, create a new layer. Then convert text to selection with context menu command. Gradient fill the selection and hide the original text layer at last. Now we got:&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" src="http://farm3.static.flickr.com/2692/4170430973_8f65b08603.jpg" alt="" width="500" height="375" /&gt;&lt;/p&gt;

&lt;p&gt;Don't stop here. Add layer effects on the new layer:
&lt;ul&gt;
    &lt;li&gt;inner shadow, black, size 2px, distance 2px, angle 90&lt;/li&gt;
    &lt;li&gt;drop shadow, light, size 2px, distance 2px, angle 90&lt;/li&gt;
&lt;/ul&gt;
&lt;img class="alignnone" src="http://farm3.static.flickr.com/2579/4170431039_4eff9b67ff.jpg" alt="" width="500" height="375" /&gt;&lt;/p&gt;

&lt;p&gt;You can tune the color schema by yourself along the process :)&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" src="http://farm3.static.flickr.com/2765/4171189918_f1ea2c2eff_o.png" alt="" width="170" height="71" /&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Tikhonov</title><link href="https://sunng.info/blog/tikhonov.html" rel="alternate"></link><published>2009-12-06T00:00:00+08:00</published><updated>2009-12-06T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-12-06:/blog/tikhonov.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;movie&lt;/li&gt;
&lt;li&gt;soviet
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;2009年12月4日，苏联功勋演员吉洪诺夫去逝，享年81岁。&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" src="http://farm3.static.flickr.com/2488/4162200310_943fc8b4c3.jpg" alt="" width="500" height="492" /&gt;&lt;/p&gt;

&lt;p&gt;吉洪诺夫是《春天的十七个瞬间》里施季里茨的扮演者，春天的 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;movie&lt;/li&gt;
&lt;li&gt;soviet
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;2009年12月4日，苏联功勋演员吉洪诺夫去逝，享年81岁。&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" src="http://farm3.static.flickr.com/2488/4162200310_943fc8b4c3.jpg" alt="" width="500" height="492" /&gt;&lt;/p&gt;

&lt;p&gt;吉洪诺夫是《春天的十七个瞬间》里施季里茨的扮演者，春天的十七个瞬间是一部70年代的间谍片，或者说电视剧也行，它有12集。吉洪诺夫扮演的是一个非常牛逼的角色，具体有多牛逼，我也不用自己孱弱的表达能力去描述了，如果有一天大家有机会有耐心看完，多少也能够了解。&lt;/p&gt;

&lt;p&gt;第一次看《春天的十七个瞬间》是在缤纷多彩的高三暑假，2005年夏天。中央八套每晚十点左右，一天两集，我印象中当时并没有播完，尤其是到了最后变成了缩减版，结尾都没有播全。那是为数不多的我和我妈都能感兴趣的事物，因为播的时间比较晚，通常是第二天我妈来向我了解剧情。现在想起来，这是一段非常值得回忆的时光。&lt;/p&gt;

&lt;p&gt;直到现在我还是很容易被老电影里的英雄人物吸引、单纯地敬仰。施季里茨送走普列什聂耳回柏林的路上，在车里睡了十五分钟。旁白大约说道，多年紧张的特工工作使他睡眠的时间可以自己掌握。这个情节印象非常深刻，以至于不断地想起来不断地试图模仿。&lt;/p&gt;

&lt;p&gt;那时还没有视频网站，为了能够看全，后来还买了DVD，是我仅有的一套DVD。从头到尾看过三四遍，还在宿舍里和同学一起看过。情节挺慢，能静下来一点一点享受完的机会不多。再，看完这么一部十二集的长片，时间成本上实在不小。不过这不影响我印象深刻，不影响每天上班路上哼电影里的插曲，不影响一个形象留给人的东西。&lt;/p&gt;

&lt;p&gt;其实能看到的吉洪诺夫演的电影不多，《春天》和《战争与和平》是其中最有名的，后者可惜我还没有看过。到了苏联解体以后，吉洪诺夫还在米亥科夫的《烈日灼人》里扮演了一个角色。这部电影我看过，但是吉洪诺夫的角色是今天上午才知道，看电影的时候完全没有发觉，因为像“施季里茨也会变老”这样的事实实在是很容易被忽略。&lt;/p&gt;

&lt;p&gt;想到这里，我大概不用再写了，因为对于我这样的观众来说，其实吉洪诺夫还有施季里茨们一直都在我们身边。即使走了，也走不远。所以我把标题里的“别了”删掉，祝他们一切都好。
&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;&lt;object width="420" height="363"&gt;&lt;param name="movie" value="http://www.tudou.com/v/VQJVmG8wUJg" /&gt;&lt;param name="allowFullScreen" value="true" /&gt;&lt;param name="allowscriptaccess" value="always" /&gt;&lt;param name="wmode" value="opaque" /&gt;&lt;embed src="http://www.tudou.com/v/VQJVmG8wUJg" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" wmode="opaque" width="420" height="363"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Adding Ubuntu-GIS repository</title><link href="https://sunng.info/blog/adding-ubuntu-gis-repository.html" rel="alternate"></link><published>2009-11-29T00:00:00+08:00</published><updated>2009-11-29T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-11-29:/blog/adding-ubuntu-gis-repository.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;GIS&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;在Ubuntu上安装GIS软件，可以添加这个PPA源：&lt;em&gt;
deb http://ppa.launchpad.net/ubuntugis/ubuntugis-unstable/ubuntu karmic main&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;导入GPG KEY
&lt;em&gt;gpg --keyserver subkeys.pgp.net --recv 089EBE08314DF160&lt;br /&gt;
gpg --export --armor 089EBE08314DF160 | sudo apt-key add -&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;这个源中 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;GIS&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;在Ubuntu上安装GIS软件，可以添加这个PPA源：&lt;em&gt;
deb http://ppa.launchpad.net/ubuntugis/ubuntugis-unstable/ubuntu karmic main&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;导入GPG KEY
&lt;em&gt;gpg --keyserver subkeys.pgp.net --recv 089EBE08314DF160&lt;br /&gt;
gpg --export --armor 089EBE08314DF160 | sudo apt-key add -&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;这个源中目前包含了最新版本的GIS软件：
&lt;ul&gt;
    &lt;li&gt;gdal 1.6&lt;/li&gt;
    &lt;li&gt;grass&lt;/li&gt;
    &lt;li&gt;qgis&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>在RHEL4上搭建Python/Lighttpd/FastCGI环境</title><link href="https://sunng.info/blog/zai-rhel4shang-da-jian-pythonlighttpdfastcgihuan-jing.html" rel="alternate"></link><published>2009-11-27T00:00:00+08:00</published><updated>2009-11-27T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-11-27:/blog/zai-rhel4shang-da-jian-pythonlighttpdfastcgihuan-jing.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;fastcgi&lt;/li&gt;
&lt;li&gt;lighttpd&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;在一台赤裸裸的RHEL4上部署web.py程序，一切从几乎是从零开始。以下操作均以root用户操作。
&lt;h2&gt;1. 安装MySQL数据库&lt;/h2&gt;
&lt;strong&gt;下载安装MySQL&lt;/strong&gt;
&lt;em&gt;wget http://dev.mysql …&lt;/em&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;fastcgi&lt;/li&gt;
&lt;li&gt;lighttpd&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;在一台赤裸裸的RHEL4上部署web.py程序，一切从几乎是从零开始。以下操作均以root用户操作。
&lt;h2&gt;1. 安装MySQL数据库&lt;/h2&gt;
&lt;strong&gt;下载安装MySQL&lt;/strong&gt;
&lt;em&gt;wget http://dev.mysql.com/get/Downloads/MySQL-5.1/mysql-5.1.41-linux-i686-glibc23.tar.gz/from/http://mirror.services.wisc.edu/mysql/&lt;/em&gt;
解压，按照INSTALL文件说明进行安装，不多赘述
&lt;h2&gt;2. 安装Python环境&lt;/h2&gt;
&lt;h3&gt;下载Python源码&lt;/h3&gt;
&lt;em&gt;wget http://www.python.org/ftp/python/2.6.4/Python-2.6.4.tar.bz2&lt;/em&gt;
解压，编译安装，不需要特殊操作。
&lt;h3&gt;下载Easy_install&lt;/h3&gt;
&lt;em&gt;wget http://pypi.python.org/packages/2.6/s/setuptools/setuptools-0.6c11-py2.6.egg&lt;/em&gt;
安装
&lt;em&gt;sh setuptools-0.6c11-py2.6.egg&lt;/em&gt;
&lt;h3&gt;安装相关Packages&lt;/h3&gt;
&lt;em&gt;easy_install DBUtils&lt;br /&gt;
easy_install flup&lt;br /&gt;
easy_install web.py&lt;/em&gt;
&lt;h3&gt;安装mysql-python&lt;/h3&gt;
mysql-python包不能用easy_install安装，手动下载
&lt;em&gt;wget http://downloads.sourceforge.net/project/mysql-python/mysql-python-test/1.2.3b1/MySQL-python-1.2.3b1.tar.gz?use_mirror=softlayer&lt;/em&gt;
加压，编辑site.cfg&lt;br /&gt;
指定mysql_config的路径，注意是新安装的mysql路径
&lt;em&gt;mysql_config=/usr/local/mysql/bin/mysql_config&lt;/em&gt;
编译、安装
&lt;em&gt;python setup.py build&lt;br /&gt;
python setup.py install&lt;/em&gt;
声明libmysqlclient路径
&lt;em&gt;export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/mysql/lib&lt;/em&gt;
&lt;h2&gt;3. 安装服务器环境&lt;/h2&gt;
&lt;h3&gt;下载安装fastcgi头文件&lt;/h3&gt;
&lt;em&gt;wget http://www.fastcgi.com/dist/fcgi.tar.gz&lt;/em&gt;
解压，默认编译安装
&lt;h3&gt;下载安装PCRE&lt;/h3&gt;
&lt;em&gt;wget ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-8.00.tar.bz2&lt;/em&gt;
解压，默认编译安装
&lt;h3&gt;下载lighttpd&lt;/h3&gt;
&lt;em&gt;wget http://download.lighttpd.net/lighttpd/releases-1.4.x/lighttpd-1.4.25.tar.bz2&lt;/em&gt;
configure时指定&lt;em&gt;--prefix=/usr/local/lighttpd&lt;/em&gt;
编译，安装
&lt;h2&gt;4. 配置&lt;/h2&gt;
lighttpd fastcgi运行web.py程序请参考
&lt;a href="http://www.classicning.com/blog/2009/11/lighttpd%e9%80%9a%e8%bf%87fastcgi%e8%bf%90%e8%a1%8cweb-py%e7%a8%8b%e5%ba%8f/"&gt;http://www.classicning.com/blog/2009/11/lighttpd%e9%80%9a%e8%bf%87fastcgi%e8%bf%90%e8%a1%8cweb-py%e7%a8%8b%e5%ba%8f/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;非常重要的几点注意：
&lt;ul&gt;
    &lt;li&gt;通过fastcgi运行的python脚本必须具有可执行权限，&lt;em&gt; chmod u+x web/main.py&lt;/em&gt;；&lt;/li&gt;
    &lt;li&gt;重启fastcgi程序需要删除/tmp/fastcgi.socket*&lt;/li&gt;
&lt;/ul&gt;
任何Python程序出错、权限错误都会导致lighttpd这样的报错：
&lt;em&gt;2009-11-27 18:12:02: (mod_fastcgi.c.1108) child exited with status 13 /home/admin/web/main.py&lt;br /&gt;
2009-11-27 18:12:02: (mod_fastcgi.c.1111) If you're trying to run your app as a FastCGI backend, make sure you're using the FastCGI-enabled version.&lt;br /&gt;
If this is PHP on Gentoo, add 'fastcgi' to the USE flags.&lt;br /&gt;
2009-11-27 18:12:02: (mod_fastcgi.c.1399) [ERROR]: spawning fcgi failed.&lt;br /&gt;
2009-11-27 18:12:02: (server.c.931) Configuration of plugins failed. Going down.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;报错会提示fastcgi未正确安装，而实际上仅仅是程序错误或权限问题而已。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Nginx HTTP Push</title><link href="https://sunng.info/blog/nginx-http-push.html" rel="alternate"></link><published>2009-11-26T00:00:00+08:00</published><updated>2009-11-26T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-11-26:/blog/nginx-http-push.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;http&lt;/li&gt;
&lt;li&gt;nginx&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;前些天看到一个Nginx的Module，用来是实现Comet，今天简单试了一下功能。作者名叫Leo Ponomarev，项目地址：http://pushmodule.slact.net/
&lt;h3&gt;安装 …&lt;/h3&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;http&lt;/li&gt;
&lt;li&gt;nginx&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;前些天看到一个Nginx的Module，用来是实现Comet，今天简单试了一下功能。作者名叫Leo Ponomarev，项目地址：http://pushmodule.slact.net/
&lt;h3&gt;安装&lt;/h3&gt;
Module需要在编译时加入nginx，同时下载nginx和nginx-push-module，在nginx configure时增加一个参数：
&lt;em&gt;./configure --add-module=path/to/nginx_http_push_module&lt;/em&gt;
&lt;h3&gt;使用&lt;/h3&gt;
编写一个非常基本的nginx配置文件：
&lt;pre class="brush:plain"&gt;events{
    worker_connections 1024;
}
http{
    server {
        listen  80;
        server_name localhost;&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;     location /publish {&lt;br /&gt;
            set $push_channel_id $arg_id;&lt;/p&gt;

&lt;p&gt;         push_publisher;&lt;/p&gt;

&lt;p&gt;         push_store_messages on;&lt;br /&gt;
            push_message_timeout 2h;&lt;br /&gt;
            push_max_message_buffer_length 10;&lt;br /&gt;
            push_min_message_recipients 0;&lt;br /&gt;
        }&lt;/p&gt;

&lt;p&gt;     location /subscribe{&lt;br /&gt;
            push_subscriber;&lt;/p&gt;

&lt;p&gt;         push_subscriber_concurrency broadcast;&lt;br /&gt;
            set $push_channel_id $arg_id;&lt;br /&gt;
            default_type text/plain;&lt;br /&gt;
        }&lt;/p&gt;

&lt;p&gt; }&lt;br /&gt;
}
一个简单的Server定义了两个路径分别用于publish和subscribe。所有相关的配置项可以在项目主页找到解释，不作赘述。&lt;/p&gt;

&lt;p&gt;启动nginx
&lt;em&gt;nginx -c /home/sun/nginxpush/nginx-push.conf&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;span style="font-style: normal; background-color: #ffffff;"&gt;打开一个终端访问subscribe
&lt;em&gt; curl -X GET http://localhost/subscribe?id=0&lt;/em&gt;&lt;/span&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;span style="background-color: #ffffff;"&gt;可以看到HTTP请求被阻塞&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span style="background-color: #ffffff;"&gt;打开另一个终端访问publish
&lt;em&gt; curl -X POST http://localhost/publish?id=0 -d "Hello World" &lt;/em&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span style="background-color: #ffffff;"&gt;此时subscriber收到字符串"Hello World" ，完成HTTP请求。&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span style="background-color: #ffffff;"&gt;subscriber可以通过设置HTTP头来对消息进行过滤，如
&lt;em&gt; curl -X POST http://localhost/publish?id=0 -d "Hello World"&lt;/em&gt;
&lt;em&gt; curl -X GET http://localhost/subscribe?id=0 --verbose&lt;/em&gt;&lt;/span&gt;
&lt;div&gt;&lt;em&gt;*&lt;/em&gt;&lt;span style="background-color: #ffffff;"&gt;&lt;em&gt; About to connect() to localhost port 80 (#0)&lt;/em&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style="background-color: #ffffff;"&gt;&lt;em&gt;*   Trying 127.0.0.1... connected&lt;/em&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style="background-color: #ffffff;"&gt;&lt;em&gt;* Connected to localhost (127.0.0.1) port 80 (#0)&lt;/em&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style="background-color: #ffffff;"&gt;&lt;em&gt;&amp;gt; GET /subscribe?id=0 HTTP/1.1&lt;/em&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style="background-color: #ffffff;"&gt;&lt;em&gt;&amp;gt; User-Agent: curl/7.19.7 (i686-pc-linux-gnu) libcurl/7.19.7 OpenSSL/0.9.8l zlib/1.2.3.3&lt;/em&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style="background-color: #ffffff;"&gt;&lt;em&gt;&amp;gt; Host: localhost&lt;/em&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style="background-color: #ffffff;"&gt;&lt;em&gt;&amp;gt; Accept: */*&lt;/em&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style="background-color: #ffffff;"&gt;&lt;em&gt;&amp;gt; &lt;/em&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style="background-color: #ffffff;"&gt;&lt;em&gt;&amp;lt; HTTP/1.1 200 OK&lt;/em&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style="background-color: #ffffff;"&gt;&lt;em&gt;&amp;lt; Server: nginx/0.8.28&lt;/em&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style="background-color: #ffffff;"&gt;&lt;em&gt;&amp;lt; Date: Thu, 26 Nov 2009 09:45:25 GMT&lt;/em&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style="background-color: #ffffff;"&gt;&lt;em&gt;&amp;lt; Content-Type: application/x-www-form-urlencoded&lt;/em&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style="background-color: #ffffff;"&gt;&lt;em&gt;&amp;lt; Content-Length: 10&lt;/em&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style="background-color: #ffffff;"&gt;&lt;em&gt;&amp;lt; Last-Modified: Thu, 26 Nov 2009 09:44:59 GMT&lt;/em&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style="background-color: #ffffff;"&gt;&lt;em&gt;&amp;lt; Connection: keep-alive&lt;/em&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style="background-color: #ffffff;"&gt;&lt;em&gt;&amp;lt; Etag: 0&lt;/em&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style="background-color: #ffffff;"&gt;&lt;em&gt;&amp;lt; Vary: If-None-Match, If-Modified-Since&lt;/em&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style="background-color: #ffffff;"&gt;&lt;em&gt;&amp;lt; &lt;/em&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style="background-color: #ffffff;"&gt;&lt;em&gt;* Connection #0 to host localhost left intact&lt;/em&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style="background-color: #ffffff;"&gt;&lt;em&gt;* Closing connection #0&lt;/em&gt;&lt;/span&gt;&lt;/div&gt;
&lt;em&gt;HelloWorld&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;从响应的头部可以看到Last-Modified: Thu, 26 Nov 2009 09:44:59 GMT的时间是上一次publish的时间，并且通过Vary字段提示了两个选项
&lt;ul&gt;
    &lt;li&gt;If-None-Match&lt;/li&gt;
    &lt;li&gt;If-Modified-Since&lt;/li&gt;
&lt;/ul&gt;
RFC中对Vary头是这样解释的：&lt;/p&gt;

&lt;p&gt;The Vary field value indicates the set of request-header fields that fully determines, while the response is fresh, whether a cache is permitted to use the response to reply to a subsequent request without revalidation.&lt;/p&gt;

&lt;p&gt;即可以通过发送If-Modified-Since来获取指定时间之后的数据&lt;/p&gt;

&lt;p&gt;&lt;em&gt;curl -X GET -H "If-Modified-Since: Thu, 26 Nov 2009 09:44:50 GMT" http://localhost/subscribe?id=0 --verbose&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;这时subscribe会重新被阻塞而不是接收上次publish的数据，充分利用了HTTP的语义。&lt;/p&gt;

&lt;p&gt;这样用push module来做Web-IM、聊天室的思路就非常清晰了：&lt;span style="background-color: #ffffff;"&gt;每个浏览器保持一个subscriber连接，在接收到消息后连接关闭。把消息打印出来，并根据消息响应的头部Last-Modified请求重新subscribe。&lt;/span&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>这一切没有想象的那么糟</title><link href="https://sunng.info/blog/zhe-yi-qie-mei-you-xiang-xiang-de-na-yao-zao.html" rel="alternate"></link><published>2009-11-23T00:00:00+08:00</published><updated>2009-11-23T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-11-23:/blog/zhe-yi-qie-mei-you-xiang-xiang-de-na-yao-zao.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;当时
tags: []
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;如果还不算太晚的话，大家抬头看看木星拱月吧，一张笑脸少了一只眼睛。今年这木星和月亮已经腻味了 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;当时
tags: []
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;如果还不算太晚的话，大家抬头看看木星拱月吧，一张笑脸少了一只眼睛。今年这木星和月亮已经腻味了半年了。我倒是清楚记得去年冬天这场面也出现过，那时依稀是火星跟金星，没今天这亮但总归比今天齐全。当然不能太强求完整不是。&lt;/p&gt;

&lt;p&gt;我们班刚工作的同学除了在张江的两位都回去考研了，顺利的话60%的大家又要团聚了。我想经历过这半年的同学回去以后肯定有更深刻的体会，我们这个专业找工作阻力很多很大，有的是没办法的事情。现在我又是在旁观者的角度看别人了，冷暖自知，自己的状况，也只有大家自己心里有数。&lt;/p&gt;

&lt;p&gt;话说，好久没有码字了，下班一路上想了不少事情，回来写起来却非常吃力，表达能力进一步退化，恶性循环。&lt;/p&gt;

&lt;p&gt;又，发现冬天听广播很有感觉，最早听广播还是某个冬天晚上六点半左右电视坏了寻找的替代，一晃今年正好是第十年。至于为什么时间记得这么清楚，呵，因为当时的节目到现在印象还很深。话说现在很多人肯定不能想象，我还有过打热线电话中奖的经历，不过中的什么门票到最后也没用着。那会南京电台还在著名的延龄巷50号，这个难找的地方着实花了我很长时间去领奖。&lt;/p&gt;

&lt;p&gt;再，这个月公司也十周年，不过到现在都没看出有什么特别的动静，也没什么可透露的，估计是比不了马云家的大场面的。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Exaile-DoubanCover 0.0.2</title><link href="https://sunng.info/blog/exaile-doubancover-002.html" rel="alternate"></link><published>2009-11-22T00:00:00+08:00</published><updated>2009-11-22T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-11-22:/blog/exaile-doubancover-002.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;Douban&lt;/li&gt;
&lt;li&gt;exaile&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;glade&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;The Exaile plugin "Douban Covers" has been upgraded to 0.0.2. Now there is a new preference pane inside exaile preference dialog. This update provides you an optional choice to specify your own apikey when access douban.com …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;Douban&lt;/li&gt;
&lt;li&gt;exaile&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;glade&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;The Exaile plugin "Douban Covers" has been upgraded to 0.0.2. Now there is a new preference pane inside exaile preference dialog. This update provides you an optional choice to specify your own apikey when access douban.com open api. With an apikey, your request frequency will be raised to 40 times per minute.&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" src="http://farm3.static.flickr.com/2727/4124219684_c93af1dd03.jpg" alt="" width="500" height="414" /&gt;&lt;/p&gt;

&lt;p&gt;Again, you can get the plugin from:
&lt;a href="http://bitbucket.org/sunng/exailedoubancovers/downloads/"&gt;http://bitbucket.org/sunng/exailedoubancovers/downloads&lt;/a&gt;/&lt;/p&gt;

&lt;p&gt;Grab source with mercurial:
&lt;em&gt;hg close http://bitbucket.org/sunng/exailedoubancovers/&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Also, exaile-cn has its first 0.3.0-compatible version released early today. As a part of Exaile-cn, Exaile-Doubancovers &lt;strong&gt;0.0.1&lt;/strong&gt; has been packaged into the public release for the first time. Find the project at: &lt;a href="http://code.google.com/p/exaile-cn/"&gt;http://code.google.com/p/exaile-cn/&lt;/a&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Groovy http-builder tips</title><link href="https://sunng.info/blog/groovy-http-builder-tips.html" rel="alternate"></link><published>2009-11-20T00:00:00+08:00</published><updated>2009-11-20T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-11-20:/blog/groovy-http-builder-tips.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;groovy&lt;/li&gt;
&lt;li&gt;java
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;最近用Groovy的HttpBuilder模块开发RESTful服务的客户端，遇到不少问题，浪费不少时间&lt;/p&gt;

&lt;p&gt;网上的例子都是从Grape开始的，但是按照例子上运行，无论是通过grape install还是在代码里 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;groovy&lt;/li&gt;
&lt;li&gt;java
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;最近用Groovy的HttpBuilder模块开发RESTful服务的客户端，遇到不少问题，浪费不少时间&lt;/p&gt;

&lt;p&gt;网上的例子都是从Grape开始的，但是按照例子上运行，无论是通过grape install还是在代码里@Grab都没有办法把http builder加到classpath里，甚至依赖关系已经下载到~/.groovy/grapes里了，但是还是在import的时候报错。用Grails的时候也是一样，如果通过ivy定义依赖，通过ant下载到lib目录中，在grails shell里还是没有办法引用。不理解了，难道还需要手动再指定path吗。最后用grails的rest插件（grails install-plugin rest）总算是下载到了path中了。&lt;/p&gt;

&lt;p&gt;第二是http builder的报错太智能了，它会把服务器端50x的报错输出在客户端。在查看错误的时候一定要分清楚。&lt;/p&gt;

&lt;p&gt;第三是net.sf.json-lib这个库在转json的时候有一点问题，比如这样的js对象
&lt;em&gt;{"result":"success"}&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;会因为key上有引号而无法解析，必须是这样的格式：
&lt;em&gt;{result: "success"}&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;至于那种是正确的json我也没有兴趣了解了，只是非常遗憾的是couchdb返回的都是前一种形式。&lt;/p&gt;

&lt;p&gt;当然了，瑕不掩瑜，http builder还是提供了非常方便的封装，比直接用HttpURLConnection或是HttpClient更加Groovy&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>听豆瓣电台的最佳姿势</title><link href="https://sunng.info/blog/ting-dou-ban-dian-tai-de-zui-jia-zi-shi.html" rel="alternate"></link><published>2009-11-15T00:00:00+08:00</published><updated>2009-11-15T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-11-15:/blog/ting-dou-ban-dian-tai-de-zui-jia-zi-shi.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;Douban&lt;/li&gt;
&lt;li&gt;firefox&lt;/li&gt;
&lt;li&gt;foss
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;豆瓣电台，如果你是豆瓣用户我不多说了；如果不是豆瓣用户，嗯，算了，您换台吧。&lt;/p&gt;

&lt;p&gt;总算找到Prism的用处了。如果你 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;Douban&lt;/li&gt;
&lt;li&gt;firefox&lt;/li&gt;
&lt;li&gt;foss
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;豆瓣电台，如果你是豆瓣用户我不多说了；如果不是豆瓣用户，嗯，算了，您换台吧。&lt;/p&gt;

&lt;p&gt;总算找到Prism的用处了。如果你在用豆瓣电台，并且你使用Firefox，那么只要安装Prism，就可以把豆瓣电台变成一个任意大小的窗口，和你的多tab的浏览器分离。如果非常巧合你是一位少数派的Windows用户，嗯，Prism在Windows上还可以最小化到系统栏。&lt;/p&gt;

&lt;p&gt;安装Prism扩展，嗯，最好确定你的Firefox是最新的（这个下雨的晚上最新的是3.5.5），当然也别新到3.6去。OK没有问题的话，就猛击下面这个链接：
&lt;a href="https://addons.mozilla.org/en-US/firefox/addon/6665"&gt;https://addons.mozilla.org/en-US/firefox/addon/6665&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;注册都不需要了，直接点安装，然后等着重开Firefox就行了。安装完成，可以看到Tools菜单下面多了一个"Convert Website to application..." 这就是Prism，点开设置如下参数：&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" src="http://farm3.static.flickr.com/2489/4105923552_6c51a64048.jpg" alt="" width="415" height="500" /&gt;&lt;/p&gt;

&lt;p&gt;Windows用户的选项会多一些，记得勾上那个show in the notification area（如果是这个名字的话），确定即可。&lt;/p&gt;

&lt;p&gt;Windows用户（注意升级到3.5.5的Firefox）这时直接点桌面上的图标就可以了。由于cookie不能共享，你需要重登录一下，有必要的话最好选上“记住我”。等豆瓣电台的flash播放器出现的时候，把窗口拖到合适的大小，Prism会记住每次正常关闭时的窗口大小，下次打开就那么大。然后你把它最小化，于是就在右下角了（不好意思，至今我都不知道那个区域到底中文名叫什么，直译是提示区，但是从来没听人用普通话这么说过）。&lt;/p&gt;

&lt;p&gt;Linux的用户稍微麻烦一些，第一次执行桌面生成的文件时会需要confirm，mark as trusted即可。因为和你的Firefox不共享数据，所以首次执行Prism程序会创建一套新的Firefox profile，然后检查插件更新，显示欢迎等等。没关系，关掉它，重新再执行一次，就会看到正常的登录框了，其他和Windows差不多。&lt;/p&gt;

&lt;p&gt;但是Linux上的Prism还没有提供最小化到右上角(GNOME)或者右下角(KDE)或者其他什么角里的功能，怎么办？咱们有Window Manager，我是菜鸟，我刚会用Compiz，开启Scale Window吧，然后可以把窗口按比例缩小，所到不影响桌面工作，设不设置Always on top就看自己的想法了。&lt;/p&gt;

&lt;p&gt;好的，就罗嗦到这里。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Use pipe in subprocess</title><link href="https://sunng.info/blog/use-pipe-in-subprocess.html" rel="alternate"></link><published>2009-11-12T00:00:00+08:00</published><updated>2009-11-12T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-11-12:/blog/use-pipe-in-subprocess.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;h3&gt;问题：&lt;/h3&gt;
您要通过python的subprocess模块执行一些命令，要在程序中获得命令的标准输出，此外，命令中还有管道操作。
&lt;h3&gt;解决：&lt;/h3&gt;
以这个命令为例 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;h3&gt;问题：&lt;/h3&gt;
您要通过python的subprocess模块执行一些命令，要在程序中获得命令的标准输出，此外，命令中还有管道操作。
&lt;h3&gt;解决：&lt;/h3&gt;
以这个命令为例：
&lt;em&gt;cat /home/admin/deploy/log/2009-11-11.log ｜grep example.com ｜wc -l&lt;/em&gt;
&lt;pre class="brush:python"&gt;
import subprocess&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;cmd1 = 'cat /home/admin/deploy/log/2009-11-11.log'&lt;br /&gt;
cmd2 = 'grep example.com'&lt;br /&gt;
cmd3 = 'wc -l'&lt;/p&gt;

&lt;p&gt;pipe1 = subprocess.Popen(cmd1.split(), stdout=subprocess.PIPE)&lt;br /&gt;
pipe2 = subprocess.Popen(cmd2.split(), stdin=pipe1, stdout=subprocess.PIPE)&lt;br /&gt;
pipe3 = subprocess.Popen(cmd3.split(), stdin=pipe2, stdout=subprocess.PIPE)&lt;/p&gt;

&lt;p&gt;result = pipe3.communicate()[0]
&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Come back to see you</title><link href="https://sunng.info/blog/come-back-to-see-you.html" rel="alternate"></link><published>2009-11-10T00:00:00+08:00</published><updated>2009-11-10T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-11-10:/blog/come-back-to-see-you.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags: []
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;在彻查病毒之前这个网站的可访问性已经差到一定程度为此我甚至加了cron任务每天定时两次恢复文件但是怎奈病毒运行时间无常最后难免心力交瘁。&lt;/p&gt;

&lt;p&gt;现在问题基本解决在可预 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags: []
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;在彻查病毒之前这个网站的可访问性已经差到一定程度为此我甚至加了cron任务每天定时两次恢复文件但是怎奈病毒运行时间无常最后难免心力交瘁。&lt;/p&gt;

&lt;p&gt;现在问题基本解决在可预见的将来网站的可访问性暂时不会再有问题了大家也不用担心打开本人网站的时候被执行广告代码或者重定向到什么世外桃源去了。&lt;/p&gt;

&lt;p&gt;在清理病毒期间正好在&lt;a href="http://linuxtoy.org/archives/douban-covers.html" target="_blank"&gt;linuxtoy上发了一篇文章&lt;/a&gt;可惜因为清理病毒没有赚到点击量和知名度了。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>DoubanCovers has been merged into Exaile-cn</title><link href="https://sunng.info/blog/doubancovers-has-been-merged-into-exaile-cn.html" rel="alternate"></link><published>2009-11-10T00:00:00+08:00</published><updated>2009-11-10T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-11-10:/blog/doubancovers-has-been-merged-into-exaile-cn.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;exaile&lt;/li&gt;
&lt;li&gt;foss
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;既然是exaile-cn，就不扯蹩脚的英语了。&lt;/p&gt;

&lt;p&gt;前天和Exaile-cn的作者进行了一些沟通，现在DoubanCovers插件已经合并到Exaile-CN项目里了。&lt;a href="http://code.google.com/p/exaile-cn/"&gt;Exaile-CN&lt;/a&gt;为Exaile提供符合中文用户习惯的一系列插件，包括歌词插件、迷你模式等。&lt;/p&gt;

&lt;p&gt;As exaile-cn has not been adapted to exaile …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;ANN
tags:&lt;/li&gt;
&lt;li&gt;exaile&lt;/li&gt;
&lt;li&gt;foss
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;既然是exaile-cn，就不扯蹩脚的英语了。&lt;/p&gt;

&lt;p&gt;前天和Exaile-cn的作者进行了一些沟通，现在DoubanCovers插件已经合并到Exaile-CN项目里了。&lt;a href="http://code.google.com/p/exaile-cn/"&gt;Exaile-CN&lt;/a&gt;为Exaile提供符合中文用户习惯的一系列插件，包括歌词插件、迷你模式等。&lt;/p&gt;

&lt;p&gt;As exaile-cn has not been adapted to exaile 0.3, the plugin will not be packaged into public release at once. if you use svn, you can checkout the plugin from branch:
&lt;em&gt;svn checkout http://exaile-cn.googlecode.com/svn/branches/0.3/&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For a ready to use package, download from here as mentioned before:
&lt;a href="http://bitbucket.org/sunng/exailedoubancovers/downloads/"&gt;http://bitbucket.org/sunng/exailedoubancovers/downloads/&lt;/a&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>'Exaile cover plugin: Douban Covers'</title><link href="https://sunng.info/blog/exaile-cover-plugin-douban-covers.html" rel="alternate"></link><published>2009-11-05T00:00:00+08:00</published><updated>2009-11-05T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-11-05:/blog/exaile-cover-plugin-douban-covers.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;Douban&lt;/li&gt;
&lt;li&gt;exaile&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;I have created a plugin for &lt;a href="http://exaile.org" target="_blank"&gt;exaile&lt;/a&gt; which fetches album cover from &lt;a href="http://douban.com" target="_blank"&gt;douban.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can download the plugin archive from:
&lt;a href="You can download the plugin archive from: http://bitbucket.org/sunng/exailedoubancovers/downloads/"&gt;http://bitbucket.org/sunng/exailedoubancovers/downloads/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To install it, open exaile, click menu edit &amp;gt;&amp;gt; preference &amp;gt;&amp;gt; plugins, hit button …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;Douban&lt;/li&gt;
&lt;li&gt;exaile&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;I have created a plugin for &lt;a href="http://exaile.org" target="_blank"&gt;exaile&lt;/a&gt; which fetches album cover from &lt;a href="http://douban.com" target="_blank"&gt;douban.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can download the plugin archive from:
&lt;a href="You can download the plugin archive from: http://bitbucket.org/sunng/exailedoubancovers/downloads/"&gt;http://bitbucket.org/sunng/exailedoubancovers/downloads/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To install it, open exaile, click menu edit &amp;gt;&amp;gt; preference &amp;gt;&amp;gt; plugins, hit button "install plugin file", select "doubancovers.tgz"(if you cannot find the file, just change filter to "all files")&lt;/p&gt;

&lt;p&gt;In some cases, the plugin could not be shown in the list at once, you can close the preference dialog and open it again, then active it.&lt;/p&gt;

&lt;p&gt;To get album art, right click the album art icon, select "fetch cover" and wait for right cover downloaded.&lt;/p&gt;

&lt;p&gt;The plugin is open sourced under GPL v2, you can grab the code from bitbucket using mercurial:
&lt;em&gt;hg clone http://bitbucket.org/sunng/exailedoubancovers/&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Some screenshots for you:&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" src="http://farm3.static.flickr.com/2628/4074746549_2398351bef.jpg" alt="" width="500" height="414" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" src="http://farm4.static.flickr.com/3512/4075502050_325281d63a_o.png" alt="" width="358" height="375" /&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Lighttpd通过FastCGI运行web.py程序</title><link href="https://sunng.info/blog/lighttpdtong-guo-fastcgiyun-xing-webpycheng-xu.html" rel="alternate"></link><published>2009-11-02T00:00:00+08:00</published><updated>2009-11-02T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-11-02:/blog/lighttpdtong-guo-fastcgiyun-xing-webpycheng-xu.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;fastcgi&lt;/li&gt;
&lt;li&gt;lighttpd&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;web.py
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://webpy.org/" target="_blank"&gt;web.py&lt;/a&gt;是个很小的python框架，特点就是小，连session都没有实现人家就发布了。前几天KungfuRails大会上他们吹牛说&lt;a href="http://www.sinatrarb.com" target="_blank"&gt;Sinatra&lt;/a&gt;可以写出世界上最小的Webapp，但是有web.py的化，那个 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;fastcgi&lt;/li&gt;
&lt;li&gt;lighttpd&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;web.py
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://webpy.org/" target="_blank"&gt;web.py&lt;/a&gt;是个很小的python框架，特点就是小，连session都没有实现人家就发布了。前几天KungfuRails大会上他们吹牛说&lt;a href="http://www.sinatrarb.com" target="_blank"&gt;Sinatra&lt;/a&gt;可以写出世界上最小的Webapp，但是有web.py的化，那个最小至少要加上个“之一”。&lt;/p&gt;

&lt;p&gt;Web.py在Lighttpd上通过fastcgi运行的配置，可以在web.py的网站上找到文档：
&lt;a href="http://webpy.org/cookbook/fastcgi-lighttpd"&gt;http://webpy.org/cookbook/fastcgi-lighttpd&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;实际我用的时候把静态的index.html用作首页，稍改动一下：
&lt;pre class="brush:plain"&gt;server.document-root = "/home/sun/projects/sdostatweb"&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;server.modules   += ( "mod_fastcgi" )&lt;br /&gt;
server.modules   += ( "mod_rewrite" )&lt;/p&gt;

&lt;p&gt;server.port = 4000&lt;/p&gt;

&lt;p&gt;mimetype.assign = (&lt;br /&gt;
    ".html" =&amp;gt; "text/html"&lt;br /&gt;
)&lt;/p&gt;

&lt;p&gt;index-file.names = ( "index.html" )&lt;/p&gt;

&lt;p&gt;fastcgi.server = ( "/sdostatweb.py" =&amp;gt;&lt;br /&gt;
(( "socket" =&amp;gt; "/tmp/fastcgi.socket",&lt;br /&gt;
        "bin-path" =&amp;gt; "/home/sun/projects/sdostatweb/sdostatweb.py",&lt;br /&gt;
        "max-procs" =&amp;gt; 5,&lt;br /&gt;
        "bin-environment" =&amp;gt; (&lt;br /&gt;
        "REAL_SCRIPT_NAME" =&amp;gt; ""&lt;br /&gt;
    ),&lt;br /&gt;
        "check-local" =&amp;gt; "disable"&lt;br /&gt;
    ))&lt;br /&gt;
)&lt;/p&gt;

&lt;p&gt;url.rewrite-once = (&lt;br /&gt;
    "^/$" =&amp;gt; "/static/index.html",&lt;br /&gt;
    "^/flotr/(.*)$" =&amp;gt; "/static/flotr/$1",&lt;br /&gt;
    "^/static/(.*)$" =&amp;gt; "/static/$1",&lt;br /&gt;
    "^/(.*)$" =&amp;gt; "/sdostatweb.py/$1",&lt;/p&gt;

&lt;p&gt;)
另外用fastcgi的方式使用web.py，需要安装flup。&lt;/p&gt;

&lt;p&gt;详细的事，可以看看新浪的Tim Yang的优化：
&lt;a href="http://timyang.net/python/python-webpy-lighttpd/"&gt;http://timyang.net/python/python-webpy-lighttpd/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;我之前真不敢相信搜狐Mail是跑在web.py上。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>芦荟和桔子们</title><link href="https://sunng.info/blog/lu-hui-he-jie-zi-men.html" rel="alternate"></link><published>2009-10-28T00:00:00+08:00</published><updated>2009-10-28T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-10-28:/blog/lu-hui-he-jie-zi-men.html</id><content type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;daf
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;img class="alignnone" src="http://farm3.static.flickr.com/2585/4052990160_e76583aed9.jpg" alt="" width="500" height="333" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" src="http://farm3.static.flickr.com/2772/4052990354_1f83589f87.jpg" alt="" width="500" height="333" /&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Restore Nautilus to open gnome menu after installed PCManFM</title><link href="https://sunng.info/blog/restore-nautilus-to-open-gnome-menu-after-installed-pcmanfm.html" rel="alternate"></link><published>2009-10-25T00:00:00+08:00</published><updated>2009-10-25T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-10-25:/blog/restore-nautilus-to-open-gnome-menu-after-installed-pcmanfm.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;nautilus
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;After installed PCMan File Manager with LXDE,  the gnome "places" menu items were opened by PCManFM as default. To switch back to nautilus, follow these steps:
&lt;ol&gt;
    &lt;li&gt;check &lt;em&gt;/usr/share/applications/nautilus-folder-hanlder.desktop&lt;/em&gt;
make sure the line "&lt;em&gt;Exec=nautilus&lt;/em&gt;" is not …&lt;/li&gt;&lt;/ol&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;nautilus
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;After installed PCMan File Manager with LXDE,  the gnome "places" menu items were opened by PCManFM as default. To switch back to nautilus, follow these steps:
&lt;ol&gt;
    &lt;li&gt;check &lt;em&gt;/usr/share/applications/nautilus-folder-hanlder.desktop&lt;/em&gt;
make sure the line "&lt;em&gt;Exec=nautilus&lt;/em&gt;" is not modified. (There are lots of hack article that teach user to change default file manager by modify this)&lt;/li&gt;
    &lt;li&gt;check &lt;em&gt;/usr/share/applications/defaults.list&lt;/em&gt;
grep the line "x-directory" by
&lt;em&gt;cat /usr/share/applications/defaults.list | grep x-directory &lt;/em&gt;
make sure the value was kept as "nautilus-folder-handler.desktop", if not, restore it.&lt;br /&gt;
Then grep the line "inode" by
&lt;em&gt;cat /usr/share/applications/defaults.list | grep inode&lt;/em&gt;
also be sure about the result "nautilus-folder-handler.desktop". if not, restore it again.&lt;/li&gt;
    &lt;li&gt;So it must be your own configuration that has been changed. check &lt;em&gt;~/.local/share/applications/defaults.list&lt;/em&gt;
grep the two lines described above, it must be changed to "pcmanfm-folder-handler.desktop", so replace them by "nautilus-folder-handler.desktop"&lt;/li&gt;
&lt;/ol&gt;
To check the result, click menu item at once (no restart or logout needed), again, you see the slow file manager...&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Yan Captcha Service</title><link href="https://sunng.info/blog/yan-captcha-service.html" rel="alternate"></link><published>2009-10-24T00:00:00+08:00</published><updated>2009-10-24T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-10-24:/blog/yan-captcha-service.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;captcha&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;Yan
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;I'd like to announce my recent works, a project called Yan Captcha Service written in Java which is aimed to provide whole solutions of captcha for your websites. It will be very easy to use the service because 1 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;captcha&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;Yan
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;I'd like to announce my recent works, a project called Yan Captcha Service written in Java which is aimed to provide whole solutions of captcha for your websites. It will be very easy to use the service because 1. interfaces are based on plain http url; 2. different kinds of usage are supported to fit your requirements; 3. the architecture is open so you can add your own solid implementation of captcha; 4. less coupling with your system. And designed for scalability, currently, it applies JGroups to share sessions (memcached support will be added soon), thus you can setup a cluster for the service.&lt;/p&gt;

&lt;p&gt;As you may know, the open-source project is split from my current work-time project because it is more closed to my idea. However, soon we will have the product opened to our thousands (millions ?) of users. I can gain feedback from the challenge and improve the open-source edition.&lt;/p&gt;

&lt;p&gt;The code is maintained by open-source scm, mercurial (also known as hg). The project is now hosted on &lt;a href="http://bitbucket.org/sunng/yan/"&gt;bitbucket.org&lt;/a&gt;. You can clone the code to local via:
&lt;em&gt;$ hg clone https://sunng@bitbucket.org/sunng/yan/&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To build the project, run this command in root of project directory:
&lt;em&gt;mvn install&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To run in a develop environment:
&lt;em&gt;mvn jetty:run&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://bitbucket.org/sunng/yan/issues/?status=new&amp;amp;status=open" target="_blank"&gt;Issue reporting&lt;/a&gt; and patches are always welcomed.&lt;/p&gt;

&lt;p&gt;Check the wiki pages for more information about the project:
&lt;a href="http://bitbucket.org/sunng/yan/wiki/Home"&gt;http://bitbucket.org/sunng/yan/wiki/Home&lt;/a&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Open Source Charting Toolkits in JavaScript</title><link href="https://sunng.info/blog/open-source-charting-toolkits-in-javascript.html" rel="alternate"></link><published>2009-10-22T00:00:00+08:00</published><updated>2009-10-22T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-10-22:/blog/open-source-charting-toolkits-in-javascript.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;canvas&lt;/li&gt;
&lt;li&gt;dojo&lt;/li&gt;
&lt;li&gt;html&lt;/li&gt;
&lt;li&gt;javascript&lt;/li&gt;
&lt;li&gt;mootools&lt;/li&gt;
&lt;li&gt;svg&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;项目的下一阶段，需要做一些数据展现方面的工作，处于对Adobe产品的抵触情绪，我个人还是倾向于用JavaScript来完成。今 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;canvas&lt;/li&gt;
&lt;li&gt;dojo&lt;/li&gt;
&lt;li&gt;html&lt;/li&gt;
&lt;li&gt;javascript&lt;/li&gt;
&lt;li&gt;mootools&lt;/li&gt;
&lt;li&gt;svg&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;项目的下一阶段，需要做一些数据展现方面的工作，处于对Adobe产品的抵触情绪，我个人还是倾向于用JavaScript来完成。今天找了几个画Chart的库，比较一下功能和编程接口的使用，为接下来的开发做一些准备。
&lt;h3&gt;dojox.charting&lt;/h3&gt;
(BSD License)&lt;br /&gt;
没什么悬念，第一个想到的就是曾经用过的&lt;a href="http://dojotoolkit.org/" target="_blank"&gt;dojo&lt;/a&gt;。之前实习时候用dojo画chart也算是积累了一些心得。dojox.charting的最主要优点是编程接口完善、全面，可以配置的项目很多，接口易于编程，易于动态地生成、操作Chart。而另一方面最主要的问题就是文档比较匮乏，不了解的人可能问了，doc.dojotoolkit.org好强大，怎么会文档匮乏呢。其实是dojo和&lt;a href="http://mootools.net"&gt;mootools&lt;/a&gt;类似，都很喜欢用option object来传递可选参数，但是这些option object在文档中没有任何涉及，很多关键的属性都在其中但是却没有办法查到，算是美中一大不足。
&lt;pre class="brush:html"&gt;&amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&amp;gt;&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;&amp;lt;html xmlns="http://www.w3.org/1999/xhtml"&amp;gt;&lt;/p&gt;

&lt;p&gt;&amp;lt;head&amp;gt;&lt;/p&gt;

&lt;p&gt; &amp;lt;script src="dojo-release-1.3.2/dojo/dojo.js" djConfig="isDebug: true"&amp;gt;&amp;lt;/script&amp;gt;&lt;/p&gt;

&lt;p&gt; &amp;lt;title&amp;gt;Dojo&amp;lt;/title&amp;gt;&lt;/p&gt;

&lt;p&gt; &amp;lt;script type="text/javascript"&amp;gt;&lt;/p&gt;

&lt;p&gt;     dojo.require("dojox.charting.Chart2D");&lt;/p&gt;

&lt;p&gt; &amp;lt;/script&amp;gt;&lt;/p&gt;

&lt;p&gt;&amp;lt;/head&amp;gt;&lt;/p&gt;

&lt;p&gt;&amp;lt;body&amp;gt;&lt;/p&gt;

&lt;p&gt; &amp;lt;h1&amp;gt;Dojox.charting&amp;lt;/h1&amp;gt;&lt;/p&gt;

&lt;p&gt; &amp;lt;div id="charting" style="width:400px; height:250px;"&amp;gt;&lt;/p&gt;

&lt;p&gt; &amp;lt;/div&amp;gt;&lt;/p&gt;

&lt;p&gt; &amp;lt;script type="text/javascript"&amp;gt;&lt;/p&gt;

&lt;p&gt;     （function charting(){&lt;/p&gt;

&lt;p&gt;         dojo.empty("charting");&lt;/p&gt;

&lt;p&gt;         var data = [2,4,5,7,7,4,4,7];&lt;/p&gt;

&lt;p&gt;         var chart = new dojox.charting.Chart2D("charting");&lt;/p&gt;

&lt;p&gt;         chart.addPlot("default", {type: "Lines", markers: true, shadows: {dw:2, dy:2, dx:2}});&lt;/p&gt;

&lt;p&gt;         chart.addPlot("additional", {type: "Areas"});&lt;/p&gt;

&lt;p&gt;         chart.addPlot("other", {type: "ClusteredColumns"})&lt;/p&gt;

&lt;p&gt;         chart.addAxis("x");&lt;/p&gt;

&lt;p&gt;         chart.addAxis("y", {vertical: true, min:0, max: 10});&lt;/p&gt;

&lt;p&gt;         chart.addSeries("Testing Data 1", data);&lt;/p&gt;

&lt;p&gt;         chart.addSeries("Average", dojo.map(data, function(it){return (Math.sin(it)+Math.random())*4}), {&lt;/p&gt;

&lt;p&gt;             fill: "rgba(145, 213, 100, 0.5)", plot:"additional"&lt;/p&gt;

&lt;p&gt;         });&lt;/p&gt;

&lt;p&gt;         chart.addSeries("All Random", dojo.map(data, function(it){return (Math.random()*5)}), {&lt;/p&gt;

&lt;p&gt;             plot: "other", fill: "#AEC6E2", stroke:{color:"#333", width:1}&lt;/p&gt;

&lt;p&gt;         });&lt;/p&gt;

&lt;p&gt;         chart.render();&lt;/p&gt;

&lt;p&gt;     }）();&lt;/p&gt;

&lt;p&gt; &amp;lt;/script&amp;gt;&lt;/p&gt;

&lt;p&gt;&amp;lt;/body&amp;gt;&lt;/p&gt;

&lt;p&gt;&amp;lt;/html&amp;gt;
&lt;h3&gt;Protovis&lt;/h3&gt;
(BSD License)
&lt;a href="http://vis.stanford.edu/protovis/" target="_blank"&gt;Protovis&lt;/a&gt;，看域名就知道是斯坦福的项目，Protovis是在prototype基础上，不仅有charting的功能，还包括其他可视化方法的实现。看了Demo会发现它真的很不错，不过Protovis的本意似乎只是借助浏览器的图形功能实现可视化，它并没有提供超出可视化范畴的其他东西，甚至看过实例的代码你会发现，作者是不折不扣的函数式编程和链式方法的爱好者，Protovis的代码算是出神入化了，如果你想看看学术的JavaScript是如何写的，那就看看这个发表了论文的JavaScript库吧。&lt;br /&gt;
看起来是很出神入化，可是这样的编程接口如果要用在动态的数据展现上，实在是难以想象。况且，pv.Panel居然没有传入dom对象的接口，无从控制chart的生成位置。说白了，Protovis并不是一个满足项目需要的Library，不过它的功能和代码还是值得多看几眼的。
&lt;pre class="brush:html"&gt;&amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&amp;gt;&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;&amp;lt;html xmlns="http://www.w3.org/1999/xhtml"&amp;gt;&lt;/p&gt;

&lt;p&gt;&amp;lt;head&amp;gt;&lt;/p&gt;

&lt;p&gt; &amp;lt;script src="protovis-3.1/protovis-r3.1.js"&amp;gt;&amp;lt;/script&amp;gt;&lt;/p&gt;

&lt;p&gt;&amp;lt;/head&amp;gt;&lt;/p&gt;

&lt;p&gt;&amp;lt;body&amp;gt;&lt;/p&gt;

&lt;p&gt; &amp;lt;div id="page"&amp;gt;&lt;/p&gt;

&lt;p&gt;     &amp;lt;h1&amp;gt;Protovis&amp;lt;/h1&amp;gt;&lt;/p&gt;

&lt;p&gt;     &amp;lt;div style="border:1px #CCC solid;"&amp;gt;&lt;/p&gt;

&lt;p&gt;         &amp;lt;script type="text/javascript"&amp;gt;&lt;/p&gt;

&lt;p&gt;             var width = 300;&lt;/p&gt;

&lt;p&gt;             var height = 225;&lt;/p&gt;

&lt;p&gt;             var data = [1,2,3,4,6,7,5,8,9];&lt;/p&gt;

&lt;p&gt;             var dataMax = data[0];&lt;/p&gt;

&lt;p&gt;             for(var i=1; i&amp;lt;data.length; i++){&lt;/p&gt;

&lt;p&gt;                 dataMax = dataMax &amp;gt; data[i] ? dataMax : data[i];&lt;/p&gt;

&lt;p&gt;             }&lt;/p&gt;

&lt;p&gt;             var x = pv.Scale.linear(0, dataMax).range(0, width);&lt;/p&gt;

&lt;p&gt;             var y = pv.Scale.ordinal(pv.range(10)).splitBanded(0, height, 0.9);&lt;/p&gt;

&lt;p&gt;             var charting = new pv.Panel();&lt;/p&gt;

&lt;p&gt;             charting.width(width).height(height).left(20).top(20);&lt;/p&gt;

&lt;p&gt;             var bar = charting.add(pv.Bar);&lt;/p&gt;

&lt;p&gt;             bar.data(data).top(function() y(this.index)).height(y.range().band).left(0).width(x);&lt;/p&gt;

&lt;p&gt;             bar.anchor("center").add(pv.Label)&lt;/p&gt;

&lt;p&gt;                 .textStyle("white")&lt;/p&gt;

&lt;p&gt;                 .textAlign("center")&lt;/p&gt;

&lt;p&gt;                 .text(function(d) d.toFixed(1));&lt;/p&gt;

&lt;p&gt;             charting.add(pv.Rule)&lt;/p&gt;

&lt;p&gt;                 .data(x.ticks())&lt;/p&gt;

&lt;p&gt;                 .left(function(d) Math.round(x(d)) - .5)&lt;/p&gt;

&lt;p&gt;                 .strokeStyle(function(d) d ? "rgba(255,255,255,.3)" : "#000")&lt;/p&gt;

&lt;p&gt;             .add(pv.Rule)&lt;/p&gt;

&lt;p&gt;                 .bottom(0)&lt;/p&gt;

&lt;p&gt;                 .height(5)&lt;/p&gt;

&lt;p&gt;                 .strokeStyle("#C00")&lt;/p&gt;

&lt;p&gt;                 .anchor("bottom").add(pv.Label)&lt;/p&gt;

&lt;p&gt;                 .text(function(d) d);&lt;/p&gt;

&lt;p&gt;             charting.render();&lt;/p&gt;

&lt;p&gt;         &amp;lt;/script&amp;gt;&lt;/p&gt;

&lt;p&gt;     &amp;lt;/div&amp;gt;&lt;/p&gt;

&lt;p&gt; &amp;lt;/div&amp;gt;&lt;/p&gt;

&lt;p&gt;&amp;lt;/body&amp;gt;&lt;/p&gt;

&lt;p&gt;&amp;lt;/html&amp;gt;
&lt;h3&gt;Flotr&lt;/h3&gt;
(MIT License)&lt;br /&gt;
不同于上面两个库通过SVG绘图，&lt;a href="http://solutoire.com/category/flotr/" target="_blank"&gt;Flotr&lt;/a&gt;是用canvas api来完成绘制的。与dojo相比，Flotr的API比较简单，主要只有一个Flotr.draw方法，可以传入三个参数：目标DOM对象，数据和绘图选项。和dojo不同，Flotr的文档里关于这个option object有非常明确的文档。另外强大的是Flotr还提供了一些交互的功能，支持事件绑定。
&lt;pre class="brush:html"&gt;&amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&amp;gt;&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;&amp;lt;html xmlns="http://www.w3.org/1999/xhtml"&amp;gt;&lt;/p&gt;

&lt;p&gt;&amp;lt;head&amp;gt;&lt;/p&gt;

&lt;p&gt; &amp;lt;script src="flotr-0.2.0-alpha/flotr/lib/prototype-1.6.0.2.js"&amp;gt;&amp;lt;/script&amp;gt;&lt;/p&gt;

&lt;p&gt; &amp;lt;script src="flotr-0.2.0-alpha/flotr/flotr-0.2.0-alpha.js"&amp;gt;&amp;lt;/script&amp;gt;&lt;/p&gt;

&lt;p&gt; &amp;lt;title&amp;gt;Flotr&amp;lt;/title&amp;gt;&lt;/p&gt;

&lt;p&gt; &amp;lt;script type="text/javascript"&amp;gt;&lt;/p&gt;

&lt;p&gt; &amp;lt;/script&amp;gt;&lt;/p&gt;

&lt;p&gt;&amp;lt;/head&amp;gt;&lt;/p&gt;

&lt;p&gt;&amp;lt;body&amp;gt;&lt;/p&gt;

&lt;p&gt; &amp;lt;h1&amp;gt;Flotr&amp;lt;/h1&amp;gt;&lt;/p&gt;

&lt;p&gt; &amp;lt;div id="charting" style="width:400px; height:250px;"&amp;gt;&lt;/p&gt;

&lt;p&gt; &amp;lt;/div&amp;gt;&lt;/p&gt;

&lt;p&gt; &amp;lt;script type="text/javascript"&amp;gt;&lt;/p&gt;

&lt;p&gt;     (function d(){&lt;/p&gt;

&lt;p&gt;         var series1 = {data: (function(){&lt;/p&gt;

&lt;p&gt;             var d = [];&lt;/p&gt;

&lt;p&gt;             for(var i=0; i&amp;lt;10; i+=0.5){&lt;/p&gt;

&lt;p&gt;                 d.push([i, Math.random()-0.5+Math.sin(i)*3]);&lt;/p&gt;

&lt;p&gt;             }&lt;/p&gt;

&lt;p&gt;             return d;&lt;/p&gt;

&lt;p&gt;         })(), label:"d1", lines:{fill:true}};&lt;/p&gt;

&lt;p&gt;         var series2 = {data: (function(){&lt;/p&gt;

&lt;p&gt;             var d = [];&lt;/p&gt;

&lt;p&gt;             for(var i=0; i&amp;lt;10; i+=0.5){&lt;/p&gt;

&lt;p&gt;                 d.push([i, (Math.random()-0.5)*2+Math.cos(i)*2]);&lt;/p&gt;

&lt;p&gt;             }&lt;/p&gt;

&lt;p&gt;             return d;&lt;/p&gt;

&lt;p&gt;         })(), label: "s2", points:{show:true}, lines:{show:true}};&lt;/p&gt;

&lt;p&gt;         Flotr.draw($("charting"), [series1, series2], {&lt;/p&gt;

&lt;p&gt;             legend: {position :"se"},&lt;/p&gt;

&lt;p&gt;             yaxis: {min: -3.5, max:3.5}&lt;/p&gt;

&lt;p&gt;         });&lt;/p&gt;

&lt;p&gt;     })();&lt;/p&gt;

&lt;p&gt; &amp;lt;/script&amp;gt;&lt;/p&gt;

&lt;p&gt;&amp;lt;/body&amp;gt;&lt;/p&gt;

&lt;p&gt;&amp;lt;/html&amp;gt;
此外，还有一个与Flotr很类似的&lt;a href="http://www.deensoft.com/lab/protochart/"&gt;Protochart&lt;/a&gt;，后者的网站上写自己是motivated by Flotr，不过我很难想象功能几乎相同是如何motivated的。
&lt;h3&gt;MilkChart&lt;/h3&gt;
(Apache License 2.0)
&lt;a href="http://code.google.com/p/milkchart/"&gt;MilkChart&lt;/a&gt;是一个建立在Mootools上的Charting库，采用canvas api绘图。它的特点是把HTML Table的数据绘制成Chart，如果场景允许，倒是省去了不少构建数组的麻烦。这个项目目前规模还不大，并且在作者积极的开发中，可以参考它的Wiki获得详细的使用方法。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Time Tracking and Task Management with TaskCoach</title><link href="https://sunng.info/blog/time-tracking-and-task-management-with-taskcoach.html" rel="alternate"></link><published>2009-10-20T00:00:00+08:00</published><updated>2009-10-20T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-10-20:/blog/time-tracking-and-task-management-with-taskcoach.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;foss
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://www.taskcoach.org/" target="_blank"&gt;TaskCoach&lt;/a&gt;其实是老朋友了，wxpython的，跨平台，最初只是TODO LIST，后来又加进了Time Tracking的功能。早先给&lt;a href="http://blog.frank05.net/?p=152" target="_blank"&gt;frank05&lt;/a&gt;推荐过，不过在学校的时候，在 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;foss
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://www.taskcoach.org/" target="_blank"&gt;TaskCoach&lt;/a&gt;其实是老朋友了，wxpython的，跨平台，最初只是TODO LIST，后来又加进了Time Tracking的功能。早先给&lt;a href="http://blog.frank05.net/?p=152" target="_blank"&gt;frank05&lt;/a&gt;推荐过，不过在学校的时候，在电脑上的时间往往是支离破碎的，没有什么tracking的价值。&lt;/p&gt;

&lt;p&gt;现在不同了，工作以后每天在电脑前面坐至少8个小时，如果没有点控制，时间一样是像以前一样莫名其妙地溜走了。去年刚开始实习的时候，我大概有个习惯是把每天的TODO写在本子上，一来当时清楚，二来时间长了也有数。今年工作以后一开始也是这样，但是时间长了发现只用笔来记录功能还是太有限，比如事情做了一半pending了，经常是画个圈，但是时间长了还是忘掉了。而进一步想做Time Tracking就更麻烦了。&lt;/p&gt;

&lt;p&gt;上周想了一下确实需要个工具，这周一开始就重新开始在开发用的Windows机器上用TaskCoach了。有将近一年的时间没关注这个软件，版本号虽然没有什么大的突破，不过用起来倒是更顺手了。现在可以最小化到系统栏，开机自动启动。本身的功能除了支持多层的Task管理，Time Tracking，还可以顺手记一些Notes，另外还有定时的事件提醒，也不会错过总也想不起来的培训了。&lt;/p&gt;

&lt;p&gt;推荐给大家在Windows上用。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>音乐节看张楚归来</title><link href="https://sunng.info/blog/yin-le-jie-kan-zhang-chu-gui-lai.html" rel="alternate"></link><published>2009-10-18T00:00:00+08:00</published><updated>2009-10-18T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-10-18:/blog/yin-le-jie-kan-zhang-chu-gui-lai.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;Music&lt;/li&gt;
&lt;li&gt;rock&lt;/li&gt;
&lt;li&gt;Shanghai&lt;/li&gt;
&lt;li&gt;zhangchu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;这周末是上海爵士音乐节，虽然名字上是爵士，但是实际上还有摇滚的场。18号下午是张楚和崔健的演出，慕名第一次去了世 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;Music&lt;/li&gt;
&lt;li&gt;rock&lt;/li&gt;
&lt;li&gt;Shanghai&lt;/li&gt;
&lt;li&gt;zhangchu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;这周末是上海爵士音乐节，虽然名字上是爵士，但是实际上还有摇滚的场。18号下午是张楚和崔健的演出，慕名第一次去了世纪公园。&lt;/p&gt;

&lt;p&gt;刚进去的时候是曹方的演出。&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" src="http://farm3.static.flickr.com/2565/4022603570_2a4c93ceb3.jpg" alt="" width="500" height="375" /&gt;&lt;/p&gt;

&lt;p&gt;这个大概是一个电子口琴吧，我是土人，不知道，声音是口琴的。&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" src="http://farm3.static.flickr.com/2579/4022604038_7e5a2b191a.jpg" alt="" width="500" height="335" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" src="http://farm3.static.flickr.com/2550/4021843829_06654d29bf.jpg" alt="" width="500" height="336" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" src="http://farm3.static.flickr.com/2778/4021845629_856399924f.jpg" alt="" width="500" height="335" /&gt;&lt;/p&gt;

&lt;p&gt;夕阳。&lt;/p&gt;

&lt;p&gt;接下来就是张楚出场了。结果因为太过兴奋，忘了把自动对焦打开，所以刚出来咔咔咔拍了好多照片都是模糊的，也不能完全怪我，第一次见到张楚，那种感觉，大家可以想象吧。&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" src="http://farm3.static.flickr.com/2509/4022639806_ff51c6d3ea.jpg" alt="" width="500" height="336" /&gt;&lt;/p&gt;

&lt;p&gt;张楚真的是张楚，和想象中的一模一样。今天一共唱了《光明大道》、《棉花》、《造飞机的工厂》、《孤独的人是可耻的》、《向日葵》、《海边》、《蚂蚁蚂蚁》、《姐姐》。除了海边是新歌一点都没听过以外，其他歌我都是从头到尾跟着一起唱下来的，后来我还想，不是花钱来听歌的，是花钱来唱歌的，还是大合唱。不过吼一吼感觉真好，今天吼了一下貌似是压抑很久了一样，算算大约自从去年夏天离开浦口宿舍就没有再这么吼过了。真过瘾。因为一直太激动，张楚的照片也没认真照，上面这张是挺有代表性的，其他的只能勉强挑挑了。&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" src="http://farm3.static.flickr.com/2575/4022640974_519d6abef7.jpg" alt="" width="335" height="500" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" src="http://farm3.static.flickr.com/2688/4021881445_ac7673235c.jpg" alt="" width="335" height="500" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" src="http://farm3.static.flickr.com/2677/4021882471_f548d13ce9.jpg" alt="" width="500" height="335" /&gt;&lt;/p&gt;

&lt;p&gt;这张和94红勘唱厕所和床的样子对比一下，还是很像的。&lt;/p&gt;

&lt;p&gt;张楚的蚂蚁蚂蚁和姐姐我都拿手机录了一下，不过回来一听姐姐的录音里基本上就是我干嚎了，不具有参考价值了。蚂蚁蚂蚁倒是很不错，没有把我嚎叫的声音录得太明显，而且这次蚂蚁蚂蚁是reggae节奏的，回头我找个地方上传一下。&lt;/p&gt;

&lt;p&gt;张楚唱完就头也不回走了，接着是崔健。不过今天真正看了崔健的演出，发现好像没有什么共鸣。我本来也就只听一块红布和新长征路那两张，结果老崔还似乎热衷于唱他的新歌。老崔的新歌啊，实在是不好评价。观众反应倒是不错，不过说实在的，这帮掺杂着90后的观众啊，我还是挤到人群外面去了。&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" src="http://farm3.static.flickr.com/2557/4021939233_652681516b.jpg" alt="" width="500" height="335" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" src="http://farm3.static.flickr.com/2489/4021940143_0ce3b2d2b5.jpg" alt="" width="335" height="500" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" src="http://farm3.static.flickr.com/2648/4021941589_335ddf1922.jpg" alt="" width="500" height="335" /&gt;&lt;/p&gt;

&lt;p&gt;说实在的，老崔的演出看到一半都有点反胃了，大概是晚上没吃饭胃酸过多的原因，最后提前回去了。&lt;/p&gt;

&lt;p&gt;这次得出一个结论，我太喜欢张楚了，下次张楚来上海演出一定还要去。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>烧饭又</title><link href="https://sunng.info/blog/shao-fan-you.html" rel="alternate"></link><published>2009-10-17T00:00:00+08:00</published><updated>2009-10-17T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-10-17:/blog/shao-fan-you.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;daf&lt;/li&gt;
&lt;li&gt;food
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;今天心血来潮，在手握土豆和鸡肉的情况下，作为一个土人，毅然决定尝试做一次咖哩。&lt;/p&gt;

&lt;p&gt;原本准备是做 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;daf&lt;/li&gt;
&lt;li&gt;food
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;今天心血来潮，在手握土豆和鸡肉的情况下，作为一个土人，毅然决定尝试做一次咖哩。&lt;/p&gt;

&lt;p&gt;原本准备是做土豆炒鸡丁的，所以鸡肉买的是鸡胸肉，没有油和骨头的，大厨曾经说还是有骨头的肉比较好。但是考虑到本人比较懒，所以吃起来相对省事要比口味的提升显得更有吸引力。&lt;/p&gt;

&lt;p&gt;土豆和鸡肉切丁，好吧，其实是切块。因为反正是要混在一起的，形状大小都自定吧。炒锅里倒油，把鸡块和土豆块都下锅，炒到鸡块大致变白就可以了，不用太长时间。心情好的话，中间可以倒一些料酒，不倒也没什么区别好像。&lt;/p&gt;

&lt;p&gt;如果你的炒锅足够深可以炖的话就直接加水好了，如果像我一样是平底锅的话，把土豆和鸡肉转移到一个汤锅里，加水。对200g鸡肉，说明上说加500ml水，总之是需要不少的。大火煮沸，用汤勺把白沫捞出去，然后用中火再煮大约7-10分钟。&lt;/p&gt;

&lt;p&gt;接下来把块状的咖哩掰开，扔进锅里。用量的话，跟据daf同学的远程协助，100g/6块大小的，大约3块就足够了。但是由于远程协助的滞后性，作为一个土人，我把6块通通扔进去了（吃完才看到说明上是3-5人的量）。开小火炖。然后到了关键的步骤，这个时候千万不能去上网，不能去调程序，一定要做到锅在人在，那勺子不断搅和。这也是远程协助才知道的，同样滞后了，结果没有融化的咖哩块沉到锅底粘住糊掉了。&lt;/p&gt;

&lt;p&gt;搅和的时间也不固定，主要看锅里还剩多少东西，总而言之，粘稠到一定程度就可以拿出来了。&lt;/p&gt;

&lt;p&gt;虽然糊了，但是味道还不错，不过考虑到本人的口味广受质疑，所以大家还是自己摸索吧。&lt;/p&gt;

&lt;p&gt;图为证&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" src="http://farm3.static.flickr.com/2609/4017962909_e693c066ac.jpg" alt="" width="500" height="335" /&gt;&lt;img class="alignnone" src="http://farm3.static.flickr.com/2751/4018722768_b094b87f8d.jpg" alt="" width="500" height="335" /&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>另一种验证码方式</title><link href="https://sunng.info/blog/ling-yi-chong-yan-zheng-ma-fang-shi.html" rel="alternate"></link><published>2009-10-15T00:00:00+08:00</published><updated>2009-10-15T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-10-15:/blog/ling-yi-chong-yan-zheng-ma-fang-shi.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;captcha&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;今天又讨论了一种验证码服务的机制，这种机制相对前两天说的简化的验证码生成的部分，由两 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;captcha&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;今天又讨论了一种验证码服务的机制，这种机制相对前两天说的简化的验证码生成的部分，由两步生成变成了一步生成，当然由于生成图片的接口直接暴露给用户，存在被刷的可能。&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" src="http://farm4.static.flickr.com/3499/4013396855_6c5a02b7c7.jpg" alt="" width="500" height="310" /&gt;
&lt;ol&gt;
    &lt;li&gt;用户浏览器向应用服务器请求包含验证码的页面；&lt;/li&gt;
    &lt;li&gt;应用服务器将包含验证码地址的网页发送给用户浏览器；&lt;/li&gt;
    &lt;li&gt;用户浏览器通过img的src中的固定链接向验证码服务器请求验证码图片；&lt;/li&gt;
    &lt;li&gt;验证码服务器输出验证码图片流到用户浏览器，将sessionid写入cookie；&lt;/li&gt;
    &lt;li&gt;用户判读验证码图片，提交表单；&lt;/li&gt;
    &lt;li&gt;应用服务器取出cookie中的sessionid和用户输入发往验证码服务器；&lt;/li&gt;
    &lt;li&gt;验证码服务器进行验证，返回通过或拒绝；&lt;/li&gt;
    &lt;li&gt;应用服务器根据验证码服务器结果进行响应。&lt;/li&gt;
&lt;/ol&gt;
优点：&lt;br /&gt;
固定链接，简化了接入，便于接入静态页面；&lt;/p&gt;

&lt;p&gt;缺点：&lt;br /&gt;
写cookie受到域的限制，只能在相同的域中使用该服务；&lt;br /&gt;
验证码接口暴露给用户，可能被穷举&lt;/p&gt;

&lt;p&gt;百度和腾讯使用的都是这种方式。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Jetty7/JSP, Eclipse/GTK2.18</title><link href="https://sunng.info/blog/jetty7jsp-eclipsegtk218.html" rel="alternate"></link><published>2009-10-14T00:00:00+08:00</published><updated>2009-10-14T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-10-14:/blog/jetty7jsp-eclipsegtk218.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;eclipse&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;jetty
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;p&gt;Jetty7.0上周发布了，这是jetty迁进eclipse社区之后的第一个正式版本。但是下载之后发现这个版本居然没有jsp支持。原来，jetty的jsp实现一直使用的是glassfish的实现，嵌入eclipse社区之后，这变成了一个问题。功能完 …&lt;/p&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;eclipse&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;jetty
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;p&gt;Jetty7.0上周发布了，这是jetty迁进eclipse社区之后的第一个正式版本。但是下载之后发现这个版本居然没有jsp支持。原来，jetty的jsp实现一直使用的是glassfish的实现，嵌入eclipse社区之后，这变成了一个问题。功能完全的版本（hightide）依然可以从codehaus的镜像里下载：&lt;br /&gt;
&lt;a href="http://dev.eclipse.org/mhonarc/lists/jetty-dev/msg00198.html"&gt;http://dist.codehaus.org/jetty/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;比较一下大小就能看出，eclipse的版本只有2.1M，hightide的版本15M。为此，社区里有详细的讨论：&lt;br /&gt;
&lt;a href="http://dev.eclipse.org/mhonarc/lists/jetty-dev/msg00198.html"&gt;http://dev.eclipse.org/mhonarc/lists/jetty-dev/msg00198.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Gnome2.28发布了，但是Eclipse在Gtk2.18上工作时，会发生按钮失灵的问题，主要集中在finish next和ok等关键的按钮。简单的解决方法是焦点在按钮上时通过按回车来执行。比较彻底的方法是在eclipse执行的环境中，设置环境变量&lt;em&gt;GDK_NATIVE_WINDOWS=true&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;这个bug被报告在这里：&lt;br /&gt;
&lt;a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=291257"&gt;https://bugs.eclipse.org/bugs/show_bug.cgi?id=291257&lt;/a&gt;&lt;/p&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>先斩后奏</title><link href="https://sunng.info/blog/xian-zhan-hou-zou.html" rel="alternate"></link><published>2009-10-13T00:00:00+08:00</published><updated>2009-10-13T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-10-13:/blog/xian-zhan-hou-zou.html</id><content type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;daf&lt;/li&gt;
&lt;li&gt;photoshop
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;daf同学最近迷上画画了。
&lt;img class="alignnone" src="http://farm3.static.flickr.com/2447/4007515745_ecb345e9f5.jpg" alt="" width="500" height="333" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" src="http://farm3.static.flickr.com/2535/4007515879_7cb87e4ee7.jpg" alt="" width="500" height="333" /&gt;&lt;/p&gt;

&lt;p&gt;这次发图是先斩后奏，嗯&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>验证码服务recaptcha和vidoop</title><link href="https://sunng.info/blog/yan-zheng-ma-fu-wu-recaptchahe-vidoop.html" rel="alternate"></link><published>2009-10-13T00:00:00+08:00</published><updated>2009-10-13T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-10-13:/blog/yan-zheng-ma-fu-wu-recaptchahe-vidoop.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;captcha&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;某人已经发展到上班时间写blog了。&lt;/p&gt;

&lt;p&gt;继续说验证码服务，找到两个比较典型的。&lt;a href="http://recaptcha.net/" target="_blank"&gt;recaptcha&lt;/a&gt;非常著名，是twitter和facebook使用的验证码服务（不知道 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;captcha&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;某人已经发展到上班时间写blog了。&lt;/p&gt;

&lt;p&gt;继续说验证码服务，找到两个比较典型的。&lt;a href="http://recaptcha.net/" target="_blank"&gt;recaptcha&lt;/a&gt;非常著名，是twitter和facebook使用的验证码服务（不知道现在的情况。。。），&lt;a href="http://vidoop.com/" target="_blank"&gt;vidoop&lt;/a&gt;提供了一种很有特点的验证码机制。&lt;/p&gt;

&lt;p&gt;两个服务都提供了python的接口封装便于接入，通过api接口可以管中窥豹，大致了解这两个验证服务的机制。
&lt;h3&gt;recaptcha&lt;/h3&gt;
下面是一段结合了web.py的简单调用
&lt;pre class="brush:python"&gt;public_key = "******"
private_key = "********"
class Recaptcha(object):
    def GET(self):
        r = recaptcha.displayhtml(public_key)
        return render.recapt(r)&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;    def POST(self):&lt;br /&gt;
        params = web.input()&lt;br /&gt;
        recaptcha_challenge_field, recaptcha_response_field = params.recaptcha_challenge_field, params.recaptcha_response_field&lt;br /&gt;
        remote_ip = web.ctx.ip&lt;br /&gt;
        result = recaptcha.submit(recaptcha_challenge_field, recaptcha_response_field, private_key, remote_ip)&lt;br /&gt;
        return result.is_valid
我在GET请求中获取验证码，在POST请求中提交验证码。&lt;/p&gt;

&lt;p&gt;recaptcha返回的是一段recaptcha自己风格的html片段，效果大家参考twitter的验证码，实际上是一个iframe，iframe的url中包含了哈希串。其中的字段名也自然被写死成recaptcha_challenge_field和recaptcha_response_field，考虑到应用服务器无需验证这两个field的输入，所以也无可厚非。recaptcha_response_field用于输入字符，recaptcha_challenge_field在载入时被修改为一个唯一key。&lt;/p&gt;

&lt;p&gt;提交验证时，recaptcha需要提供以上两个用户输入和应用的privatekey以及浏览器ip。recaptcha通过recaptcha_challenge_field 应用的private key以及用户出口ip可以唯一标示用户，并包含一些冗余实现安全相关策略。
&lt;h3&gt;Vidoop&lt;/h3&gt;
Vidoop提供的服务机制与recaptcha大同小异
&lt;pre class="brush:python"&gt;
customer_id = "***"
site_id = "localtest"
api_username = "******"
api_password = "******"
vdp = VidoopSecure(api_username, api_password, customer_id, site_id)&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;class Vidoop(object):&lt;br /&gt;
    def GET(self):&lt;br /&gt;
        captcha_id, captcha_url, captcha_categories, captcha_text = vdp.create_captcha()&lt;br /&gt;
        return render.vidoop(captcha_id, captcha_url, captcha_categories, captcha_text)&lt;/p&gt;

&lt;p&gt;    def POST(self):&lt;br /&gt;
        params =web.input()&lt;br /&gt;
        captcha_id, captcha_code = params.id, params.code&lt;br /&gt;
        try:&lt;br /&gt;
            vdp.submit_captcha(captcha_id, captcha_code)&lt;br /&gt;
            return True&lt;br /&gt;
        except:&lt;br /&gt;
            return False
&lt;/p&gt;

&lt;p&gt;提交验证时只需要要captcha_id和用户输入，这里相对recaptcha做了简化，但已经足够验证需要了。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>验证码服务的两种方式</title><link href="https://sunng.info/blog/yan-zheng-ma-fu-wu-de-liang-chong-fang-shi.html" rel="alternate"></link><published>2009-10-12T00:00:00+08:00</published><updated>2009-10-12T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-10-12:/blog/yan-zheng-ma-fu-wu-de-liang-chong-fang-shi.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;captcha&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;h3&gt;方式1，应用服务器负责生成验证码字符，验证码服务器主要负责验证码图片生成。&lt;/h3&gt;
&lt;img class="alignnone" src="http://farm3.static.flickr.com/2624/4005000566_e100e098ef.jpg" alt="" width="500" height="310" /&gt;
&lt;ol&gt;
    &lt;li&gt;用户浏览器向 …&lt;/li&gt;&lt;/ol&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;captcha&lt;/li&gt;
&lt;li&gt;project&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;h3&gt;方式1，应用服务器负责生成验证码字符，验证码服务器主要负责验证码图片生成。&lt;/h3&gt;
&lt;img class="alignnone" src="http://farm3.static.flickr.com/2624/4005000566_e100e098ef.jpg" alt="" width="500" height="310" /&gt;
&lt;ol&gt;
    &lt;li&gt;用户浏览器向应用服务器请求包含验证码的页面；&lt;/li&gt;
    &lt;li&gt;应用服务器生成验证码字符，存储在session中；应用服务器发送相关图片参数（验证码字符、宽、高、复杂度、背景色等）到验证码服务器；&lt;/li&gt;
    &lt;li&gt;验证码服务器返回图片地址到应用服务器；&lt;/li&gt;
    &lt;li&gt;应用服务器将包含验证码地址的网页发送给用户浏览器；&lt;/li&gt;
    &lt;li&gt;用户浏览器通过img的src方式向验证码服务器请求验证码图片；&lt;/li&gt;
    &lt;li&gt;验证码服务器输出验证码图片流到用户浏览器；&lt;/li&gt;
    &lt;li&gt;用户判读验证码图片，提交表单；&lt;/li&gt;
    &lt;li&gt;应用服务器取出session中的验证码字符比对，返回结果。&lt;/li&gt;
&lt;/ol&gt;
这种方式的优点：
&lt;ol&gt;
    &lt;li&gt;较少的HTTP请求调用&lt;/li&gt;
    &lt;li&gt; 替换原应用中独立的验证码功能相对容易&lt;/li&gt;
    &lt;li&gt; 验证码服务器相对简单&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;方式2，验证码服务器承担验证功能，应用服务器在验证中仅起到传递作用。&lt;/h3&gt;
&lt;img class="alignnone" src="http://farm3.static.flickr.com/2464/4005000760_cc402da4c1.jpg" alt="" width="500" height="310" /&gt;
&lt;ol&gt;
    &lt;li&gt;用户浏览器向应用服务器请求包含验证码的页面；&lt;/li&gt;
    &lt;li&gt;应用服务器发送相关图片参数（宽、高、复杂度、背景色等）到验证码服务器；&lt;/li&gt;
    &lt;li&gt;验证码服务器返回图片地址、惟一的会话id到应用服务器；&lt;/li&gt;
    &lt;li&gt;应用服务器将包含验证码地址的网页发送给用户浏览器；&lt;/li&gt;
    &lt;li&gt;用户浏览器通过img的src方式向验证码服务器请求验证码图片；&lt;/li&gt;
    &lt;li&gt;验证码服务器输出验证码图片流到用户浏览器；&lt;/li&gt;
    &lt;li&gt;用户判读验证码图片，提交表单；&lt;/li&gt;
    &lt;li&gt;应用服务器将第三步获得的会话id和用户输入的验证码字符传给验证码服务器；&lt;/li&gt;
    &lt;li&gt;验证码服务器进行验证，返回通过或拒绝；&lt;/li&gt;
    &lt;li&gt;应用服务器根据验证码服务器结果进行响应。&lt;/li&gt;
&lt;/ol&gt;
这种方式的优点：
&lt;ol&gt;
    &lt;li&gt;验证码服务功能完善，涵盖整个验证流程；&lt;/li&gt;
    &lt;li&gt; 验证码服务端有详细的验证日志记录，便于数据分析；&lt;/li&gt;
&lt;/ol&gt;
欢迎大家就两种方式发表意见～&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>接受参观，欢迎留言</title><link href="https://sunng.info/blog/jie-shou-can-guan-huan-ying-liu-yan.html" rel="alternate"></link><published>2009-10-10T00:00:00+08:00</published><updated>2009-10-10T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-10-10:/blog/jie-shou-can-guan-huan-ying-liu-yan.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;clip&lt;/li&gt;
&lt;li&gt;daf
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;img class="alignnone" src="http://farm4.static.flickr.com/3448/3997603627_6b2ce9be05.jpg" alt="" width="500" height="333" /&gt;&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" src="http://farm3.static.flickr.com/2569/3997603859_500a635a3f.jpg" alt="" width="500" height="357" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" src="http://farm3.static.flickr.com/2442/3998364828_72f5b4f4bc.jpg" alt="" width="500" height="208" /&gt;
&lt;span style="color: #999999;"&gt;A: 对对对&lt;br /&gt;
A: 有个那个什么的请求&lt;br /&gt;
A: 我想被flatter&lt;br /&gt;
A: 你把那三张图post到你blog&lt;br /&gt;
A: 成不~&lt;br /&gt;
B: 没问题&lt;br /&gt;
B: 呵呵&lt;br /&gt;
A: 好~&lt;br /&gt;
B: 稍 …&lt;/span&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;clip&lt;/li&gt;
&lt;li&gt;daf
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;img class="alignnone" src="http://farm4.static.flickr.com/3448/3997603627_6b2ce9be05.jpg" alt="" width="500" height="333" /&gt;&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" src="http://farm3.static.flickr.com/2569/3997603859_500a635a3f.jpg" alt="" width="500" height="357" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" src="http://farm3.static.flickr.com/2442/3998364828_72f5b4f4bc.jpg" alt="" width="500" height="208" /&gt;
&lt;span style="color: #999999;"&gt;A: 对对对&lt;br /&gt;
A: 有个那个什么的请求&lt;br /&gt;
A: 我想被flatter&lt;br /&gt;
A: 你把那三张图post到你blog&lt;br /&gt;
A: 成不~&lt;br /&gt;
B: 没问题&lt;br /&gt;
B: 呵呵&lt;br /&gt;
A: 好~&lt;br /&gt;
B: 稍等～&lt;br /&gt;
A: 更新好~&lt;br /&gt;
A: 我来瞅瞅&lt;br /&gt;
B: 很快就好&lt;br /&gt;
B: 你去刷牙&lt;br /&gt;
A: 然后豆瓣也share&lt;br /&gt;
A: 然后继续接受flatter&lt;br /&gt;
A: XDDDDDD&lt;br /&gt;
B: 呵呵&lt;br /&gt;
B: 行啊&lt;br /&gt;
A: 哇哦&lt;br /&gt;
A: 被flatter够了以后继续画 嗯&lt;/span&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>豆瓣架构发展PPT[转]</title><link href="https://sunng.info/blog/dou-ban-jia-gou-fa-zhan-pptzhuan.html" rel="alternate"></link><published>2009-10-09T00:00:00+08:00</published><updated>2009-10-09T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-10-09:/blog/dou-ban-jia-gou-fa-zhan-pptzhuan.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;他山
tags:&lt;/li&gt;
&lt;li&gt;Architecture&lt;/li&gt;
&lt;li&gt;Douban
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;今天下午看到了这个豆瓣架构发展历程的PPT，是今年上半年QCon上的，看了以后感觉很有启发意义。随着用 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;他山
tags:&lt;/li&gt;
&lt;li&gt;Architecture&lt;/li&gt;
&lt;li&gt;Douban
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;今天下午看到了这个豆瓣架构发展历程的PPT，是今年上半年QCon上的，看了以后感觉很有启发意义。随着用户量的增大，压力的增加，功能的扩展，豆瓣架构上做出相应的变化，从最初一台机器上跑lighttpd/mysql/Quixote/memcached到现在的DoubanFS存储，DoubanDB分布式Key-Value Store，加上多台mysql，多台appserver，专门的后台运算服务器等等。&lt;/p&gt;

&lt;p&gt;PPT地址：
&lt;a href="http://www.riawork.org/iarch/douban/douban_qcon2009_beijing.pdf" target="_blank"&gt;http://www.riawork.org/iarch/douban/douban_qcon2009_beijing.pdf&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;可惜因为字体的问题好像在Evince里很多中文字看不到，还得开一下虚拟机。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>阿巴</title><link href="https://sunng.info/blog/a-ba.html" rel="alternate"></link><published>2009-10-08T00:00:00+08:00</published><updated>2009-10-08T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-10-08:/blog/a-ba.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;早晨八点半下楼开自行车锁，突然听见背后有微弱的声音。回头一看是个老太太坐在轮椅上，眼睛直直 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;早晨八点半下楼开自行车锁，突然听见背后有微弱的声音。回头一看是个老太太坐在轮椅上，眼睛直直地盯着我，嘴里含糊不清地发出阿巴阿巴的声音。我自然是听不懂她说什么了，继续开锁。十月份虽然还是阳光普照，但是风里已经有点肃穆的意思了。这样的天气，年纪大了的人独自在外面这么坐着，想起来还是很残酷的。我推车要走，老太太还是盯着我阿巴阿巴地喊。假日的早晨小区里空空荡荡，遇到这样的场面还真有点超出“奇怪”能表达的意思。我又不好意思头也不回就走，推着自行车走上前去，想问老太太点什么，但是好像又无从问起，最后大约问了些“您怎么在这里”，“您的家里人在哪”这类的问题。最终都没有形成对话，老太太还是自顾自颤颤巍巍地喊阿巴阿巴。这个时候旁边单元出来个中年妇女远远说了什么一句，我听不懂，只是听到“神经病”三个字。一看有亲戚在旁边，我想眼前这一切大约都还在别人的可控范围里，可以走了。&lt;/p&gt;

&lt;p&gt;也不知道这个阿巴到底是什么意思，或者说于老人自己是什么意思。路上我故意想得戏剧性一些，难道是老太太的什么亲人，早年走失的儿子？当然了，很有可能只是一个老人丧失语言能力之后惟一能发出的声音而已。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>到头了</title><link href="https://sunng.info/blog/dao-tou-liao.html" rel="alternate"></link><published>2009-10-06T00:00:00+08:00</published><updated>2009-10-06T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-10-06:/blog/dao-tou-liao.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;不管这是不是30号下午我伸直了脖子期待的国庆假期，总之伴随明天早晨8点45分打卡机一声鸣音它是要见鬼去了。8号？8号用来中和后一天的周一综合症吧，不指望了。今年国庆真是无聊到一定程度了，我印象中似乎 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;不管这是不是30号下午我伸直了脖子期待的国庆假期，总之伴随明天早晨8点45分打卡机一声鸣音它是要见鬼去了。8号？8号用来中和后一天的周一综合症吧，不指望了。今年国庆真是无聊到一定程度了，我印象中似乎从来都还没有这么无聊过。本来还想着靠自己一些勉强存在的兴趣爱好来抵抗无聊的，后来发现是不堪一击。概括地说这六天看了整整六天的喜羊羊。上帝啊。好像也不完全是，还听了很多革命歌曲看了一些老电影。总之正经事是没有什么明显的进度，哎呦不说了，我现在连什么是正经事都说不清楚。&lt;/p&gt;

&lt;p&gt;在上海一点出门的欲望了都没有，六天以来取得最远的地方就是旁边的超市，其中还有包括今天在内的三天连楼都没下去。豆瓣上本来还有个老电影的活动来着，结果正好是明天加班，想去也去不成。形势危急，主办方发了三封豆邮说大家快去，不然这个活动办不下去了。我想着在北京的时候还有点兴趣爱好什么的，那么严酷的斗争环境，周末还去听个讲座参加个沙龙什么的，在上海就一点胃口都没有了。要不以后还是找机会去北京吧，但是去干什么呢，真无聊。&lt;/p&gt;

&lt;p&gt;今天突然冒出个想法，要是能重活一次的话，我马上毫不犹豫爬到楼顶跳下来reset。&lt;/p&gt;

&lt;p&gt;好在好在，明天就回公司了，要试着配一下ehcache的集群，有时间的话比较一下RMI方式和JGroup方式的性能，再做下一步打算。对了，还有一个系统的维护指南要写。&lt;/p&gt;

&lt;p&gt;我还是想想明天上班Syu时的情景吧。&lt;/p&gt;

&lt;p&gt;PS:此文是用Firefox的插件ScribeFire发的&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Mapping Geometry in Grails and MySQL</title><link href="https://sunng.info/blog/mapping-geometry-in-grails-and-mysql.html" rel="alternate"></link><published>2009-10-06T00:00:00+08:00</published><updated>2009-10-06T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-10-06:/blog/mapping-geometry-in-grails-and-mysql.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;GIS&lt;/li&gt;
&lt;li&gt;Grails&lt;/li&gt;
&lt;li&gt;groovy&lt;/li&gt;
&lt;li&gt;jts&lt;/li&gt;
&lt;li&gt;mysql
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;针对地理数据的ORM，有一个Hibernate的扩展&lt;a href="http://www.hibernatespatial.org/" target="_blank"&gt;HibernateSpatial&lt;/a&gt;项目可以将JTS对象映射到MySQL/PostGIS/Oracle中。这个扩展同样可以用在Grails里，这里有一篇简 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;GIS&lt;/li&gt;
&lt;li&gt;Grails&lt;/li&gt;
&lt;li&gt;groovy&lt;/li&gt;
&lt;li&gt;jts&lt;/li&gt;
&lt;li&gt;mysql
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;针对地理数据的ORM，有一个Hibernate的扩展&lt;a href="http://www.hibernatespatial.org/" target="_blank"&gt;HibernateSpatial&lt;/a&gt;项目可以将JTS对象映射到MySQL/PostGIS/Oracle中。这个扩展同样可以用在Grails里，这里有一篇简单的介绍，关于在Grails和MySQL中管理地理数据：
&lt;a href="http://www.grails.org/MySQL+GIS-Geometry+with+Grails" target="_blank"&gt;http://www.grails.org/MySQL+GIS-Geometry+with+Grails&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;不过按照这个文章里介绍的方法用，很可能会遭遇这样的报错：
&lt;blockquote&gt;org.hibernate.MappingException: No Dialect mapping for JDBC type: 2003&lt;/blockquote&gt;
这个问题最终在这里得到了解答：
&lt;a href="http://n2.nabble.com/No-Dialect-mapping-for-JDBC-type-2003-td1141106.html" target="_blank"&gt;http://n2.nabble.com/No-Dialect-mapping-for-JDBC-type-2003-td1141106.html&lt;/a&gt;
按照邮件列表里的反映，上面的配置在Postgis里是可以work的，但是如果用Mysql还需要指定JPA的columnDefinition，对应的Hibernate属性是sql-type。虽然作者承诺会在今后的版本里修改这个问题，不过眼下的M2版本还没有修正这个问题。为此，Grails的用户特地提出在Grails中加入sql-type的支持：
&lt;a href="http://jira.codehaus.org/browse/GRAILS-3201" target="_blank"&gt;http://jira.codehaus.org/browse/GRAILS-3201&lt;/a&gt;
现在按照下面文档的说明，可以在mapping里指定sqlType了：
&lt;a href="http://grails.org/doc/latest/ref/Database%20Mapping/column.html" target="_blank"&gt;http://grails.org/doc/latest/ref/Database%20Mapping/column.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;实例代码里的domain定义应该改成：
&lt;pre class="brush:groovy"&gt;
import com.vividsolutions.jts.geom.Polygon
import org.hibernatespatial.GeometryUserType&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;public class MyPoly {&lt;br /&gt;
    String name&lt;br /&gt;
    Polygon poly&lt;/p&gt;

&lt;p&gt;    static mapping = {&lt;br /&gt;
        poly type: GeometryUserType， sqlType:"GEOMETRY"&lt;br /&gt;
    }&lt;/p&gt;

&lt;p&gt;}

于是，再也没有莫名其妙的No Dialect报错了。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Reading GPS Data From EXIF Using Groovy</title><link href="https://sunng.info/blog/reading-gps-data-from-exif-using-groovy.html" rel="alternate"></link><published>2009-10-05T00:00:00+08:00</published><updated>2009-10-05T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-10-05:/blog/reading-gps-data-from-exif-using-groovy.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;GIS&lt;/li&gt;
&lt;li&gt;groovy&lt;/li&gt;
&lt;li&gt;java
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;废话不说了，直接上code吧：&lt;/p&gt;

&lt;p&gt;&lt;pre class="brush:groovy"&gt;
import com.drew.imaging.jpeg.*;
import com.drew.metadata.*;
import com.drew.metadata.exif.*;&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;file = new File("sample.jpg");&lt;br /&gt;
meta = JpegMetadataReader.readMetadata(file);&lt;/p&gt;

&lt;p&gt;gpsdir = meta.getDirectory(GpsDirectory.class);&lt;br /&gt;
lat = gpsdir.getRationalArray …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;GIS&lt;/li&gt;
&lt;li&gt;groovy&lt;/li&gt;
&lt;li&gt;java
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;废话不说了，直接上code吧：&lt;/p&gt;

&lt;p&gt;&lt;pre class="brush:groovy"&gt;
import com.drew.imaging.jpeg.*;
import com.drew.metadata.*;
import com.drew.metadata.exif.*;&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;file = new File("sample.jpg");&lt;br /&gt;
meta = JpegMetadataReader.readMetadata(file);&lt;/p&gt;

&lt;p&gt;gpsdir = meta.getDirectory(GpsDirectory.class);&lt;br /&gt;
lat = gpsdir.getRationalArray(GpsDirectory.TAG_GPS_LATITUDE);&lt;br /&gt;
lon = gpsdir.getRationalArray(GpsDirectory.TAG_GPS_LONGITUDE);&lt;br /&gt;
lats = lat[0].doubleValue() + lat[1].doubleValue()/60 + lat[2].doubleValue()/3600;&lt;br /&gt;
lons = lon[0].doubleValue() + lon[1].doubleValue()/60 + lon[2].doubleValue()/3600;&lt;br /&gt;
println(lats);&lt;br /&gt;
println(lons);
&lt;/p&gt;

&lt;p&gt;metadata-extractor似乎是现在惟一的读取exif信息的java库。在ivy中可以添加这样一个依赖：&lt;/p&gt;

&lt;p&gt;&lt;pre class="brush:xml"&gt;
&amp;lt;dependency org=&amp;quot;com.drewnoakes&amp;quot; name=&amp;quot;metadata-extractor&amp;quot; rev=&amp;quot;2.4.0-beta-1&amp;quot; conf=&amp;quot;runtime&amp;quot;/&amp;gt;
&lt;/pre&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Gnome Shell</title><link href="https://sunng.info/blog/gnome-shell.html" rel="alternate"></link><published>2009-10-04T00:00:00+08:00</published><updated>2009-10-04T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-10-04:/blog/gnome-shell.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;续昨，好吧，这个无聊的国庆假期就献给karmic了。gnome-shell已经可以在软件仓库里找到了，apt-get安装即可。要使用gnome-shell可以执行
&lt;em&gt;gnome-shell --replace&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;要把gnome-shell作为默认的窗口管理器，打开gnome的配置编辑器，找到/desktop/gnome/session/required_components，把panel和windown-manager全部改成gnome-shell，重 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;续昨，好吧，这个无聊的国庆假期就献给karmic了。gnome-shell已经可以在软件仓库里找到了，apt-get安装即可。要使用gnome-shell可以执行
&lt;em&gt;gnome-shell --replace&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;要把gnome-shell作为默认的窗口管理器，打开gnome的配置编辑器，找到/desktop/gnome/session/required_components，把panel和windown-manager全部改成gnome-shell，重新登录即可。&lt;/p&gt;

&lt;p&gt;上个图：
&lt;img class="alignnone" src="http://pic.yupoo.com/classicning/0026182bb7d6/medium.jpg" alt="" width="500" height="313" /&gt;&lt;/p&gt;

&lt;p&gt;当然了，还没有正式release，大家试用尝鲜注意安全第一。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>'libchamplain: Map Widget of Gnome Desktop'</title><link href="https://sunng.info/blog/libchamplain-map-widget-of-gnome-desktop.html" rel="alternate"></link><published>2009-10-03T00:00:00+08:00</published><updated>2009-10-03T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-10-03:/blog/libchamplain-map-widget-of-gnome-desktop.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;GIS&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;linux
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;续昨，虽然昨天升级到了gnome2.28，也安装了libchamplain，但是却没有发现这个库应用。Ubuntu上的Empathy似乎是没有把location support编译进去。看不到效果怎么办 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;GIS&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;linux
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;续昨，虽然昨天升级到了gnome2.28，也安装了libchamplain，但是却没有发现这个库应用。Ubuntu上的Empathy似乎是没有把location support编译进去。看不到效果怎么办，好在&lt;a href="http://www.pierlux.com/" target="_blank"&gt;libchamplain的作者&lt;/a&gt;还提供了另一个应用。这是一个eog的插件，用于读取照片的exif信息中的gps latitude和longitude，进而将他显示在libchamplain的地图里。&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" src="http://pic.yupoo.com/classicning/2698882a6ac0/medium.jpg" alt="" width="500" height="343" /&gt;&lt;/p&gt;

&lt;p&gt;安装这个插件可以follow&lt;a href="http://live.gnome.org/EyeOfGnome/Plugins" target="_blank"&gt;官方网站上的做法&lt;/a&gt;，当然开发包、头文件之类的东西要齐备。&lt;/p&gt;

&lt;p&gt;libchamplain使用了&lt;a href="http://www.clutter-project.org/" target="_blank"&gt;clutter&lt;/a&gt;，在载入地图的时候还有fadein的效果。clutter是将要应用在下一代桌面gnome shell中的图形特效库。&lt;/p&gt;

&lt;p&gt;插件是用C写的，可以看成是libchamplain的一个例子，作者说已经有了其他语言的binding，如此未来在gnome桌面上开发tile地图程序会变得简单。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Upgrade to Karmic</title><link href="https://sunng.info/blog/upgrade-to-karmic.html" rel="alternate"></link><published>2009-10-02T00:00:00+08:00</published><updated>2009-10-02T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-10-02:/blog/upgrade-to-karmic.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;今天凌晨Ubuntu 9.10 Karmic的beta发布了，实在忍耐不到月底了，上午就update-manager -d升级了发行版。从下在到安装完成用了大约三个小时，当 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;今天凌晨Ubuntu 9.10 Karmic的beta发布了，实在忍耐不到月底了，上午就update-manager -d升级了发行版。从下在到安装完成用了大约三个小时，当然升级不用花什么精力，只要让程序自动去处理就好了，现在跨版本升级越来越轻松了。&lt;/p&gt;

&lt;p&gt;配好以后感觉还是很值得升级的，大概多半是因为Kernel 2.6.31，这次桌面性能的提升真的非常大，之前从来没有这么明显的感受。举个例子，很惭愧，我这机器在上一个9.04版本里alt-table的Application Switcher都还会卡，现在已经一如之前不开compositing时的性能了。&lt;/p&gt;

&lt;p&gt;既然是ubuntu，还是要提一下面子上的事情。GDM有变化，按照release notes上说的是全部重写了。在从gdm登录到桌面载入之间有了一个loading效果，体验很不错。然后是Ubuntu主题变了，human不再是黄的了，变成了深褐色，有了新的图标主题humanity，尤其是在桌面的右上角区域很有特点。&lt;/p&gt;

&lt;p&gt;再有就是Gnome2.28的变化还有Ubuntu对gnome相关的增强了，这次激进了一些，居然赶在arch更新之前在ubuntu上体验了gnome的新版本。主要有几个变化：&lt;/p&gt;

&lt;p&gt;indicator变得更强大了，现在indicator和evolution empathy gwibber等等软件有了更好的集成，在indicator-applet里可以看到这些软件的新消息通知，另外indicator和原来Ubuntu的session manager也集成了，可以通过session manager设置empathy的状态。&lt;/p&gt;

&lt;p&gt;empathy变化很大，据说已经支持xmpp的geolocation extension了，早先我就转载过一个在osm上显示xmpp好友位置的截图。不过这个功能似乎又没有在ubuntu的版本里找到，可能ubuntu这边还是有点谨慎吧。倒是geoclue这个框架已经被加进apt-get了，可以看出相关的工作确实是在进行中。&lt;/p&gt;

&lt;p&gt;epiphany开始用webkit内核了，这样可以把midori彻底踢开了，epiphany也算是找到了自己在linux桌面上存在的意义。empathy可以抢走pidgin的位置，epiphany想强firefox的位置还是挺难的。&lt;/p&gt;

&lt;p&gt;还有Ubuntu似乎修改了gnome桌面gnome-panel/panel-menu-item.c里面的参数，之前当places里的书签数大于5之后会产生一个Bookmarks子菜单，现在这个数值似乎变成了8，当然只是猜测，因为gnome的代码里这个值仍然是5。发现这个变化的过程实在比较无聊。。。&lt;/p&gt;

&lt;p&gt;据说2.28的Evince看书的时候记annotation了，不过我好像没有发现这个功能，Gnome的release notes上也只是提了一下并没有发截图。&lt;/p&gt;

&lt;p&gt;我还是很感激每半年就有很多新特性可以去discover的，这种感觉很不错。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>A shorten url app</title><link href="https://sunng.info/blog/a-shorten-url-app.html" rel="alternate"></link><published>2009-09-30T00:00:00+08:00</published><updated>2009-09-30T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-09-30:/blog/a-shorten-url-app.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;css&lt;/li&gt;
&lt;li&gt;django&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;一不小心又是大概有一年多时间没有用过Django了。最近经常有之前部门的同事来讨论Django和Python Web开发，大概是他 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;css&lt;/li&gt;
&lt;li&gt;django&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;一不小心又是大概有一年多时间没有用过Django了。最近经常有之前部门的同事来讨论Django和Python Web开发，大概是他们用asp做的客服系统终于要换架构了。今天国庆提前放假，用了一个下午的时间重温一下。Django的Template还是相对弱一些，居然无法在template里调用model的方法。这样auth模块里判断用户登录的is_authticated()方法就不能在template里调，如此非得在view判断（Django是MTV模型，它的view即MVC里的controller），那如果是用generic_view，这个似乎就无解了？？&lt;/p&gt;

&lt;p&gt;贴个图。体验一下Firefox3.5开始支持的@font-face，Webkit核心和Opera浏览器都支持这个功能。不过我Ubuntu上的Midori貌似遇到Mootools就直接崩溃，看来要等到gnome2.28里epiphany了。&lt;img class="alignnone" src="http://pic.yupoo.com/classicning/65666826c591/medium.jpg" alt="" width="500" height="365" /&gt;&lt;/p&gt;

&lt;p&gt;忘了提了，域名是我yy的，之所以有这样的效果是修改了hosts&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>首页更新</title><link href="https://sunng.info/blog/shou-ye-geng-xin.html" rel="alternate"></link><published>2009-09-29T00:00:00+08:00</published><updated>2009-09-29T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-09-29:/blog/shou-ye-geng-xin.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;jsonp&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;半年没碰那个首页了，最近看到commandlinefu提供了jsonp接口，顺手把commandlinefu加了进来：
&lt;em&gt;Classicning7.clifu();&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;这个程序是去年此时还在用JavaScript工作的时候写的，隔了一年再看，很难 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;jsonp&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;半年没碰那个首页了，最近看到commandlinefu提供了jsonp接口，顺手把commandlinefu加了进来：
&lt;em&gt;Classicning7.clifu();&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;这个程序是去年此时还在用JavaScript工作的时候写的，隔了一年再看，很难不感慨啊。话说，大家要是发现一些好玩的，好用的JSONP服务可以分享一下。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>'0:2'</title><link href="https://sunng.info/blog/02.html" rel="alternate"></link><published>2009-09-27T00:00:00+08:00</published><updated>2009-09-27T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-09-27:/blog/02.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;football&lt;/li&gt;
&lt;li&gt;job&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;以下是今天的闲言碎语。
&lt;ol&gt;
    &lt;li&gt;其实中超真挺好看的，我不是说球，纯技术的欣赏还是交给伪球迷吧。群雄 …&lt;/li&gt;&lt;/ol&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;football&lt;/li&gt;
&lt;li&gt;job&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;以下是今天的闲言碎语。
&lt;ol&gt;
    &lt;li&gt;其实中超真挺好看的，我不是说球，纯技术的欣赏还是交给伪球迷吧。群雄逐鹿，乱七八糟，踢球的踢球，骂人的骂人。&lt;a href="http://sports.sina.com.cn/j/2009-09-27/14304607690.shtml" target="_blank"&gt;广州的徐亮&lt;/a&gt;应该算是现在国内足坛最TM有个性的球员了，有点当年范志毅郝海东的意思了，可惜这样的球员还是少，多点的话就更热闹了。舜天队还是那个样子，今天的比赛看了半场，也不像是不想赢，但是派个&lt;a href="http://www.rodolfoalemao.com.br/" target="_blank"&gt;傻大个&lt;/a&gt;在前面挥霍机会，难道是轻敌了？下半场据说是风云突变，反正最后黑色三分钟，就冲这个架势，看来夺冠还是纯YY。&lt;/li&gt;
    &lt;li&gt;办人事档案的手续，发现同样是纳爱斯，也有真纳爱斯和假纳爱斯，或者说是伪纳爱斯。同样是春风，有的是吹面不寒杨柳风，有的是二月春风似剪刀，后者用范伟的话说，咔咔的。说来说去还是家乡人淳朴，不像那个哪，看谁都跟骗子一样。&lt;/li&gt;
    &lt;li&gt;看建国大业了，豆瓣上有个说法叫视频幻灯片，很典型。印象深的，除了某幕后人员，配角里就属最后刘德华，因为看了两遍。国军衣服就是好看，当时那张脸就是典型的“如果说一切都是天意一切都是命运很早已注定”，苦啊，在肚子里。不说电影本身了，倒是想想这个事情，从井冈山被打得到处跑，到胡宗南占了延安又是到处跑，无数的挫折、看似没有希望，从一开始到最后成功漫长的20年，且不跟你们讨论这件事到底怎么样，但是把它当成一个人或者说一个团体的事业，实在是壮观壮丽。&lt;/li&gt;
    &lt;li&gt;回上海，在火车站看到广告说“学习北京奥运会，办好上海世博会”，说实在的，真学的话，要是地铁能两块钱随便坐，全国人民也请上海人民和上海黑户坐一年地铁那是最好了。估计是不可能了，怎么办，唯有调整心态：话说我每天都请北京的谁谁还有谁谁谁坐地铁坐公交，有这种心态，刷卡过闸机的时候动作会相对潇洒一些。&lt;/li&gt;
    &lt;li&gt;工作那边，要准备申请立个项目，项目的点子是部门的负责人想的，咱来do it。让个应届生当PM又不靠谱，架构师来挂个名，可是人家忙得要死，只能深情款款地对我说，开发暂时就你一个人。现在项目组变成运维、测试、项管群殴我一个，就着这两天的所见所闻，大约也能想象今年剩下几个月的日子。&lt;/li&gt;
    &lt;li&gt;又到了一年一度的recruitment，看着大家跃跃欲试，我在这里祝大家过得比我好。&lt;/li&gt;
&lt;/ol&gt;
洗完澡回来补一枪，上周买了一本&lt;a href="http://www.douban.com/subject/3924175/" target="_blank"&gt;这个&lt;/a&gt;，周五在火车上看了前面一两章，感觉还不错，尤其是压力测试方面解决了我最近的一些疑问。同时我也感觉到写这么一本书确实要有一大瓶水和可观的时间和精力，这个还是很难的。熟悉俺的人都知道出本误人子弟的书是俺长久以来的梦想，不过看了&lt;a href="http://bbs.51js.com/viewthread.php?tid=85328&amp;amp;extra=&amp;amp;page=1" target="_blank"&gt;这位28本书的作者&lt;/a&gt;的状况，我这个梦想离照进现实还是有距离的。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Performance Visualization with Gnuplot, continued</title><link href="https://sunng.info/blog/performance-visualization-with-gnuplot-continued.html" rel="alternate"></link><published>2009-09-24T00:00:00+08:00</published><updated>2009-09-24T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-09-24:/blog/performance-visualization-with-gnuplot-continued.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;gnuplot&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;shell&lt;/li&gt;
&lt;li&gt;vmstat
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;After I post my command to &lt;a href="http://www.commandlinefu.com/" target="_blank"&gt;commandlinefu.com&lt;/a&gt;, there is an alternative command followed:
&lt;em&gt;(echo "set terminal png;plot '-' u 1:2 t 'cpu' w linespoints;"; sudo vmstat 2 10 | awk 'NR &amp;gt; 2 {print NR, $13}') | gnuplot &amp;gt; plot …&lt;/em&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;gnuplot&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;shell&lt;/li&gt;
&lt;li&gt;vmstat
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;After I post my command to &lt;a href="http://www.commandlinefu.com/" target="_blank"&gt;commandlinefu.com&lt;/a&gt;, there is an alternative command followed:
&lt;em&gt;(echo "set terminal png;plot '-' u 1:2 t 'cpu' w linespoints;"; sudo vmstat 2 10 | awk 'NR &amp;gt; 2 {print NR, $13}') | gnuplot &amp;gt; plot.png&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This one is appreciated for using pipes and redirection. It works on Ubuntu Jaunty, on which, gnuplot doesn't provide a -e option. The author also showed an link to an open source project 'vmplot' on &lt;a href="http://freshmeat.net/projects/vmplot/" target="_blank"&gt;freshmeat&lt;/a&gt;. Vmplot is written in python, which combines parameters with scripts and end up with an os.system call. Grab the source from &lt;a href="http://elvis.elte.hu/~fuji/stuff/vmplot.py" target="_blank"&gt;here&lt;/a&gt;. (Why use python instead on a simple bash command? because it's difficult to draw a multiplot without temp file or variables, you cannot read '-' twice.)&lt;/p&gt;

&lt;p&gt;There are several people vote me down for my command! It must be primers whose use ubuntu and knows the world via Synaptic only, because they failed to run the command on their dated machine (*_*)&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Performance Visualization with Gnuplot</title><link href="https://sunng.info/blog/performance-visualization-with-gnuplot.html" rel="alternate"></link><published>2009-09-23T00:00:00+08:00</published><updated>2009-09-23T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-09-23:/blog/performance-visualization-with-gnuplot.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;awk&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;gnuplot&lt;/li&gt;
&lt;li&gt;linux
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Gnuplot is considered to be one of the most famous plotting tools on both linux and windows. With Gnuplot, generation of charts becomes agile and easy. Gnuplot supports sorts of terminals range from gui, image to printer. To enable …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;awk&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;gnuplot&lt;/li&gt;
&lt;li&gt;linux
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Gnuplot is considered to be one of the most famous plotting tools on both linux and windows. With Gnuplot, generation of charts becomes agile and easy. Gnuplot supports sorts of terminals range from gui, image to printer. To enable png terminal support, we will build gnuplot with following steps. (However, on most linux distributions you don't have to do these manually, install gnuplot with package manager is doubtlessly the best choice for most cases.)&lt;/p&gt;

&lt;p&gt;Well, for a naked machine, it won't be too long to type commands in gnuplot.
&lt;h3&gt;Installing libpng&lt;/h3&gt;
linpng provides functionality to write png images.&lt;br /&gt;
Grab source form libpng's website:
&lt;em&gt;wget http://downloads.sourceforge.net/project/libpng/00-libpng-stable/1.2.39/libpng-1.2.39.tar.gz?use_mirror=ncu&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Uncompress the package:
&lt;em&gt;tar xfz libpng-1.2.39.tar.gz&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;cd to result directory and build it:
&lt;em&gt;cd libpng-1.2.39&lt;br /&gt;
./configure&lt;br /&gt;
make&lt;br /&gt;
make install&lt;/em&gt;
&lt;h3&gt;Installing libgd&lt;/h3&gt;
gd provides api for programmer to draw images like Graphics2d in java. Gnuplot uses gd to draw charts on images.&lt;br /&gt;
Grab source from gd's website:
&lt;em&gt;wget http://www.libgd.org/releases/gd-2.0.35.tar.gz&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Uncompress it:
&lt;em&gt;tar xfz gd-2.0.35.tar.gz&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;build it:
&lt;em&gt;cd gd-2.0.35&lt;br /&gt;
./configure&lt;br /&gt;
make&lt;br /&gt;
make install&lt;/em&gt;
&lt;h3&gt;Compiling and building Gnuplot&lt;/h3&gt;
Grab source from sourceforge:
&lt;em&gt;wget http://downloads.sourceforge.net/project/gnuplot/gnuplot/4.2.6/gnuplot-4.2.6.tar.gz?use_mirror=ncu&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Uncompress and compile:
&lt;em&gt;tar xfz gnuplot-4.2.6.tar.gz&lt;br /&gt;
cd gnuplot&lt;br /&gt;
./configure&lt;br /&gt;
make&lt;br /&gt;
make install&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;if "&lt;em&gt;'@LIBICONV@' not found&lt;/em&gt;" encountered during make, just edit &lt;em&gt;src/Makefile&lt;/em&gt; to replace &lt;em&gt;-l@LIBICONV@&lt;/em&gt; to &lt;em&gt;-liconv&lt;/em&gt;, and make again.&lt;br /&gt;
Try to start gnuplot simply with:
&lt;em&gt;gnuplot&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now you may be stopped by error message like&lt;em&gt; libiconv.so.2&lt;/em&gt; not found, then you have to copy it from somewhere(such as&lt;em&gt; /usr/local/lib&lt;/em&gt;) to&lt;em&gt; /usr/lib&lt;/em&gt;, the it will work.
&lt;h3&gt;Using Gnuplot&lt;/h3&gt;
As the installation completed, we start to use it.&lt;/p&gt;

&lt;p&gt;vmstat is a great utility shows you the status of your process queue, memory, swap, io rates, interrupts and cpu usage. With command-line arguments, we can dump these data in a fixed interval and fixed count:
&lt;em&gt;vmstat 5 10&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;awk is a well-known editor to analyze and extract text from input, we can filter specified fields with awk like this:
&lt;em&gt;vmstat 5 10 | awk 'NR &amp;gt; 2 {print NR-2, $13}'&lt;/em&gt;
The header was ignored by the conditional expression NR&amp;gt;2 which NR stands for Number-of-Row&lt;/p&gt;

&lt;p&gt;The output is just enough for gnuplot, now use pipe to connect them together:
&lt;em&gt;vmstat 5 10 | awk 'NR &amp;gt; 2 {print NR, $13}' | gnuplot -e "set terminal png; set output 'v.png'; plot '-' u 1:2 t 'cpu' w linespoints;"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To gather more information at one time, a more complex command is needed:
&lt;em&gt;vmstat 10 360 | awk 'NR &amp;gt; 2 {print NR, $4, $9, $10, $13}' &amp;gt; vmstat.dat ; gnuplot -e "set terminal png;set output 'vmstat.png';set grid; set multiplot;set size 0.5, 0.5;set origin 0, 0;plot 'vmstat.dat' u 1:2 t 'freemem' w linespoints;set size 0.5, 0.5;set origin 0.5, 0;plot 'vmstat.dat' u 1:3 t 'bi' w linespoints;set size 0.5, 0.5;set origin 0, 0.5;plot 'vmstat.dat' u 1:4 t 'bo' w linespoints;set size 0.5, 0.5;set origin 0.5, 0.5;plot 'vmstat.dat' u 1:5 t 'cpu' w linespoints;unset multiplot;"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Unfortunately, gnuplot in Ubuntu jaunty is at version of 4.2.4, which doesn't support -e option. So you cannot send gnuplot commands directly in command-line. The solution is split it to several steps:
&lt;em&gt;vmstat 10 360 | awk 'NR &amp;gt; 2 {print NR, $4, $9, $10, $13}' &amp;gt; vmstat.dat&lt;br /&gt;
gnuplot&lt;br /&gt;
&amp;gt;set terminal png;&lt;br /&gt;
&amp;gt;set output 'vmstat.png';&lt;br /&gt;
&amp;gt;set grid;&lt;br /&gt;
&amp;gt;set multiplot;&lt;br /&gt;
&amp;gt;set size 0.5, 0.5;&lt;br /&gt;
&amp;gt;set origin 0, 0;&lt;br /&gt;
&amp;gt;plot 'vmstat.dat' u 1:2 t 'freemem' w linespoints;&lt;br /&gt;
&amp;gt;set size 0.5, 0.5;&lt;br /&gt;
&amp;gt;set origin 0.5, 0;&lt;br /&gt;
&amp;gt;plot 'vmstat.dat' u 1:3 t 'bi' w linespoints;&lt;br /&gt;
&amp;gt;set size 0.5, 0.5;&lt;br /&gt;
&amp;gt;set origin 0, 0.5;plot 'vmstat.dat' u 1:4 t 'bo' w linespoints;&lt;br /&gt;
&amp;gt;set size 0.5, 0.5;&lt;br /&gt;
&amp;gt;set origin 0.5, 0.5;&lt;br /&gt;
&amp;gt;plot 'vmstat.dat' u 1:5 t 'cpu' w linespoints;&lt;br /&gt;
&amp;gt;unset multiplot;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This picture is a stat result of performance test this afternoon&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" src="http://pic.yupoo.com/classicning/3794181d957c/medium.jpg" alt="" width="500" height="375" /&gt;&lt;/p&gt;

&lt;p&gt;For more detailed tutorials, check the article on ibm dw:
&lt;a href="http://www.ibm.com/developerworks/library/l-gnuplot/" target="_blank"&gt;http://www.ibm.com/developerworks/library/l-gnuplot/&lt;/a&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Some python segments</title><link href="https://sunng.info/blog/some-python-segments.html" rel="alternate"></link><published>2009-09-22T00:00:00+08:00</published><updated>2009-09-22T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-09-22:/blog/some-python-segments.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;pre class="brush:python"&gt;
import MySQLdb
import subprocess
from smtplib import *
from datetime import date, timedelta&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;class StatEntry(object):&lt;br /&gt;
    def __init__(self, name, count):&lt;br /&gt;
        self.name = name&lt;br /&gt;
        self.count = count&lt;br /&gt;
    def __str__(self):&lt;br /&gt;
        return "%s\t%d" % (self.name, self.count)&lt;/p&gt;

&lt;p&gt;def fetchdb(date):&lt;br /&gt;
    conn = MySQLdb …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;pre class="brush:python"&gt;
import MySQLdb
import subprocess
from smtplib import *
from datetime import date, timedelta&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;class StatEntry(object):&lt;br /&gt;
    def __init__(self, name, count):&lt;br /&gt;
        self.name = name&lt;br /&gt;
        self.count = count&lt;br /&gt;
    def __str__(self):&lt;br /&gt;
        return "%s\t%d" % (self.name, self.count)&lt;/p&gt;

&lt;p&gt;def fetchdb(date):&lt;br /&gt;
    conn = MySQLdb.connect(host="localhost", user="root", passwd="acd", db="cls")&lt;br /&gt;
    cursor = conn.cursor()&lt;br /&gt;
    cursor.execute("SELECT link, count(*) FROM sdocom WHERE DATE(logtime) = DATE('%s') GROUP BY link" % date)&lt;br /&gt;
    rows = cursor.fetchall()&lt;br /&gt;
    result = map(lambda x: StatEntry(*x), rows)&lt;br /&gt;
    return result&lt;/p&gt;

&lt;p&gt;def archive(datelist):&lt;br /&gt;
    for date in datelist:&lt;br /&gt;
        subprocess.call(["gzip", "cls-"+date+".log"])&lt;/p&gt;

&lt;p&gt;def sendmail(results):&lt;br /&gt;
    mailcontent = reduce(lambda x,y: x+str(y)+"\n", results, "")&lt;br /&gt;
    smtp = SMTP("your.smtp.server")&lt;br /&gt;
    smtp.login("user","passwd")&lt;br /&gt;
    smtp.sendmail("cls-no-reply@smtp.server", ["your@mail.server"], mailcontent)&lt;br /&gt;
    smtp.quit()&lt;/p&gt;

&lt;p&gt;def dumpall(data):&lt;br /&gt;
    content = reduce(lambda x,y: x+str(y)+"\n", data, "")&lt;br /&gt;
    print content&lt;/p&gt;

&lt;p&gt;def main():&lt;br /&gt;
    yesterday = str(date.today()-timedelta(1))&lt;br /&gt;
    results = fetchdb(yesterday)&lt;br /&gt;
    #dumpall(results)&lt;br /&gt;
    sendmail(results)&lt;br /&gt;
    archive([yesterday])&lt;/p&gt;

&lt;p&gt;if __name__ == '__main__':&lt;br /&gt;
    main()
&lt;/p&gt;

&lt;p&gt;As log file was imported to database automatically by other process, the script retrieves statistical data from mysql db and sends the result to your mail box via smtp. Also, log file would be compressed by gzip to minimize storage overhead. Add it to crontab, you will get daily statistical report.&lt;/p&gt;

&lt;p&gt;This example shows you basic usage of mysql-python lib, smtplib and how to calculate yesterday by datetime.&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>烧饭再</title><link href="https://sunng.info/blog/shao-fan-zai.html" rel="alternate"></link><published>2009-09-20T00:00:00+08:00</published><updated>2009-09-20T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-09-20:/blog/shao-fan-zai.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;food&lt;/li&gt;
&lt;li&gt;Life&lt;/li&gt;
&lt;li&gt;shaofan
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://blog.samsonis.me/2009/09/update-20/" target="_blank"&gt;看了烧饭1，就想看烧饭2&lt;/a&gt;.可是我思前想后，烧饭2这个标题总有那么一点不太对劲（大家自己体会），于是还是叫烧饭再吧。&lt;/p&gt;

&lt;p&gt;今天介绍 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;food&lt;/li&gt;
&lt;li&gt;Life&lt;/li&gt;
&lt;li&gt;shaofan
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://blog.samsonis.me/2009/09/update-20/" target="_blank"&gt;看了烧饭1，就想看烧饭2&lt;/a&gt;.可是我思前想后，烧饭2这个标题总有那么一点不太对劲（大家自己体会），于是还是叫烧饭再吧。&lt;/p&gt;

&lt;p&gt;今天介绍酱爆鸡丁，娘的，在北京的夏天吃这个吃得都快吐了，时隔一年本人亲自操刀上阵，不为别的，只为再吐一次。&lt;/p&gt;

&lt;p&gt;用料：鸡胸肉一块，黄瓜一根，甜面酱，葱，料酒，酱油，盐，味精，淀粉，糖，自来水，菜刀，案板，锅，电磁炉，油烟机，自行车。。。&lt;/p&gt;

&lt;p&gt;主要步骤如下：
&lt;ol&gt;
    &lt;li&gt;鸡肉化冻，黄瓜大葱洗干净；&lt;/li&gt;
    &lt;li&gt;鸡肉切丁，大小以大块熟了小块还没有糊为宜，放碗里加淀粉、盐，抓之；&lt;/li&gt;
    &lt;li&gt;黄瓜切丁，大小自便，装盘；&lt;/li&gt;
    &lt;li&gt;葱切段，葱的用量大约根据以下公式计算： 葱用量=家里储备葱的总长度/未来可预计的几天內做菜总次数；&lt;/li&gt;
    &lt;li&gt;小碗装酱油、料酒、味精、水淀粉、糖搅和成汁备用；&lt;/li&gt;
    &lt;li&gt;开火倒油加热冒烟，把1/3葱和鸡丁扔进锅里，炒到洁白的鸡丁如嚼过的口香糖一般出锅；&lt;/li&gt;
    &lt;li&gt;锅里留一些油，油少了可以再加一些，待油热，放两勺甜面酱（这里用的是李锦记的沪式甜酱，这个味道比较符合本人年幼时候对甜面酱的最初记忆），把剩下的葱一起扔锅里炒到寂寞为止；&lt;/li&gt;
    &lt;li&gt;加黄瓜丁，盐，炒，加鸡丁，炒，加第5步准备的汁，炒；&lt;/li&gt;
    &lt;li&gt;等到粘稠之后即可出锅，如果没有粘稠也见好就收吧；&lt;/li&gt;
    &lt;li&gt;#&lt;/li&gt;
&lt;/ol&gt;
有图不一定是真相：
&lt;img class="alignnone" title="-" src="http://pic.yupoo.com/classicning/484888192078/medium.jpg" alt="" width="500" height="336" /&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>GeoHash</title><link href="https://sunng.info/blog/geohash.html" rel="alternate"></link><published>2009-09-19T00:00:00+08:00</published><updated>2009-09-19T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-09-19:/blog/geohash.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;GIS&lt;/li&gt;
&lt;li&gt;ruby
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;今天偶然看到一个Ruby的GeoHash库，功能非常简单，就是将经纬度坐标哈希成字符串，并且，利用这个字符串可以粗 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;GIS&lt;/li&gt;
&lt;li&gt;ruby
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;今天偶然看到一个Ruby的GeoHash库，功能非常简单，就是将经纬度坐标哈希成字符串，并且，利用这个字符串可以粗略地比较两点的距离。这个库的代码host在GitHub上，要安装，需要将github加入gem：
&lt;em&gt;sudo gem sources -a http://gems.github.com&lt;br /&gt;
sudo gem install davotroy-geohash&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;用法，irb：
&lt;em&gt;irb(main):001:0&amp;gt; require 'rubygems'&lt;br /&gt;
=&amp;gt; true&lt;br /&gt;
irb(main):002:0&amp;gt; require 'geohash'&lt;br /&gt;
=&amp;gt; true&lt;br /&gt;
irb(main):003:0&amp;gt; GeoHash.encode(32.168, 118.54)&lt;br /&gt;
=&amp;gt; "wtsr12n0nj"&lt;br /&gt;
irb(main):004:0&amp;gt; GeoHash.decode('wtsr12n0nj')&lt;br /&gt;
=&amp;gt; [32.168, 118.54]&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;代码可以从github上获得
&lt;em&gt;git clone git://github.com/davetroy/geohash.git&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;GeoHash的核心部分用C实现，仔细看一下可以发现是一个四叉树的结构：
&lt;pre&gt;[codesyntax lang="c"]
#define BASE32  "0123456789bcdefghjkmnpqrstuvwxyz"
static void encode_geohash(double latitude, double longitude, int precision, char *geohash) {
    int is_even=1, i=0;
    double lat[2], lon[2], mid;
    char bits[] = {16,8,4,2,1};
    int bit=0, ch=0;&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt; lat[0] = -90.0;  lat[1] = 90.0;&lt;br /&gt;
    lon[0] = -180.0; lon[1] = 180.0;&lt;/p&gt;

&lt;p&gt; while (i &amp;lt; precision) {&lt;br /&gt;
        if (is_even) {&lt;br /&gt;
            mid = (lon[0] + lon[1]) / 2;&lt;br /&gt;
            if (longitude &amp;gt; mid) {&lt;br /&gt;
                ch |= bits[bit];&lt;br /&gt;
                lon[0] = mid;&lt;br /&gt;
            } else&lt;br /&gt;
                lon[1] = mid;&lt;br /&gt;
        } else {&lt;br /&gt;
            mid = (lat[0] + lat[1]) / 2;&lt;br /&gt;
            if (latitude &amp;gt; mid) {&lt;br /&gt;
                ch |= bits[bit];&lt;br /&gt;
                lat[0] = mid;&lt;br /&gt;
            } else&lt;br /&gt;
                lat[1] = mid;&lt;br /&gt;
        }&lt;/p&gt;

&lt;p&gt;     is_even = !is_even;&lt;br /&gt;
        if (bit &amp;lt; 4)&lt;br /&gt;
            bit++;&lt;br /&gt;
        else {&lt;br /&gt;
            geohash[i++] = BASE32[ch];&lt;br /&gt;
            bit = 0;&lt;br /&gt;
            ch = 0;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    geohash[i] = 0;&lt;br /&gt;
}&lt;/p&gt;

&lt;p&gt;[/codesyntax]

可惜在学校的时候没有看到这么精致的代码。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>RDF, Sparql query with python</title><link href="https://sunng.info/blog/rdf-sparql-query-with-python.html" rel="alternate"></link><published>2009-09-13T00:00:00+08:00</published><updated>2009-09-13T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-09-13:/blog/rdf-sparql-query-with-python.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;rdf
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;A simple query to test &lt;a href="http://en.wikipedia.org/wiki/Resource_Description_Framework"&gt;rdf&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/Sparql"&gt;sparql&lt;/a&gt;.
&lt;pre&gt;[codesyntax lang="python"]
import sys
import rdflib
from rdflib.Graph import ConjunctiveGraph&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;g = ConjunctiveGraph()&lt;/p&gt;

&lt;p&gt;g.parse(sys.argv[1], format="xml")&lt;/p&gt;

&lt;p&gt;fbns = rdflib.Namespace("http://rdf.freebase.com/ns/")&lt;/p&gt;

&lt;p&gt;player_refs = g.query("""SELECT …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;rdf
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;A simple query to test &lt;a href="http://en.wikipedia.org/wiki/Resource_Description_Framework"&gt;rdf&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/Sparql"&gt;sparql&lt;/a&gt;.
&lt;pre&gt;[codesyntax lang="python"]
import sys
import rdflib
from rdflib.Graph import ConjunctiveGraph&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;g = ConjunctiveGraph()&lt;/p&gt;

&lt;p&gt;g.parse(sys.argv[1], format="xml")&lt;/p&gt;

&lt;p&gt;fbns = rdflib.Namespace("http://rdf.freebase.com/ns/")&lt;/p&gt;

&lt;p&gt;player_refs = g.query("""SELECT ?player&lt;br /&gt;
        WHERE {&lt;br /&gt;
            ?root fb:soccer.football_roster_position.player ?player .&lt;br /&gt;
        }""", initNs={'fb':fbns})&lt;br /&gt;
players = []&lt;br /&gt;
for player in player_refs:&lt;br /&gt;
    gp = ConjunctiveGraph()&lt;br /&gt;
    gp.parse(player[0], format="xml")&lt;br /&gt;
    results = gp.query("""SELECT ?player_name ?player_position&lt;br /&gt;
        WHERE {&lt;br /&gt;
            ?player fb:type.object.name ?player_name .&lt;br /&gt;
            ?player fb:soccer.football_player.position_s ?player_position .&lt;br /&gt;
        }""", initNs={'fb':fbns})&lt;br /&gt;
    l = list(results)&lt;br /&gt;
    for r in l:&lt;br /&gt;
        if r[0].language == 'en':&lt;br /&gt;
            players.append(r)&lt;/p&gt;

&lt;p&gt;for player in players:&lt;br /&gt;
    print player&lt;/p&gt;

&lt;p&gt;[/codesyntax]
The query will return all player-position tuple from freebase's db for specified resource.&lt;br /&gt;
To query for players of AC Milan, execute the script with resource location:
&lt;em&gt;python rdfquery.py http://rdf.freebase.com/rdf/en/ac_milan&lt;/em&gt;
got:
&lt;em&gt;(rdflib.Literal('Yoann Gourcuff', language=u'en', datatype=None), rdflib.URIRef('http://rdf.freebase.com/ns/en.midfielder'))&lt;br /&gt;
(rdflib.Literal('Andrea Pirlo', language=u'en', datatype=None), rdflib.URIRef('http://rdf.freebase.com/ns/en.midfielder'))&lt;br /&gt;
(rdflib.Literal('Paolo Maldini', language=u'en', datatype=None), rdflib.URIRef('http://rdf.freebase.com/ns/en.defender'))&lt;br /&gt;
(rdflib.Literal('Gianluca Zambrotta', language=u'en', datatype=None), rdflib.URIRef('http://rdf.freebase.com/ns/en.defender'))&lt;br /&gt;
(rdflib.Literal('Davide Facchin', language=u'en', datatype=None), rdflib.URIRef('http://rdf.freebase.com/ns/guid.9202a8c04000641f8000000000c75e75'))&lt;br /&gt;
(rdflib.Literal('Marcus Plinio Diniz Paixao', language=u'en', datatype=None), rdflib.URIRef('http://rdf.freebase.com/ns/en.defender'))&lt;br /&gt;
...&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;All resources are stored as rdf:resource references. If you are also Milan fan like me, take a quick look at the players, well, the data is not up to date:)&lt;/p&gt;

&lt;p&gt;I have also tried to fetch data for my favorite local team.
&lt;em&gt;python rdfquery.py http://rdf.freebase.com/rdf/en/jiangsu_shuntian&lt;br /&gt;
(rdflib.Literal('Yao Hanlin', language=u'en', datatype=None), rdflib.URIRef('http://rdf.freebase.com/ns/en.midfielder'))&lt;/em&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>烧饭</title><link href="https://sunng.info/blog/shao-fan.html" rel="alternate"></link><published>2009-09-13T00:00:00+08:00</published><updated>2009-09-13T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-09-13:/blog/shao-fan.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;留影&lt;/li&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;food&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;首先再祝&lt;a href="http://blog.nulltao.net/"&gt;标题中的人&lt;/a&gt;生日快乐。&lt;/p&gt;

&lt;p&gt;虽说daf同学做菜好看好吃（看出奉承意思的读者请可以买棒棒糖奖励自己），但是这 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;留影&lt;/li&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;food&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;首先再祝&lt;a href="http://blog.nulltao.net/"&gt;标题中的人&lt;/a&gt;生日快乐。&lt;/p&gt;

&lt;p&gt;虽说daf同学做菜好看好吃（看出奉承意思的读者请可以买棒棒糖奖励自己），但是这不能成为本人自甘堕落自甘刷碗停滞不前的理由。况且，作为一个口味疑似诡异的人，如果不练好技术，掌握一技之长，将来很难在厨房立足，更不要提取得话语权。甚至没有在饭前进厨房的机会，下场会很惨。&lt;/p&gt;

&lt;p&gt;以上是关于烧饭必要性的论述。入正题，今天迎着正午的朝阳，从超市买回了青椒芹菜牛肉鸡肉，为了能让才华体现得淋漓尽致，做了两个菜。简述过程的话大抵是以下&lt;a href="http://blog.samsonis.me/tag/update/"&gt;update体&lt;/a&gt;流程：
&lt;ol&gt;
    &lt;li&gt;洗菜；&lt;/li&gt;
    &lt;li&gt;牛肉切丝（个中坎坷不作赘述），加料酒、淀粉、盐；&lt;/li&gt;
    &lt;li&gt;鸡肉切片（个中曲折不必了解），加蛋清、盐、淀粉；&lt;/li&gt;
    &lt;li&gt;芹菜切段；&lt;/li&gt;
    &lt;li&gt;小碗调酱油、醋、糖、料酒，备用；&lt;/li&gt;
    &lt;li&gt;葱姜切末备用；&lt;/li&gt;
    &lt;li&gt;开火倒油，油热了加葱姜末和辣椒酱（家里没有正经辣椒酱了，就用辣椒豆豉代替，此决定终遗千古恨），牛肉丝下锅，变色，取出；&lt;/li&gt;
    &lt;li&gt;炒芹菜；&lt;/li&gt;
    &lt;li&gt;牛肉芹菜混合，再炒，把第5步的混合液体倒进锅里，搅搅，加味精装盘；&lt;/li&gt;
    &lt;li&gt;洗锅；&lt;/li&gt;
    &lt;li&gt;开火倒油，油热了加葱姜末，鸡肉下锅，待变色后变糊前取出；&lt;/li&gt;
    &lt;li&gt;青椒撕碎下锅，不时加水，炒到上一步油烟被吸得差不多为止；&lt;/li&gt;
    &lt;li&gt;混合，加味精，出锅。&lt;/li&gt;
    &lt;li&gt;#&lt;/li&gt;
&lt;/ol&gt;
留张照片有图有真相：&lt;/p&gt;

&lt;p&gt;&lt;img title="food" src="http://pic.yupoo.com/classicning/829998100bcc/small.jpg" alt="" width="240" height="161" /&gt;&lt;/p&gt;

&lt;p&gt;关于照片中出现的“暗物质”，绝对不是因为鸡肉片切得太不均匀导致有的糊了有的还没熟，最后葱姜通通糊掉油烟满厨房的结果。还有为什么只有一张照片呢？原因是另一盘菜拍照构图的时候发现需要的曝光时间比较长，没有带三脚架所以拍不出理想的效果，故而没有把照片发上来。画外音：就是酱油放多了太黑了，画外音毕。画外音：把刚才那个画外音的拖出去砍了，画外音毕。&lt;/p&gt;

&lt;p&gt;最后引用一句名人名言，你永远不会知道自己的下一盘菜到底是什么味道。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>3 Screenshots</title><link href="https://sunng.info/blog/3-screenshots.html" rel="alternate"></link><published>2009-09-10T00:00:00+08:00</published><updated>2009-09-10T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-09-10:/blog/3-screenshots.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;game&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;jconsole&lt;/li&gt;
&lt;li&gt;VirtualBox
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;h3&gt;JConsole&lt;/h3&gt;
下午给验证码做压力测试，用JMX监控Tomcat性能情况，200并发。Tomcat的配置，initial memory pool是512M，Max是768M。在15:30时我手动执行了一次GC。&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" title="jconsole" src="http://pic.yupoo.com/classicning/8583480c7842/medium.jpg" alt="" width="500" height="417" /&gt;
&lt;h3&gt;Netpanzer&lt;/h3&gt;
跨平台的联机坦克游戏，今天才知道 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;game&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;jconsole&lt;/li&gt;
&lt;li&gt;VirtualBox
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;h3&gt;JConsole&lt;/h3&gt;
下午给验证码做压力测试，用JMX监控Tomcat性能情况，200并发。Tomcat的配置，initial memory pool是512M，Max是768M。在15:30时我手动执行了一次GC。&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" title="jconsole" src="http://pic.yupoo.com/classicning/8583480c7842/medium.jpg" alt="" width="500" height="417" /&gt;
&lt;h3&gt;Netpanzer&lt;/h3&gt;
跨平台的联机坦克游戏，今天才知道可以和Bot玩，来看看这种寂寞的玩法。&lt;br /&gt;
开一个terminal（或者用screen也可以）：
&lt;em&gt;netpanzer -d &lt;/em&gt;&lt;/p&gt;

&lt;p&gt;再开一个terminal，添加两个bot
&lt;em&gt;netpanzer -b localhost &amp;amp;&lt;br /&gt;
netpanzer -b localhost &amp;amp;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;自己进游戏
&lt;em&gt;netpanzer -c localhost&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;之前一直不知道，也怪自己，不知道打游戏也可以&lt;em&gt;--help&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" title="netpanzer" src="http://pic.yupoo.com/classicning/4416780c6f23/medium.jpg" alt="" width="500" height="378" /&gt;
&lt;h3&gt;VirtualBox&lt;/h3&gt;
VirtualBox 3.0.6 今天发布了，fix了从3.0.2开始与archLinux 2.6.30内核的冲突，阔别已久的arch在VirtualBox上终于又可以运行了，但是：&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" title="arch" src="http://pic.yupoo.com/classicning/4844580c74c5/medium.jpg" alt="" width="500" height="354" /&gt;
&lt;div id="_mcePaste" style="overflow: hidden; position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px;"&gt;Two Screenshots&lt;/div&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>梳理</title><link href="https://sunng.info/blog/shu-li.html" rel="alternate"></link><published>2009-09-06T00:00:00+08:00</published><updated>2009-09-06T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-09-06:/blog/shu-li.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;career&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;梳理一下。
&lt;ol&gt;
    &lt;li&gt;桂花陆续开了，昨天在衡山路一带飘的时候就问道久违的香味，今天在张江也闻到桂花 …&lt;/li&gt;&lt;/ol&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;career&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;梳理一下。
&lt;ol&gt;
    &lt;li&gt;桂花陆续开了，昨天在衡山路一带飘的时候就问道久违的香味，今天在张江也闻到桂花味道。于是乎，八月了，不早了，今年又这样了。&lt;/li&gt;
    &lt;li&gt;最近工作还行，公司这部分业务刚刚正规刚刚起步，几个老大都是最近从竞争对手和相关领域挖来的人，这种情况是三月份时根本没有迹象的。公司Java这块还是很缺人的，仔细观察几乎就是类似一个大牛带着一群喽啰。虽然现状不能说非常好，但是情况应该是在上升的，我猜。&lt;/li&gt;
    &lt;li&gt;前两天鲜果机房出了问题，还好不是另一个饭否。我把OPML倒出来开始盘算着用一个桌面的Reader。Gnome上比较有名的Reader是Liferea，Blam和Straw。Liferea貌似吃不消大量的Feed，导入当即就死掉了；Blam是Webkit核心的，不幸中文乱码；Straw勉强能用，然后Straw不支持outline嵌套，即不支持分组，so，也不是非常完美。&lt;/li&gt;
    &lt;li&gt;前几天关注自己blog不够，不小心又被挂了马，而且这次闹得比较大，被google加了黑名单，Twitter立刻也发了malware in profile的通知。好在现在改起来已经驾轻就熟，用fgrep很快就能找到被加恶意代码的文件，只可惜还没有办法杜绝。&lt;/li&gt;
&lt;/ol&gt;
9月20号去看顽主。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>'ActiveMQ: UnknownHostException on startup'</title><link href="https://sunng.info/blog/activemq-unknownhostexception-on-startup.html" rel="alternate"></link><published>2009-08-27T00:00:00+08:00</published><updated>2009-08-27T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-08-27:/blog/activemq-unknownhostexception-on-startup.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;ActiveMQ&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;JMS&lt;/li&gt;
&lt;li&gt;linux
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;在ArchLinux上使用ActiveMQ，执行bin/activemq，报错UnknownHostException，Transport Connection无法建立，可以取到/etc/ec.conf中设置的hostname（默认myhost）&lt;/p&gt;

&lt;p&gt;解决方法，编辑/etc/hosts，添加127.0.0.1 myhost myhost。再次 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;ActiveMQ&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;JMS&lt;/li&gt;
&lt;li&gt;linux
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;在ArchLinux上使用ActiveMQ，执行bin/activemq，报错UnknownHostException，Transport Connection无法建立，可以取到/etc/ec.conf中设置的hostname（默认myhost）&lt;/p&gt;

&lt;p&gt;解决方法，编辑/etc/hosts，添加127.0.0.1 myhost myhost。再次启动即可。Ubuntu上hosts自动把计算机名解析到127.0.0.1，ArchLinux上需要你手动做这件事了。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>YAMB is now hosted on kenai.com</title><link href="https://sunng.info/blog/yamb-is-now-hosted-on-kenaicom.html" rel="alternate"></link><published>2009-08-24T00:00:00+08:00</published><updated>2009-08-24T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-08-24:/blog/yamb-is-now-hosted-on-kenaicom.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;kenai&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;YAMB(Yet Another Microblogging Tool) which is mentioned several days ago is now hosted on Sun's project-kenai licensed with GPL2.
&lt;a href="http://kenai.com/projects/yamb"&gt;http://kenai.com/projects/yamb&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can grab the source code from following link via mercurial(hg).
&lt;a href="https://kenai.com/hg/yamb~hg-repos "&gt;https://kenai.com …&lt;/a&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;kenai&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;YAMB(Yet Another Microblogging Tool) which is mentioned several days ago is now hosted on Sun's project-kenai licensed with GPL2.
&lt;a href="http://kenai.com/projects/yamb"&gt;http://kenai.com/projects/yamb&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can grab the source code from following link via mercurial(hg).
&lt;a href="https://kenai.com/hg/yamb~hg-repos "&gt;https://kenai.com/hg/yamb~hg-repos&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Actually, there is nothing new.&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Inside Grails Flash Scope</title><link href="https://sunng.info/blog/inside-grails-flash-scope.html" rel="alternate"></link><published>2009-08-07T00:00:00+08:00</published><updated>2009-08-07T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-08-07:/blog/inside-grails-flash-scope.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;Grails&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://www.grails.org" target="_blank"&gt;Grails&lt;/a&gt;的在Servlet API的基础上增加了一个非常实用的FlashScope，FlashScope的生命周期为两次请求（也就是在一次重定向）。它的典型应用是POST方式提交后显示服务器端发给用户的提示信息，在平时的应用中会经常使用。&lt;/p&gt;

&lt;p&gt;Grails的FlashScope接口定义在org.codehaus.groovy.grails.web …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;Grails&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href="http://www.grails.org" target="_blank"&gt;Grails&lt;/a&gt;的在Servlet API的基础上增加了一个非常实用的FlashScope，FlashScope的生命周期为两次请求（也就是在一次重定向）。它的典型应用是POST方式提交后显示服务器端发给用户的提示信息，在平时的应用中会经常使用。&lt;/p&gt;

&lt;p&gt;Grails的FlashScope接口定义在org.codehaus.groovy.grails.web.servlet包中，这个接口继承了Map接口，并定义了一个方法next()。&lt;/p&gt;

&lt;p&gt;Grails的默认实现在org.codehaus.groovy.grails.web.servlet包中，GrailsFlashScope。这个实现内部定义了两个Map（生命周期为两个请求），current和next，这两个Map不断滚动，保持在一个请求中可以且仅可以访问到当前和前一次请求的上下文。&lt;br /&gt;
[codesyntax lang="java"]&lt;br /&gt;
public void next() {&lt;br /&gt;
current.clear();&lt;br /&gt;
current = (HashMap)next.clone();&lt;br /&gt;
next.clear();&lt;br /&gt;
reassociateObjectsWithErrors(current);&lt;br /&gt;
}
[/codesyntax]&lt;/p&gt;

&lt;p&gt;put的时候，Grails只把新制放到next表中，因为next将在下一次请求时继续保存&lt;br /&gt;
[codesyntax lang="java"]&lt;br /&gt;
public Object put(Object key, Object value) {&lt;br /&gt;
// create the session if it doesn't exist&lt;br /&gt;
registerWithSessionIfNecessary();&lt;br /&gt;
if(current.containsKey(key)) {&lt;br /&gt;
current.remove(key);&lt;br /&gt;
}
storeErrorsIfPossible(next,value);&lt;/p&gt;

&lt;p&gt;return next.put(key,value);&lt;br /&gt;
}
[/codesyntax]&lt;/p&gt;

&lt;p&gt;get的时候，Grails在两个Map中查找&lt;br /&gt;
[codesyntax lang="java"]&lt;br /&gt;
public Object get(Object key) {&lt;br /&gt;
if(next.containsKey(key))&lt;br /&gt;
return next.get(key);&lt;br /&gt;
return current.get(key);&lt;br /&gt;
}
[/codesyntax]&lt;/p&gt;

&lt;p&gt;此外，FlashScope本身还是被放在session中&lt;br /&gt;
[codesyntax lang="java"]&lt;br /&gt;
private void registerWithSessionIfNecessary() {&lt;br /&gt;
GrailsWebRequest webRequest = (GrailsWebRequest) RequestContextHolder.currentRequestAttributes();&lt;br /&gt;
HttpSession session = webRequest.getCurrentRequest().getSession(true);&lt;br /&gt;
if(session.getAttribute(GrailsApplicationAttributes.FLASH_SCOPE) == null)&lt;br /&gt;
session.setAttribute(GrailsApplicationAttributes.FLASH_SCOPE, this);&lt;br /&gt;
}
[/codesyntax]&lt;/p&gt;

&lt;p&gt;然后，滚动FlashScope的行为在GrailsWebRequestFilter中被调用，GrailsWebRequestFilter继承自spring-web的OncePerRequestFilter&lt;br /&gt;
[codesyntax lang="java"]&lt;br /&gt;
protected void doFilterInternal(&lt;br /&gt;
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)&lt;br /&gt;
throws ServletException, IOException {&lt;br /&gt;
...&lt;/p&gt;

&lt;p&gt;// Set the flash scope instance to its next state. We do&lt;br /&gt;
// this here so that the flash is available from Grails&lt;br /&gt;
// filters in a valid state.&lt;br /&gt;
FlashScope fs = webRequest.getAttributes().getFlashScope(request);&lt;br /&gt;
fs.next();&lt;br /&gt;
...&lt;br /&gt;
}
[/codesyntax]&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>史有先例的一跤</title><link href="https://sunng.info/blog/shi-you-xian-li-de-yi-jiao.html" rel="alternate"></link><published>2009-08-06T00:00:00+08:00</published><updated>2009-08-06T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-08-06:/blog/shi-you-xian-li-de-yi-jiao.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;听说要来台风，今天特地想着把自行车推进楼道里。楼道到平地有一个坡道，就是大家都见过的那种坡 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;听说要来台风，今天特地想着把自行车推进楼道里。楼道到平地有一个坡道，就是大家都见过的那种坡道。于是我骑着自行车冲了上去，试图把动能转化成势能。不料时间停止在了坡道的三分之二处。现在不仅大脑的并行处理能力下降，小脑更是时常挂起。你说我当时怎么就没有想到使把劲蹬上去呢，我确实没有这么想，连条件反射都没有。事后觉着大概是沉醉于那一刻的静止了。两个轮子终归是不暇流连的，接下来势能没有舍得转化给摩擦，它一定程度上在我的授意之下换了个方向通通变成动能了。躺在地面上的时候我就在想上次如此贴近大地是什么时候了，大约应该在最近三十年之内。整个过程发生在下班回家热火最朝天的时候，我的视界里倒是没有发现其他眼睛，但是可以确定的是此时此刻一定有不少痴痴的目光对着我的后脑勺。希望这一切没有颠覆牛顿们这么多年来给他们建立的世界观，不过其实整个过程更象是风力作用的自然现象，只可惜台风还远，另外，我不知道风是在哪一个方向吹。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Another simple micro-blogging tool initialized</title><link href="https://sunng.info/blog/another-simple-micro-blogging-tool-initialized.html" rel="alternate"></link><published>2009-08-03T00:00:00+08:00</published><updated>2009-08-03T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-08-03:/blog/another-simple-micro-blogging-tool-initialized.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;ibatis&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;spring&lt;/li&gt;
&lt;li&gt;strust2&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;A simple micro blogging tool based on Java web framework stack (Struts2/Spring/iBatis). It costs me five days to develop such a prototype version which supports basic functions(view, post, follow and tag). And I will try …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;ibatis&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;spring&lt;/li&gt;
&lt;li&gt;strust2&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;A simple micro blogging tool based on Java web framework stack (Struts2/Spring/iBatis). It costs me five days to develop such a prototype version which supports basic functions(view, post, follow and tag). And I will try to deploy it on GAE later(I hope it is possible). More improvements will also come up in next severals day.&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" title="Screenshot for YAMB" src="http://pic.yupoo.com/classicning/837997da6200/medium.jpg" alt="" width="500" height="407" /&gt;&lt;/p&gt;

&lt;p&gt;The prototype is just simple and plain which, i think, might be a good instance for books that titled with "Teach Yourself Java Web in 7 Days",(hough it's not a popular topic any more):). I'm also thinking of the table of content:
&lt;ol&gt;
    &lt;li&gt;Introduction to 3-Tier Java Web Develop&lt;/li&gt;
    &lt;li&gt;Setup Your Maven Environment&lt;/li&gt;
    &lt;li&gt;Setup Your Eclipse IDE&lt;/li&gt;
    &lt;li&gt;Creating Domain Objects and Tables&lt;/li&gt;
    &lt;li&gt;Writing Down Your First iBatis DAO&lt;/li&gt;
    &lt;li&gt;Spring for Bean Wiring&lt;/li&gt;
    &lt;li&gt;Unit Test with JUnit in Spring&lt;/li&gt;
    &lt;li&gt;Struts2 in Action&lt;/li&gt;
    &lt;li&gt;Integrating Everything with Spring&lt;/li&gt;
    &lt;li&gt;Create Ajax enabled Frontend&lt;/li&gt;
    &lt;li&gt;Easy Deployment&lt;/li&gt;
&lt;/ol&gt;
This is my first maven managed web project. When using maven with hsqldb, there is no extra configuration needed if you switch develop environment. Really effective!&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Notes of Even Faster Web Sites</title><link href="https://sunng.info/blog/notes-of-even-faster-web-sites.html" rel="alternate"></link><published>2009-08-02T00:00:00+08:00</published><updated>2009-08-02T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-08-02:/blog/notes-of-even-faster-web-sites.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;javascript&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;ol&gt;
    &lt;li&gt;异步、模拟多线程式的JavaScript，防止界面锁死。例如Google Gears，Firefox3.5 WebWorker异步API。&lt;/li&gt;
    &lt;li&gt;拆分JavaScript文件，即需即载入即运行，避免首次载入的 …&lt;/li&gt;&lt;/ol&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;javascript&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;ol&gt;
    &lt;li&gt;异步、模拟多线程式的JavaScript，防止界面锁死。例如Google Gears，Firefox3.5 WebWorker异步API。&lt;/li&gt;
    &lt;li&gt;拆分JavaScript文件，即需即载入即运行，避免首次载入的高代价。类似dojo的分包机制。&lt;/li&gt;
    &lt;li&gt;普通的script会阻塞页面的载入直至脚本下载执行完成，采用其它方法可以改变这种情况：XHR Evel, XHR Inject, Script DOM, Script Defer, Script in Iframe, Document.write Script Tag。&lt;/li&gt;
    &lt;li&gt;由于外部script和内联代码异步载入造成的冲突，解决方法1. 在外部编码种硬编码回调，2. 在Window.onload中调用 3. 用timer监控载入情况 4.scripttag.onreadystatechange&lt;/li&gt;
    &lt;li&gt;把异步载入外部脚本的方法编写为可复用的module&lt;/li&gt;
    &lt;li&gt;处理内联脚本，解决方法：1.将内联脚本移至页面尾部 2.异步执行JavaScript回调(setTimeout(0)) 3.使用script defer属性 内联脚本执行会被&lt;/li&gt;
    &lt;li&gt;语言级别上提高JavaScript执行效率的一些手段，Zakas&lt;/li&gt;
    &lt;li&gt;Comet的前景和现阶段实现，以及WebSocket&lt;/li&gt;
    &lt;li&gt;优化HTML文件&lt;/li&gt;
    &lt;li&gt;优化图片的utilities和几种格式的特点&lt;/li&gt;
    &lt;li&gt;sharding文件到不同的服务器上增加并行下载&lt;/li&gt;
    &lt;li&gt;正确地使用flush提高部分页面的加载速度 影响因素1.是否开启了Output_Buffer，2.是否使用了chunked encoding， 3.是否启用了gzip并在apache228上运行 4. 是否被代理和AV软件影响了 5.是否因为相同的域名的文件下载被block了 6.是否是webkit阈值以下不能渲染&lt;/li&gt;
    &lt;li&gt;慎用iframe，DOM操作代价过高，在html中设置src的iframe会阻塞onload，iframe与style script标签的位置&lt;/li&gt;
    &lt;li&gt;高性能的CSS选择器，避免全局rule，避免div#sele，避免div.sele，避免使用长选择器，避免使用decendant选择器，避免使用tag-child选择器，查看所有child选择器，使用继承&lt;/li&gt;
&lt;/ol&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Alexander Sukhanov</title><link href="https://sunng.info/blog/alexander-sukhanov.html" rel="alternate"></link><published>2009-08-01T00:00:00+08:00</published><updated>2009-08-01T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-08-01:/blog/alexander-sukhanov.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;Music&lt;/li&gt;
&lt;li&gt;Russia
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;img class="alignnone" title="亚历山大 舒克汉诺夫" src="http://www.math.msu.su/~sukh/im11.jpg" alt="" width="448" height="336" /&gt;&lt;/p&gt;

&lt;p&gt;Alexander Alekseevich Sukhanov，亚历山大.舒克汉诺夫1952年出生，莫斯科大学Mechanics and mathematics faculty, Department of computational mathematics, Laboratory of computing methods 任教，数学博士。他的另一个 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;Music&lt;/li&gt;
&lt;li&gt;Russia
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;img class="alignnone" title="亚历山大 舒克汉诺夫" src="http://www.math.msu.su/~sukh/im11.jpg" alt="" width="448" height="336" /&gt;&lt;/p&gt;

&lt;p&gt;Alexander Alekseevich Sukhanov，亚历山大.舒克汉诺夫1952年出生，莫斯科大学Mechanics and mathematics faculty, Department of computational mathematics, Laboratory of computing methods 任教，数学博士。他的另一个重要身份是歌手和诗人。从1969年开始创作，拥有近150首作品。&lt;/p&gt;

&lt;p&gt;这里可以找到大量作品：
&lt;a href="http://bards.pp.ru/Suhanov/" target="_blank"&gt;http://bards.pp.ru/Suhanov/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;个人主页：
&lt;a href="http://www.math.msu.su/~sukh/" target="_blank"&gt;http://www.math.msu.su/~sukh/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Wiki Page：
&lt;a href="http://en.wikipedia.org/wiki/Alexander_Sukhanov" target="_blank"&gt;http://en.wikipedia.org/wiki/Alexander_Sukhanov&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;魅力啊～
&lt;div id="_mcePaste" style="overflow: hidden; position: absolute; left: -10000px; top: 348px; width: 1px; height: 1px;"&gt;个人主页：&lt;/div&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>贝塔</title><link href="https://sunng.info/blog/bei-ta.html" rel="alternate"></link><published>2009-07-27T00:00:00+08:00</published><updated>2009-07-27T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-07-27:/blog/bei-ta.html</id><content type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;inkscape
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;[caption id="" align="alignnone" width="340" caption="贝塔 inkscape"]&lt;img title="贝塔" src="http://farm3.static.flickr.com/2426/3762066304_0a56f126f0_o.png" alt="贝塔 inkscape" width="340" height="329" /&gt;[/caption]&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Using MySQL on ArchLinux</title><link href="https://sunng.info/blog/using-mysql-on-archlinux.html" rel="alternate"></link><published>2009-07-27T00:00:00+08:00</published><updated>2009-07-27T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-07-27:/blog/using-mysql-on-archlinux.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺&lt;/li&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;ArchLinux&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;mysql
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;ol&gt;
    &lt;li&gt;&lt;em&gt;su -&lt;/em&gt;&lt;/li&gt;
    &lt;li&gt;&lt;em&gt;pacman -S mysql mysql-gui-tools
&lt;/em&gt;&lt;/li&gt;
    &lt;li&gt;run &lt;em&gt;mysql_install_db --user=mysql&lt;/em&gt; in &lt;em&gt;/var/lib/mysql&lt;/em&gt;&lt;/li&gt;
    &lt;li&gt;&lt;em&gt;vim /etc/my.cnf&lt;/em&gt;, disable &lt;em&gt;skip_networking &lt;/em&gt;to allow tcp/ip connection&lt;em&gt;
&lt;/em&gt;&lt;/li&gt;
    &lt;li&gt;&lt;em&gt;vim /etc/hosts.allow&lt;/em&gt;, add &lt;em&gt;mysql:all:allow&lt;/em&gt;&lt;/li&gt;
    &lt;li&gt;&lt;em&gt;/etc/rc.d/mysql start&lt;/em&gt;&lt;/li&gt;
    &lt;li&gt;&lt;em&gt;mysqladmin …&lt;/em&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺&lt;/li&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;ArchLinux&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;mysql
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;ol&gt;
    &lt;li&gt;&lt;em&gt;su -&lt;/em&gt;&lt;/li&gt;
    &lt;li&gt;&lt;em&gt;pacman -S mysql mysql-gui-tools
&lt;/em&gt;&lt;/li&gt;
    &lt;li&gt;run &lt;em&gt;mysql_install_db --user=mysql&lt;/em&gt; in &lt;em&gt;/var/lib/mysql&lt;/em&gt;&lt;/li&gt;
    &lt;li&gt;&lt;em&gt;vim /etc/my.cnf&lt;/em&gt;, disable &lt;em&gt;skip_networking &lt;/em&gt;to allow tcp/ip connection&lt;em&gt;
&lt;/em&gt;&lt;/li&gt;
    &lt;li&gt;&lt;em&gt;vim /etc/hosts.allow&lt;/em&gt;, add &lt;em&gt;mysql:all:allow&lt;/em&gt;&lt;/li&gt;
    &lt;li&gt;&lt;em&gt;/etc/rc.d/mysql start&lt;/em&gt;&lt;/li&gt;
    &lt;li&gt;&lt;em&gt;mysqladmin -u root password #NEW_PASSWORD#&lt;/em&gt; to set root password&lt;em&gt;
&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div id="_mcePaste" style="overflow: hidden; position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px;"&gt;
&lt;ol&gt;
    &lt;li&gt;&lt;/li&gt;
    &lt;li&gt;&lt;em&gt;su -&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>OSM Static API with JavaFX</title><link href="https://sunng.info/blog/osm-static-api-with-javafx.html" rel="alternate"></link><published>2009-07-18T00:00:00+08:00</published><updated>2009-07-18T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-07-18:/blog/osm-static-api-with-javafx.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;GIS&lt;/li&gt;
&lt;li&gt;JavaFX&lt;/li&gt;
&lt;li&gt;map&lt;/li&gt;
&lt;li&gt;OpenStreetMap&lt;/li&gt;
&lt;li&gt;RIA&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;OSM(http://www.osm.org) has released their first prototype of static map api. It is aimed to make web mapping easier to refer just like google has done.&lt;/p&gt;

&lt;p&gt;All interfaces are listed in following page …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;GIS&lt;/li&gt;
&lt;li&gt;JavaFX&lt;/li&gt;
&lt;li&gt;map&lt;/li&gt;
&lt;li&gt;OpenStreetMap&lt;/li&gt;
&lt;li&gt;RIA&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;OSM(http://www.osm.org) has released their first prototype of static map api. It is aimed to make web mapping easier to refer just like google has done.&lt;/p&gt;

&lt;p&gt;All interfaces are listed in following page:
&lt;a href="http://dev.openstreetmap.org/~pafciu17/" target="_blank"&gt;http://dev.openstreetmap.org/~pafciu17/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With OSM static api, you can:
&lt;ul&gt;
    &lt;li&gt;Show a map centered at a specified point. (using center, height and width)&lt;/li&gt;
    &lt;li&gt;Show a map contains specified bound box. (using bbox, zoom/height/width)&lt;/li&gt;
    &lt;li&gt;Create point/path/polygon in static map.&lt;/li&gt;
    &lt;li&gt;Choose different map render engines.&lt;/li&gt;
    &lt;li&gt;Control logo position in output image.&lt;/li&gt;
&lt;/ul&gt;
I just tested it with a simple javafx applet. For there is no crossdomain.xml on the server of OSM, to get cross-domain images, you will be asked to confirm security. Feel free to change center latitude/longitude and zoom level.&lt;/p&gt;

&lt;p&gt;&lt;!--more--&gt;
&lt;script src="http://dl.javafx.com/1.2/dtfx.js"&gt;&lt;/script&gt; &lt;script type="text/javascript"&gt;// &lt;![CDATA[&lt;br&gt;
      javafx(         {               archive: "http://www.classicning.com/archives/OSMStaticMap.jar",               width: 500,               height: 500,               code: "OSMStaticMap",               name: "OSMStaticMap"         }     );&lt;br&gt;
// ]]&gt;&lt;/script&gt;&lt;/p&gt;

&lt;p&gt;Enjoy all these free data on free applications :)&lt;/p&gt;

&lt;p&gt;Steps to create signed javafx applet
&lt;ul&gt;
    &lt;li&gt;javafxc OSMStaticMap.fx&lt;/li&gt;
    &lt;li&gt;javafxpackger javafxpackager -src . -appClass OSMStaticMap -appWidth 500 -appHeight 500&lt;/li&gt;
    &lt;li&gt;cd dist&lt;/li&gt;
    &lt;li&gt;keytool -genkey -keystore classicning.keys -alias  http://www.classicning.com/ -validity 365&lt;/li&gt;
    &lt;li&gt;jarsigner --keystore classicning.keys --storepass ******** OSMStaticMap.jar http://www.classicning.com/&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>送往 迎来 送往 迎来</title><link href="https://sunng.info/blog/song-wang-ying-lai-song-wang-ying-lai.html" rel="alternate"></link><published>2009-07-16T00:00:00+08:00</published><updated>2009-07-16T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-07-16:/blog/song-wang-ying-lai-song-wang-ying-lai.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;daf&lt;/li&gt;
&lt;li&gt;friends&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;最近事情多了一点，首先我在张江安家（如果这也算家的话）了，然后也上班了。上班的一开始就坎坷 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;daf&lt;/li&gt;
&lt;li&gt;friends&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;最近事情多了一点，首先我在张江安家（如果这也算家的话）了，然后也上班了。上班的一开始就坎坷了，不过我觉得应该也没什么不好的。这个不多说，留给大家尽情的揣测其中的细节吧。&lt;/p&gt;

&lt;p&gt;15号上午和妈妈从张江的住处出发去耀华路拿行李，恰好遭遇了二号线的凄惨故障，我妈也算是见识了一个加强版、精华版的二号线早高峰，对我三月份的经历有了比我还深刻的体会。好在现在我身为纯张江男，不用再去天天强化二号线的印象了。当天中午到了浦东机场，一切顺利，看着妈妈进了登机入口。这样应该会有一个半年时间的分别，也好，对妈妈来说也是一个锻炼吧。之后从机场坐磁悬浮回来，票价50,本来觉着贵了，后来想想打车要100呢，这已经很温柔了。磁悬浮最快到了420km/h,基本上就是加速到最大速度，然后就开始减速。&lt;/p&gt;

&lt;p&gt;当天晚上三胖从北京坐火车过来签证，我又跑到张江地铁站去接。还是很轻松就找到了，三胖总是这么显著。要说知我者非三胖莫属，一进屋掏出三大件茴香饺子、稻香村的起子馍还有北方的大桃子，我想到的没想到的都带来了。晚上赶紧把他那茴香饺子煮了，虽然效果不太好，但是感觉还是很不错的。&lt;/p&gt;

&lt;p&gt;今天三胖签证顺利，我想着不能再吃速冻食品了啊。于是下班去买了面条啥啥的，还煮了点蔬菜肉圆啥啥的。要知道两个自顾尚且不暇的男人能吃到这种水平，在我看来已经是很有成就感了。三胖是来匆匆去匆匆，吃了面条洗个澡就收拾东西又上路了。话说出门饺子回家面，要不是那第二袋茴香饺子实在没法吃了。。。。走了以后才发现三胖同志把我的冰箱用橙子苹果啥的塞了个满，真是，哎&lt;/p&gt;

&lt;p&gt;再有就是daf同学三周后就要回来了。daf同学最近比较振荡，每日行情经常是低开低走，有时候好不容易尾市上扬了，第二天不知道为啥开盘还是那么低。也许是大盘整体走低吧，世界形势不好。可怜的daf同学一振荡，全世界都跟着做提心吊胆的简谐运动。好在三周后中期年报就要出来了，希望下半年的行情能一路走高。当然了，套牢也不要紧。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Поет гитара</title><link href="https://sunng.info/blog/poet-gitara.html" rel="alternate"></link><published>2009-07-13T00:00:00+08:00</published><updated>2009-07-13T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-07-13:/blog/poet-gitara.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;当时&lt;/li&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;Books&lt;/li&gt;
&lt;li&gt;DM&lt;/li&gt;
&lt;li&gt;Life&lt;/li&gt;
&lt;li&gt;RU&lt;/li&gt;
&lt;li&gt;ZhangJiang
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;ol&gt;
    &lt;li&gt;好久没有用ol了&lt;/li&gt;
    &lt;li&gt;本文标题是扯淡的&lt;/li&gt;
    &lt;li&gt;部署在张江的过程基本上算是smooth，目前为止没有遭遇 …&lt;/li&gt;&lt;/ol&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;当时&lt;/li&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;Books&lt;/li&gt;
&lt;li&gt;DM&lt;/li&gt;
&lt;li&gt;Life&lt;/li&gt;
&lt;li&gt;RU&lt;/li&gt;
&lt;li&gt;ZhangJiang
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;ol&gt;
    &lt;li&gt;好久没有用ol了&lt;/li&gt;
    &lt;li&gt;本文标题是扯淡的&lt;/li&gt;
    &lt;li&gt;部署在张江的过程基本上算是smooth，目前为止没有遭遇颠覆世界观的新消息。对房子对周边环境可以认为是非常满意。尤其是今天把屋里的网线接到电脑上居然就直接变成了192.168.0.4然后打开了百度，一时间有种难以名状的激动──似乎是整整一年没有遇到比这个更加轻松实现的事情了。霎时间就有苦将尽甘欲来，瑞雪兆丰年等等系列的感觉。&lt;/li&gt;
    &lt;li&gt;今天上海天上的云彩异常绚丽，遗憾相机没有随身，不然应该能拍出点什么的。这种效果在南京好像只有台风等等极度扭曲的天气里才有。昨晚从超市满载而归就看见天上橙色的闪电蠢蠢欲动。天气不错的。&lt;/li&gt;
    &lt;li&gt;我有心专心生活了。现在床上放了两本书：&lt;a href="http://www.douban.com/subject/2035162/" target="_blank"&gt;刀锋&lt;/a&gt;和&lt;a href="http://www.douban.com/subject/3243006/" target="_blank"&gt;实现模式&lt;/a&gt;，争取找个机会都翻开。等到日出云开的时候，还打算做一个Twitter分组好友的小应用，也不知道有没有人做过，大致意思就是说查看Tweets的界面增加一个分组的功能，只显示部分好友的更新，这样看起来会比较轻松（现在的情况是当我试图看&lt;a href="http://www.lilystudio.org/" target="_blank"&gt;工作室&lt;/a&gt;朋友们的更新时总要绕过&lt;a href="https://twitter.com/KentBeck" target="_blank"&gt;Kent Beck&lt;/a&gt;等人的絮叨），这个东西就放在GAE上吧，也许应该练习一下Java了，手都凉了。&lt;/li&gt;
    &lt;li&gt;本来想着到了上海买一本《&lt;a href="http://www.douban.com/subject/3224524/" target="_blank"&gt;代码之美&lt;/a&gt;》抚慰心灵的（英文版看得实在是太吃力了），结果一看价格居然真的99,当当折扣之后也要80+.这个抚慰心灵的作用估计是没有了，不小心还要让钱包被划一道口子。看来11月寒潮来袭的时候还是要闷头去看电子版了。&lt;/li&gt;
    &lt;li&gt;由于我兴趣过度广泛，在很长一段时间里都不知道自己的兴趣何在了。最近一年以来逐渐感到可能对DM还是有一些纯情的兴趣的，柜子里有一本影印的&lt;a href="http://www.douban.com/subject/1465939/" target="_blank"&gt;数据挖掘导论&lt;/a&gt;准备要在年内吃下去。电脑里还有Manning的&lt;a href="http://www.douban.com/subject/3093012/" target="_blank"&gt;Collective Intelligence in Action&lt;/a&gt;电子版可以看，那位&lt;a href="http://blog.kiwitobes.com/" target="_blank"&gt;Segaran&lt;/a&gt;同志又马不停蹄地出版了&lt;a href="http://oreilly.com/catalog/9780596157111/" target="_blank"&gt;Beautiful Data&lt;/a&gt;和&lt;a href="http://oreilly.com/catalog/9780596802066/" target="_blank"&gt;Programming the Semantic Web&lt;/a&gt;，他也不管我们进食的速度。&lt;/li&gt;
    &lt;li&gt;实在空虚的话再学点俄语玩玩。我还是没有改掉一听俄国国歌就激动的坏毛病，虽然最近在俄国又有不愉快的事情发生了。&lt;/li&gt;
    &lt;li&gt;不熟悉PHP是我作为Web开发人员巨大的短板。&lt;/li&gt;
&lt;/ol&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>DB dump with chroot</title><link href="https://sunng.info/blog/db-dump-with-chroot.html" rel="alternate"></link><published>2009-07-10T00:00:00+08:00</published><updated>2009-07-10T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-07-10:/blog/db-dump-with-chroot.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;database&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;mysql&lt;/li&gt;
&lt;li&gt;postgresql&lt;/li&gt;
&lt;li&gt;shell&lt;/li&gt;
&lt;li&gt;sqlite
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;I caught chroot first time when attempting to install gentoo linux. Of course, its a tool of great useful. Two months ago I had my laptop crashed, and all static files were recovery easily by copy while …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;database&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;mysql&lt;/li&gt;
&lt;li&gt;postgresql&lt;/li&gt;
&lt;li&gt;shell&lt;/li&gt;
&lt;li&gt;sqlite
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;I caught chroot first time when attempting to install gentoo linux. Of course, its a tool of great useful. Two months ago I had my laptop crashed, and all static files were recovery easily by copy while raw db data file seems to be difficult to handle.&lt;/p&gt;

&lt;p&gt;The old file system is in a mobile disk, mounted at /media/disk-1/. Now I try to use chroot to rollback to that environment, so that all standard dump tools will be available.&lt;/p&gt;

&lt;p&gt;Before you chroot to the system, don't forget to bind /dev to new location. Or you might get error prompt such as "/dev/null Permission Denied" (it's a common error). Just execute command below:
&lt;em&gt;$ sudo mount --bind /dev /media/disk-1/dev&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Then change root to my old system:
&lt;em&gt;$ sudo chroot /media/disk-1&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now you become root user automatically.
&lt;h3&gt;MySQL Dump&lt;/h3&gt;
In a standard ubuntu mysql installation, we should launch mysql db from init.d by:
&lt;em&gt;# /etc/init.d/mysql start&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;But it's no longer available in such environment. Fortunately, there is a direct way:
&lt;em&gt;# mysqld_safe &amp;amp;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;OK, go on to dump database with mysqldump:
&lt;em&gt;# mysqldump --all-databases &amp;gt; mysql_dump_file&lt;/em&gt;
&lt;h3&gt;Postgresql Dump&lt;/h3&gt;
We cannot use init.d to start pgsql either. Therefore, try to run it by:
&lt;em&gt;# su postgres -c "/usr/lib/postgresql/8.3/bin/pg_ctl start -D /etc/postgresql/8.3/main"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;/etc/postgresql/8.3/main&lt;/em&gt; is the default data directory in standard installation(with apt-get). This directory is supposed to contain a file named with "postgesql.conf"&lt;/p&gt;

&lt;p&gt;Now pgsql db is also running. As root user we have no privilege to run pgsql utilities. so take following steps:
&lt;em&gt;# touch pgsql_dump_file&lt;br /&gt;
# chmod a+w pgsql_dump_file&lt;br /&gt;
# su postgres -c "pg_dumpall -f pgsql_dump_file"&lt;/em&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
Personally, I found it's too complex and low efficiency to backup and restore data with mysql and postgresql. If there is no critical requirement(just like personal data management), file based db(Surely I mean sqlite) is no doubt better choice. Easy management(lots of gui tool now provided), smooth copy and move, standard db interface for programming, and enough functionality.&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>'[旧]Up and Running: PostgreSQL and PostGIS on Ubuntu 8.10, Step By Step'</title><link href="https://sunng.info/blog/jiu-up-and-running-postgresql-and-postgis-on-ubuntu-810-step-by-step.html" rel="alternate"></link><published>2009-07-10T00:00:00+08:00</published><updated>2009-07-10T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-07-10:/blog/jiu-up-and-running-postgresql-and-postgis-on-ubuntu-810-step-by-step.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;postgresql&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;每次安装pgsql的过程都是这么震撼人心。以下在Ubuntu 8.10上安装配置运行pgsql及postgis的简单步骤。和Windows相比，用apt-get安装不会再有服务安装不上的问题，但是可能会有包装不全、缺少配置的问题。&lt;/p&gt;

&lt;p&gt;首先用apt-get下载安装pgsql和postgis的需要的包&lt;/p&gt;

&lt;p&gt;&lt;em&gt;sudo apt-get install postgresql-8 …&lt;/em&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;postgresql&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;每次安装pgsql的过程都是这么震撼人心。以下在Ubuntu 8.10上安装配置运行pgsql及postgis的简单步骤。和Windows相比，用apt-get安装不会再有服务安装不上的问题，但是可能会有包装不全、缺少配置的问题。&lt;/p&gt;

&lt;p&gt;首先用apt-get下载安装pgsql和postgis的需要的包&lt;/p&gt;

&lt;p&gt;&lt;em&gt;sudo apt-get install postgresql-8.3 postgresql-8.3-postgis postgresql-client-8.3 postgresql-contrib-8.3 pgadmin3&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;安装过程中会提示创建一个默认名为&lt;em&gt;postgres&lt;/em&gt;的用户。&lt;/p&gt;

&lt;p&gt;安装结束后，启动pgsql服务器&lt;/p&gt;

&lt;p&gt;&lt;em&gt;sudo /etc/init.d/postgresql-8.3 start&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;在默认情况下pgsql ident的设置为postgres用户用ident sameuser的方式登录，这种方式类似于sqlserver使用windows的用户管理。因此要用postgres用户登录系统。&lt;/p&gt;

&lt;p&gt;修改postgres用户的密码&lt;/p&gt;

&lt;p&gt;&lt;em&gt;sudo passwd postgres&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;用新密码登录为postgres&lt;/p&gt;

&lt;p&gt;&lt;em&gt;su - postgres&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;运行psql，可以检查pgsql的运行情况&lt;/p&gt;

&lt;p&gt;&lt;em&gt;psql&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;可以在psql中给postgres用户设置数据库密码&lt;/p&gt;

&lt;p&gt;&lt;em&gt;ALTER USER postgres ENCRYPTED PASSWORD 'yournewpassword';&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;继续在psql中创建plsql语言。如果安装时遗漏了contrib包这一步是不能完成的。&lt;/p&gt;

&lt;p&gt;&lt;em&gt;CREATE LANGUAGE plsql;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;退出psql，创建Postgis数据库。&lt;/p&gt;

&lt;p&gt;&lt;em&gt;createdb postgis&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;导入postgis的两个sql文件，其中定义了EPSG数据库和Geometry类型。&lt;/p&gt;

&lt;p&gt;&lt;em&gt;psql -d postgis -f /usr/share/postgresql-8.3-postgis/lwpostgis.sql&lt;br /&gt;
psql -d postgis -f /usr/share/postgresql-8.3-postgis/spatial_ref_sys.sql&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;进入psql对当前用户GRAND ALL&lt;/p&gt;

&lt;p&gt;&lt;em&gt;GRANT ALL ON TABLE geometry_columns TO postgres;&lt;br /&gt;
GRANT ALL ON TABLE spatial_ref_sys TO postgres;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;这样做的结果是这两张表被导入到默认的postgres数据库中。这样今后创建postgis数据库就可以以postgres为template_db，不过这么做的后果是这两张表的owner都是postgres。在GeoServer里创建FeatureType时必须用postgres用户登录，否则权限不足。&lt;/p&gt;

&lt;p&gt;这时PostGIS应该可以正常工作了，可以尝试导入shp&lt;/p&gt;

&lt;p&gt;&lt;em&gt;shp2pgsql /your/shp/file tablename | psql newgisdb&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;创建一个新的数据库用户&lt;/p&gt;

&lt;p&gt;&lt;em&gt;createuser -p username&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;修改&lt;em&gt;/etc/postgresql/8.3/main/pg_hda.conf&lt;/em&gt;，设置数据库访问的认证方式&lt;/p&gt;

&lt;p&gt;将local all all一行的验证方式(method)由ident sameuser改为password就可以用密码登录psql了，在python－psycopg中用新建的用户连接pgsql也不会报ident失败了。&lt;/p&gt;

&lt;p&gt;将host all 127.0.0.1/32一行的验证方式也改为password就可以在本地用jdbc以用户名密码连接数据库了。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Opera-mini</title><link href="https://sunng.info/blog/opera-mini.html" rel="alternate"></link><published>2009-07-10T00:00:00+08:00</published><updated>2009-07-10T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-07-10:/blog/opera-mini.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;internet&lt;/li&gt;
&lt;li&gt;mobile&lt;/li&gt;
&lt;li&gt;opera
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;偌大的互联网，却找不到一个带提交按钮的文本框。&lt;/p&gt;

&lt;p&gt;昨天为了能看一眼facebook上的留言（汗，其实我对翻墙的要求 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;internet&lt;/li&gt;
&lt;li&gt;mobile&lt;/li&gt;
&lt;li&gt;opera
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;偌大的互联网，却找不到一个带提交按钮的文本框。&lt;/p&gt;

&lt;p&gt;昨天为了能看一眼facebook上的留言（汗，其实我对翻墙的要求也够低的）可谓无所不用其极。首先是用tor+privoxy，结果tor的速度实在是太慢，除了tor的测试页面其它根本就打不开。然后看到了ssh -D的方法，这个速度也是很慢，但是不小心打开了twitter.com，然后又海枯石烂地登录成功，看到没有新的direct msg，也就算达到目的了。&lt;/p&gt;

&lt;p&gt;再后来就看到opera-mini的在线&lt;a title="Opera Mini Demo" href="http://demo.opera-mini.net/demo.html" target="_blank"&gt;DEMO&lt;/a&gt;，一个j2me simulator，借助这个applet可以顺顺利利地打开facebook twiiter之类的网站，但是因为Linux上JRE中文字体的问题，看起来多少还有点吃力。然后我就弱弱地睡下来，片刻之后才反应过来这一切多有意思。看看Opera-mini最大的卖点：&lt;br /&gt;
http://en.wikipedia.org/wiki/Opera_Mini#Functionality&lt;/p&gt;

&lt;p&gt;在Server和终端之间有一个Proxy，这个Proxy整好运行在墙外面，也就不奇怪twitter facebook这样的网站可以轻松地访问了。不仅仅是在线demo，用手机终端也是一样的道理。我之前还一直以为是中移动的疏忽，看来真是错怪他们的工作了，呵呵。
&lt;div id="_mcePaste" style="overflow: hidden; position: absolute; left: -10000px; top: 44px; width: 1px; height: 1px;"&gt;http://en.wikipedia.org/wiki/Opera_Mini#Functionality&lt;/div&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>张江深处</title><link href="https://sunng.info/blog/zhang-jiang-shen-chu.html" rel="alternate"></link><published>2009-07-09T00:00:00+08:00</published><updated>2009-07-09T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-07-09:/blog/zhang-jiang-shen-chu.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;fanfou&lt;/li&gt;
&lt;li&gt;Life&lt;/li&gt;
&lt;li&gt;Shanghai
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;我都不好意思跟人说我去了上海，因为我很快就要窝在张江这么个村不村镇不镇的地方了。也说 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;fanfou&lt;/li&gt;
&lt;li&gt;Life&lt;/li&gt;
&lt;li&gt;Shanghai
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;我都不好意思跟人说我去了上海，因为我很快就要窝在张江这么个村不村镇不镇的地方了。也说不清是好是坏，远离闹市中心倒是求得清静；但是真住在陌生城市最边缘的地方心里倒是一阵失落，还要花着血汗钱养活黑心的房东，我这是干什么来了。以上是例行的牢骚，我也无力阻止它的生成。好在有像WG和Samson这样的好友，讨论一下大家的处境，虽然也不会有什么具体的变化，但是心态总能相对缓和一些。嗨，生活就这个样子了，关键在你怎么看待它。&lt;/p&gt;

&lt;p&gt;饭否这下恐怕真是没了，离了饭否，又是极大的空虚和不安以及痛心。不出意外的话，这事的起因又是针对时事的言论传播。我是保守人士，对于很多肆无忌惮地甚至就是具有挑逗性的言论其实非常反感。不怕被骂，我时常感受到这一代粪青内心和大脑的空虚，通过传播一些具有挑逗性（实则极度干瘪老套）的言论获得一些挑战“常规”的隐隐快感。这群人称为“不明真相的群众”实在是恰如其分。&lt;/p&gt;

&lt;p&gt;看出来了吧，我确实边缘了，住到边缘的地方也是罪有应得。&lt;/p&gt;

&lt;p&gt;我还是希望饭否能恢复，为了让我抢救出一些重要的对话留作纪念。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Pylons Tips</title><link href="https://sunng.info/blog/pylons-tips.html" rel="alternate"></link><published>2009-07-02T00:00:00+08:00</published><updated>2009-07-02T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-07-02:/blog/pylons-tips.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;pylons&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;All scripts created by paster(paster create, paster controller and etc.) uses 4 spaces as indent while many editors(vim) uses tabs automatically. Different indent in one file will cause fatal error.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;h.url_for&lt;/em&gt; is a popular helper function in …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;pylons&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;web
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;All scripts created by paster(paster create, paster controller and etc.) uses 4 spaces as indent while many editors(vim) uses tabs automatically. Different indent in one file will cause fatal error.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;h.url_for&lt;/em&gt; is a popular helper function in many books and tutorials which to solve url mapping. This tool is no longer available by default since 0.97. If you got error message like "AttributeError: 'module' object has no attribute 'url_for'", just add&lt;/p&gt;

&lt;p&gt;[codesyntax lang="python"]from routes import url_for[/codesyntax]&lt;br /&gt;
to&lt;em&gt; yourapp/libs/helper.py&lt;/em&gt; .&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>莫愁前路无知己</title><link href="https://sunng.info/blog/mo-chou-qian-lu-wu-zhi-ji.html" rel="alternate"></link><published>2009-06-25T00:00:00+08:00</published><updated>2009-06-25T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-06-25:/blog/mo-chou-qian-lu-wu-zhi-ji.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;'214'&lt;/li&gt;
&lt;li&gt;friends&lt;/li&gt;
&lt;li&gt;NJU
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;六月见底，我是无暇总结和回顾自己难堪的大学生活了，只是对这越来越近的离愁别绪多少开始 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;'214'&lt;/li&gt;
&lt;li&gt;friends&lt;/li&gt;
&lt;li&gt;NJU
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;六月见底，我是无暇总结和回顾自己难堪的大学生活了，只是对这越来越近的离愁别绪多少开始有些感慨。&lt;/p&gt;

&lt;p&gt;今天三胖在上海签证遭遇了坎坷，后面的日程又发生了变化。这样原本计划的一次聚会怕是随风了。中午老大坐上了回家的火车，虽说是三十号就要回来了，但可能因为带回去的行李比较多，心中的化学反应也强烈了一些。短信收到说火车离站，想哭。老大多愁善感也不是一两天的事情了，不过这次都能体会都有感触。好在他三十号回来，还有一聚，到时在哭天抢地也尚有机会。倒是三胖的短信说“我那份你替我吃了吧”难免遗憾。虽然下次上海还有见面的机会，不过这一屋子四人想要重逢，就不知道是猴年马月了。我们宿舍四个人走全了四个方向，老四继续读研，老大献身国防，三胖外出读书，我就卷进那啥啥的洪流里了。可能有这种情况的还不多吧，料想数载重逢一定是丰富多彩不知所云。&lt;/p&gt;

&lt;p&gt;人生啊。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>'20090617'</title><link href="https://sunng.info/blog/20090617.html" rel="alternate"></link><published>2009-06-17T00:00:00+08:00</published><updated>2009-06-17T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-06-17:/blog/20090617.html</id><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags: []
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Nobody talks about techniques and related any more.&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>这也是一种</title><link href="https://sunng.info/blog/zhe-ye-shi-yi-chong.html" rel="alternate"></link><published>2009-06-10T00:00:00+08:00</published><updated>2009-06-10T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-06-10:/blog/zhe-ye-shi-yi-chong.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;GIS&lt;/li&gt;
&lt;li&gt;Life&lt;/li&gt;
&lt;li&gt;Milan
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;ol&gt;
    &lt;li&gt;今天是高考最后一天，血淋淋的事实证明，高考是一个结束更是一个开始。望广大考生注意考后体 …&lt;/li&gt;&lt;/ol&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;GIS&lt;/li&gt;
&lt;li&gt;Life&lt;/li&gt;
&lt;li&gt;Milan
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;ol&gt;
    &lt;li&gt;今天是高考最后一天，血淋淋的事实证明，高考是一个结束更是一个开始。望广大考生注意考后体形的保持。&lt;/li&gt;
    &lt;li&gt;绿坝来了，我在想是否今后就不允许出售预装Linux系统的个人电脑了。关于绿坝最niubility的评论，个人觉得还是要数这个OSI九层模型了：“物理层，链路层，网络层，传输层，会话层，GFW层，表现层，应用层，绿坝层”。呵呵，当然，仅供娱乐，不能作为考研参考资料。&lt;/li&gt;
    &lt;li&gt;GIS方面，詹姆斯.废同志提出来的GSS看起来确实要比SLD要理想一些。SLD太冗繁了，适合程序来生成，手工书写效率太低。况且况且现在能生成SLD的还是只有uDig吧，生成的还有限。用CSS的模式，加上属性选择器（强大的属性选择器，定然要支持数学和逻辑计算，比CSS的强大），还是非常不错的。当然，名字得改改，叫gCSS好。&lt;/li&gt;
    &lt;li&gt;我就快要毕业了，在同学和老师亲切的关怀下，之前答辩和论文还算较为顺利。鉴于这篇文章的正经程度有限，因此不在此特地感谢导师了。下次有机会要专门写一下。&lt;/li&gt;
    &lt;li&gt;卡卡去皇马了，这种感觉再锐化一下就是被人甩了的悲伤。&lt;/li&gt;
    &lt;li&gt;复习正有条不紊地进行中，信心决心一定程度上呈振幅不大的正弦曲线。现在还在如此忐忑的，除了中考的同学们就数咱这样的了。不过中考的同学还年轻，以后选择还多，美好的未来就在不远处（高考的同学同样适用）。&lt;/li&gt;
    &lt;li&gt;人生还是原函数靠谱，不过能向&lt;a href="http://blog.williamgates.biz/2009/05/better-than-i-think/" target="_blank"&gt;WG这样&lt;/a&gt;获得了一阶导数的跳跃间断点，多少也是看到了希望。有了niubility的一阶导数，原函数也就有了期待。归根结底来说，原函数才是根本，一阶导数勉强可以让人握紧双拳仰望蓝天感叹要好好战斗下去；至于二阶导数，天边浮云了。还是要呼唤间断点，跳远间断点，不是那种不着调的可去间断点。&lt;/li&gt;
    &lt;li&gt;本周末和妈妈去上海签证，说起来甚是无奈。儿子不争气，只能妈先签了。沾妈的光，去远远看一下领事馆，不管以后能否有机会，至少认个路吧（如果能认下的话）。&lt;/li&gt;
    &lt;li&gt;没办法，生活是如此人生啊。白天见不着星星，晚上看不到阳光，没点境界如何坚持下去呢。话说啥时候要是连贫都不贫了，料想当是荒诞剧收场，台下观众破涕为笑，台上演员目瞪口呆，抄起一把菜刀到后台说要和编剧玩命，这时字幕徐上：自编自导自演自缢。玩笑，呵呵。&lt;/li&gt;
    &lt;li&gt;我会继续关注伊朗总统内贾德和波利维亚总统莫拉莱斯关于购买天然铀的会晤的。&lt;/li&gt;
&lt;/ol&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>How you waste your storage?</title><link href="https://sunng.info/blog/how-you-waste-your-storage.html" rel="alternate"></link><published>2009-06-06T00:00:00+08:00</published><updated>2009-06-06T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-06-06:/blog/how-you-waste-your-storage.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;VirtualBox
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;img class="alignnone" title="some kinds of waste" src="http://pic.yupoo.com/classicning/2074478db967/epko3a17.jpg" alt="" width="200" height="381" /&gt;&lt;/p&gt;

&lt;p&gt;250G硬盘是这样被使用的。一个K桌面的ArchLinux，一个俄语界面的LinuxMint，一个出于好奇心的OpenSolaris，一个用来测试的Ubuntu Server，以及，以及一个不能免俗 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;VirtualBox
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;img class="alignnone" title="some kinds of waste" src="http://pic.yupoo.com/classicning/2074478db967/epko3a17.jpg" alt="" width="200" height="381" /&gt;&lt;/p&gt;

&lt;p&gt;250G硬盘是这样被使用的。一个K桌面的ArchLinux，一个俄语界面的LinuxMint，一个出于好奇心的OpenSolaris，一个用来测试的Ubuntu Server，以及，以及一个不能免俗的盗版Windows。&lt;/p&gt;

&lt;p&gt;&lt;a href="http://blog.samsonis.me/2009/06/install-ubuntu-on-virtualbox-part-1/" target="_blank"&gt;Check here&lt;/a&gt; to see a complete installation guide of &lt;a href="http://www.ubuntu.com" target="_blank"&gt;Ubuntu Linux&lt;/a&gt; on &lt;a href="http://www.virtualbox.org" target="_blank"&gt;VirtualBox&lt;/a&gt;.&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>The Day of Silence</title><link href="https://sunng.info/blog/the-day-of-silence.html" rel="alternate"></link><published>2009-06-04T00:00:00+08:00</published><updated>2009-06-04T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-06-04:/blog/the-day-of-silence.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;zoo
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;今天是一年一度的无言日，每到这一天，中国大陆地区的网民会通过自觉停止发言、停止更改头像的方 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;zoo
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;今天是一年一度的无言日，每到这一天，中国大陆地区的网民会通过自觉停止发言、停止更改头像的方式进行庆祝。网友们希望通过这种方式，减少由于不必要的操作造成的存储设备的损耗和CPU计算导致的耗电放热，提醒人们保护地球环境、减少能源消耗并减缓全球变暖。&lt;/p&gt;

&lt;p&gt;据报道，今年的无言日是该活动举办以来规模最大的一次，除了中国大陆地区的新兴Web2.0网站豆瓣、饭否，以及一些人气较旺的论坛PCHOME、VeryCD等早早参加了这项活动，包括Twitter、Live Space、Flickr等著名网站今年也首次加入到这项活动中来，而Wordpress.com和Blogspot.com等长期关注环境保护的网站则是如往年一样早早关闭了自己的网站访问功能。值得一提的是，微软近期刚刚发布的搜索引擎Bing.com也在今天关闭了，据微软工作人员介绍，他们希望通过此举表明微软公司一直以来在环境保护和社会公益方面的立场和决心。与之相对应的是Google等互联网巨头仍然没有参加今年的无言日活动，此举遭到了多数网民的强烈抗议和反对。&lt;/p&gt;

&lt;p&gt;研究统计表明，每年由于网友在网上滥发言论滥改头像造成的存储设备损失数以T记，而由此造成的电力损耗更是不计其数。随着Twitter类型网站的普及，滥发言论滥改头像已经成为危及世界环境的重要因素。近年来的数据表明，用户每发一条微博信息，南极冰层厚度都有相应减小。著名的Amazon公司甚至推出了专门的存储服务试图对存储设备集中管理以减少全球存储设备的消耗和浪费。但是就其根源来说，唯有全球网友减少发表言论的操作，才能真正实现节约存储设备，促进可持续发展。&lt;/p&gt;

&lt;p&gt;一名网友在接受采访时说，今年他选择动物园度过无言日，希望能够一改从前在网上说话的不良习惯，与人类的朋友进行更多心灵的交流，以和谐生态和谐世界。&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" title="草泥马" src="http://pic.yupoo.com/classicning/0421978af4d7/medium.jpg" alt="" width="500" height="335" /&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>desktop-switcher以下</title><link href="https://sunng.info/blog/desktop-switcheryi-xia.html" rel="alternate"></link><published>2009-05-29T00:00:00+08:00</published><updated>2009-05-29T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-05-29:/blog/desktop-switcheryi-xia.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;bug&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;今天中午在synaptic里闲搜，发现了一个叫做desktop-switcher的小工具，它的功能是在ubuntu的netbook小桌面模式和经典模式之间切换。好奇心起，就装上试试。一试不得了，两个不太明 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;bug&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;今天中午在synaptic里闲搜，发现了一个叫做desktop-switcher的小工具，它的功能是在ubuntu的netbook小桌面模式和经典模式之间切换。好奇心起，就装上试试。一试不得了，两个不太明确的报错之后，面板没有了，桌面也只剩下背景图片，左右键通通废掉，登出也只能按电源开关。尤其是xorg1.6之后，ctrl+alt+backspace也不好使了，甚是无奈。&lt;br /&gt;
这下岂不是又要重装系统了？还好这次冷静（现在越来越冷静了，我是说经历了水泼电脑之后），先新建了一个用户重新进桌面上网搜一下。还真搜到了问题，果然是这个切换工具有bug。&lt;br /&gt;
首先看一下gnome配置中有关的配置&lt;br /&gt;
gconftool-2 --get /desktop/gnome/session/required_components&lt;br /&gt;
这个值居然被清空了，参考launchpad上的讨论，要加上这几项&lt;br /&gt;
gconftool-2 --set /desktop/gnome/session/required_components --type=list --list-type=string ["panel","filemanager","windowmanager"]&lt;br /&gt;
重登录之后panel总算是出来了，原有panel上所有的配置都被清空了，连主菜单都没有了。不过这个没有问题，手动重新添加问题不大。panel的样式需要重新设置，因为这个样式已经被desktop-switcher切换到netbook版本的样式（名字忘了）。&lt;br /&gt;
还有一些小问题，可以在Gnome设置工具里修改：&lt;br /&gt;
1. 桌面右键无效、图标消失了&lt;br /&gt;
查找这个键值：/apps/nautilus/preferences/show_desktop 将其设置为true，让nautilus管理桌面&lt;br /&gt;
2. User Switcher Applet无法添加&lt;br /&gt;
查找这个键值：/desktop/gnome/lockdown/disable_user_switching 将其设置为false，允许用户切换&lt;/p&gt;

&lt;p&gt;ok，这样就只剩下虚惊了。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>我走向急流的河畔</title><link href="https://sunng.info/blog/wo-zou-xiang-ji-liu-de-he-pan.html" rel="alternate"></link><published>2009-05-22T00:00:00+08:00</published><updated>2009-05-22T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-05-22:/blog/wo-zou-xiang-ji-liu-de-he-pan.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;laptop&lt;/li&gt;
&lt;li&gt;NJU&lt;/li&gt;
&lt;li&gt;thinkpad
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;p&gt;标题是随便起的，是背景音乐。最近比较忙，这会偷闲&lt;a href="http://blog.samsonis.me/2009/05/update-9/"&gt;update&lt;/a&gt;一下。话说用ol写blog已经非常普及了。&lt;/p&gt;&lt;/p&gt;

&lt;p&gt;&lt;ol&gt;
&lt;li&gt;首当其冲，上上周六晚十点，本 …&lt;/li&gt;&lt;/ol&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;laptop&lt;/li&gt;
&lt;li&gt;NJU&lt;/li&gt;
&lt;li&gt;thinkpad
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;p&gt;标题是随便起的，是背景音乐。最近比较忙，这会偷闲&lt;a href="http://blog.samsonis.me/2009/05/update-9/"&gt;update&lt;/a&gt;一下。话说用ol写blog已经非常普及了。&lt;/p&gt;&lt;/p&gt;

&lt;p&gt;&lt;ol&gt;
&lt;li&gt;首当其冲，上上周六晚十点，本人亲手把右手边一杯茶水碰倒在桌子上导致电脑进水。最为发指的是当时第一反应还不是抢救电脑，是擦桌子。省略掉一些不愿再回忆的过程，上周日修电脑的人说找不出问题，要换主板。鉴于一切一切，最后还是报废了旧电脑。换了现在的Think三鹿400脑残残型号。这事来得太突然以至于我买电脑那天都没有刮胡子。&lt;/li&gt;
&lt;li&gt;从外观和重量上来说，想买一台比我原先的还让人不满意的电脑是挺难的。那个时候太care配置，想要榨干性价比。其实用了这么长时间发现其实配置啊她是浮云，关键还是要外表长得好看，然后不能太沉，抱着不能太吃力。&lt;/li&gt;
&lt;li&gt;这次的三鹿400脑残残型是集显了，再也不用为A卡的驱动苦恼了，而且我感觉现在的集显是不是比以前的独显还要强大。另外脑残残型也是667的内存，只有1G（居然也敢装Vista），正好把旧电脑上的内存拆下来发挥余热。这都是之前计划好的，没想到的是装内存的时候险些把塑料盖摧毁，看来以后还是得小心，得看说明书。&lt;/li&gt;
&lt;li&gt;新电脑继续用ubuntu，当然重装完成后lenovo care和无线网卡的灯都不明原因的永远不亮了。能用就好，灯不亮还比较含蓄。&lt;/li&gt;
&lt;li&gt;毕业论文第一稿写了八页就热情洋溢地给老师发过去了，结果几乎被骂被告知怎么也要二十页。再一打听，确实周围同学不是二十页就是三十页，你要用五号字都不好意思和别人打招呼。于是连续三天所谓无所不用其极，本着就不贴代码的原则，搞了一堆图片。今天闲谈起来居然听说有人致谢谢了两页，我原本以为谢全家也不过就是两三行的水平，看来，他们是真的像赵本山说的去谢祖先了。&lt;/li&gt;
&lt;li&gt;复习的进度有条不紊（其实我也想加快），今天又多亏许鼎鼎帮忙弄到了组成原理的课本还有离散和编译原理，现在纸张是足够了，就等写完blog去看了。我热切地期待着回去给大家当师弟的那一天。&lt;/li&gt;
&lt;li&gt;前段时间想戒除开心网瘾了，结果上次传说DNS被攻击之后偶的两台linux机器干脆直接不能访问了，倒是windows却依然正常，没有想到歧视的水平已然这么高了（我们不就是用免费的操作系统了吗，我们不就是用免费的操作系统还要了张免费CD吗，算了，第二句确实应该歧视）。用nslookup命令看呢，南京电信用的两台DNS 218.2.135.1和61.147.37.1，查www.kaixin001.com都是连接被拒绝。目前还没有发现其他域名有类似的情况，难道说这就是那啥冥冥之中吗？大概也是应该顺应天意的时候了||||&lt;/li&gt;
&lt;li&gt;最后简洁一条，今年生日过得不错。&lt;/li&gt;
&lt;/ol&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Lily Studio New Official Site</title><link href="https://sunng.info/blog/lily-studio-new-official-site.html" rel="alternate"></link><published>2009-05-08T00:00:00+08:00</published><updated>2009-05-08T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-05-08:/blog/lily-studio-new-official-site.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;friends&lt;/li&gt;
&lt;li&gt;LilyStudio
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;江山带有人才出啊，自从看到&lt;a href="http://remember.yo2.cn/" target="_parent"&gt;Remember2015&lt;/a&gt;和&lt;a href="http://www.lightory.net/" target="_parent"&gt;Lightory&lt;/a&gt;这么伟岸的人才们之后，我都不敢想像后面的人再出来以后我 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;friends&lt;/li&gt;
&lt;li&gt;LilyStudio
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;江山带有人才出啊，自从看到&lt;a href="http://remember.yo2.cn/" target="_parent"&gt;Remember2015&lt;/a&gt;和&lt;a href="http://www.lightory.net/" target="_parent"&gt;Lightory&lt;/a&gt;这么伟岸的人才们之后，我都不敢想像后面的人再出来以后我上哪蹲着去了。&lt;/p&gt;

&lt;p&gt;隆重推出工作室的全新官网：
&lt;a href="http://www.lilystudio.org"&gt;http://www.lilystudio.org/&lt;/a&gt;
如果没有搞错的话，美工Remember2015，编辑Lightory，这个组合太强大了，嗯。&lt;/p&gt;

&lt;p&gt;Any feedback? Try http://www.lilystudio.org/bbs/&lt;/p&gt;

&lt;p&gt;最后引用何勇的一句名言：感谢大家的工作！&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>早起，写一个变色的小程序</title><link href="https://sunng.info/blog/zao-qi-xie-yi-ge-bian-se-de-xiao-cheng-xu.html" rel="alternate"></link><published>2009-05-05T00:00:00+08:00</published><updated>2009-05-05T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-05-05:/blog/zao-qi-xie-yi-ge-bian-se-de-xiao-cheng-xu.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;shell
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;以前用Windows的时候，我是说用XP的时候，因为不支持颜色主题，也无从知晓如何通过自动化的方式更换Windows的主题，所以，经常 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;shell
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;以前用Windows的时候，我是说用XP的时候，因为不支持颜色主题，也无从知晓如何通过自动化的方式更换Windows的主题，所以，经常是人肉地每天换主题（其实只是为了换标题栏的颜色），追求新鲜感。&lt;br /&gt;
现在用Gnome桌面以后依然保持着这种爱好。遗憾是之前一直没有找到设置存放的位置。前两天在&lt;a href="http://www.commandlinefu.com" target="_parent"&gt;command-line-fu&lt;/a&gt;上恰好看到一个变换桌面背景的命令，原来这类配置都存放在gconf里，用gconftool就可以进行操作了。&lt;br /&gt;
然后很容易地就能在图形化配置管理器里找到颜色配置的键值位于/desktop/gnome/interface/gtk_color_scheme，值是一个用换行符隔开的名值对，内容就是我们在Appearance配置窗口里看到的那些。good，写个shell小程序每天换颜色！&lt;br /&gt;
以下是我孱弱的shell脚本：&lt;br /&gt;
[codesyntax lang="bash" lines="fancy"]
&lt;pre&gt;case `date +%w` in
    0) color='#C4C4A0A00000';;
    1) color='#CECE5C5C0000';;
    2) color='#8F8F59590202';;
    3) color='#4E4E9A9A0606';;
    4) color='#20204A4A8787';;
    5) color='#5C5C35356666';;
    6) color='#A4A400000000';;
esac&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;sedcommand="s/\(selected_bg_color:\)\(.*\)/\1$color/"&lt;/p&gt;

&lt;p&gt;color_scheme=`gconftool-2 -g /desktop/gnome/interface/gtk_color_scheme | sed $sedcommand`&lt;/p&gt;

&lt;p&gt;gconftool-2 -t string -s /desktop/gnome/interface/gtk_color_scheme "$color_scheme"
[/codesyntax]&lt;br /&gt;
每天一换，颜色是tango调色板里的色彩，HOHOHAHA&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>把wx的窗体绘图输出到位图</title><link href="https://sunng.info/blog/ba-wxde-chuang-ti-hui-tu-shu-chu-dao-wei-tu.html" rel="alternate"></link><published>2009-04-22T00:00:00+08:00</published><updated>2009-04-22T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-04-22:/blog/ba-wxde-chuang-ti-hui-tu-shu-chu-dao-wei-tu.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;wx
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;我想用这种低调的含糊不清的标题也没有什么坏处。&lt;/p&gt;

&lt;p&gt;问题很简单，我们要把在wx窗体上绘制的图形保存到一个位图文件中去。&lt;/p&gt;

&lt;p&gt;首先看一下wx的绘图机制，所有 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;python&lt;/li&gt;
&lt;li&gt;wx
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;我想用这种低调的含糊不清的标题也没有什么坏处。&lt;/p&gt;

&lt;p&gt;问题很简单，我们要把在wx窗体上绘制的图形保存到一个位图文件中去。&lt;/p&gt;

&lt;p&gt;首先看一下wx的绘图机制，所有和绘图相关的操作，都从wx.DC这样一个父类继承。DC即Device Context，文档里对这个类的功能有这样的描述
&lt;blockquote&gt;A wx.DC is a device context onto which graphics and text can be drawn. It is intended to represent a number of output devices in a generic way, so a window can have a device context associated with it, and a printer also has a device context. In this way, the same piece of code may write to a number of different devices, if the device context is used as a parameter.&lt;/blockquote&gt;
进而，这样的DC包括子类WindowDC，PrinterDC等等，用于在不同的设备上画图。WindowDC又有两个子类ClientDC和MemoryDC。在窗体上画图，就是在EVT_PAINT事件里，应该使用ClientDC的子类PaintDC。而MemoryDC就是在内存中的位图上绘图。要把窗体上的图形输出到位图文件中去，只要把PaintDC中的绘制的图形数据拷贝到MemoryDC上就可以了。&lt;/p&gt;

&lt;p&gt;以下是一个典型的OnPaint方法&lt;br /&gt;
[codesyntax lang="python" lines="fancy"]&lt;br /&gt;
class CanvasAlpha(wx.Panel):&lt;br /&gt;
    def __init__(self, parent):&lt;br /&gt;
        wx.Panel.__init__(self, parent, -1)&lt;br /&gt;
        self.SetBackgroundColour(wx.Color(255, 255, 255))&lt;br /&gt;
        self.Bind(wx.EVT_PAINT, self.OnPaint)&lt;/p&gt;

&lt;p&gt;    def OnPaint(self, evt):&lt;br /&gt;
        dc = wx.PaintDC(self)&lt;br /&gt;
        gc = wx.GraphicsContext.Create(dc)&lt;/p&gt;

&lt;p&gt;        ## draw ...&lt;br /&gt;
[/codesyntax]&lt;br /&gt;
在拷贝之前，首先创建一个新的位图并且指定他的宽高&lt;br /&gt;
bitmap = wx.EmptyBitmap(width, height, -1)&lt;br /&gt;
获得memoryDC&lt;br /&gt;
memDC = wx.MemoryDC()&lt;br /&gt;
指定memoryDC的内存对象&lt;br /&gt;
memDC.SelectObject(bitmap)&lt;br /&gt;
利用DC中一个非常重要的Blit方法，把PaintDC上的图形轻松地拷贝到MemoryDC上（关于参数的意义请参考wx的文档了）&lt;br /&gt;
memDC.Blit(0, 0, width, height, dc, 0, 0)&lt;br /&gt;
这还没有完，将memoryDC的内存对象设置为null，即传入一个null对象。这一步相当于flush。&lt;br /&gt;
memDC.SelectObject(wx.NullBitmap)&lt;br /&gt;
最后，用bitmap的Save方法就可以非常轻松地输出图片了&lt;br /&gt;
bitmap.SaveFile(filename, wx.BITMAP_TYPE_JPEG)&lt;/p&gt;

&lt;p&gt;这样，在窗口中截图也就实现了。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>这就是生活</title><link href="https://sunng.info/blog/zhe-jiu-shi-sheng-huo.html" rel="alternate"></link><published>2009-04-19T00:00:00+08:00</published><updated>2009-04-19T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-04-19:/blog/zhe-jiu-shi-sheng-huo.html</id><content type="html">&lt;ul&gt;
&lt;li&gt;留影
tags:&lt;/li&gt;
&lt;li&gt;D60&lt;/li&gt;
&lt;li&gt;Photos
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a title="来YUPOO看我的照片" href="http://www.yupoo.com/photos/view?id=ff80808120a21f410120bceff5343024"&gt;&lt;img src="http://pic.yupoo.com/classicning/2707974e3267/medium.jpg" border="0" alt="DSC_0092.resized" width="500" height="335" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a title="来YUPOO看我的照片" href="http://www.yupoo.com/photos/view?id=ff80808120a21f410120bceff5a73025"&gt;&lt;img src="http://pic.yupoo.com/classicning/6559674e3267/medium.jpg" border="0" alt="DSC_0096.resized" width="500" height="335" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a title="来YUPOO看我的照片" href="http://www.yupoo.com/photos/view?id=ff80808120a21f410120bceff62f3026"&gt;&lt;img src="http://pic.yupoo.com/classicning/1559774e3267/medium.jpg" border="0" alt="DSC_0097.resized" width="500" height="335" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a title="来YUPOO看我的照片" href="http://www.yupoo.com/photos/view?id=ff80808120a21f410120bceff6923027"&gt;&lt;img src="http://pic.yupoo.com/classicning/8321874e3267/medium.jpg" border="0" alt="DSC_0099.resized" width="500" height="335" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a title="来YUPOO看我的照片" href="http://www.yupoo.com/photos/view?id=ff80808120a21f410120bceff7083028"&gt;&lt;img src="http://pic.yupoo.com/classicning/3035274e3268/medium.jpg" border="0" alt="DSC_0100.resized" width="500" height="335" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a title="来YUPOO看我的照片" href="http://www.yupoo.com/photos/view?id=ff80808120a21f410120bceff75b3029"&gt;&lt;img src="http://pic.yupoo.com/classicning/6112274e3267/medium.jpg" border="0" alt="DSC_0101.resized" width="500" height="335" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a title="来YUPOO看我的照片" href="http://www.yupoo.com/photos/view?id=ff80808120a21f410120bceff7d1302a"&gt;&lt;img src="http://pic.yupoo.com/classicning/7376674e3267/medium.jpg" border="0" alt="DSC_0103.resized" width="500" height="335" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a title="来YUPOO看我的照片" href="http://www.yupoo.com/photos/view?id=ff80808120a21f410120bceff8c7302c"&gt;&lt;img src="http://pic.yupoo.com/classicning/0262174e3269/medium.jpg" border="0" alt="DSC_0106.resized" width="500" height="335" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a title="来YUPOO看我的照片" href="http://www.yupoo.com/photos/view?id=ff80808120a21f410120bceff996302e"&gt;&lt;img src="http://pic.yupoo.com/classicning/6751174e3269/medium.jpg" border="0" alt="DSC_0108.resized" width="500" height="335" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a title="来YUPOO看我的照片" href="http://www.yupoo.com/photos/view?id=ff80808120a21f410120bceffa4a3030"&gt;&lt;img src="http://pic.yupoo.com/classicning/4131874e3269/medium.jpg" border="0" alt="DSC_0110.resized" width="500" height="335" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a title="来YUPOO看我的照片" href="http://www.yupoo.com/photos/view?id=ff80808120a21f410120bceffaa83031"&gt;&lt;img src="http://pic.yupoo.com/classicning/8581174e3269/medium.jpg" border="0" alt="DSC_0111.resized" width="500" height="335" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a title="来YUPOO看我的照片" href="http://www.yupoo.com/photos/view?id=ff80808120a21f410120bceffb213032"&gt;&lt;img src="http://pic.yupoo.com/classicning/1083774e3269/medium.jpg" border="0" alt="DSC_0112.resized" width="500" height="335" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a title="来YUPOO看我的照片" href="http://www.yupoo.com/photos/view?id=ff80808120a21f410120bceffd893036"&gt;&lt;img src="http://pic.yupoo.com/classicning/8879974e3269/medium.jpg" border="0" alt="DSC_0117.resized" width="500" height="335" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a title="来YUPOO看我的照片" href="http://www.yupoo.com/photos/view?id=ff80808120a21f410120bceffdeb3037"&gt;&lt;img src="http://pic.yupoo.com/classicning/0634574e3269/medium.jpg" border="0" alt="DSC_0118.resized" width="500" height="335" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a title="来YUPOO看我的照片" href="http://www.yupoo.com/photos/view?id=ff80808120a21f410120bceffe3a3038"&gt;&lt;img src="http://pic.yupoo.com/classicning/6126974e3269/medium.jpg" border="0" alt="DSC_0119.resized" width="500" height="335" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a title="来YUPOO看我的照片" href="http://www.yupoo.com/photos/view?id=ff80808120a21f410120bceffeb13039"&gt;&lt;img src="http://pic.yupoo.com/classicning/8238474e326a/medium.jpg" border="0" alt="DSC_0121.resized" width="500" height="335" /&gt;&lt;/a&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Yes, it is</title><link href="https://sunng.info/blog/yes-it-is.html" rel="alternate"></link><published>2009-04-18T00:00:00+08:00</published><updated>2009-04-18T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-04-18:/blog/yes-it-is.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;football&lt;/li&gt;
&lt;li&gt;friends&lt;/li&gt;
&lt;li&gt;Nanjing&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;我发现&lt;a title="Samson" href="http://blog.samsonis.me/"&gt;samson&lt;/a&gt;的blog写法是effective的，于是我决定山寨他。
&lt;ol&gt;
    &lt;li&gt;我又回南京了，虽然对某些组织我还宣称自己身在外地。这次 …&lt;/li&gt;&lt;/ol&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;football&lt;/li&gt;
&lt;li&gt;friends&lt;/li&gt;
&lt;li&gt;Nanjing&lt;/li&gt;
&lt;li&gt;ubuntu
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;我发现&lt;a title="Samson" href="http://blog.samsonis.me/"&gt;samson&lt;/a&gt;的blog写法是effective的，于是我决定山寨他。
&lt;ol&gt;
    &lt;li&gt;我又回南京了，虽然对某些组织我还宣称自己身在外地。这次可以多留一段时间，南京还是非常好的。我家出来这里就是两条河，波光粼粼，现在又是绿色最绿青草更青的季节，凉风徐徐树影婆娑，春光明媚心潮暗涌，让人没法不热爱生活。&lt;/li&gt;
    &lt;li&gt;回家之后我吹牛说我妈已经可以从厨房退休了。这两天午饭都是我把我妈关在外面，一人独自在厨房里意气风发挥斥方遒。除了切出了钢笔一样的肉丝而且最后根本咬不动以外，其他都还是一切安好的。可歌可泣的是，再也没有没有糊味从厨房传出来了。&lt;/li&gt;
    &lt;li&gt;昨天再超市突然发现地上有个秤，于是迈上我轻盈的步伐piapia地站上去，指针的速度那叫一风驰电掣，它在越过70之后依然没有慢下来的迹象。想知道最后它停在哪了吗，移动用户你就编辑短信去吧。&lt;/li&gt;
    &lt;li&gt;除了毕业要做的东西，这两天在看微机原理，看CPU指令系统看内存寻址啥啥的。我要是说自己看不懂是不是太丢人了，那我就说看不太懂吧。好像还不知道考什么，下周找pipitu这样的成功人士求经了。顺便把我北京的公交卡郑重地交给她，里面的钱正好够她从火车站到五道口。嗯，另外20块押金的事情面谈。&lt;/li&gt;
    &lt;li&gt;今天终于不能忍了，开始升级ubuntu9.04，现在两岸人民虽然还是血浓于水，但是tw.archive源确实没有以前快了。结果换了一个源，到目前为止快6个小时了，1400个软件包还是没有下载完（刚才变成1700了）。反正现在知道的，xorg1.6了，ati的fglrx驱动是不能用了，compositing没了，但一样可以欣赏朴素的美。&lt;/li&gt;
    &lt;li&gt;然后昨天舜天队赢球了，超乎想像异乎寻常，客场两球干净利落地拿下领头羊天津。下周主场对河南我是无论如何要亲临现场了，单身看球，看一场少一场了。&lt;/li&gt;
    &lt;li&gt;人生啊。&lt;/li&gt;
&lt;/ol&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>cp bookmakes to another pdf file</title><link href="https://sunng.info/blog/cp-bookmakes-to-another-pdf-file.html" rel="alternate"></link><published>2009-03-25T00:00:00+08:00</published><updated>2009-03-25T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-03-25:/blog/cp-bookmakes-to-another-pdf-file.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;pdf
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;p&gt;其实这事不好。&lt;/p&gt;
&lt;p&gt;看电子书的时候，有的没有Bookmake书签，或者叫目录也成。这样读起来就非常疲劳，经常一恍惚就 …&lt;/p&gt;&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;把戏
tags:&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;pdf
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;p&gt;其实这事不好。&lt;/p&gt;
&lt;p&gt;看电子书的时候，有的没有Bookmake书签，或者叫目录也成。这样读起来就非常疲劳，经常一恍惚就根本不知道读到哪里了，上下文都弄不清怎么办。这种问题尤其对于一本1500页的电子书来说更严重。想办法自己加书签吧，reader根本不提供这个功能。这能自立更生了。&lt;/p&gt;
&lt;p&gt;对于apress的电子书（我在写这些的时候内心很痛苦，作为一个第三世界的民工，这个出版社的书我一直是看电子版），如果电子书没有书签，可以从apress的官方网站上下载目录，这个目录恰好就包含书签。这样的话只要把这个书签拷贝到我们之前的电子书里就可以了。&lt;/p&gt;
&lt;p&gt;于是需要一个能够读写pdf文件的库，我找到一个pdfbox，它现在是apache incubator中的一个项目，貌似签到apache之后还没有一个正式的release。上一个release可以在sourceforge上&lt;a href="http://sourceforge.net/projects/pdfbox/" target="_blank"&gt;项目原来的页面&lt;/a&gt;找到，版本号我不记得了。&lt;/p&gt;
&lt;p&gt;最近比较怀旧直接用Java写。其实很简单，pdfbox都已经建模了，只要取出来、放进去、写下来（ZB一些应该是get出来，set进去，write下来）就可以了。以下是个sample&lt;/p&gt;
[codesyntax lang="java" lines="fancy"]&lt;br /&gt;
public class Main {&lt;/p&gt;

&lt;p&gt; /**&lt;br /&gt;
     * @param args&lt;br /&gt;
     */&lt;br /&gt;
    public static void main(String[] args) throws Exception{&lt;/p&gt;

&lt;p&gt;     FileInputStream ctfile = new FileInputStream("E:\\cs_content.pdf");&lt;br /&gt;
        PDFParser parser = new PDFParser(ctfile);&lt;br /&gt;
        parser.parse();&lt;br /&gt;
        PDDocument ctpdf = parser.getPDDocument();&lt;/p&gt;

&lt;p&gt;     PDDocument rlpdf = PDDocument.load("E:\\cs.pdf");&lt;/p&gt;

&lt;p&gt;     PDDocumentOutline outline = ctpdf.getDocumentCatalog().getDocumentOutline();&lt;br /&gt;
        rlpdf.getDocumentCatalog().setDocumentOutline(outline);&lt;/p&gt;

&lt;p&gt;     FileOutputStream out = new FileOutputStream("E:\\cs_out.pdf");&lt;br /&gt;
        COSWriter writer = new COSWriter(out);&lt;br /&gt;
        writer.write(rlpdf);&lt;br /&gt;
        writer.close();&lt;br /&gt;
    }&lt;br /&gt;
}
[/codesyntax]
&lt;p&gt;非常简单，就不解释了。只是有一个小小的问题（其实相当严重），Apress的目录文件里bookmark是没有设置destination的（这也是ZB的说法，其实就是点了没反应）。我想了一下如果要让书签真正起到作用要去读pdf的目录页面，那里面有页码，虽然这个页码和pdf的页码有时候会有一个固定的偏移。不过读pdf似乎有那么一点麻烦，因为里面有不少控制字符。&lt;/p&gt;
&lt;p&gt;暂时就是这样。不知道有没有其他什么更好的办法。&lt;/p&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>上海这几天</title><link href="https://sunng.info/blog/shang-hai-zhe-ji-tian.html" rel="alternate"></link><published>2009-03-14T00:00:00+08:00</published><updated>2009-03-14T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-03-14:/blog/shang-hai-zhe-ji-tian.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;到上海一个星期了，借宿在亲戚家里没有办法上网。所以一直不能更新，这种状况从现在起还要延续将 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;Life
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;到上海一个星期了，借宿在亲戚家里没有办法上网。所以一直不能更新，这种状况从现在起还要延续将近一个月左右。&lt;/p&gt;

&lt;p&gt;最近已经在新公司开始被培训了。情况简单地来说就是不太理想，即将面对的困难、问题比想象中的还要多还要复杂。&lt;strong&gt;画外音：出于安全的考虑希望大家不要在留言中透露公司的名字&lt;/strong&gt;。这种时候呢就越发怀念之前在北京CRL实习时候的经历，相比相对照实在是点点点点点。当然在现在这里也是机会与挑战并存，只是可能要面对不少单纯技术之外的挑战。也有好处，催化成熟，促进衰老。&lt;/p&gt;

&lt;p&gt;倒是在这种环境里更加坚定了我的决心，之前一直抱怨没有足够的时间能够认认真真做一些事情。现在恐怕还是没有时间，但是为了个人以后的发展，也不能有什么顾虑了，该学习的要学习，该花时间花精力的也不能无奈了。万事开头难，有意一起开头的请留言或Email，咱们一起找个头开。&lt;/p&gt;

&lt;p&gt;上海在我眼里是个比较变态的地方，也是真正到了这里我才发现自己虽然不至于热爱山洞里的居住，但是也和这样所谓现代大都市格格不入。在北京的时候还有文化上的偏好和归属感，以及对人生对未来的希望在左右，到了上海没有了这二者虚无飘渺的光环，生活啊，它一下就变得很艰难很残酷了。呵呵。&lt;/p&gt;

&lt;p&gt;也就是这种气氛，让人不由自主地去想未来改变这种生活状态的手法和途径，然而就像高旗唱的“对明天的恐惧来自对今天的厌倦”一样，迷茫，不知所措。呵呵。惟望后来者珍惜眼前的生活，把握提升自我的机会，依靠自己的努力去获得一个自己满意的过程和结果。&lt;/p&gt;

&lt;p&gt;以上皆真情流露，如有扯淡，也有可能。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>看图不说话</title><link href="https://sunng.info/blog/kan-tu-bu-shuo-hua.html" rel="alternate"></link><published>2009-03-04T00:00:00+08:00</published><updated>2009-03-04T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-03-04:/blog/kan-tu-bu-shuo-hua.html</id><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;opera
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;EXE是Windows上的可执行文件，那么在Linux上系统默认是如何对待它的呢。请看下图。&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" title="看图不说话" src="http://pic.yupoo.com/classicning/7941771206a6/onmtjqmk.jpg" alt="" width="440" height="256" /&gt;&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Gwibber and WebKit</title><link href="https://sunng.info/blog/gwibber-and-webkit.html" rel="alternate"></link><published>2009-03-03T00:00:00+08:00</published><updated>2009-03-03T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-03-03:/blog/gwibber-and-webkit.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;css&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;twitter&lt;/li&gt;
&lt;li&gt;web&lt;/li&gt;
&lt;li&gt;webkit
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Gwibber是一个用来接收流行的web2.0应用的消息的客户端。（靠，这句绝望了。）今天LinuxToy才刚刚介绍，不过我前几天就发现了，哦hohohoho。&lt;/p&gt;

&lt;p&gt;Gwibber的架构很容易扩展，稍微看一看就清楚了，我就不描 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;css&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;twitter&lt;/li&gt;
&lt;li&gt;web&lt;/li&gt;
&lt;li&gt;webkit
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Gwibber是一个用来接收流行的web2.0应用的消息的客户端。（靠，这句绝望了。）今天LinuxToy才刚刚介绍，不过我前几天就发现了，哦hohohoho。&lt;/p&gt;

&lt;p&gt;Gwibber的架构很容易扩展，稍微看一看就清楚了，我就不描述了。正好饭否和twitter的接口是保持一致的，拷拷改改很容易就可以给Gwibber增加饭否的支持。如此一来就可以同时看饭否看twitter发饭否发twitter了。&lt;/p&gt;

&lt;p&gt;你以为这是一张截图，其实它是分隔线。&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone" title="Gwibber with fanfou support" src="http://pic.yupoo.com/classicning/15491710661a/xaq77l24.jpg" alt="" width="338" height="528" /&gt;&lt;/p&gt;

&lt;p&gt;注意上图，gwibber是一个简单的pyGtk程序加上Webkit，它的核心部分就是html网页。这个网页除了头像以外可是没有图片的，那几个效果是怎么实现的呢。一个一个说。
&lt;ul&gt;
    &lt;li&gt;圆角 -webkit-border-radius: 7px;&lt;/li&gt;
    &lt;li&gt;文字阴影 text-shadow: 1px 1px black;&lt;/li&gt;
    &lt;li&gt;渐变 background:  -webkit-gradient(linear, left top, left 220%, from(rgba(254, 240, 152, 0.9)), to(black));&lt;/li&gt;
&lt;/ul&gt;
比较神奇的就是这三个属性。所以也难怪现在用WebKit核心的程序（不完全是浏览器）越来越多。没有记错的话，这三个丰富特性除了text-shadow在Firefox3.1开始支持其他的Gecko都还不支持。&lt;/p&gt;

&lt;p&gt;总而言之，用CSS就可以绘制出精致的图形，而且完全可以生长在桌面。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>最后一课</title><link href="https://sunng.info/blog/zui-hou-yi-ke.html" rel="alternate"></link><published>2009-03-02T00:00:00+08:00</published><updated>2009-03-02T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-03-02:/blog/zui-hou-yi-ke.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;NJU
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;因为下周就要到新的公司实习了，算算时间今天是最后一次在学校上课，没准就是最后一次坐在课堂 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;当时
tags:&lt;/li&gt;
&lt;li&gt;NJU
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;因为下周就要到新的公司实习了，算算时间今天是最后一次在学校上课，没准就是最后一次坐在课堂上绝望了。&lt;/p&gt;

&lt;p&gt;应该说起来上了这么多年课，真正到了这最后一次，还是挺容易感慨的。为了防止感慨，这最后一节课就在看小说中度过了。仔细回忆一下，我上了这么多年课，虽然也有上课看小说的经历，不过肯定是比多数人要少的。我对自己高中和高中之前上课的态度以及对老师的尊重还是有足够的信心的。&lt;/p&gt;

&lt;p&gt;真要离开学校了，实际上还是非常遗憾的，我对自己肚子里的油水还是有数的，不会的东西要学的东西太多了，实在是太多了。我是多希望能够在学校里哪怕只再多留一年，一年足以改变很多情况了。去年下半年的实习生涯让我知道一旦出来，精力就非常有限了，恐怕是很难有时间系统地去扎实基础了。在学校的时候往往不能明白这个道理，等到失去的时候才追悔莫及。为什么不考研呢，实在是没有心气和信心去面对考试了。一年级的数学分就不高，二年级的数理统计分更是突破心理底线了（我到现在也只是承认分不高，不接受没学好的事实，当然可能这两者本身区别不大，尤其是放下这么长时间以后，确实没什么区别了）。之后的考试也是一塌糊涂，更是不能忍受那种没有自觉意识的背书过程。总之，这么早早地告别校园也是自己的选择，后果自然是自负。&lt;/p&gt;

&lt;p&gt;今天有个好消息是，我们班考本校研究生的同学悉数过关，皆大欢喜。这个是不错的事情，如果落榜今年确实是不方便找到好的出路。&lt;/p&gt;

&lt;p&gt;我只能希望以后如果有同学聚会的话，不会感到非常尴尬。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Location Support on Empathy</title><link href="https://sunng.info/blog/location-support-on-empathy.html" rel="alternate"></link><published>2009-03-01T00:00:00+08:00</published><updated>2009-03-01T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-03-01:/blog/location-support-on-empathy.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;location
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;好久不写了 ||&lt;/p&gt;

&lt;p&gt;昨天看到了这么一篇介绍Gnome桌面上对地理位置的支持的文章（汗这句子）。地址在&lt;a href="http://bergie.iki.fi/blog/making_the_gnome_desktop_location-aware/" target="_blank"&gt;这里&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;我印象中最早的就是Gnome 2.24开始那个日期applet增加的一个地图，可 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;gnome&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;location
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;好久不写了 ||&lt;/p&gt;

&lt;p&gt;昨天看到了这么一篇介绍Gnome桌面上对地理位置的支持的文章（汗这句子）。地址在&lt;a href="http://bergie.iki.fi/blog/making_the_gnome_desktop_location-aware/" target="_blank"&gt;这里&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;我印象中最早的就是Gnome 2.24开始那个日期applet增加的一个地图，可以选择和设置当前的location，于是有了比较好的时区支持和天气服务。文章里还提到居然Tracker搜索也支持这么一个地理元信息，可以搜出在某个地方（是说地球上的某个地方）编辑的文件。这个很强。&lt;/p&gt;

&lt;p&gt;然后就是Empathy对location的支持了。这个看起来很强大，截图和介绍可以看这里看这里看&lt;a href="http://blog.pierlux.com/2009/01/22/empathy-where-are-you/en/" target="_blank"&gt;这里&lt;/a&gt;。Empathy可以用来发布你evolution的联系人地址。话说，很容易就想起来了，XMPP协议有一个地理位置的扩展。不过文中说几乎除了GoogleTalk其他都支持地理位置扩展。好家伙，这个又只能遥远地YY了。&lt;/p&gt;

&lt;p&gt;文中提到的提供LBS的库叫做Geoclue，而在Gnome上显示地图是一个叫做libchamplain的widget。而地图，当然毫无疑问是OSM了。有兴趣的朋友可以去关注。&lt;/p&gt;

&lt;p&gt;没了，静静等待这个Empathy变得越来越强大吧。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>SQuirrel SQL Client 3发布了</title><link href="https://sunng.info/blog/squirrel-sql-client-3fa-bu-liao.html" rel="alternate"></link><published>2009-02-22T00:00:00+08:00</published><updated>2009-02-22T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-02-22:/blog/squirrel-sql-client-3fa-bu-liao.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;swing&lt;/li&gt;
&lt;li&gt;tools
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;SQuirrel SQL Client是一个用Java写的数据库客户端，用JDBC统一数据库访问接口以后，可以通过一个统一的用户界面来操作MySQL PostgreSQL MSSQL Oracle等等任何支持JDBC访问的数据库。使用起来非常方便。而且，SQuirrel SQL Client还是一个典型的Swing程序，也 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;装备
tags:&lt;/li&gt;
&lt;li&gt;foss&lt;/li&gt;
&lt;li&gt;java&lt;/li&gt;
&lt;li&gt;swing&lt;/li&gt;
&lt;li&gt;tools
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;SQuirrel SQL Client是一个用Java写的数据库客户端，用JDBC统一数据库访问接口以后，可以通过一个统一的用户界面来操作MySQL PostgreSQL MSSQL Oracle等等任何支持JDBC访问的数据库。使用起来非常方便。而且，SQuirrel SQL Client还是一个典型的Swing程序，也算是Swing的一个比较成功的应用了。&lt;/p&gt;

&lt;p&gt;2.x版本的SQuirrel SQL Client一直是用JDesktopPane和JInternalFrame来组织界面。这次发布的3.0最大的变化就是改用现在更加流行的Dock方式组织窗口。&lt;/p&gt;

&lt;p&gt;07年开始我一直用这个工具，后来在某处实习时，意外地发现组里以前也有人用这个工具（不过后来没有人再用了），一问，原来也是之前我们学校的同学。看来，一个学校的果然还是有一点相同，呵呵。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>Python读取二进制文件</title><link href="https://sunng.info/blog/pythondu-qu-er-jin-zhi-wen-jian.html" rel="alternate"></link><published>2009-02-20T00:00:00+08:00</published><updated>2009-02-20T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-02-20:/blog/pythondu-qu-er-jin-zhi-wen-jian.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;c&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Python里虽然可以通过'b'的标记来区别普通文件和二进制文件，但是依然是把二进制文件当作普通文本处理。要读写二进制文件，需要用struct库来作pack和unpack。&lt;/p&gt;

&lt;p&gt;比如我最近拿到一个 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;手艺
tags:&lt;/li&gt;
&lt;li&gt;c&lt;/li&gt;
&lt;li&gt;python
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Python里虽然可以通过'b'的标记来区别普通文件和二进制文件，但是依然是把二进制文件当作普通文本处理。要读写二进制文件，需要用struct库来作pack和unpack。&lt;/p&gt;

&lt;p&gt;比如我最近拿到一个如下数据结构的二进制文件&lt;/p&gt;

&lt;p&gt;[codesyntax lang="c" lines="fancy"]&lt;br /&gt;
struct DEMTYPE {&lt;br /&gt;
    int row;&lt;br /&gt;
    int col;&lt;br /&gt;
    float xmin;&lt;br /&gt;
    float ymin;&lt;br /&gt;
    float xmax;&lt;br /&gt;
    float ymax;&lt;br /&gt;
    float size;&lt;br /&gt;
    float vmin;&lt;br /&gt;
    float vmax;&lt;br /&gt;
    float scale;&lt;br /&gt;
    float *data;&lt;br /&gt;
};&lt;br /&gt;
[/codesyntax]&lt;/p&gt;

&lt;p&gt;其中data是一个长度为row*col的数组。要读取这样一个二进制文件，可以用这样的代码&lt;br /&gt;
[codesyntax lang="python" lines="fancy"]&lt;br /&gt;
row, col, xmin, ymin, xmax, ymax, size, vmin, vmax, scale = \&lt;br /&gt;
        struct.unpack('&amp;lt;2i8f', datastring[:(4*2+4*8)])&lt;br /&gt;
vdata = struct.unpack('&amp;lt;'+str(row*col)+'f', datastring[(4*2+4*8):])&lt;br /&gt;
[/codesyntax]&lt;br /&gt;
核心是unpack的第一个参数，用来标识二进制数据的格式，其中&amp;lt;表示little-endian，i表示整型数，f表示单精度浮点数，数字是量词。&lt;/p&gt;

&lt;p&gt;更详细的说明可以看manual：
&lt;a href="http://docs.python.org/library/struct.html" target="_blank"&gt;http://docs.python.org/library/struct.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;好吧，我承认我写这个是来测试highlight插件的。&lt;/p&gt;</content><category term="misc"></category></entry><entry><title>野火烧不尽，草木花鸟鱼</title><link href="https://sunng.info/blog/ye-huo-shao-bu-jin-cao-mu-hua-niao-yu.html" rel="alternate"></link><published>2009-02-18T00:00:00+08:00</published><updated>2009-02-18T00:00:00+08:00</updated><author><name>Ning Sun</name></author><id>tag:sunng.info,2009-02-18:/blog/ye-huo-shao-bu-jin-cao-mu-hua-niao-yu.html</id><summary type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;blog&lt;/li&gt;
&lt;li&gt;friends&lt;/li&gt;
&lt;li&gt;www
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Blog被封很长时间了，让大家担心了，不知道的朋友恐怕还以为我在单向街书店被捅了吧，好在那位不是我，我还没有 …&lt;/p&gt;</summary><content type="html">&lt;ul&gt;
&lt;li&gt;自话
tags:&lt;/li&gt;
&lt;li&gt;blog&lt;/li&gt;
&lt;li&gt;friends&lt;/li&gt;
&lt;li&gt;www
published: true
comments: true&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Blog被封很长时间了，让大家担心了，不知道的朋友恐怕还以为我在单向街书店被捅了吧，好在那位不是我，我还没有恶心到把那个部位当作我的网名。&lt;/p&gt;

&lt;p&gt;经过一系列折腾，我现在终于搬到好友WG的地盘上继续写东西了。原本大算使用habari来做blog系统，但是没有部署成功，对PHP/Apache的问题我只能望洋兴叹。最后还是随了大流，用了Wordpress。随大流对我来说是一件困难的事情，为此我经历了相当痛苦的抉择。&lt;/p&gt;

&lt;p&gt;其实原有的文章我已经在网站被封之前抢救下来了，并且在habari中实现了恢复。不过既然habari部署搞不定，那也就算了，毕竟以前的东西在新的系统里总有些不伦不类。而且更重要的是，大家的评论我没有精力恢复了。既然没有了大家的评论，以前的blog也就不重要了，嗯。将近两年的log我就自己收藏了，不在与大家分享了，也算是我即将告别校园，有一些历史数据出于安全考虑需要封存。&lt;/p&gt;

&lt;p&gt;blog刚刚架起来，还有很多东西要改。最后十分感谢WG在为难时刻出手相助。&lt;/p&gt;</content><category term="misc"></category></entry></feed>