当前位置

技术

技术

发现 PyCrypto 不如 ctypes 来得方便

至少对于一个C程序员来说是这样..

有一段RSA签名的程序,原本是用 C 调 libcrypto 包装成 jni 由 java 使用的,现在打算在 python 里面实现一个类似的功能,这段 C 代码大概是这样:

  1. int rsa_sign(char *private_keyfile, char *message, int message_len, char *buf, int buf_size)
  2. {
  3.     RSA *key;
  4.     int malloc_size, rsa_size;
  5.     unsigned char *rsa;
  6.  
  7.     BIO *bio;
  8.  
  9.     bio = BIO_new_file(private_keyfile, "r");
  10.     if (NULL == bio) {
  11.         return 0;
  12.     }
  13.     key = (RSA *)PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, NULL);
  14.     BIO_free(bio);
  15.     if (NULL == key) {
  16.         return 0;
  17.     }
  18.  
  19.     malloc_size = RSA_size(key);
  20.     rsa = (unsigned char *)malloc(malloc_size);
  21.     if (NULL == rsa) {
  22.         RSA_free(key);
  23.         return 0;
  24.     }
  25.  
  26.     if (0 == RSA_sign(NID_md5, message, message_len, rsa, &rsa_size, key)) {
  27.         free(rsa);
  28.         RSA_free(key);
  29.         return 0;
  30.     }
  31.     RSA_free(key);
  32.  
  33.     if (rsa_size/3 * 4 >= buf_size) { /* 结果的 Base64 编码后长度大于 buf_size, 等于也错误(后面需要放 '\0' 的位置) */
  34.         free(rsa);
  35.         return 0;
  36.     }
  37.  
  38.     b64_encode(rsa, rsa_size, buf);
  39.  
  40.     free(rsa);
  41.     return 1;
  42. }

可是找遍 PyCrypto, 楞是没有看到怎么实现 PEM_read_bio_RSAPrivateKey 这样从一个 PEM 格式的私钥文件里加载的功能。然后去看哪些基于 PyCrypto 包装的其他的包,比如 ezPyCrypto, Keyczar(这还是 google 发布的),包括 mail-list 上提供的 patch.... 这些似乎倒是可以 load key 了,可缺省好像也并没有对 NID_MD5 的支持, 绝望~~~~

于是抱着试试的心态用 ctypes 直接去调 libcrypto——以前只是在 PyS60 上玩票似的用过 ctypes,以及在 Win32 下用过,而且那时候 ctypes 还不太成熟——结果发现 python2.5 的 ctypes 竟如此的好用:

  1. import ctypes
  2. import base64
  3.  
  4. crypto_handler = ctypes.cdll.LoadLibrary("/usr/lib/libcrypto.so")
  5. BIO_new_file = crypto_handler.BIO_new_file
  6. PEM_read_bio_RSAPrivateKey = crypto_handler.PEM_read_bio_RSAPrivateKey
  7. RSA_size = crypto_handler.RSA_size
  8. RSA_sign = crypto_handler.RSA_sign
  9. BIO_free = crypto_handler.BIO_free
  10. RSA_free = crypto_handler.RSA_free
  11. NID_md5 = 4
  12.  
  13. bio = BIO_new_file("/yourpathfile.key", "r")
  14. key = PEM_read_bio_RSAPrivateKey(bio, 0, 0, 0)
  15. r = BIO_free(bio)
  16. if r != 1:
  17.     # break here
  18.     print 'BIO_free error'
  19.  
  20. rsa_size = RSA_size(key)
  21. rsa = ctypes.create_string_buffer(rsa_size)
  22. sign_size = ctypes.create_string_buffer(4)
  23.  
  24. message = "foobar"
  25. if 1 != RSA_sign(NID_md5, message, len(message), rsa, sign_size, key):
  26.     print 'RSA_sign error'
  27. RSA_free(key)
  28.  
  29. print base64.urlsafe_b64encode(rsa.raw)
