2010年6月19日星期六

2010/6/20 一种 Database+程序 的ID生成策略

特点:持久化、通用性、性能好、多进程共用。
这种实现保证了id递增的唯一性,并且多个VM同时使用同一个Database也没问题,vm突然强制关闭也没问题。

方法概述:采用 Database+VM(表示一个程序)结合的策略生成ID,生成的ID为递增的整数。用Database做id生成的持久层,采用vm进行分段递增。由程序负责一个段内的递增,vm每次开始分配id前先向数据库申请一段可用id。数据库记录上次分配出去的id最大值。

具体方法:数据库中建一张表,表中有一个长整数的Id字段,负责记录已分配出的ID最大值。然后程序在开始生成id前,先向数据库申请一段id,获得这段id的起始值和最大值,然后程序自己就可以在这段范围内采用原子递增方式分配id了。id段用完了就再次向数据库申请。申请的id段的大小由程序自己指定,大小适中即可。

申请的过程就是get and set,这个过程要保证数据一致。不一定采用事物的方式来保证,因为每个程序可以分配一段,所以冲突的可能性小,属于乐观的情况,所以可以采用非事物的方式如果不一致重试几次即可。

所谓ID,即唯一标识符,所以ID生成器的实现目标就是要保证每次分配的id唯一。
既然id生成过程是在程序中完成的,如果程序退出或强制关闭了,那么下次程序启动时会再次向数据库申请新的id段,因而id唯一性是有保证的,但不保证id一定是连续的,程序退出时可能会浪费掉少量段内还没分配完的id。

特点:
  1. 持久化:使用数据库记录上次分配出的最大id。一般而言,如果仅使用程序自身生成id,只能保证程序一次性运行中生成的id可靠,下次再运行就不成了。除了UUID等特殊算法外,一般ID生成策略是需要持久化一些数据的,而持久化的方法无疑最好是使用数据库。
  2. 通用性:只是自己在数据库新建一个简单的表,其它工作都有程序完成,因而不依赖于特定数据库的主键或Sequence生成方式,具有很好的可移植性。(不依赖于特定数据库带来的好处是可移植性,坏处就是这部分工作转给了程序自己实现)
  3. 性能好:因为不是每生成一个id都要更新数据库,而是在程序中完成的,所以效率高,性能好。
  4. 多进程共用:多个程序同时使用一张数据表也没问题,只要保证vm向数据库申请id段的一致性即可,由数据库做中介。

上面描述的方法只是利用了数据库表中的一条记录维护一个递增的ID,可以使用多条记录维护多种不相关的ID,供系统中的多处使用。

这个实现方法是在 Openfire 中使用的 ID 生成方法,具体实现见org.jivesoftware.database.SequenceManager。
表结构:
CREATE TABLE ofID (
  idType                INTEGER         NOT NULL,
  id                    BIGINT          NOT NULL,
  PRIMARY KEY (idType)
);

2010/6/9 Ajax的同步调用场景——使用同步Ajax在 onunload 事件时通知服务器

在一种场景下,server端维护了浏览器客户端的状态信息,当浏览器关闭时需要立刻通知server用户已经离开了,server端好清理状态。这种情况的典型案例就是:浏览器客户端的即时通信,即web IM。因为用户关闭了窗口就表明用户已经不在线了,所有对话都结束了,server端和对话的另一方需要立刻知道这个状态变化。而一般情况下,server端并不需要立刻通知的情况,那就无所谓了,只要过期超时即可。

要想在用户关闭浏览器后,server端得到通知,一般有两种做法:
方法一:通过 javascript 轮询的方式向 server 发送心跳(使用异步ajax),server端记下上次心跳时间,server端Timer定期检查会话的心跳时间是否超时来判断用户是否已经离开了。这种方法的缺点是,要想即时通知就得把心跳间隔时间设得短,server端检查的时间也要短,这样会导致server端的负担大大增加,所能承载的用户就会较少。
方法二:不采用发送心跳的方式,而是在用户关闭浏览器时,通知server即可。这样server端的负担就小了。
window.onunload = function(){
ajax.callServer();
}

重点说一下方法二的实现。
首先,页面上注册 onunload 事件。如 body元素的 onunload 属性,或直接设置 window.onunload 。
注意是 onunload 而不是 onbeforeunload 事件,因为用户可以在 onbeforeunload 事件时选择“取消”从而不关闭窗口,而onunload 事件意味着窗口肯定是要被关闭的。

其次,在onunload事件中调用同步的ajax请求通知server,而非默认的异步ajax请求。因为调用异步的ajax请求在一些浏览器下无法真正使server得到通知,实际上根本无法保障!在发出了这个异步请求之后,浏览器就要关闭窗口并清理所有相关资源,因为这是异步请求,所以浏览器有理由认为无需等待该请求完成即可开始关闭窗口,这样该请求就可能失败(IE上就是这样,用异步请求无法通知到server)。这也解释了为什么异步callServer()调用后面再加个alert语句就没问题了,因为alert语句在后面挡着,需要人来点确认,所以可以完成对server的请求。而用同步的ajax请求则浏览器会等待该请求完成后才关闭窗口清理资源,因而就不会有这个问题。

