Author Archives: jeff

理解lisp中的cons

列表是Lisp的核心数据结构及语法。在Lisp里面,列表的存储方式比较特别,一个列表通常由1个或N个cons来组成的。那个list又与cons是什么关系呢?搞清楚之前,我混乱了许久,读过很多文档及实作才豁然开朗,概要记录一下,看似内容不多,但要理解的东西多了去,整理出来的文字比起之前做的笔记要少多了 :) 关于Cons cons是一种简单的数据结构,一个cons是由头尾两个元素构成:如下创建一个cons对象: > (setf x (cons 1 2) ) > x (1 . 2 ) 这创建了一个头是1,尾是2的cons对象。 对cons的操作有两个,car及cdr,分别是读取cons的头和尾元素: > (car x) 1 > (cdr x) 2 cons的元素可以是任意类型的,当元素也是cons时: > (setf y (cons 1 (cons 2 (cons 3 4)))) > y … Continue reading

Posted in 技术 | Tagged , , , | Leave a comment

Emacs打开python文件时加载ropemacs

用Emacs作为日常的编辑器有些日子了,各种爽,只是之前的配置会让Emacs在启动的时候自动加载ropemacs,这个东西只有在编辑python文件时才有用,启动时加载会让Emacs的整个启动速度变慢。于是对此作了一下微调,使用eval-after-load宏,在python-mode完成加载后(即打开一个python文件后)才开始加载ropemacs。 我的init.el里面,关于python的配置修改如下: BTW:因为感冒才有借口不务正业一番,又重温了一下lisp。lisp太美了,决定以后就拿学习lisp当休闲,虽然短时间内工作中用不上lisp,但我已经相信熟悉lisp能让自己的编程水平更上一层楼,至少在视野上。

Posted in 技术 | Tagged , | 2 Comments

我的2011 — 创业前记

由于苦逼地埋头苦干的缘故,我已经快有半年时间没有更新博客,这些时间里,我欠下的文字债累累: – 参加了gurudigger第一季的城市越野活动,欠下Mike一篇总结文字。 – 我升级当老爸了,我没有为蓝蓝妈妈或者是蓝蓝写下半片博文。 – 离开了工作近七年的Botwave,我竟然连一个字儿的离职信都没有。 – 创业了,关于产品,关于技术,关于团队,心中每日都有千百种感想,却始终只是闷声加速。 最近思考过多过快,没有停下来整理思路,沟通的时候嘴巴往往跟不上头脑的速度了。善哉。 萝卜闷在坑里会烂,话儿闷在心里会淡。今天晚上以“家属”的身份参加了Botwave的年会,席上和旧同事聊得很有感触,趁有感觉,顺便把今年的事儿都马克一下,不然放着放着就会忘记了。同时,希望这篇文字可以一次性把欠下的债还了。@peter @treeinthewall @onedear @nino @kevin @steven @blue @ken @sven @rocket, @mike @cngump @vera @anson @linluxiang @michael @forever @bill @bigbigtooth,由于涉及范围较多并且发散,大家就挑感兴趣的来看吧。原谅我的婆妈吧。 Botwave,外星人和他的朋友们 博汇(botwave)是我迄今为止,我除了自己的家以外,所呆的最长的地方了,我在这里生活了7年的时间,从一个懵懂的程序员成长至产品研发总监,从一个小屁孩升级为人夫,继而为人父。这里就是我第二个家。我偶尔还暗暗为自己7年前的选择庆幸,我没有受邀去某上市公司当钉子,而是留在了一家不起眼的创业小公司,饱经磨练,修来周身刀,现在自己创业大派用场。 botwave是一片自由的土壤,在这里,只有你认为自己有能力把控好一样技术,你就可以把它引入进来,我在第2年就获得为公司最大的项目设计架构的机会、以及在以后的时间里影响整个核心技术部的技术格局。在产品方面同样,有好的想法和执行能力就可以放手去干,试手机网的诞生与落幕让我获益良多,从此与产品结下不解之缘。 botwave是一个有爱的家:Boss们绝对亲民随和,peter混在员工里面,很难认出是CEO,扫地老僧Bruise把技术人的幽默感在技术上挥洒得淋漓尽致,长发披肩的jas可以男扮女装和我走catwalk;你可以终年拖鞋短裤地上班,只要你能完成任务,睡到中午12点来上班也没问题。最重要的是,这是一家正直的公司,引用Peter一句话:我们要站着把钱赚了。 2010年我的创业计划流产后,继续留在公司,带领外星人团队(EasyTrace团队,简称ET)进行研发工作。我给团队的目标是快速响应市场需求,推进研发的过程、每个队员全面发展(包括技术以外的能力,如沟通能力、产品感觉)并术业有专攻。这一年多以来,ET团队阵容空前稳定、每个人都在团队中找到了自己的位置并独当一面,产品也顺利地往前推进。在这个时候,我知道ET更多需要的是来自市场扑面而来的动力和紧迫感,我也到了离开、为自己的梦想去奋斗一把的时候了。 在Botwave很快乐,我享受技术难题带来的挑战,享受产品失败给我带来的痛的体验,享受和你们在一起的晨会、推心置腹的交谈、面红耳赤的争论。我为团队成员的成长而感到愉悦,我更为曾经是一名botwaver而光荣。我爱你,botwave。 一起,爱朋四友和图睿和下一个产品 2011年初,作为珠三角技术沙龙的哎呀主席,我在主办过N次的线下沙龙之后,发现了一个相对小众,但是又比较强烈的需求:基于活动的社交服务。于是某一天,我快速地在evernote上写下几行文字,发给了anson,老甘,小林,没想到大家都对此非常兴奋并一拍即合。在2011年3月份的某一天,爱朋四友(iapp4you)在维多利星巴克进行了第一次正式会晤,然后便马不停蹄地展开了产品的开发。对了,这个产品起初名叫eventking。 如果你用webQQ,现在你应该可以在应用排行榜前100位找到一个叫点歌台的应用,该应用由上线之日起,用户数就一路飙升,最终超越豆瓣电台、音悦台、虾米电台等大牌应用,稳稳当当地挤进应用排行前100。它出自一个名叫图睿的团队。团队主要有三个核心成员:michael, forever, bill.一个产品达人,一个开放平台的周伯通,一个一流的UI设计师。 … Continue reading