Topic: 

Apache Module 之在各个 request 之间共享数据(资源)的方法

不论是1.3还是2.x,apache 的模式都包括一个进程服务好多次请求后再退出。现在有一个需求,在每个 request handler 里面希望保存一些数据,这些数据在以后该进程处理其他的 request 中可能还要用到。

最开始我从 request->server->process->pool 顺藤摸瓜看到一个貌似是和进程有关系的资源池。于是仿照 RUN_INIT_ONCE 的apr_pool_userdata_get/apr_pool_userdata_set 来访问资源。这样做的确可以在 request handler 之间共享数据,可是进程退出(apachectl stop)的时候不会去调 cleanup,非常之讨厌。

google 了半天,并参考 apache 自己的 LDAP 模块,发现官方解决方案好像是这样的:

  • 定义module_config结构体的时候,增加一个资源指针
  • 注册child_init的hook函数,从传入的第一个参数(pool指针)里面申请资源
  • 用apr_pool_cleanup_register注册cleanup函数在这个 pool 上(这个是cleanup的核心步骤,我感觉理论上完全可以自己重新创建一个pool,或哪怕是自己malloc,但关键是把cleanup函数注册到这个pool上。因为在进程退出的时候apache只会去销毁这个pool上的资源,而cleanup函数是"资源"之一)
  • ap_get_module_config, 把申请到的资源挂到自己定义的config结构体里

这样在进程退出的时候就自动 cleanup 了。这也是为什么到了 apache 2.0 后就不再提供 child_exit hook 的原因,因为开发者觉得由于有 cleanup 注册的机制,对应 child_init 的 child_exit 就不再有存在的必要。

唯一的疑问就是,request->server->process->pool 和在 child_init 时传入的 pool 是什么关系..... 不过懒得再去寻根究底了,先把需求实现了再说。


2008/11/25 UPDATE:上述的过程有一些问题,就是用 module_config 放资源指针,这样会导致虚拟主机的访问可能取不到 config。最简单粗暴的方法可能也是最有效的方法是用全局变量,当然别忘了 static 修饰。

Topic: 

取本机所有的 IP

代码修改自 qmail 的 ipme.c , 要点是对于 struct ifreq 里定义了 ifr_addr.sa_len 的系统(比如 FreeBSD),是和 Linux 不太一样的。
在 CentOS 3 和 FreeBSD 4.7 上测试通过

  1. /* ipme.c from qmail */
  2. #include "sys/types.h"
  3. #include "sys/ioctl.h"
  4. #include "sys/socket.h"
  5. #include "netinet/in.h"
  6. #include "net/if.h"
  7.  
  8. int main(int argc, char *argv[])
  9. {
  10.     struct ifconf ifc;
  11.     int sockfd;
  12.     char buf[20000];
  13.     char *ptr;
  14.  
  15.     if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
  16.         return -1;
  17.     }
  18.  
  19.     ifc.ifc_buf = buf;
  20.     ifc.ifc_len = sizeof(buf);
  21.     if (ioctl(sockfd, SIOCGIFCONF, &ifc) == -1) {
  22.         return -1;
  23.     }
  24.  
  25.     ptr = buf;
  26.     while (ptr < buf + ifc.ifc_len) {
  27.         struct ifreq *ifr;
  28.         struct sockaddr_in *sin;
  29.         int len;
  30.  
  31.         ifr = (struct ifreq *)ptr;
  32. #ifdef __FreeBSD__
  33.         len = sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len;
  34.  
  35.         if (ifr->ifr_addr.sa_family == AF_INET) {
  36.             printf("%s", ifr->ifr_name);
  37.             sin = (struct sockaddr_in *)&ifr->ifr_addr;
  38.             printf("\t%s", inet_ntoa(sin->sin_addr));
  39.             if (ioctl(sockfd, SIOCGIFFLAGS, ifr) == 0) {
  40.                 if (ifr->ifr_flags & IFF_UP) {
  41.                     printf("\tUP\n");
  42.                 } else {
  43.                     printf("\tDOWN\n");
  44.                 }
  45.             }
  46.         }
  47.  
  48.         // 这段代码来自 qmail 的 ipme.c, 在 FreeBSD 上实测中没有出现过 len 小的情况
  49.         if (len < sizeof(struct ifreq))
  50. #else
  51.         if (ioctl(sockfd, SIOCGIFFLAGS, ifr) == 0) {
  52.             if (ifr->ifr_flags & IFF_UP) {
  53.                 if (ioctl(sockfd, SIOCGIFADDR, ifr) == 0) {
  54.                     if (ifr->ifr_addr.sa_family == AF_INET) {
  55.                         sin = (struct sockaddr_in *)&ifr->ifr_addr;
  56.                         printf("%s\t%s\n", ifr->ifr_name, inet_ntoa(sin->sin_addr));
  57.                     }
  58.                 }
  59.             }
  60.         }
  61. #endif
  62.         len = sizeof(struct ifreq);
  63.         ptr += len;
  64.     }
  65. }