这就是一种使用同步ajax的场景,ajax中第一个a指的是 asynchronous 异步,当初引入ajax的主要目的就是引入一种异步执行的方法,绝大部分情况下应该使用异步方式执行ajax。同步ajax也许应该叫做 sjax ( s 是 synchronously)。
XMLHttpRequest 的 open 方法中的第三参数表示是同步还是异步,xmlHttp.open("GET", url, true); 各种支持Ajax的框架,如prototype、ext、dwr等肯定都有选项设置是否为异步模式。

最后,实际情况的做法是要结合方法一和方法二。方法二由于各种原因(比如用户电脑突然停电、浏览器崩溃等,这你总没办法吧),总是难免浏览器在关闭时没有成功的通知server。尽管是少数情况,但server端是要保证长期运行稳定的,要避免资源泄漏,所以必然server端还是需要定期检查会话是否过期的。但因为已经保证了大部分情况下server得到了通知,所以server端的定期检查可以时间长一些,心跳间隔也可以时间长一些(但要小于server检查的时间间隔),因而对server也不造成什么负担。

相关参考资料:
Making AJAX calls onUnload: http://www.livelearncode.com/archives/11

2010/5/25 Eclipse 中 drop to frame 的调试技巧

前些天和同事交流调试技巧时,知道了 Eclipse debug 时有个 drop to frame 的技巧。这是我以前不知道的,自己又查了一下这个功能的含义。官方的解释是:

Select the Drop to Frame command [ Drop to Frame ] to re-enter the selected stack frame in the Debug View.

Note this command is only available if the current VM supports drop to frame and the selected stackframe is not the top frame or a frame in a native method.

就是说,这个功能可以重新跳到当前方法的开始处重新执行,并且所有上下文变量的值也回到那个时候。不一定是当前方法,可以点击当前调用栈中的任何一个frame跳到那里(除了最开始的那个frame)。主要用途是所有变量状态快速恢复到方法开始时候的样子重新执行一遍,即可以一遍又一遍地在那个你关注的上下文中进行多次调试(结合改变变量值等其它功能),而不用重来一遍调试到哪里了。当然,原来执行过程中产生的副作用是不可逆的(比如你往数据库中插入了一条记录)。
其实这个功能早就有了,就是一般人不知道或很少使用。

2010/5/24 收到联通寄来的0元发票

你们见过面额为0元的发票吗?反正我以前没见过,但上周真的见到了,中国联通开的。

上周帮人在联通网上营业厅买了个3g手机号,只买号不要钱,并且免费寄送给你。在下单的时候,可以填写发票抬头,我一看金额为0,觉得很有意思,没花钱也给开发票?结果在收到东西的时候还真有这个0元的发票。

根据自己的个人经验,开发票一般就有点麻烦,更何况是没花钱还要让人给你开发票。我猜可能联通规定,只要办了业务就开对应的发票,不管金额多少。

2010/5/20 抱着做产品的心态做开发

我这半年主要在负责做一个产品。
有几个不错的同类产品,同事发了那个产品的反馈意见的论坛链接。里面有很多人提出的建议。

我在邮件的回复了下面这些内容:
只有当你的产品对别人有用,并且有很多人用的时候,你才会收到很多别人给你的建议。所以先参考一下这个比较实际一些。

我浏览了一下第一页的标题,从别人的建议中,我发现其实都不是什么技术上的难题,我相信他们和我们还有其它同类产品都可以解决这些问题。从技术人的角度看,往往喜欢分析技术本身是不是够难,说这个我们能做、那个也可以做、这个地方只要如何如何就实现了。其实,我想对开发人员说,技术上一旦确定了架构,能力就已经确定了,这个架构提供了基本模型,并且子模块都是可扩展的,满足各种未来的扩展需要。所以,我明确告诉大家技术上的问题都可以解决。现在是能不能做出一个好产品的问题。这要求大家非常注重产品的每一个细节,精益求精,多从使用者的角度出发理解各种业务功能而不是从技术实现的角度,提升产品的品质和价值。从根上说,就是大家要抱着一种做产品的心态来开发,你敢自豪地向别人说这个产品是我做的。内核虽是以开源项目为基础的,但是产品是我们自己做的!任何人都可以拿这个核来做产品,而大家工作的真正意义在于所有人都拿这个核做产品的情况下我们做出来的产品是最好的,这是我们存在的意义。比较直观的一个类比是,很多浏览器都以IE或Webkit为内核的,比如chrome、傲游、搜狗等浏览器,浏览器才是最终用户使用的产品,内核只有那么少数几个,但浏览器确非常多。你想想既然傲游是以IE为内核的,但为什么在国内用傲游的甚至比用IE的人还多?