Posted in 感悟, 技术, 生活 | 7 Comments

移动应用开发的终极武器

不,我并没有在讲HTML5,也不是讲PhoneGap这类号称跨平台的FrameWork。我讲的是我的一些iDea和YC的一个即将发布的产品Parse。 我在做iOS应用的时候,前至手机端的一个像素,后至服务端的某条推送服务的守护进程,我统统都结结实实地打过交道。作为一个自命有追求的研发工作者,我总结出,在ios应用开发的很多个环节可以抽象出来进行重用,并且再进一步包装的话,可以做成面向开发者的第三方服务,如下面我提出的三个: 推送服务 在服务器端搭建和维护一个稳定靠谱的推送服务不简单。当我完成了我的产品的推送服务时,我马上就想到可以做一个叫“代客推送”的生意。但是有更聪明的人想到了,并把生意做了。 应用内聊天(in-app chat) 现在好像不管什么应用,都指望给自己的应用加上一个能让用户点对点的发消息功能,有消息功能后,又想要及时一点的聊天。嗯,这又是一个可以做的生意呀。如果有一个服务给你提供一份SDK,可以三行代码实现用户应用内实时聊天,外带推送,200个使用用户内免费,你会考虑吗? 用户及关系中心(SocialCenter) 这样说吧,你有一个应用A,允许用户绑定微博,twitter,facebook的帐号。然后有下面的场景: 用户在一台终端绑定三种帐号。 用户在另一台终端再次登录,无需重新绑定即可直接访问三种帐号的资源。 好了,作为开发者的你,又开发了应用B,与应用A一样有着上面一样的需求,你可以把应用A里实现的那套代码(包括前端和服务器)照抄一遍,也可以继续思考: 针对于绑定用户这个功能而言,应用A和应用B的服务端Host为同一个,会怎样呢?那就只需要维护一套代码咯,但这还不是全部。 对于用户来讲,更神奇的事情发生了,我在A应用绑定过三个帐号后,在B应用使用微博帐号登录,居然那三个帐号的绑定在B应用里也生效了,这说明什么?用户在所有的应用里有可能不断重复绑定社交网络的动作,其实可以简化为绑定一次! 其他的就不多讲了,它是一个类似about.me和gameCenter结合的东西,我认为基于它之上可以挖掘的东西太多了。 歇一歇 以上三个东西是我一直嚷着要做的第三方服务,原先我的想法是继续多做几个应用,经过几个应用的沉淀,它们自然就会作为副产品沉淀出来。其实这三个想法都有一个一致的理念:为开发者提供更方便的基础服务,降低开发成本(虽然第三个看起来理想更远大)。我深信这个方向是可以产生价值的。一直到今天,我看到了YC的一个未发布的产品Parse,我更加坚定了我的想法。 Parse Parse是一个完整的 iOS,android 后端支持平台,它可以让开发者完成忘掉服务器端的事情(parse透明地为你提供服务端的支持),全情投入在客户端的开发上面。还有人把它类比成手机开发中的Rails。上面我提到的三点想法里面,Parse提供了其中两点: 推送服务 用户、社交网络连接(含twitter,facebook) 此外,Parse还提供了本地数据与服务端数据同步的服务,开发者只需要对本地的数据进行操作就行,多舒服啊。 有这样好的SDK和服务提供给你时,做一个iOS或andoird应用变得更加容易了。如今这样的第三方SDK和服务越来越多,原来在Web2.0里面出现的第三方服务,如评论,用户反馈托管(如userVoice),表单等己经开始全面移植到移动互联网的世界。可以想像,未来的移动应用也可以简单的MashUp出来。 现在你知道为什么我说的终极武器并不是哪一门子的技术了,丰富而强大的基础服务才是。 关于我的idea们 在没有成熟的类似的第三方服务出来之前,我会选择在以后的应用里面(包括“一起”)继续打磨这些基础套件,如果打磨得好用,我再考虑作为独立产品发布出来。如果有朋友现在就感兴趣和有时间,那请你们赶紧做吧,我一定会成为你的客户。