Topic: 

文本聚类研究

最近一直在研究文本分类。到今天,终于搞出一个收敛的单词聚类算法。

最主要的时间是在磨刀上——前前后后总共差不多有 1 周的时间在看 sogou 分词库的文档,以及给它开发一个 python-binding

然后是差不多花了半天的时间写爬虫,获得最初的文档库

利用"[PDF] WRM :一种基于单词相关度的文档聚类新方法"里介绍的概念计算单词两两之间的相关度

最麻烦的就是利用相关度对单词聚类,论文里介绍的方法好像并不好用。从周二到周日,都在不停的找论文,试验新算法,运行测试,修正... 的循环里度过

失败无数次后,最后在 Fuzzy C-Means Clustering 的指导下,投机取巧的设计了一个方法以及判断收敛的机制,今天下午已经成功的跑完一次聚类到 250 个集合的过程,另一个聚类到 100 个集合的过程虽然比较慢,但在 2 个小时前也已经结束。Oh, my god, 看起来是可以正常工作了。

感谢分词库开发者,感谢搜狗新闻、关键字广告 TEAM 曾给的帮助
【尤其是 sogou 的开发包,能返回词的词性... 虽然还有瑕疵,但已经对我帮助很大了】

感谢python, 没有它不可能短时间内把算法体无完肤的改上10几次

感谢《鬼吹灯》,周末的晚上是它让我保持清醒,时不时的去看一眼后台跑的计算过程.

感谢家里人忍受我最近的加班

Topic: 

在 PyS60 上使用 ctypes 修改 WIFI 的 MTU

自从换了 DELL 无线路由后,俺的 E61i 也出现无法正常连接 WLAN 上网的情况,而且和当初 Vista 的故障一样——可以访问 google,但大部分网站的连接不上,据以往经验,估计还是因为手机的 MTU 大于这个路由器最大支持的能力。

在网上搜了半天,发现在 S60 2nd 机型上,有一种改 genericnif.ini 的方法可以改变 MTU,但是到了 S60 3rd 机型,貌似就只能自己动手写程序了。

但 Symbian 下的 C++ 开发俺不熟啊,PyS60 没有接口啊,怎么办。正好在查相关开发信息的时候,发现 Symbian 出了一个 POSIX 兼容层(S60 平台上的称呼是 Open C),PyS60 社区有人据此移植了 libffi_ctypes 过去;又有人包装了兼容 Python2.5 的 ctypes 提供一些外围函数;wiki 上还有一个 open c 下执行 setsockopt 的例子

于是安装了 Open C 的 SDK, 然后在它的文档里找到这么一段:
The following flags will work only on hardware because it is supported by the underlying symbian API.

  • SIOCGIFMETRIC
  • SIOCGIFMTU
  • SIOCGIFNETMASK
  • SIOCGIFBRDADDR
  • SIOCGIFFLAGS
  • SIOCGIFDSTADDR
  • SIOCGIFPHYS
  • SIOCSIFMETRIC
  • SIOCSIFMTU
  • SIOCSIFFLAGS