2010/5/13 Javascript实现输入内容变化的监控通知

目前做个即时通讯的web客户端,类似于Web QQ。我们使用qq、msn时,经常会看到“对方正在打字”的提示,这样就知道对方正在输入一些东西。实现就是当用户做出一些改变的时候,向对方发送一个通知信息。至于如何定义什么时候发这个通知,则由客户端实现自己决定。一般就是监测用户的键盘事件或输入内容的变化。

实现目标:当用户在输入框中输入或改变了一些内容时,发送一个通知。
起初的简单实现:监听浏览器中输入框的 keypress 事件。因为不应该每一个改变都应该立刻发送通知,那样过于频繁了,所以设置一个计数器counter,counter大于一个值(比如3)才是发送通知,再把counter设为0。

除了常规输入(输入字符和数字)外,用户按下Backspace和Delete键时会怎样?自己测试时发现一些问题:
keypress 事件在按下非字符键时,有些键会触发,有些键不会触发,而且具体随浏览器还不同。比如在ie中,仅字符键会触发keypress,那就是说你在IE上按Backspace删除了很多内容时,也不会发通知,因为没有触发keypress事件,而我的目标是应该发送通知。但在 Firefox 上就会触发keypress事件,也就是会发送通知。
一种解决方式是,你需要在specialKey中单独监控Backspace键,就可以在不同浏览器上都监控好。
但是我发现同样的方法监控不到Delete键。

后来又发现中文输入法也会干扰正常的事件,影响我目标的实现。
在输入中文时,在输入法窗口中显示了很多字,但只会产生一个keypress事件,这是正常的。但这样我原来的简单实现就没有很好地达到我的目的。而这时不应该仅靠事件触发的次数,还应该看每次事件时,字数变化的多少。

而且不同的输入法的兼容性和不同的浏览器实现都会有影响。
比如在IE上,使用谷歌拼音输入法,按数字键或输入中文,虽然都正常输入了,但都不会产生事件,而用智能abc输入中文没有事件,输入数字有事件;
而在Firefox上,使用谷歌拼音输入法,事件都是正常的。

小结:不同浏览器的键盘事件的处理有差异,同时中文输入法事件也有影响,并且还是二者交错的,比较复杂。
可以采用另一种方法绕开浏览器事件差异、中文输入和输入法兼容的影响,而实现我们原本的目的:
在浏览器上采用 timer 的方式,比如每隔0.5秒检查一下输入框中的内容变化,只根据内容的变化情况决定是否发送通知(据说Google搜索的自动补全就是这样做的)。
比如记录上次发送通知时输入框的内容(lastInput),每隔0.5秒查看当前内容(currentInput)与 lastInput 的长度之差的绝对值是否大于3。

参考资料:
JavaScript Madness: Keyboard Events:http://unixpapa.com/js/key.html
从谷歌的一个Bug说起,谈谈键盘事件的兼容性:http://www.javaeye.com/topic/233038

2010/4/15 XMPP服务器的SRV DNS配置

对XMPP客户端而言,首先查询 DNS 中的 SRV 记录如:_xmpp-client._TCP.example.com 。从而获得 xmpp
 server 的 hostname 和 port,然后使用这个进行连接。如果没有找到 SRV 记录,则会直接使用该 domain 和 默认端口进行连接。所以需要在DNS中设置 SRV 记录。

比如在Windows上可以使用 nslookup 命令查询 srv 的配置(linux就用 dig 命令):
nslookup -qt=SRV _xmpp-client._TCP.gmail.com
得到的是 Google Talk 服务的SRV配置,Google Talk 使用的xmpp hostname 是 talk.google.com。 

参考资料:
XMPP RFC 3920 中的关于 SRV 的说明 http://xmpp.org/rfcs/rfc3920.html#rfc.section.14.3
如果用 Smack api 作为xmpp client api,smack 中使用的是 org.jivesoftware.smack.util.DNSUtil 的方法按照上述原则执行查找的。

XMPP服务器的SRV DNS配置 - hgk - 韩国恺的博客

2010/4/11 手机应该有联系人状态信息

我们都用过IM,如msn、qq等。IM中有个人状态信息,有“离线”和“在线”两大状态,其中在线状态又可以有分为“空闲”、“离开”和“忙碌”等。因此,你就可以看到你好友的状态,根据对方状态来决定自己该如何做。

总之,好友状态信息还有很有用的。

今早,我的一个同事灵光一现,告诉我“手机也应该像IM一样,支持多种状态”。手机可以有这些状态,“开机”、“关机”、“占线”、“静音”、“免打扰”等状态。比如,对方现在是“静音”或“忙碌”状态,我就不给你打电话了,而选择给你发短信。

我觉得这是一个非常好的想法!我知道手机上的联系人的状态会很有用。任何一部手机里,都有一个最基本的功能——通讯录,这就类似于IM的好友列表。

