当前位置

技术

技术

最近报告出去的两个 bug

一个是腾讯 TT 的 bug,keydown 事件无法捕获 'l'、'n'、'o'、's' 四个按键。这个 bug 真是匪夷所思,不晓得开发人员怎么犯下的如此的弱智错误。不过我们的项目最后还是绕过了过去,就是额外写了不少垃圾代码。然后在 QQ 的论坛上报告,昨天有一个腾讯的测试人员联系我要测试用例,于是写了这个 http://www.dup2.net/tt.html,今天一大早就告诉我开发人员已经改过来了,等下个版本了。估计 TT 的开发人员也经常加班工作,呵呵。

另一个是 twisted.words.protocols.msn 的 bug。严格来说不是它的问题,而是 MSN Shell 没有很好的遵从 MSN 协议规范发消息;但结果会导致无法和 MSN Shell 正常通话。写了一个 patch 解决这个问题提交给 twisted,还不晓得维护人员愿不愿意为了一个中国的 MSN 插件而接受这个 patch

Topic: 

老婆果然是比我聪明...

听说北京神州行、动感地带即将单向收费后,我今天在下班的公交上突然想到这样就可以一有来电就自动接通,然后向打电话的人放 mp3,甚至可以做到不同的拨打者放不同的歌;按下接听则将声音输入从 mp3 转到 Mic 上。

这样岂不是比现有的彩铃灵活很多?简直是语音业务的又一大 killer app 啊。就算手机厂商不做这个功能,在 S60/WindowsMobile 这样开发性很好的平台上应该是很容易实现的。貌似唯一的遗憾就是有些费电.....

于是俺回家后兴冲冲的把这个主意告诉了老婆。老婆想都没想就一棒子把我打回原形:接电话是不掏钱了,那打电话的呢?

......无语......

Topic: 

twisted 简单的 daemonize,以及 cron 功能的实现

年前系统监控脚本已经写好了,同事实现了检查 resin 状态和 https 用户口令校验,出状况则短信通知... 春节期间一直没有收到报警短信 :)

但还是比较粗糙,比如脚本是要依赖 nohup 启动的,想起以前写 echo server 的时候曾考察过 daemonize ,于是把代码抄来试试

结果发现直接 copy 过来的东东在 twisted 2.5 下不能运行。追踪了一下,发现是不知道从什么版本开始完善了对 win32 平台的支持,它将原来 twistd 的东西又封装了一遍。现在把 "from twisted.scripts import twistd" 这行改成 "from twisted.scripts import _twistd_unix as twistd" 就好了。

另外貌似 twisted 并不推荐这种直接初始化 App() 的方法,而是用 twistd 程序来启动 app,回头需要再学习学习..

顺便又了解一下怎么实现一个简单的 cron,发现可以用 twisted.internet.task 封装的 LoopingCall 来完成,核心应该算是一个叫 callLater 的东东。

Sample:

  1. import os, time
  2. from twisted.internet import selectreactor as bestreactor
  3. bestreactor.install()
  4.  
  5. from twisted.internet import reactor
  6.  
  7. def crontask():
  8.     open("/tmp/crontask",'ab+').write(str(time.time())+"\n")
  9.     time.sleep(2)
  10.  
  11. class App:
  12.     pidfile = "/tmp/cron.pid"
  13.  
  14.     def __init__(self):
  15.         twistd.checkPID(self.pidfile)
  16.         # checkPID 后再 daemonize,否则看不到 checkPID 向终端输出的错误报告
  17.         twistd.daemonize()
  18.         # 先 daemonize 再写 pidfile。因为写入的得是 daemonize 后的 PID
  19.         open(self.pidfile,'wb').write(str(os.getpid()))
  20.         reactor.addSystemEventTrigger('before', 'shutdown', self.shuttingDown)
  21.         cron = task.LoopingCall(crontask)
  22.         cron.start(5)
  23.  
  24.     def shuttingDown(self):
  25.         twistd.removePID(self.pidfile)
  26.  
  27. #from twisted.scripts import twistd
  28. from twisted.scripts import _twistd_unix as twistd
  29.  
  30. from twisted.internet import task
  31.  
  32. def main():
  33.     app = App()
  34.     reactor.run()
  35.  
  36. if __name__ == "__main__":
  37.     main()
Topic: 

twisted.words.protocols.msn 协议简述

首先推荐一个关于 MSN 协议描述的网址:
http://www.hypothetic.org/docs/msn/index.php

在试着改写 twisted sample 的过程中,基本上是靠这个网站上的内容来学习 MSN 协议的。

在 twisted 里面定义了三类 ClientFactory,分别对应 dispatch server、notification server、switchboard server

客户端启动的时候首先去请求 dispatch server (messenger.msn.com),它会告诉客户端一个可用的 notification server,然后立刻关闭连接。

客户端接下去连接 notification server,其间还会到别的服务器上去认证一下(就是通过 https 把用户名密码送到一台机器上,然后获得一个临时令牌,再告诉 notification 这个令牌信息,认证就通过了)。最终身份确认后,在 MSN 登录其间就不会再断掉这个 TCP 连接了,所有的 presence 信息就是通过这个链接传递的。