只要确认 SIOCSIFMTU 是可以用的就好办了,对一个熟练的 C 程序员且有一定 ctypes 使用经验的人来说,除了平台移植问题外,开发是没有什么困难的。

开发过程如下

  1. 在手机上安装 libffi、Ctypes (_ctypes.pyd)、ctypes
  2. 在 Windows 下安装 S60 C++ SDK,以及 Open C plugin。可以找到 pips_nokia_1_3_SS.sis 等一共 4 个 .sis 文件,都安装到手机上
  3. 找到 winscw\udeb\libc.lib,然后用 dumpbin.exe /exports libc.lib 寻找函数名对应的序号,PyS60 的 ctypes 必须得用序号去定位函数,比如这样
    1. from _ctypes import *
    2. from ctypes import *
    3. libc = dlopen("libc.dll")
    4.  
    5. socket  = lambda x,y,z: call_function(dlsym(libc, "339"),   (x,y,z))
    6. ioctl   = lambda x,y,z: call_function(dlsym(libc, "180"),   (x,y,z))
    7. close   = lambda x:     call_function(dlsym(libc, "57"),    (x,))
    8. perror  = lambda x:     call_function(dlsym(libc, "255"),   (x,))
    9. dup     = lambda x:     call_function(dlsym(libc, "67"),    (x,))
    10. close(2)
    11. dup(1) #把标准错误重定向到标准输出,这样 perror 就输出到标准输出了
    12.  
    13. ntohl   = lambda x:     call_function(dlsym(libc, "168"),   (x,))
    14. ntohs   = lambda x:     call_function(dlsym(libc, "169"),   (x,))
    15. print ntohs(1), ntohl(256)  #简单的测试脚本
  4. 没有 dumpbin.exe 这个工具(VC++)的,也可以用 MinGW 的 nm.exe 来处理 libc.lib,只不过输出结果还需要再处理一下
    1. f = open("c:\libc.txt")
    2. idx = 1
    3. for i in f.readlines():
    4.     if i.startswith("00000000 T "):
    5.         if i[11] == '.':
    6.             continue
    7.         print idx, i[11:].strip()
    8.         idx += 1
  5. 根据 SDK 的头文件,找出 SIOCSIFMTU 这样的常量定义写成 python 语句
    1. IOCPARM_MASK = 0x1fff
    2. _IOC = lambda inout,g,n,l: (inout | (l & IOCPARM_MASK) << 16 | ord(g) << 8 | n)
    3. _IOR = lambda group, num, len: _IOC(0x40000000L, group, num, len)
    4. _IOW = lambda group, num, len: _IOC(0x80000000L, group, num, len)
    5. _IOWR = lambda group, num, len: _IOC(0x40000000L|0x80000000L, group, num, len)
    6.  
    7. SIOCGIFCONF     = _IOWR('s', 3, SIZEOF_IFCONF)
    8. SIOCGIFFLAGS    = _IOWR('i', 17, SIZEOF_IFREQ)
    9. SIOCGIFMTU      = _IOWR('i', 51, SIZEOF_IFREQ)
    10. SIOCSIFMTU      = _IOW('i', 52, SIZEOF_IFREQ)
    11.  
    12. IFF_UP          = 0x1
    13.  
    14. AF_INET         = 0x0800
    15. SOCK_DGRAM      = 2
    16. IPPROTO_UDP     = 17
  6. 还要去找 struct ifconf, struct ifreq 是怎样定义结构体的,结构体大小是多少
    1. SIZEOF_IFREQ = 84
    2. SIZEOF_IFCONF = 8
    3. IFREQ_IDXFMT = '52sH'
    4. IFREQ_MTUFMT = '52sI'
    5. IFREQ_FLAGSFMT = '52sBB'
    6. autopadding = lambda start, size: start + str(size - calcsize(start)) + 's'
    7. ifreq_idxfmt = autopadding(IFREQ_IDXFMT, SIZEOF_IFREQ)
    8. ifreq_mtufmt = autopadding(IFREQ_MTUFMT, SIZEOF_IFREQ)
    9. ifreq_flagsfmt = autopadding(IFREQ_FLAGSFMT, SIZEOF_IFREQ)
  7. 真正的操作很简单:
    1. sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
    2.  
    3. result = create_string_buffer(SIZEOF_IFREQ * 20)
    4. ifc = create_string_buffer(pack("II", SIZEOF_IFREQ * 20, addressof(result)), SIZEOF_IFCONF)
    5.  
    6. ret = ioctl(sockfd, SIOCGIFCONF, ifc)
    7. ifc_len, ifc_buf = unpack("II", ifc.raw)
    8. count = ifc_len / SIZEOF_IFREQ
    9. for i in range(0, ifc_len, SIZEOF_IFREQ):
    10.     ifreq = result.raw[i:i+SIZEOF_IFREQ]
    11.     ifr_name, ifr_idx, padding = unpack(ifreq_idxfmt, ifreq)
    12.     ifr = create_string_buffer(ifr_name, SIZEOF_IFREQ)
    13.     ret = ioctl(sockfd, SIOCGIFFLAGS, ifr)
    14.     ifr_name, ifr_flags, ifr_flagshigh, padding = unpack(ifreq_flagsfmt, ifr)
    15.     if not (ifr_flags & IFF_UP):
    16.         print "%s: DOWN" % ifr_name
    17.         continue
    18.     ret = ioctl(sockfd, SIOCGIFMTU, ifr)
    19.     ifr_name, ifr_mtu, padding = unpack(ifreq_mtufmt, ifr)
    20.     print "%s: UP, MTU: %d" % (ifr_name, ifr_mtu)
    21.     if ifr_mtu > 1400: #凡是 MTU > 1400 的,都改成 1400
    22.         ifr = create_string_buffer(pack(ifreq_mtufmt, ifr_name, 1400, padding), SIZEOF_IFREQ)
    23.         if -1 == ioctl(sockfd, SIOCSIFMTU, ifr): foo = perror("why")
  8. 但...为什么 perror 会报 Permission denied 呢? 后来查了半天,发现是 SIOCSIFMTU 需要有 NetworkControl 权限。于是去 这里 申请一个自己手机 IMEI 对应的证书(免费的),证书对应的 key 在这里
  9. ensymble 这个工具来给 libffi/Ctypes/PythonScriptShell(unsigned_testrange) 重新签名,加上 NetworkControl 权限
    ensymble.py signsis --cert=20081008001.cer --privkey=dospy.key --passphrase="" --execaps="ALL-TCB-ALLFILES-DRM" PythonScriptShell_1_4_4_3rdEd_unsigned_testrange.SIS imei_PythonScriptShell_1_4_4_3rdEd.sis

    ensymble.py signsis --cert=20081008001.cer --privkey=dospy.key --passphrase="" --dllcaps="ALL-TCB-ALLFILES-DRM" libffi.sisx imei_libffi.sis

    ensymble.py signsis --cert=20081008001.cer --privkey=dospy.key --passphrase="" --dllcaps="ALL-TCB-ALLFILES-DRM" ctypes.sisx imei_ctypes.sis

  10. 这下大功告成,我....我终于又可以在家里坐在马桶上用手机上无线了!!