我从IM通信协议的角度谈一下现在的手机还无法做到的原因。以XMPP协议为例,其它msn、qq的协议也都是类似的。在IM上你有一个花名册(Roster),你有哪些好友都记录在里面。另外,还有状态(Presence)订阅信息。因为系统中有无数的用户,不可能把所有人的状态信息都推给你,而是只把你需要的人的状态信息推给你。你需要订阅别人的状态,这样只有这些人的状态才会通知你,或他们状态发生变化时通知你。当然,你订阅别人的状态,需要对方同意才成,就好象你加别人为好友要对方同意一样。而这些信息都是记录在Server端(运营商)的,而不是客户端(手机)上。在Server端,任何一个人的状态发生变化了,Server将负责通知所有订阅了他状态信息的人。
所以,关键的问题就是,传统的电话没有这方面的通信协议,电信运营商不知道该把哪些人的状态通知你。

我目前就在做XMPP(一种即时通讯协议)方面的东西。

2010/4/10 我使用的 Android 程序

去年开始用了android手机(G2 Magic),下面是我自己手机上使用的 android 程序。希望和喜欢 Google Android 手机的人交流一下你们在用些什么程序。

手机通信:
  • chompSMS(最流行的手机短信程序,类似iphone上的。系统还有一个自带短信程序)
  • 智能拨号
  • 语音拨号
  • 语音搜索

工具类: 
  • 系统自带 Android Market (这个当然了)
  • ASTRO File Manager (瑞士军刀,全功能系统管理工具)
  • Advanced Task Killer (杀掉进程的工具)
  • Battery Widget (桌面显示电池电量的 widget,同时还有快速设置 wifi/gps/蓝牙 的功能。)
  • WiFi OnOff (桌面 开关 Wifi 的widget)
  • Wifi 分析仪 (可视化显示比较附近wifi信号源的强度)
  • Alipay (支付宝官方的android程序)
  • SMS Backup & Restore (短信备份)
  • Barcode Scanner (条形码扫描器)
  • Digital Clock Widget (桌面时钟)
  • Retro Clock Widget (另一个很不错的桌面时钟)
  • android系统自带圆盘桌面时钟
  • gTasks (一个Google task程序)
  • APNdroid 或 APN开关 (控制 gprs/edge/3g 启用的总开关)
  • apkInstaller (apk安装器)
  • Compass (指南针)
  • StopWatch (全功能的跑表,计数器等功能)
  • 大众点评(官方出的,可以根据你的位置显示附近的餐馆)
  • 本地通(Google Places Directory,根据你的位置提供各种生活实用信息,不止餐馆哦)
  • Text-To-Speech Extended (文本发音引擎,一些程序需要用到这个,中英文都能发音,但中文发音很差)
  • Backgrounds(网络壁纸)
  • 南方周末新闻阅读器(官方出的)
  • AK Notepad (记事本)
  • Text Edit (记事本)
  • NetCounter (网络流量统计)
  • GPS Status
  • Flashlight (手电筒,就是全屏最高亮度的白屏当手电筒用)
  • 北京地铁 (北京地铁地图)
  • 系统自带照相机/摄像机/媒体库
  • 系统自带电子邮件客户端程序
  • MarketEnabler (欺骗android market,设置某个手机运营商,因为android market是区分国家地区和运营商的)
  • PDF查看器
  • Quickoffice (office查看器)
  • 闹钟程序

输入法:
  • Google Pinyin IME (谷歌拼音)
  • 系统还自带了 Touch Input 输入法也很不错(包括手写/笔画/拼音等多种输入方式)

即时通讯工具:
  • QQ (腾讯官方qq for android)
  • eBuddy Messenger(eBuddy android版,支持msn等多种IM)
  • 自带 Gtalk

计算器:
  • 自带计数器。只有加减乘除,功能虽少但已够用,并且屏幕按键大使用非常方便(这也因为功能少,所以屏幕上要显示的元素就少,自然就简单方便)
  • RealCalc Scientific Calculator 功能较多的计算器
  • handyCalc 超级计算器,什么功能都有包括解方程什么的,就怕你不会用。还带了单位转换/汇率转换工具。

Google应用(这些只是我在用的,Google出的应用不只这些):
  • 自带Google浏览器
  • 自带Gmail应用
  • 自带Google Maps(地图)
  • 自带Gtalk
  • Shopper (只要扫描商品的条形码或封面即可查到商品)
  • My Tracks (利用GPS记录完整行程,可导出为kml文件)
  • 本地通(Google Places Directory)
  • Google Pinyin IME (谷歌拼音)
  • Google日历
  • Google 搜索,还包括强大的 语音搜索 (语音搜索最适合手机了)

浏览器:
  • UC浏览器
  • 自带Chrome Lite
  • Opera Mini 5

多媒体:
  • Meridian 子午播放器
  • TuneWiki Social Media Player
  • 自带音乐播放器