Posted in 技术 | Tagged , , | 9 Comments

使用VitrualEnvWrapper隔离python项目的库依赖

是什么 VirtualEnv用于在一台机器上创建多个独立的python运行环境,VirtualEnvWrapper为前者提供了一些便利的命令行上的封装。 为什么要用 – 隔离项目之间的第三方包依赖,如A项目依赖django1.2.5,B项目依赖django1.3。 – 为部署应用提供方便,把开发环境的虚拟环境打包到生产环境即可,不需要在服务器上再折腾一翻。 怎么用 安装 – pip install virtualenvwrapper – 把下面这句加到~/.bash_profile里面,如不嫌麻烦,也可以每次都手动执行。 source /usr/local/bin/virtualenvwrapper.sh 常用命令 创新的虚拟环境 – mkvirtualenv [env1] 该命令会帮我们创建一个新环境,默认情况下,环境的目录是.virtualenv/en1,创建过程中它会自动帮我们安装pip,以后我们要安装新依赖时可直接使用pip命令。 创建完之后,自动切换到该环境下工作,可看到提示符变为: (env1)$ 在这个环境下安装的依赖不会影响到其他的环境 – lssitepackages 显示该环境中所安装的包 切换环境 – workon [env] 随时使用“workon 环境名”可以进行环境切换,如果不带环境名参数,则显示当前使用的环境 – deactivate 在某个环境中使用,切换到系统的python环境 其他命令 – … Continue reading

Posted in 技术 | Tagged , | 2 Comments

用Redis实现分布式锁