Topic: 

给奥组委做的邮件系统容灾方案

据说一共有5家厂商提供了方案,俺代表搜狐提的方案最后得到采纳,日后要说起bj2008,我可以对达达说:当年你老爸也是为奥运建设出过一份力的...

现在残奥会也已经结束,按照当初商定的合同,搜狐的支持服务在一周后也就是9/30将正式终止,我想现在讲一下技术细节应该不会有什么影响了。

要解决的问题简单说就是:万一奥组委的邮件系统瘫痪,怎样才能让邮件系统尽快可用。

系统出问题有很多种,除了硬件故障、软件故障外,网络故障和机房故障也是需要考虑在内的。我根据搜狐邮件中心所能做的工作,提出的是一个异地容灾方案:

灾备系统在我们这里,把它设为低级别的 MX,平时不启动 25 端口,一旦奥组委确认需要启动该灾备系统,则启动 25 端口。将所有收到的邮件,比如说发给 foo@beijing2008.cn 的,利用 Milter 协议的 addRcpt 功能,增加一个 foo@vip.beijing2008.cn 的收件人。vip.beijing2008.cn 是本地 host 的一个邮件域,在紧急情况下,奥组委邮箱的原用户可以登录到这里查看并回复新收到的邮件。而发给 foo@beijing2008.cn 的邮件会在我们的 postfix 队列里等待,一旦奥组委的故障恢复,自然就能重新投递过去。