图片处理:
  • PicSay - Photo Editor (让图片“说话”)
  • FxCamera (一些照相特效)
  • Photoshop.com Mobile (官方Photoshop android版)

游戏类:
  • Bonsai Blast - Blu Mobile (非常棒的泡泡龙)
  • Air Control (调度飞机在机场降落)
  • BreakTheBlocks Lite (打砖块)
  • PingPong Lite (乒乓球)
  • Light Racer 
  • aiMinesweeper (扫雷)
  • Smart Tac Toe (五子棋,有多种难度)
  • 星际之战
  • Labyrinth Lite (重力感应的小球游戏,系统还自带了一个Teeter)
  • Speed Forge 3D Demo (很好的3D赛车游戏)
  • Robo Defense FREE  (非常棒的防御类游戏)
  • ThrottleCopter 
  • Tic Tac Toe (小时候玩的“一条龙”)

字典类(都是各自官方出的):
  • Google 翻译
  • 有道词典
  • 金山词霸

2010/4/6 Notepad++ 中文查找问题临时解决方法

我一直在用 Notepad++ 作为文本编辑器,很好用,开源免费。
但最近发现查找中文字符时有问题,找到的结果根本就不是你要查的内容。可能是这个版本 5.6.8 的问题,以前没问题的。以下两种临时解决方法任选一种即可解决:
方法一:查找时,在“查找模式”中,由“普通”改为“正则表达式”。
方法二:在菜单栏的“格式”中,默认是“以ANSI格式编码”,改为使用“GB2312”就好了(格式->Character sets->Chinese->GB2312)。Windows上的中文默认是按GB18030编码的。

2010/4/5 游木渎古镇

最近苏州出差,赶上清明放假,去了木渎古镇。联票包括: 严家花园,虹饮山房,古松园,榜眼府第。

在街上,有很多小店提供古代女装,帮你打扮好照相的服务。因为看起来确实很漂亮,所以吸引了很多少女身着古代服饰在路边照相。路上有一些小桥,其中一个就叫西施桥。还有很多刺绣、珍珠等手工艺品的小店。街上的小吃有公婆饼、臭豆腐、芡实饼等。
游木渎古镇 - hgk - 韩国恺的博客

严家花园:江南非常有名的一个园。开了很多海棠花。主要有三个堂:怡宾厅、尚贤堂、明是楼,刚进去的地方还看到了古代花轿。台湾政要严家淦就是在此长大的,此园是当时木渎首富,严家淦的祖父买下的,所以叫严家花园了。
游木渎古镇 - hgk - 韩国恺的博客

虹饮山房:乾隆多次来过的地方,有戏楼,刘墉的扁,圣旨参观,科举纪念,沈寿纪念,满汉全席模型等。
游木渎古镇 - hgk - 韩国恺的博客游木渎古镇 - hgk - 韩国恺的博客游木渎古镇 - hgk - 韩国恺的博客

古松园:有明代罗汉松,古松堂,凤凰楼,双层长廊,北侧有姚建萍刺绣艺术馆。
游木渎古镇 - hgk - 韩国恺的博客游木渎古镇 - hgk - 韩国恺的博客游木渎古镇 - hgk - 韩国恺的博客

榜眼府第:晚清冯桂芬故居,大厅叫显志堂,有石刻《盛世滋生图》。


旅游提示:门票60,从网上订的,又是清明特价,实际是35,够便宜吧。
我是坐苏州64路公交车去的。而有3个叫木渎的车站,分别是木渎、木渎人民医院和木渎严家花园,去的时候不知道,我想就应该在木渎下,实际应该在木渎严家花园下,自己多走了两站地。

2010/4/2 腾讯官方推出 QQ for Android

腾讯官方本周已经推出 QQ for Android。目前支持Android 1.5、1.6、2.0、2.1。
官方网址:http://im.qq.com/qq/android/ ,最新版本是QQ for Android Beta 1 SP 2,支持单人和群,有状态栏图标,界面还不错。
由于android用户增长很快,腾讯官方终于出android版了,这一天让我等了很久。
腾讯官方推出 QQ for Android - hgk - 韩国恺的博客

2010/3/22 同里古镇游记

“小桥流水人家”是大家对同里古镇的印象,于是我一大早就起来,从苏州汽车南站花8元钱坐车到同里。
下车后,2元的电瓶车到门口。门票80,因为在网上订的,所以只要64元。
先参观了世界文化遗产——退思园,退思园是任兰生修建,由袁龙设计。
三桥的景点是最好的,江南水乡的特色,还看到了鱼鹰捕鱼的表演。
当地人活在那里真滋润呀。
门票上其它一些景点也去了:耕乐堂、嘉荫堂、崇本堂也还行,松石悟园、古风园没什么意思。
珍珠塔也是比较大的一个园子,明代的。
下午,广场上有地方戏的表演。
最后出来时,还在一个偏僻的地方找到了陈去病故居。
总之,江南水乡特色,加上园林、厅堂,还是很值得去的。
同里古镇游记 - hgk - 韩国恺的博客同里古镇游记 - hgk - 韩国恺的博客同里古镇游记 - hgk - 韩国恺的博客同里古镇游记 - hgk - 韩国恺的博客