可以想象 MSN 有无数台 NS,那么连接在不同 NS 上的用户之间怎么传递消息呢?Jabber/XMPP 的类似机制是 router,用一个专门的服务来帮助寻径。MSN 的解决方案是 switchboard server。

就是说 A 想和 B 说话的时候,就临时请求一个 switchboard server 资源,然后自己连接上去,B 也会接受到一个通知对话发起的 notification,里面包括 sb server 的地址。B 连接上去以后两个人就可以通过这台 sb server 进行通信了。

写到这里突然想到,notification 好像都是明文传输。理论上我们可以监听 MSN 的 notification 拿到 IP/Port/SessionID/Key,然后先 B 一步连接上 sb server,这样就可以冒充 B 的身份和 A 聊天了,想恶作剧的可以尝试一下(黑客可以利用这个来伪造信任身份传恶意 URL,还是挺可怕的)。

除了 dispatch、notification、switchboard 的概念外,我的例子里面还涉及到了联系人的信任状态,即 forward、reverse、allow、block。其实这篇文档里面介绍的很详细了,英文也很容易懂,就不再多说了。

Topic: 

利用 twisted 来写 MSN 机器人 (有例子下载)

具体到 MSN 机器人的实现,我倾向于使用 twisted 框架。无奈网上相关的资料非常少,找了半天只有这个中文 blog entry 稍微有些帮助。后来把 twisted 自带的 sample 增加了一些功能,现在放出来供需要的人参考。

msnclient.py (因为是修改自 twisted 的代码,所以也用 MIT license 吧)

这个简单的 MSN 机器人目前可以自动加好友,以及实现了一个消息 echo 机制——对它说的话会原文返回来。为了帮助理解 MSN 协议,我也打印了大量的协议文本到终端上。