技术核心就是 Postfix 的 Milter 支持能力,以及针对 beijing2008.cn 的投递不是 DNS 查询而是 transport_table 指定。实施上最麻烦的是用户数据问题,和邮件没任何关系,却花了大量的时间在上面。

灾备准备好了,奥运期间也没有出现突发事件需要考验俺的这个方案。算是好消息,但也有点小遗憾。

最后要说的是,奥组委的IT部门,业务能力还是很不错的,比我以前做项目时接触的几个甲方单位强多了;还有一个首信的同仁,不知道是做项目监理还是负责整个的项目集成,几个电话打下来对他的敬业精神和认真态度也是很佩服。从小见大,奥运会本身怎么怎么样不去评论,但确实是把中国最优秀的人才集中在了一起去支持这个 big project。。。咳咳,上句所说的最优秀人才不包括我.

Topic: 

python 性能调优一则

最近几天新写的一个日志处理程序深受性能不足所困扰(每5分钟处理一次httpd日志,统计用户的连续访问和停留时间)。显然每5分钟处理过的数据必须至少保留 30 分钟后才能销毁,否则所谓连续访问无从算起。

问题是第一次处理的时候,程序很快就结束了,大概30秒;但第二次时间就增加到60秒左右,第三次时间增加到90秒上下...

很容易想到这是由于数据没有释放,导致在做查找操作的时候时间也随之延长...但我这里明明用的是 dict,应该是 o(1) 的复杂度,为什么这里表现出了线性增长关系呢??

代码很简单,我检查了又检查,函数封装了一个又一个(为了便于profile),从 hotshot 换到 cProfile,就是找不出程序慢在何处。

从 profile 结果看,最让人惊讶的就是主函数所调用的各个子函数的用时之和远远小于主函数所消耗时间(主函数内的计算量可以忽略不计),比如这样:

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000  180.979  180.979 :1()
        1  114.522  114.522  180.979  180.979 test.py:33(main)
  5521410   32.147    0.000   32.147    0.000 {method 'split' of 'str' objects}
  5521409   16.107    0.000   24.152    0.000 test.py:7(process)
 11042818    6.586    0.000    6.586    0.000 {method 'has_key' of 'dict' objects}
  5521413    4.577    0.000    4.577    0.000 {method 'readline' of 'file' objects}
        3    3.594    1.198    3.602    1.201 test.py:20(commit)
  5521409    1.979    0.000    1.979    0.000 {method 'strip' of 'str' objects}
  3854644    1.459    0.000    1.459    0.000 {method 'append' of 'list' objects}
        3    0.000    0.000    0.008    0.003 {time.strptime}
        1    0.001    0.001    0.006    0.006 /usr/local/lib/python2.5/_strptime.py:12()
       10    0.000    0.000    0.004    0.000 /usr/local/lib/python2.5/re.py:186(compile)