同里古镇现在是4A景区,但当地到处都打着争做5A景区的口号。
个人消费记录:实际门票64;往返路费16;小吃8;又买了两节电池12;电瓶车2;出发前自带了两瓶水和面包8.5;共110.5元。

2010/3/17 example.com真的存在

example.com真的存在!
经常见到在各种文档中使用example.com作为举例,今天突发奇想是不是真的有example.com这个网站呢?这个域名值多少钱?于是自己在浏览器中输入example.com,真的有。但上面非常简单的写着:
You have reached this web page by typing "example.com", "example.net", or "example.org" into your web browser.These domain names are reserved for use in documentation and are not available for registration. See RFC 2606, Section 3.

原来在RFC 2606中规定,example.com、example.net和example.org这三个是被保留的域名,仅仅由官方保留,不卖的。维基百科里也是这样解释的。

2010/3/14 拙政园小游

近来在苏州出差,还是第一次来。今天周六,天气也特别好。那就先去苏州园林看看,于是上午坐车来到了拙政园。
园林很有文化气息,听导游说了不少。不是很大,但感觉非常精致、别致。照了不少照片。
具体介绍就不说了,可以参考百度知道维基百科
门票50元,还算是淡季的,还好公司给报销。

随便挑的几张拍的照片:
拙政园小游 - hgk - 韩国恺的博客拙政园小游 - hgk - 韩国恺的博客拙政园小游 - hgk - 韩国恺的博客拙政园小游 - hgk - 韩国恺的博客拙政园小游 - hgk - 韩国恺的博客

尽管事先查好了路线,知道坐什么车往返。但人算不如天算,那附近在修路,只有去的车,返程的车改线了。人生地不熟的,找了半天才找到回来的车。

2010/3/11 证件照冲印价格真贵

现在大家都用数码相机了,如果需要冲印照片可以到数码冲印店洗出来。冲印标准大小的一张照片一般是0.5-1.0元之间,而如果是证件照价格则是5-10元,是普通冲印的10倍!发现这是普遍现象,算是冲印店的行规。

前两天公司需要1‘和2’照片,一批同事用个人的数码相机每人照了一张,拿到外面冲印出来。工作人员将我们自拍的照片做了数码裁剪和修饰,然后就可以打印了,帮你裁好。冲印的1张照片可以包括6张1‘或4张2’的,都是8元。这样每人就是16元(8×2,1‘2’的都要),8个人就是128元。真贵呀,跟会计一说价格大家都很惊讶。

2010/3/7 解决QQ影音升级后播放mpg的图像问题

Update: QQ影音2.2已经解决了此问题,升级即可。

QQ影音在用户体验和功能等方面做得不错,所以我去年开始用QQ影音取代之前常用的MPC。
现象:之前用的是QQ影音1.7 (568),一直挺好的。后来,自动升级到2.1 (649),发现播放很多(不是所有)mpg文件有问题,有声但是无图,图像静止在初始图上。
自己尝试了一下,解决方法是修改“播放器设置->高清加速->滤镜配置”。默认的设置是“智能高清模式”,似乎就是在升级之后,这个模式下的默认设置导致的问题。所以,改为“自定义优化模式”,然后在下面的分离器的mpeg选项中,不要选择Haali那个,用其它的基本都没问题。总之,在“滤镜配置”的“分离器”和“视频解码器”中调整一下就好了。

2010/3/4 XEP-0142: Workgroup Queues协议介绍

原创内容
此协议是Openfire的fastpath插件主要实现的功能(smack库中也有workgroup部分),以前是Openfire的商业插件,后来开源了,目前处于不活跃状态。
作者:Matt Tucker,Openfire主程序员之一,此协议目前处于搁置状态(Deferred),所以XMPP不建议实现此协议,因为你只是拿XMPP当IM使用的话,不需要此功能。

介绍:此协议目的是使一个用户可以和一个组织或工作组的代表对话,而不需要知道该组织中的特定成员的地址。以及提供排队等待与服务容量控制功能。这些特性特别适合在线客服的场景。
动机:只使用标准XMPP协议(就像一般IM一样),一个用户想与一个工作组的成员对话,那么他将向该成员直接发起一个的对话或多人对话。
而使用Workgroup协议的话,用户只需向工作组(如support@workgroup.example.com)发起对话,对话请求被放到一个队列中,server将对话请求路由给该工作组中的某个成员。该成员可以接受或拒绝该对话请求,一旦接受了该请求,对话将改为使用标准XMPP协议通信。
概念:此扩展协议在"http://jabber.org/protocol/workgroup" namespace下使用,使用iq元素来执行,使用presence元素声明状态更新。
workgroup活动的最终结果是协商和路由一个用户和工作组成员(也叫做agent)到一个使用MUC(multi-user chat)协议的多人聊天室中来对话。当workgroup协议成功完成时,本质上是MUC接管了,所以workgroup协议与Muc协议并没有重叠。