Redis有一系列的命令,特点是以NX结尾,NX是Not eXists的缩写,如SETNX命令就应该理解为:SET if Not eXists。这系列的命令非常有用,这里讲使用SETNX来实现分布式锁。 用SETNX实现分布式锁 利用SETNX非常简单地实现分布式锁。例如:某客户端要获得一个名字foo的锁,客户端使用下面的命令进行获取: SETNX lock.foo <current Unix time + lock timeout + 1>  如返回1,则该客户端获得锁,把lock.foo的键值设置为时间值表示该键已被锁定,该客户端最后可以通过DEL lock.foo来释放该锁。  如返回0,表明该锁已被其他客户端取得,这时我们可以先返回或进行重试等对方完成或等待锁超时。 解决死锁 上面的锁定逻辑有一个问题:如果一个持有锁的客户端失败或崩溃了不能释放锁,该怎么解决?我们可以通过锁的键对应的时间戳来判断这种情况是否发生了,如果当前的时间已经大于lock.foo的值,说明该锁已失效,可以被重新使用。 发生这种情况时,可不能简单的通过DEL来删除锁,然后再SETNX一次,当多个客户端检测到锁超时后都会尝试去释放它,这里就可能出现一个竞态条件,让我们模拟一下这个场景:  C0操作超时了,但它还持有着锁,C1和C2读取lock.foo检查时间戳,先后发现超时了。  C1 发送DEL lock.foo  C1 发送SETNX lock.foo 并且成功了。  C2 发送DEL lock.foo  C2 发送SETNX lock.foo 并且成功了。 这样一来,C1,C2都拿到了锁!问题大了! 幸好这种问题是可以避免D,让我们来看看C3这个客户端是怎样做的: … Continue reading

Posted in 技术 | Tagged , , , | 12 Comments

Redis点滴

最近试验在产品中使用Redis来完成以前MongoDB做的一些工作,发现在大量消息采集的场景下(咱们这次不谈查询什么的),redis比mongoDB表现更好──这里主要是指编程更简便、逻辑更清晰。下面我举一些小例子说说Redis都为我们解决了什么问题,技术上下文关键字:高并发、分布式。 插入与更新操作的无差别性 Redis的所有SET(包括MSET,HMSET)操作都是:存在则更新,不存在则插入,即insert if not exists。所以在编程的时候开发人员不需要关心所做的操作属于更新还是插入,减免了判断,因此也避免了判断操作可能带来的锁定。 MongoDB也有同样的操作,update操作的upsert参数调为True即可,不过经过测试,MongoDB为查询条件为了索引后使用update with upsert来代替insert操作,效率比光insert要低5倍以上,而redis的HMSET操作的效率要胜出。 GETSET的妙用 上一个经验虽说可以解决这条数据该“插入还是更新”的问题,但需要知道当前操作是否针对某数据的首次操作的需求还不少。例如我的程序会在不同时间接收到同一条消息的不同分片信息包,我需要在收到该消息的首个信息包(发送是无序的)时做些特殊处理。 早些时候的做法是为消息在MongoDB维护一个状态对象,有信息包来的时候就走“上锁->检查消息状态->根据状态决定做不做特殊操作->解锁” 这个流程,虽然同事已经把锁的粒度控制得非常细了,但有锁的程序遇上多实例部署就歇了。 Redis的GETSET是解决这个问题的终极武器,只需要把当前信息包的唯一标识对指定的状态属性进行一次GETSET操作,再判断返回值是否为空则知道是否首次操作。GETSET替我们把两次读写的操作封装成了原子操作,V5啊。 山寨版数据过期策略 我曾经想过要写服务器端的脚本来扩展redis,试图要拿到数据过期的事件,用来做一些回调来处理过期数据,但很快我发现这个不现实。于是我选择通过使用排序集合(SORTEDSET)来实现一个山寨的数据过期策略:需要定时过期的数据,统一添加到一个排序集合:ZADD expiringKey timestamp data。在这里我使用了时间值(毫秒为单位的长整型)作为数据的分数,那么很自然的,早期的数据总会排在集合前面;然后我写一个程序会定时地过来打理这些过期的数据就好了。 存储结构化数据 例如有“通讯录”这样的数据,包含有”name”,”city”,”gender”等8个属性,使用mongoDB保存就很简单,创建一个Document,设置属性后存储即可,而Redis本身并非Document型的DB而是Key Value DB,要存储这种数据,还得在Key上面花一点功夫:使用contact:id:name,contact:id:city,contact:id:gender之类的Key来存储其对应的值。当然,这只是使用redis存储结构化数据最原始的办法,更建议的办法是使用Hash存储,如 hmset contact:id name jeff contact xx@gmail.com gender male。相对set操作而言,hmset既节省了存储空间又提高了存储效率。 使用MongoDB来存储这些数据是小菜一碟,但鉴于第一点经验,我还是愿意使用Redis。 比较可惜的是,目前Redis的Hash存储仅支持字符类型的值,不支持其他数据结构,我非常期待它日后会支持其他数据结构,甚至支持Hash的嵌套。关于这点,@wuvist 同学认为十分有可能。 小结 上面这些Case都只是Redis牛刀小用,但实际上给程序带来的便利是非常明显的,最明显的就是可以把原来的程序上使用的锁都抛弃掉,甚至直接支持分布式运行和水平扩展了。 顺便在此小结一点高并发分布式应用程序编写的一些推荐的注意事项吧,当然这是我的个人偏好并结合了一些特定业务领域的性质: 1. 程序对资源最好是只读或只写,明确分工。不要在一个程序里同时对资源进行读写,除非是原子操作,如GETSET。 2. … Continue reading