总共 180 秒执行时间,但其它所有函数加起来也不超过 80 秒,另外的那 100 秒去哪里了呢?

然后就去翻 python 手册的 profile 部分,在看 timeit 的时候无意中扫过这么一段:
Note: By default, timeit() temporarily turns off garbage collection during the timing. The advantage of this approach is that it makes independent timings more comparable. This disadvantage is that GC may be an important component of the performance of the function being measured. If so, GC can be re-enabled as the first statement in the setup string.

我突然福至心灵,在代码里面禁止了自动垃圾收集,然后去测试,哈,问题立刻解决。不仅仅是丢掉的 100 秒找回来了,所有的操作速度都大大提高。原来分析5分钟数据要30秒的,现在10秒就搞定了!

这下就明白刚才的问题出在哪里了——在日志分析过程中创建出了大量的对象,而这些对象还都不能销毁,等待以后使用,结果就导致 python 频繁的 gc(陈儒的《Python源码剖析》里介绍是缺省每创建700个对象就执行一次 gc),而gc遍历队列是在线性增长的,这就表现为大量的时间被消耗掉,而且执行时间越来越长。

以前从来没有深入了解过 java,今天是第一次感受 gc 的作用 :PP

附:感觉还是 cProfile 比 hotshot 好,用 hotshot 很多系统内置函数的执行时间(比如字典的has_key, 字符串的 split)是看不到的。

Topic: 

Gears 0.4!!

自从 gears 0.3 发布的时候宣称下一版本将会有 Blob() 支持的时候,我就在一直等着 0.4 的到来,还为此特意加入了它的 google group。

现在,无需自己编写浏览器插件,就可以利用 gears 方便的实现 web 的文件断点上传了(我今天已经写了一个小例子验证)。甚至 gears 还拟定了一个 ResumableHttp Upload 的协议规范

如果没有许可协议的限制,以后我们就会鼓励用户下载 gears 来上传大附件了。

现在发现 Gears 的目标是给浏览器打 patch,让老古董们能跟得上 HTML5 的进展。以前觉得它只是一个 offline 应用的框架,理解还是狭隘了。

Topic: 

现在实施的技术交流会

小熊留言问到 screen 是不是窦窦同学的报告,这里说一下我们现在的技术交流会形式:

定期指定一个技术课题交给某人做研究,大概三周后把其研究结果给所有技术人员做报告。

1. 这个技术课题是未来工作中可能会应用,但现在大部分同事还没有接触过的领域

2. 该流程的目的是开拓大家的技术知识面,所以研究工作会交给本来对该方面不太熟悉的人进行——虽然 screen 这个软件窦窦很早以前就用过,但这个做报告的同学是对 screen 一无所知的

3. 该流程的另外一个目的是给每个人上台做演示的机会。增强各个小组之间的交流

4. 三周时间准备是不是有些太短?从研究的角度来看,是有点短;但另一方面这样每个季度也就不过4次集体交流,我觉得周期不能再长了。所以我们不会选太高深的课题。另外,该研究完全可以在上班时间来进行,这是工作任务的一部分。

4. 三周时间是不是有些长?因为在第二周会首先试讲一下,听众是他的leader和我。我们会指出一些问题,帮助他下一次能在众人面前做更好的表演

5. 技术课题是谁提出的?现在还是由我来提出,希望以后这个事情可以交由邮件中心技术委员会来讨论决定。

6. 该活动我们已经进行了两次。第一次是 mysql-proxy,第二次是 screen,下一次是 javascript 混淆工具

7. 上面提到的"邮件中心技术委员会"是个什么机构?我们是一个很官僚的部门,因此成立这个委员会展示我们的行政作风。

hehe,大型 E-Mail 系统是一个涉及面很广的项目,在 N 个方面都需要有技术专家来支撑,需要有一个核心团队来推动提升部门的技术能力

Topic: 
订阅 RSS - 技术