角色
User:用户向workgroup的一个成员发起一个私有对话
Service:workgroup service使用workgroup地址发送和接收消息。一个workgroup地址表示一个一般的联系人地址,该地址允许用户找到workgroup的成员对话,而不需要知道任何特定工作组成员的私有地址。workgroup service管理用户和成员(agent)的交互。
Agent:agent是workgroup中的一个成员。
比如说:User地址是user@example.net/home,service地址是support@workgroup.example.com,agent地址是alice@example.com/work和bob@example.com/work。
一个workgroup可以包含若干个队列,使用不同的resource表示,如support@workgroup.example.com/platinum-plan or support@workgroup.example.com/xmpp-products。

责任
User:1)在发起请求前能知道当前workgroup队列的状态。这个信息使用户知道当前workgroup是否可用,并知道大概需要等待多长时间开始对话。2)当处在请求队列中时能够知道请求的状态。3)随时可以取消对话请求。
Workgroup agent:1)能够知道workgroup队列的状态。2)能够接受或拒绝对话请求。3)能够表明处理对话的可用性。
Workgroup service:1)控制工作组请求队列。2)管理队列状态信息的更新。3)决定用户如何被队列化,和队列请求如何被路由给工作组成员。队列路由算法取决于实现者(如简单轮转、基于优先级、基于规则等)。

User协议部分
workgroup协议包括一些xmpp数据包的交换,这些交换改变user、agent和service之间关系的状态。
User状态:User加入到工作组队列中等待与一个agent的对话。一旦用户加入了队列,用户可能收到0或多个来自workgroup service的状态更新通知他们在队列中的状态。用户有权随时取消他们的对话请求。
当agent准备好与用户的对话时,用户收到一个groupchat的邀请到聊天室中,收到该邀请表明用户将不在队列中并且将使用标准xmpp groupchat协议进入聊天室,与agent对话。
使用多人对话是因为它提供了一些好处:1)允许超过一个agent加入对话。2)允许manager监视对话来保证服务质量。3)提供一种简单的方法来决定对话中的什么内容被记录和收集其它统计信息。4)允许一种方便的机制引入对话机器人(如回答FAQ)。
下图表示数据包交换与状态改变的关系:
              +-------+
              | Start |<------+
              +-------+       |
                  |           |
                  | Join      |
                  v           |
             +---------+      |
      +----->| Queued  |      |
      |      +---------+      |
      | Status |  |  | Depart |
      +--------+  |  +--------+
                  |
                  | Invite
                  v
           
+-----------+
            | Chat room |
            +-----------+
           
1)User Join :user发起一个join请求给workgroup service。service可以同意或拒绝,一个用户会话可能只允许同时发出一个加入请求。workgroup可能要求用户填写一些特定的信息才能加入,这种情况workgroup应该以not-acceptable的error返回,拒绝用户加入。用户应该获取该表单内容,填写后提交它来加入队列。提交的内容可能包含应用特定的meta-data,可以用来决定用户的队列选择或其它一些事情,这取决于实现。

User                          Service
  |        Join Request          |
  |----------------------------->|
  |                              |
  |        Join Response         |
  |<-----------------------------|
  |                              |
               
2)Depart:一般是用户希望离开队列,也可能是service主动通知用户从队列中被删除。
Requester                     Service
  |        Depart Request        |
  |----------------------------->|
  |        Depart Response       |
  |<-----------------------------|
  |                              |
User                          Service
  |        Depart Message        |
  |<-----------------------------|
  |                              |
3)状态更新:用户在队列中的状态更新(可选)
User                          Service
  |      User Status Push        |
  |<-----------------------------|
  |                              |
  |     User Status Request      |
  |----------------------------->|
  |     User Status Response     |
  |<-----------------------------|
  |                              |
4)User Invite:邀请队列中的用户到聊天室和agent对话。
User                          Service
  |          User Invite         |
  |<-----------------------------|
  |                              |

  
Agent协议部分
agent的状态:
agent加入到workgroup中表示他能够处理与user的对话。在workgroup中的agent会员资格期望是长期的、持久的关系,类似于花名册会员资格。比如,一个客户支持(agent),当他开始为example.com公司工作时,他加入到support@workgroup.example.com workgroup中,仅在他离开他的工作职务时才离开这个workgroup。
一旦一个agent加入到workgroup中,他们将收到workgroup的状态更新来通知他们workgroup中其他成员的状态。agent通过使用presence信息负责更新workgroup service,这样service能够智能地路由对话请求给最合适的agent。agent的presence使用标准的xmpp的presence包,可选地携带meta-data数据来帮助将对话请求路由给agent。这些特定应用的meta-data可以用来决定如何做出路由。
一般的agent workgroup状态图如下:
            +-----------+
      +---->| Workgroup |<-----+
      |     +-----------+      |
      |        |     |Agent    |
      | Status |     |Presence |
      +--------+     +---------+