如果是在 win32 环境下调试,pyOpenSSL 主页上并没有最新版本的预编译包,我现在用的是 webcleaner 编译好的(http://webcleaner.sourceforge.net/pyOpenSSL-0.6.win32-py2.5.exe),同时还需要安装 OpenSSL 的 win32 库

MSN 的字符流都是 UTF-8 的,本来我在 Linux 下的 zh_CN.UTF-8 环境下信息输出的很好,但在中文 windows 上就不能正确输出,无奈在例子里很多地方增加了 locale 判断

  1. default_locale = locale.getdefaultlocale()
  2. foobar_str.decode('UTF-8').encode(default_locale[1])
Topic: 

计划用 python 实现一个运营监控系统

现在搜狐通行证项目在技术实现上已经差不多了,接下来要考虑的事情就是运营保障,以及数据统计分析。

关于运营监控这部分 CPC 已经有系统在做了,主要就是请求 URL,然后看返回结果;有问题就发 email 或短信。不过我觉得还不太好,比如如果要处理 HTTP POST 检查,或者是监控 MySQL 的话,扩展性就太差;而且出问题了话报告哪些人也不容易扩展;另外,还很希望实现 MSN 报警,这样比起 EMAIL/SMS,还能实现一些交互的能力。

我现在的计划是这样:对每一项需要监控的服务实例化一个特定的对象,包括执行怎么样的操作,操作返回结果怎样才算正常,连续出错 N 次后向哪些人去报警,系统恢复正常后再向哪些人去通知。基本上 EMAIL 报警可以考虑取消了,首先看报警接受人是否 MSN 在线,如果在线则发报警信息;报警信息包括一个流水号,接受人必须在 MSN 上响应这个流水号,否则不停的在 MSN 上发送报警信息,直至最后发送短信进行提醒。

Topic: 

DV-2-XviD 0.8.3

又是一个老外报告了 bug,如果目标文件夹路径包括空格的话,lame.exe 执行会失败。很容易就改好了。

关于 DV-2-XviD 的中文信息可参考这里

另:今天看到一篇文章 (http://weblogs.mozillazine.org/roadmap/archives/2007/02/threads_suck.html)

看起来 JS 未来将会加上对线程的支持,就是说 Ajax 应用将会更有效的利用你的多核系统的计算资源。感觉 Gecko 在 JS/ECMAScript 上还是很有话语权的。

Topic: 

人品爆发

周五下午,作为年终的一系列活动之一,CPC 和 NO 在一起举行一个小小的抽奖仪式。结果抽中一个睡袋,结束 N 年以前抽奖不中的记录.. 不过真正的人品爆发是下班前的一件事情:

不知道为什么,临下班的时候内部网络出现故障,结果导致 MySQL 连接过多,too many connections,当时也没有可用的 mysql client 连接,无法去 show processlist 看看都发生了什么。心急如焚,这得影响多少网民啊;另一个心急如焚的原因是偶要赶晚上8点13的火车去天津,票都已经买好,如果为了不被网民以及上级骂死而耽误火车,势必会被老婆骂死。

终于在一个偶然的机会,mysql 连接了上去(很快连接又被占满了)。看了看 processlist,一堆的 unauthenticated user 在试图 connect/login

我立刻想起就在昨天赵宏威在 MSN 上问我同样的问题,当时偶也觉得这个事情比较诡异,但没有帮他找答案,没想到第二天同样的问题就碰上我了。赶紧在 MSN 上问他最后是怎么解决的故障,说是启动的时候加 --skip-name-resolve 参数就好了。

赶紧告诉 DBA 重启,果然问题消失。然后感叹人品之坚挺——出的问题正好是别人刚碰到的,而且他又正好来问我,真是幸运啊幸运。

重新探讨故障原因,应该是 MySQL 的安全模型里面进行用户认证和授权的过程中,除了对 IP 地址做校验之外,还包括了对 HostName 验证的配置。因此所有的用户连接进来以后都需要做一次反向域名查询,然后根据 IP 和 HOST 来进行授权。这样如果因为网络故障导致反向域名解析很慢的话,就会让一个连接很长时间也无法完成...

由此引申开去:
一、MySQL 是否能处理的更好一些呢?相关的 BUG 在 2005 年就有人报告。我觉得在执行反向域名解析之前,至少可以先判断一下来源 IP 是否已经在策略表里面存在,如果存在的话就可以直接做用户认证了

二、标准 C 库(glibc)能不能想法降低做反向解析的时间?比如执行查询之前可以设置一个超时时间?因为反向解析而给用户带来困惑我已经见过好多好多回了

三、我们使用的连接池程序(据说是 resin 自带)是否有问题?当然我不是 Java 程序员,而且这次故障原因和 Java 没有任何的关系。可是明明有连接池限制怎么还会出现 too many connections?看起来是因为 mysql 连接迟迟没有响应,导致连接池程序开始后继的尝试。做新的尝试没有问题,为什么不去 close 以前的连接??或许是写这个连接池的人对 GC 过于自信了?很难想象一个有本事写连接池的 C 程序员会犯这样的失误。嘻嘻,腹谤一次 java

最后虽然有些赶时间,但还是坐上了开往天津的火车。我很严肃的思索了一下为什么今天会有如此之好的人品——结论是周五我做的唯一和以往不同的事情就是偷偷使用了老婆的保湿水和面霜,看来美容活动得坚持下去啊...

以前听人说:女人三十岁前的模样是天生的,三十岁后的模样是自己给的。我补充一下,男人三十岁前的模样是天生的,三十岁后的模样是老婆给的。

Topic: 

SWT 的多线程

前段时间的工作里需要做个 GUI 工具,要用 Java 里的 SWT。以前完全没接触过 SWT,就玩过一阵 SWING。其他没什么,就是多线程的时候似乎比 SWING 麻烦点,后台干活跟窗口里面的进度条之间的配合碰到了问题。google 了一下,看了123等网页,克服代码里碰到的问题后,总结如下:

1.干活是一个线程,UI是另一个线程,不要在干活的线程里牵扯到UI的操作。可以在干活的线程里用设boolean类型变量的方法,通知UI线程做相应变化。
2.使用Display.syncexec/asyncexec(Runnable) 来执行非主线程的代码。

大概的样子就是如下

  1. public work() {
  2.         aaa().start();
  3.         bbb().start();
  4. }
  5.  
  6. private Thread aaa() {
  7.         return new Thread() {
  8.                 public void run() {
  9.                         /* 干活 */
  10.                 }
  11.         };
  12. }
  13.  
  14. private Thread bbb() {
  15.         return new Thread() {
  16.                 public void run() {
  17.                         Display.getDefault().asyncExec(new Runnable() {
  18.                                 public void run() {
  19.                                         /* UI操作 */                         
  20.                                 }
  21.                         });
  22.                 }
  23.         };
  24. }
Topic: 

俺也回来了

过了三天没有泡网的日子。

周四、周五、周六参加新员工培训,周四、五是在十五层的四海会议室,周六是参加一个拓展训练。正是项目最紧张的时候,前两天培训的时候每当有休息时间,我就赶紧冲回办公室,检查我们部门和其他业务部门的进度情况。周四晚上还别人都下班了还得回复并撰写一堆 email。周六晚上打开 bloglines,顿时发现三天时间积攒了好多好多的 feeds,硬着头皮看下来......找两条有意思的吧:

1. 云风也开始讨论用户身份认证方案。内容没有什么,倒是回复中有人提到多服务 SSO (single sign on) 的标准解决方案是用 kerberos。其它解决方案也是用 kerberos 的模型,日后有时间学习学习 kerberos

2. 最新的一期 Firefox trunk build 增加了好多特性。Trunk 版本自从 Win32 加入 Cairo 后我基本上就天天用了;办公室的系统由于 FireBug 还不完全支持 trunk (无法在 console 中输入命令),所以最近一直在用 2.0。不过总体来说 trunk 的可用性稳定性和正式版没有什么区别,没事可以考虑装个试试看,不过还是应该创建一个和正式版不一样的 profile。

Topic: 
订阅 RSS - 技术