使用defrecord与defprotocol的注意事项

简单地说,protocol是clojure中的接口,record是clojure中的数据类型。

可以通过这样的code定义一个protocol

(defprotoco DummyProtocol
  "doc string..."
  (method-one [self x] "doc string..."))

需要注意的是,protocol里所有方法的第一个参数都是self/this参数(类似python),从第二个开始才是调用时传入的参数。如果方法要重载呢?

(defprotocol DummyProtocol
  "doc string..."
  (method-one [self x] [self x y] "doc string")
)

Apress的 Practical Clojure 书里的例子,给重载的参数表加上了括号,这样会导致编译错误(注记)。

定义一个record实现protocol

(defrecord DummyRecord [a b c]
  DummyProtocol
  (method-one [self x] (+ a x))
  (method-one [self x y] (+ a x y)))

Practical Clojure里关于这部分的代码,又丢掉了self参数(注记)。

最后还有一个问题,如果直接use你的ns,你会发现调用record时出现:
java.lang.IllegalArgumentException: Unable to resolve classname: DummyRecord

怎么回事,不是都use了吗?原因是record被编译成了java对象,所以引用时要用java对象的引用方式,import之。

HeatCanvas support for Leaflet

Leaflet is a light weight web mapping library developed by Cloudmade. Leaflet is designed for compatibility with both desktop browser and mobile browser.

HeatCanvas-Leaflet extension enables heat map on Leaflet. You can create heat map layer and add it to Leatlet map:

var heatmap = new L.TileLayer.HeatCanvas("Heat Canvas", map, {},
                        {'step':0.3, 'degree':HeatCanvas.QUAD, 'opacity':0.7});
heatmap.pushData(32.1104, 118.0852, 14);
//push more data ...
map.addLayer(heatmap);

You can find live demo on:
http://sunng87.github.com/heatcanvas/leaflet.html
Mobile browser is also supported. (Tested on Firefox Android and default Android browser)

Find more about HeatCanvas on github page.

Leaflet is still buggy for extension. There is a latlng-pixel coordinate conversion issue in low zoom level, affects this demo. Hope it could be fixed soon.

工作第二年

这个周末就是我正式工作两年的日子了,第二年过得稍微有些起伏,晚上写了一点总结。但是刚才读了一下觉得不太满意,也不太合适发出来。这样,即将跨入三年级的时候,我就改改风格谈谈愿望吧。

1. 把日常工作做好,积累技术,流程,项目管理的经验
2. 业余时间拓展周边领域的知识:数据挖掘,数据分析,可视化方面
3. 希望能主导去做成一点有始有终的事情
4. 希望不久的将来能参与一个志同道合齐心协力(self-motivated)的团队一起做好一个产品

Staged Event-Driven Architecture

按照传统的编写应用程序的思路,当server接到请求,包装完成之后,分配到线程池中交给一个线程完成,返回。Java的servlet容器就是这么设计的,这么多年大部分的应用程序也是在这种模式下编写的。当任务的代价比较大,比如多次和数据库交互,并且可能和后端其他服务通信,这是线程被任务占用的时间就相对较长。如果请求的并发量很大,容器的线程池耗尽,新的请求就无法被处理,导致并发性无法提高,吞吐量也无法提高。

实际上在大部分应用程序里,所谓代价很高的任务,往往都是I/O Bound。涉及网络通信,磁盘读写,线程被迫wait,CPU却是空闲的。所以,I/O Bound的程序理论上都还有优化的空间。于是有了这种Staged Event-Driven Architecture,即SEDA。

SEDA的思路是将原先由一个线程完成的任务,分割为相对独立的多个阶段。每个阶段由专用的一组线程负责执行,阶段之间用过队列交互。又是上次提到的老话:If you cannot split it, you cannot scale it.

例如,我们的业务逻辑是读取请求,操作数据库,与后端通信,操作数据库,写回结果。如果采用NIO的方式,网络通信可以认为是非阻塞的。而目前与数据库的交互,通常还是阻塞式的风格。这样这五个阶段分别是非阻塞、阻塞、非阻塞、阻塞、非阻塞。组塞操作容易成为性能瓶颈,CPU时间用于等待,占用率不高,所以对组塞操作可以分配相对多的线程;非阻塞操作速度较快,为了避免快速的切换导致sy升高,采用和CPU核数一样多的线程即可。

这样,原本一个线程反复等待的阻塞操作,变成了部分线程等待的同时,其他线程仍然在处理自己的业务,有效使用CPU。利用的多核的优势,提高了CPU的占用,即提高了系统的处理能力。

而对外部接口来说,当读取数据的线程在读取完毕之后,将任务dispatch到相应队列即返回,前端可以保证很高的处理速度,并发性也可以保证。任务被积压在阻塞操作的队列中,而消费阻塞操作的线程要多于提供者,在一定程度上也保证了处理速度。再退一步说,当阻塞操作的速度却是无法消费大量任务时,前端可以根据队列的大小判断当前系统的load,拒绝服务。

当然,采用这种方式,只有在并发量提高到一定程度,并发成为系统瓶颈时才能体现价值。就单个操作而言,由于队列的传递,他的latency一定是有所上升的。

关于SEDA,可以参考Matt Welsh的相关论文:The Staged Event-Driven Architecture for Highly-Concurrent Server Applications。

自制山寨摩卡

一共也没去过几次咖啡店,但是几乎每次要的都是摩卡,感情特深。所谓摩卡就是不明颗粒+奶油(optional)+牛奶+咖啡+巧克力(排名分先后)。我晕,扯这些没用的干啥,先上图:
39171a47e65850f64ab50b4188e3c1846d177c05_wmeg_00001

这是自制摩卡,奶油暂时还做不来,所以放弃了。不明颗粒太麻烦,就拿巧克力代替了。

值得一提的是这次的巧克力是从可可粉熬过来的。先拿一个小的奶锅装一点牛奶,待烧开以后加两勺可可粉和一定量的白糖,然后就在小火上搅拌即可。有点类似过去搅咖哩,你不搅它它必然糊掉。等到液体粘稠到一定程度以后出锅就可以了。在实际操作里,我的牛奶加多了,以至于最后也没有真正的粘稠,这个过程有待优化。如果觉得这个过程麻烦的同学,可以直接买巧克力酱,或者拿现成的巧克力熬。当然如果你从可可粉开始熬的话,可定制性更强一些。这个直接JDBC,或是用Hibernate类似。

出锅的巧克力放一边,凉了也不太要紧,我还滴了一点香草糖浆,不过似乎那味道只有在一开始,后来就消失了。

拿一个大一些的杯子,把巧克力倒进去垫底。继续做咖啡,打牛奶。完成以后依次把咖啡和牛奶倒进杯子。摩卡好像不需要太多泡沫,打牛奶的时候可以收敛一些,不过看个人爱好了。我现在还处在杯子里没有泡沫就没有成就感的阶段,so

这个山寨版本还是很简单的,连我都可以轻松搞定。如果你觉得太山寨了,实在看不下去,千万不要人身攻击我。。。

味道?还行吧。

Edit(20100711):老师指点说你没有可可脂是永远熬不出粘稠的感觉的。我明白真相了。