一旦agent加入了workgroup并且当前可用,那么agent将会收到由workgroup service提供的chat offers。chat offers将提供给agent,并且agent有机会接受或拒绝每一个offer。workgroup service也可能撤销一个offer。例如,在指定时间内,offer仍然没有得到响应,那么service可能撤销该offer以快速响应用户请求。
一旦offer被接受,agent必须等待一个来自service的标准的groupchat邀请。service在这个阶段也可能撤销这个offer。这使得service可以同时发送offer给多个agent,选择一个最佳的接受offer的agent。这个过程的状态图如下:
              +-------+
              | Start |<---------+
              +-------+          |
                  |              |
                  | Offer        |
                  v              |
            +---------------+    |
            | Offer Pending |    |
            +---------------+    |
                 |  |  | Revoke  |
                 |  |  +-------->|
                 |  | Reject     |
          Accept |  +----------->|
                 v               |
            +--------------+     |
            | Chat Pending |     |
            +--------------+     |
                 |   | Revoke    |
          Invite |   +-----------+
                 V
             
+-----------+
              | Chat room |
              +-----------+
1)Agent Presence Protocol
agent提供他当前的状态给workgroup,也就是通知更新workgroup。
Agent                         Service
  |       Presence Update        |
  |----------------------------->|
  |                              |
agent必须通知workgroup他当前的状态(presence)。这个presence使用标准的xmpp presence,可以附加可选的meta-data。除了标准的xmpp支持的状态信息外,其中必须有一个agent-status子元素表示和workgroup相关的状态更新信息。比如agent不可用了,那么workgroup将不再路由给他。在workgroup上下文中,标准的xmpp状态所表示的意义如下:
chat:表示agent可以对话(空闲或可以支持更多对话)
away:agent忙碌(可能正在和别人对话)。agent可能仍然可以处理其他对话,但offer可能被拒绝。
xa:agent在物理上离开了他的机器,并且不应该把对话路由给他。
dnd:agent忙碌,不应该给打扰。然而,特殊或紧急情况下,对话让可能被offer给他,尽管offer很可能被拒绝或超时。
agent可以嵌入meta-data信息帮助路由对话请求,使用max-chats元素指明agent可以处理的最大对话数量,如果没有这个信息的话,service使用默认设置的值。
2)Workgroup Status Update Protocol
此部分可选实现。在agent向workgroup声明了他的状态后,他可能会收到来自workgroup的状态更新信息。如workgroup中agent的数量、所有对话数量、最大对话数量。
3)Queue Status Update Protocol
此部分可选实现。在agent向workgroup声明了他的状态后,他可能收到来自workgroup的队列状态信息。
count:队列中的user总数
oldest:队列中最老的成员加入队列的时间
time:队列中user等待的平均时间
status:队列的状态。队列可能处于active状态,但不接受新的对话请求。这个状态的典型的原因是队列将要关闭,但要先处理完已经加入到队列中的用户请求,或者因为队列中已经有太多的请求不能再接受更多的请求了。
open:active,并且可以接受新的对话请求
active:active,但是不接受新的对话请求
closed:Not active,不接受新的对话请求
4)Agent Status Update Protocol
此部分可选实现。用来更新workgroup中其它agent的状态信息。
Agent                         Service
  |    Request Agent Status      |
  |----------------------------->|
  |         Agent List           |
  |<-----------------------------|
  |                              |
  |     Agent Presence Pushes    |
  |<-----------------------------|
5)Agent Offer Protocol
这部分定义agent接收service的chat offer。
Agent                         Service
  |         Offer Request        |
  |<-----------------------------|
  |         Offer Response       |
  |----------------------------->|
  |                              |
6)Agent Offer Accept/Reject Protocol
这部分定义agent接受或拒绝offer。
Agent                         Service
  |  Offer Accept/Reject Request |
  |----------------------------->|
  | Offer Accept/Reject Response |
  |<-----------------------------|
  |                              |
7)Agent Offer Revoke Protocol
这部分定义service撤销一个offer。典型情况下当offer请求超时时或有更合适的agent处理对话时,service会撤销offer。注意,在agent收到对话邀请前的任何时候都可能撤销offer,即使agent已经同意接受offer时。
Agent                         Service
  |     Offer Revoke Request     |
  |<-----------------------------|
  |    Offer Revoke Response     |
  |----------------------------->|
  |                              |
8)Agent Invite Protocol
这部分定义agent收到邀请加入到与用户的对话。这个邀请的格式同MUC一致。invite元素的from属性必须设置为workgroup的JID。为了能够匹配invitation和offer,邀请信息中应该包含一个带jid属性的offer元素的元数据。
Agent                         Service
  |         Agent Invite         |
  |<-----------------------------|
  |                              |