Posted in 技术 | Tagged , , , | 3 Comments

Autoforms使用指南

Autoforms是Jeff开源的一个基于Django的自定义表单引擎,可用于调查、投票、信息收集甚至是整合至工作流程引擎和PAAS平台当中。目前AutoForms主要用于支持珠三角地区几个技术社区的线下活动报名、反馈收集等。jeff在本站上也搭建了一个autoforms的实例,可以免费为大家提供简单的在线表单服务,有兴趣者,可联系我开通帐号。最近对AutoForms的使用反馈越来越多,所以更新会相对频繁一些,嗯,有人使用就有动力呀! AutoForms(今天下午发布最新版本为0.4)的主要特性: 支持14种字段类型13种html输入控件(包括日期选择组件),可以快速定制大部份常见的表单。 保存用户提交的表单数据,并向表单作者展示,同时提供数据导出功能。 支持表单继承,需要周期性地使用相同的表单时,一点也不费力。 用户填写表单后,邮件通知表单作者。 提供了丰富的API,容易与其他应用程序作整合。 关于AutoForms的安装请参考github上面的说明,本文主要的内容是指导表单用户如何使用Autoforms。 登录 首先登录至管理界面(http://f.jeffkit.info/admin/ 帐号为 form/form),会看到Autoforms的应用,该应用有两个模型可供管理: 表单管理提供快速创建、修改表单;预览表单,查看表单数据以及生成某入式表单代表等功能。 表单域管理则提供对某些表单域的高级设置功能。 创建表单 下面我通过创建一个“AutoForm用户反馈表单”为例,示范一下如何使用AutoForms来创建表单。 首先,我们点击表单管理的增加按钮开始,打开增加表单的页面,表单上半部份是表单的基本信息,下半部分是定义该表单所包括的字段(表单域)。下图是填写AutoForm用户反馈表单的示例数据: 补充说明一下上面页面一些容易迷惑的地方: 父表单:如果指定某个表单为父表单,那么所创建的表单将自动获得父表单的所有字段。 启用:如果处于勾选状态,则表单可供填写,否则表单只能查看而不能提交。 表单域里的组件:组件是指在页面上展示表单时使用的HTML控件,如你的字段是密码类的,你最好选择“密码输入框”组件。一般情况下,使用默认的组件已经足够。 接下来,点击”保存并继续编辑”按钮,这时在页面的面上角出现了一些快捷按钮: 预览,让您预览所创建的表单的展示效果。 数据,用户通过表单提交数据后,表单作者可以在此看到所有提交的数据并可将数据导出为CSV格式。 嵌入,autoforms允许用户通过嵌入页面的方式展现在第三方网站上面,点击该按钮可获得嵌入的HTML代码。 历史,Django的内置的数据为更记录。 在网站上查看,点击后打开填写表单页面。 预览表单 我们先点击预览按钮,看看刚才创建的表单的效果如何!结果有点意外,“评分”和“在哪了解到autoforms”两个字段光秃秃的! 其实并不意外,我们在表单是创建了两个选项/多选值类型的字段,而我们还没有为这两个字段提供选项数据,所以它们看上去是光秃秃的,那么下一步,我们就是要为选项类型的字段加上选项数据。 编辑字段 返回管理首页,点击表单域进入表单域列表页面,在这里可以找到刚在才创建表单时生成的几个字段,点击“在哪了解到autoforms”进入该字段的编辑页面,页面由一个展开区域和(字段基础信息),三个隐藏区域(高级设置、选项、错误信息)组成。高级设置暂略过不理,选项区就是我们要为选项类型的字段提供数据的地方,而错误信息则允许我们自定议错误提示内容。 下面我要为“在哪了解到autoforms”字段提供一些选项数据,如下图这般添加一些数据: 保存字段的修改,再依法泡制一下“评分”字段,一个表单就完成了。最终结果就是下面的表单了,各位使用过autoforms的同学,来这里给我一些建议吧。 收集数据 表单定义好了,是时候发出去给大家填写了,收集上一段时间后,可以通过“数据”页面来查看已经收集好的数据,如果你愿意,可以点击右上角的导出按钮,以CVS的格式导出数据: 好了,AutoForms的基本使用就这么一些,如果你是开发人员,对API感兴趣,欢迎阅读代码,如果你有些空闲时间,希望掺和一些开源项目,AutoForms也欢迎你加入到开发者行列。

Posted in 技术 | Tagged , , , | 7 Comments

初品RabbitMQ

《taste rabbitmq》是我在珠三角技术沙龙4月份深圳场分享的一个主题,由于时间紧迫,我在赶往深圳的和谐号上还努力跑着测试程序,但数据始终不好看,直至昨晚,才有时间把测试重新进一行遍,更新了一下slide,现在发布出来。废话不说,大家直接看slide和代码。录音稍后奉上。 Taste Rabbitmq View more presentations from jeff kit 我还没有正式记录持久化模式的性能测试,因为该模式下的数据极其不稳定,但与非持久化模式对比起来: 1、持久模式的入队列速度会稍慢,大概可能有10%左右的下降(当然消息体积大小也会有影响)。虽然rabbitmq是异步处理持久化事宜的。 2、持久模式的出队列速度明显慢很多。如果是完全从硬盘消费消息,那速度更是让人抓狂,尽管这种情况只会在重启服务器时才发生。 3、持久模式,rabbitmq的服务进程长期活跃着,占用着不少CPU资源。 如果你使用持久模式,rabbitmq会很负责任地保证你的数据安全,如果你的应用对消息消费者的性能要求不高,可以考虑使用。 最后模拟一次实际使用的场景:消费者一直处于待消费状态,生产者往队列里喂消息,结果是生产者消息入队列的速度与单测入队速度没明显差异,而消费者则有稍变慢,rabbitmq是尽量地让消息生产者始终保持高效啊。 我的测试代码放在了github上面。README有测试方法和参数说明,感兴趣的同学可以玩玩。

Posted in 技术 | Tagged , | 6 Comments

解一道SQL问题:找出成绩优秀的学生

今天中午,珠三角技术沙龙官方群(103903642)的Crazy同学给大家出了一道SQL的题目,据说来自某个微群: “SQL开发的一道小问题,一个学校的老师需要评选一组学生作为优秀学生,条件为最多只有2个科目在80-85之间,其他科目在85分以上或者所有科目成绩都在85分以上,表中包含StuId,SubjectId,Score,求最简单且效率最高的语句。 ” 我有点无聊加手欠,就试着解了一下,建立测试用数据表及数据如下,一共有5位同学,4个科目,共20条数据。我的测试环境是老式macbook 402,4G内存,mysql 5.1.42,MyISAM引擎。 我们首先把非优秀学生的条件重新整理一下: 1、凡有一科分数低于80分的,都不能称为优秀学生 2、凡有两科以上分数在80至85分之间的,也不能称为优秀学生 这两个条件是或的关系。 同时整理一下优秀学生的条件: 1、称得上优秀学生的,必须至少所有科目都在80以上。 2、称得上优秀学生的,分数在80至85分的科目必须小于2科。 这两个条件是与的关系。 先找出非优秀学生还是先找出优秀学生,是两种不同的解题思路,下面我分别从这两种思路出发解决问题: 思路一:先找出非优秀的学生,反过来得到优秀的学生 1、首先找出有个别科目分数低于80分的学生 select StuID,count(*) from tb_score where Score < 80 group by StuID 2、然后找出有科目分数在80至85分超过两科的学生 select StuID,count(*) from tb_score where Score between 80 and 85 group by StuID having count(*) >2 … Continue reading

Posted in 技术 | Tagged , | 18 Comments