自我介绍

一个互联网工作者,顺便聊聊其他的一些有趣的事情

日历

May 2013
M T W T F S S
« Oct    
 12345
6789101112
13141516171819
20212223242526
2728293031  

Google 广告

关于数据分析的一些事情

这几个月在一家为客户在Facebook上做广告的加拿大公司工作。简单说说他们对于数据的态度吧。这是一家小型Startup公司,总共不到20个人。其中4个人(包括我)是技术,剩下的除了CEO都是Account Manager。当然CEO很多时候也在做Account Manager的事情。

刚到这个公司的时候,觉得他们的code很烂,他们的数据库设计也很烂。后来才知道,当初startup的时候,是找了印度公司做外包的,他们对这个外包很不满意,所以一期项目搞定之后,就全部拿过来自己搞了。但是后遗症也留下了。

这个公司的数据模型很清楚,只要通过低于广告主给出的CPA价格能赚到钱,就想办法增加广告覆盖率。但是常识大家都明白,增加覆盖率很可能导致转化率下降。但是如果接受这个假设,那么就没有什么赚钱的机会了。恰恰是因为他们相信,除了常识之外,还有一些事情是经验之外的。

比如说关键词……有些关键词对某些人有用,对另外一些人没用。如果不做数据挖掘,生想广告词或者关键词的组合,累死了也赚不到什么钱。

所以……这个公司在代码中设计了几个基本核心算法:
1. 一种止损的trigger,对于任何亏钱的广告,自动停止。
2. 一个自动发布广告的cron,程序一直在扫描。一旦发现一些广告能赚钱,就自由组合这些广告元素再自动发布到广告系统里面。这样,就能出乎意料的发现一些更加赚钱的广告形式。
3. 做了很多广告更新的算法,搞了一个自动化的A/B测试策略来针对Facebook广告价格的浮动,来更新广告的价格。

通过阅读这些算法让我感受很深。所谓的数据分析,不是一个产品经理跑到运维,数据库管理员或者工程师那里说:我现在要跟踪什么什么数据,你帮我出一下吧。然后再对着跑出来的数据琢磨这些数据是否合理。

在这个公司里,只要发现一个数据模式对收入有影响,就会直接编码到系统里,变成自动执行的代码。基于这样的数据导向原则,代码面临无穷多次的重构,因为谁也不知道,下一个数据模式会发生在哪个层面,哪几个数据之间会发生关系。

我觉得国内的不少公司,还在以daily report分析数据,还在说数据只是为了验证产品经理想法的阶段。这动作是不是太慢了?

接下来的话,随便说说,不一定有参考价值:

1. 对于大多数网站,如果你想用数据为导向,必须建立系统级的A/B测试机制。对于界面层面的重构,一个产品经理+一个工程师,一天用这个系统一天至少能做3-4个。系统级别的A/B测试要能够保证快速上线,第一时间看到数据,一旦超过临界值直接结束测试、保留数据并生成报告(直接邮件发送,而不是让产品经理想起来跑到后台再查)

2. 对于做社交网站,或者有复杂用户数据模型的公司,要在界面呈现和用户数据之间建立匹配系统。这样产品经理可以设计几种呈现模式,丢到匹配系统中,过不了多久,就能发现用户对不同呈现的数据反映的不同,然后系统性地固化这种机制。

3. 通过cookie或者用户登录信息,建立针对不同用户的内部tag系统,看这些tag在系统2里有没有明显差异。如果有就可以固化下来,用来提高关键指标。

所以,我现在对于数据分析的感觉是:
1.要提高一个数据指标,盯着它是没有用的。必须找到影响这个数据的另几个可操作性更强的数据指标,调整它们。
2.分析数据的可能性要充分,充分分析的基础是测试充分多的可能性。如果你想测试图标的颜色从绿色变成红色会不会更好。那为什么不测试一下蓝色,紫色和黄色呢?
3. 如果小规模数据已经可以说明问题,就没有必要延长测试时间,也没有必要扩大测试范围。
4. 要充分利用计算机来帮你做数据采集和分析,缩短数据分析的周期,降低数据分析的成本。
5. 有必要的时候,可以让计算机帮你找pattern,因为计算机没有偏见。

欢迎你,HTML5

我看HTML5到来的那一日,会比我们想象的都要快。虽然微软的IE还占据这大部分的浏览器市场,但是不要对它拖后腿的力量估计的太高。也许2011,也许2012年,大量基于HTML5的应用将要出现在我们的身边。

也许那一天,我们还会发现大量的桌面浏览器是基于Trident内核的IE6,IE7,IE8,遨游,TT。但是这又有什么关系呢?因为HTML5将首先出现在移动终端中!出现在Android和iPhone的手机里,然后是大量的手机厂商。对于他们来说,无非是更换一个手机浏览器的版本而已。何乐而不为呢?如果手机厂商们发现,只要更新一个浏览器版本,会支持更多的基于云端的离线应用,而不仅仅是看看网页的话,基于浏览器的app,就会和基于java的app一样,成为手机的一个增值点。

当然这不是运营商喜欢的模式,移动、联通们刚开始动app store的念头,连钱的腥味都还没有闻到的时候,科技浪潮已经把他们拍死在了礁石上。如果那些交互不太丰富的终端,html5的技术已经完全可以支持。那么app store又要开给谁呢?运营商就应该把服务做好,并且安心的收取它们应得的流量费而不是其他的什么。

由于HTML5的蓬勃发展,它必然首先从移动终端平台起步,进而带动大量的云存储产品提供html5的支持。接下来,就要冲击以flash为核心的sns app市场了。有谁不希望通过facebook实现一个基于html5的游戏平台,不但让游戏可以在浏览器内可以玩,还可以在移动终端上玩呢?(看起来苹果的态度是那么地坚决)然后,很多网站发现,它们做一个基于浏览器版本,并且稍微增加一点html5的特性是有利可图的。于是更加适应小屏幕(640*480以下)的页面的应用会发生。直到有一天,我们发现通过移动终端打开的flash网站都变成了html5网站。

但是客观的讲,在ie家族还没有从这个世界上销声匿迹之前,对html5的支持始终是一个痛。好在html5已经有了一个出口,这里有google和apple两家公司开道,移动终端会发展得比互联网应用更加快。年轻的开发者们已经看到了这里的机会,而在Windows平台或者传统互联网上赚钱的公司,正在变得越来越难。两个善于利用他人智慧的公司,走在了html5的前面,虽然它们各自有自己的小算盘。但是至少通往html5的大门已经因为它们而打开了。

上帝保佑HTML5。因为它将大大缩短程序员开发的时间和发布代码的周期,让移动终端的应用变得更加方便和易用。

Google Wave & Buzz - The last mile problem

今天大清早起来,就看到Twitter上很多人在说Buzz的事情,因为在做其他的事情,没有仔细地去用。等晚上回到家的时候,才开始在iPod touch上摆弄,然后再电脑上继续摆弄这个。Buzz的小宇宙确实很强大,尤其是将它和地图连接起来之后,Buzz给人的感觉就是可以看到周围很多人正在讨论什么问题。不是从人的熟悉程度来看,而是从人的物理位置这个角度来看的。但问题是,之前已经有不少公司做过了这样的项目,而且项目的结果都很惨淡。

难道Google的Buzz会有所不同么?

Google Buzz是基于Gmail的一个应用,然后在刚刚开始试用的时候,就已经有人说:我要关掉他。理由就是隐私问题。如果是一个新的应用,很好。但是请不要和我的Gmail绑定起来,至少允许我建立另外一套好友关系。就好像我在碗Twitter的时候,并不愿意我的合作伙伴和我的老板都看到我每天絮絮叨叨些什么。这会让人感觉很郁闷。

Google Buzz的另外一个问题是关于时间轴。对于一个人的操作,有主题和回复两个模块。Twitter的时间轴是很清楚的,一切都是主题,所有的回复也是主题。Facebook’s Wall时间轴也是很清楚的,只跟着主题走,而主题跟着人走,而所有回复或者再回复,都是通过通知来搞定,但是Facebook’s Wall的量不会很大,因为主题隶属于人的关系,阅读是分裂的,也恰恰是因为这个原因,所以通知量不会很大,可以解决。

Google Buzz刚好介于两者之间,一方面把所有朋友的主题都搜罗到一个时间轴中,一方面回复又需要通过通知来解决。这样的结果就会造成通知量大增,但是阅读又是割裂的(你得到主题下面找到相应的回复,再继续往下读)。

Buzz这个产品,让我想起了之前雷声很大的Wave项目。Wave这个项目应该是失败了的,死用户很多,因为这个功能非常强大的Wave,除了折腾人之外,不知道能够让人获得什么。相对来说,我觉得Buzz的命运会好很多,但是也仅仅能座位Gmail的一个附属功能而存在。也是因为它依附于Gmail,所以才会继续存活下去。说起来还是有点让人伤心。

互联网的作用,就是让信息流变得更快,促进了信息的沟通,就会让项目成功。而Googler现在的问题在于,虽然他们试图利用自己可以控制的资源,让信息流通更快起来,但是他们却忽略了一个很重要的问题——最后一公里。最后一公里,往往决定了许多电信公司是否能够活下去,就算是它的主干网络再如何先进,如果没有最后一公里的服务和落地的政策保护,所有的强项都没有用。

当Google在致力于聚集所有的信息,并且以最快的速度传达到个人手中的时候,它需要解决的问题无非是两个:1. 如何有序的汇总信息,2.如何知道哪个用户需要什么信息。在这个基础上,各种模式都是可以探讨的,但是任何模式都不能缺少一个重要的环节——直观的界面。

现在我相信,一个好的互联网产品,需要的可能不仅仅是技术,或者交互思想。它可能需要一点点philosophy,对于人类应该成为这样的一点点思考。虽然我现在已经很久没有用FriendFeed了,但是它和Twitter一样,闪现出这方面的很多想法。那么Buzz究竟是一个东西呢?为什么它应该寄宿于Gmail之中呢?这些问题,我想,Buzz的产品经理不知道是否考虑过,希望不是因为这是一个Gmail team做的东西,而利用这个平台更好推广之类的原因吧。

从iPad看到Apple的未来钱景

如果只是从外观上看,iPad确实有点丑,甚至在刚打开的时候,发现里面的图标按钮,以及在iPhone中被津津乐道的锁屏模式,在iPad中都显得那么得可笑。坦率的说,在看直播的前30分钟,我觉得iPad将是一个极其中庸的,大屏幕的iPod touch而已。即便是在展示各种原本在iPhone中已经存在的各种App store应用的都是,也只觉得这一些无非是那些创意的再复制。一种完全无所谓的态度。

iWork和客户端产业:

当演讲的高潮部分来临的时候,我彻底被震慑住了。那是从展示iWork开始的……如果说前面的那些功能都是为了娱乐消费而使用的话,那么iWork成为一个在操作上使得iPad不同于iPhone系列产品的一个重要的分水岭。对于我来说,这样的意义不仅仅在于iPhone那么支持更多的Socialnetwork的App,或者一定程度上促进了社交媒体的客户端化。而是将iPad定位在一个更长被使用的办公室人群的产品。

在加上体验恰当的软键盘,我觉得这几乎会吃掉很大一块原来用笔记本电脑来处理WORD文档、整理Execl数据,或者用PPT写presentation的人们。iWork已经基本兼容了WORD,EXCEL,PPT的所有基本功能(当然,客观地说,做大数据处理的时候,EXCEL的处理能力比Numbers要优秀很多)。这个很酷的上网本的价格只有3500RMB左右,而一个无限路由器的价格现在不过100元钱!在家又可以方便无限上网的人来说,这样的用户体验,比用笔记本电脑要强很多。

iWork的植入给所有原来基于Windows、MacOS或者是Linux系统下的桌面应用软件一个很好的示范,那就是Apple将给所有的桌面应用开发者一个和iPhone一样强大的软件销售平台。在iPhone成功案例的影响之下,很难不让那些原来在Windows、MaxOS上作出优秀软件,但是盈利一般的公司把认真地考虑把自己的优秀软件移植到iPad上。

我所期待的OmniGraffle,MindManager会不会也在iPad上销售,只卖USD9.99呢?如果这些软件都跟杀毒软件的价格一样,你会不会买呢?事实上,iPad大大降低了桌面软件的营销成本,从而也直接降低了它的单个售价,可以让桌面软件向iPhone中的软件那样,卖的更多,单价更便宜。

如果事情朝这个方向发展的话,其他的公司即便是能够卖出更加便宜的上网本,那又有什么意义呢?

iPad给了用户和开发者一个双赢的机会,将必然改变客户端软件的产业链。这个产业链曾经被完全开放的Windows操作系统搞得支离破碎,大公司靠成本高昂的法律手段来保护自己的利益,而小公司则只能因为盗版横行而忍气吞声。iPad缓解了这个矛盾,让用户更愿意为好的应用付费,让好的工程师加入到桌面应用的开发中来!

USD499,为什么这么便宜?

在App Store的成功和潜力之下,苹果也开始意识到,硬件的利润不一定是唯一丰厚的利润来源。还有一个可能行,就是对软件发行平台抽税。当然,我相信,iPad的成本价格并不会高于USD499。而作为一个大版的iTouch来说,USD499的价格,显然利润不是那么丰厚(相对于iPhone以及Mac的利润率来说)。

而USD499的价格,对于几乎所有制作上网本的硬件厂商来说,无疑是一个打击。它虽然贵,但是贵得有限。这样的价格,甚至比高配的iPhone还要便宜。而对于大部分的Windows用户来说,唯一担心就是他们买了iPad之后会不会使用其中的应用。而不再是,它是一个昂贵的奢侈品(大部分人买iPhone 3GS的时候就有这种感觉,也是我至今没有入手iPhone的原因)。

所以低配在USD499的主要原因就是抢占市场,为上面的App store提供足够的盈利空间。这就是一个培养产业链的公司的做法。这种做法有点类似于PSP——可惜的是,PSP只针对游戏,而且是赔本卖的。

作为一个历来纵向垄断从软件到硬件的公司来说,Apple现在正在走向一条,产业链垄断的道路。而有趣的是,这套垄断的道路是建立在处处合作的基础之上的。

在这次发布会上,我们可以看到,iPad并没有过分地注重本身硬件,外观,以及新的特性。甚至在研发的过程中,也没有太过考虑本身的完美性。我们发现除了一些引领方向的产品,比如iWork之外,大部分特性都是基于第三方的技术,从大版本的New york times,到need for speed。

而更需要注意的是两个大型的合作:

iBook的推出,直接降低的书籍的价格,通过让出更多的利润,让书商和读者得到双向的好处。又有谁能肯定地说,现代美国人读书少,不是因为书太贵而去图书馆借阅更麻烦了呢?这一举措直接打击了Amazon的书商市场。又一次以合作攻击垄断的策略。

我们也看到和AT&T的合作,不是语音服务,而是单独的数据服务。iPad不是要营造一个全能的硬件,而是要制造一个无意识的无线网络。在有Wifi的时候使用Wifi,在没有Wifi的时候自动切换成有费用的3G/GPRS服务。且不管运营商策略如何,但是在这样的策略之下,无疑让客户得到的是性价比最好的无线用户体验。AT&T因此而放弃了绑定的谈判条件,但是因为iPad的推介会上,提供了一个价格适中的,不绑架用户的盈利模式。消除了任何用户的不愉快。

是的,如果一个运营商的服务足够好的话,何必需要通过绑定服务期限来挟持用户呢?这就是现在Apple对自己产品的信心导致的产业链的变化。

总结

从外观上,iPad不能打动我。但是从产业链的角度思考。iPad产品不得不说是一个粘着力极强的产品,不仅仅对用户,而且对开发者。客户端的丰富程度,已经成为硬件终端生存的必然前提。iPhone因此而成功,iPad也会因此而成功。这一点都不用担心。

年底销售1000万台,也许真的不是一个梦。

话不多说了,我要下载Snow leopard的OS和 iPhone SDK 3.2,然后花点时间,详细看看iPad的设计中,到底有些什么。这对于我们这些潜在的开发者,又意味着什么。

猜猜希拉里的自由访问互联网是什么?

技术这个事情从来不神秘,问题只是有多大的胆子敢想。

从来没有听说过政府领导了什么互联网的研发项目,而改变了世界。大多数的时候,互联网是一个比较松散的地方,自由生长的地方。所以就算IPV6这样的协议,推广了那么多年,还是没有任何的进展。既然希拉里◎克林顿作出了这样的承诺(呃,其实我只是道听途说,没有看到正式的原文呢),不妨猜猜看这是一项什么样的技术。

首先,这项技术是一项躲过审查的技术,而不是建立一个原本无法建立链接的技术。所以利用通讯卫星,给全世界人民人手发一个锅,这样的技术应该不是下周这项新技术的核心思想。所以,这个项目在很大程度上还是基于IP协议的。

既然是基于IP协议的,就跑不开PROXY或者是VPN技术。就算是实现了加密的独有协议,也顶多是一个TOR。这些协议的实现方式,在技术领域里面已经不再是什么新鲜的事情。而新鲜的事情是,如果这项技术还是基于代理服务的话,以美国政府的力量,他会怎么利用这个VPN。

让我们想象一下,GFW还有一念之仁,允许大部分的中国人访问美国的网站,比如说MSN,那么就必定存在一个被允许的路由,从美国人架设的交换机上经过。那么在任何一个可以由政府控制的交换机上,就可以挂上一个旁路,模拟该IP对来源于中国的请求作出应答。这个应答的意义就在于,只要客户端能够发起的符合一定特征的字符流,不管走任何的路由,都能获得一个可以打开通向自由之路的VPN的public key。

接下来的事情就简单了。不管使用IPV4还是IPV6,只需要把没有被利用的IP地址,什么已经使用的IP地址,但是没有使用的PORT,短暂地被当作VPN地入口地址来使用。而这些IP地址的分发,都是随机的。

一个强大的anti-GFW集群就诞生了。而anti-GFW的服务器可以直接和各地的美国控制的交换机建立一个通道,放在FBI的核心机房里面。

重新回过来回顾这个架构,还有一个需要修改的地方,就是如何制造一个不易识别的pattern,可以被anti-GFW识别,而不容易被GFW识别。要知道anti-GFW虽然可以开发这个客户端,但是GFW的decompress能力是很强的。就算是搭载在流行的FireFox或者Chrome浏览器,GFW也能拿到客户端进行完全反编译。从而知道,加密的策略是什么。

所以最简单的办法就是使用一个通用,但是使用范围又很有限制的服务,比如说POP3服务。而客户端内置一个公钥,发送一个经过公钥加密的认证串,完全模拟加载了PGP加密算法的邮件。而服务器端,则只需要对新发起的POP3协议进行解密。如果发现认证串合法,就提供一个可靠的VPN地址用于穿越。由于GFW没有私钥,所以暴力破解需要花费相当大的运算量,唯一的办法就是禁止这个协议的使用。

那么最终的结果就是所有使用PGP算法加密的POP3邮件都无法发送,或者是所有的HTTPS协议都被禁止。这会让GFW陷入更加危险的境地。这会直接侵害到所有在华机构的利益,包括各国的大使馆的邮件系统,以及各个商业公私需要内部保密的信息,将无法通过互联网进行传输。

总的来说,这将是两个超级互联网国家综合运算能力的拼搏。在这方面,协议制定者的美国付出的代价会更小一些。而GFW有伤敌100,自残5000的必杀技——全球网站白名单(IP+PORT),让全球的互联网公司都向网监处备案吧。

看看希拉里的新方案到底是什么样的吧。让我们拭目以待。

走读Kestrel,了解Scala(8-完)

代码读到这里,基本上已经把核心部分的代码都已经读完了。只剩下一个KestrelHandler.scala,我们就简单地说一说。

如果对Apache Mina的API有一个简单的了解之后,阅读KestrelHandler.scala已经不会有什么问题。KestrelHandler是当Mina获得一个新的链接请求之后创建出来的对象,对象的主流程就是一个act。通过 react 的机制来处理所有往来的信息,当然除了异常处理之外,只有MinaMessage.MessageReceive(msg)是会调用到PersistentQueue的一些操作。具体的函数就是handle,这里用到了一个叫做asInstanceOf的方法,这是Scala最基础类的一个方法,用途就是强制类型转换,在做与Java接口的时候,可能会经常遇到它。

handle处理报文的格式,兼容memcached的协议,也没有太多需要解释的地方。

总结一下:

读完 Kestrel 之后,给我的感觉就是,Scala的语法非常轻盈,跟六脉神剑+凌波微步似的,有的时候还真抓不住。Scala的语法结构和相当灵活,比如有些地方需要严格地写,会有很多的 () 和 . ,但是简化的写法,就用空格隔开了。另外Scala也沿袭了Ruby的语法特点,不需要每句话的最后都要;,直接用回车对语法进行判断,这些优化让程序看起来更像是一些自然语言,或者——呃——更加智能。

由于Scala弱化了变量类型,同时还强化了类的定义,所有变量都是类,所有的方法也都是类,连消息传递中,也使用了case class这样的特殊类,并且让常见的单例模式,或者常用的需要重载的函数,都由编译器自动实现,确实可以让代码大大的精简,变得很轻盈。

这种语言的设置,经过先辈语言,比如Lisp,Python证明,对于把Scala语言作为初学语言的人来说,几乎是没有门槛的,因为这无非是一套剑谱而已。而对于从C,Java这样的语言转过去的工程师来说,确实有点困难,因为打惯了横拳的人,直接告诉他,虚空一指就能让敌人动弹不得,用这样的招式多半有点心虚,担心这真气不那么靠谱。而且有的时候,因为不熟指谱,打出来的命中率也不高。所以如果可以的话,还是会坚持用自己习惯的语法来写Scala。

写惯Pascal的人,写C,总用不习惯 a++这样的语法一样,写出漂亮的Scala程序,还是需要慢慢地才会适应过来。

关于Kestrel本身,就算是不学习Scala语言,它的一些设计方式也是非常值得学习的。比如说Journal.scala对文件缓存这个机制的处理方式,非常的巧妙。而为了提高每个链接的效率,而让client等待一段时间,不是直接告诉client没有新的消息,这样的细节处理,会大大提高消息队列整体的性能。非常值得学习。

毕竟它是用在Twitter上的一个核心消息队列算法呀。

走读Kestrel,了解Scala(7)

在PersistentQueue之下,有一个Journal.scala的类,支撑了消息队列的存储问题。这是Kestrel提供的另外一个特性:通过文件系统保存消息队列,避免服务重启的时候,Kestrel的队列丢失。

通过前面一段时间的阅读,我们对Scala的语法已经有一个基本的把握,所以在阅读Journal的时候,我们就更注重实现的方式,而不是语法细节了。当然Journal也没有太多的语法细节需要讲的了。当然出了还没有详细说过的case class和case object。

在Journal.scala的开始部分就定义了一个abstract class类JournalItem,并且定义了它的许多子类,这些子类是用来和PersistentQueue进行消息传递的。case class/case object是一种特殊的class/object,其功能是在对象里面增加了几个功能

1. 把所有的建构函数的var变成val,也就是变成了不可变的常量
2.自动实现了equal, hashCode和toString三个方法
3.当对象出现在case之后的时候,会自动apply出一个对象,对象的值和创建的时候一样,这个功能保证了可以和match…case语法可以写得很简练。

关于Case class的具体说明可以参考:CaseClasses和MatchingOnCaseClasses。关于第三条特性,还可以参考CompanionObjects。

简单的理解,我们就把case object/case class当作消息传递中需要使用的对象类型就可以了。

Journal使用了noi的FileChannel,来处理文件的读取和存储。核心的算法,可以只看readJournalEntry和replay两个方法。readJournalEntry的功能是从文件中读取数据,并且根据格式组成各种case class/case object,并且同时返回字节数。而在上层的方法,比如replay,则根据得到的不同数据类型,调用更上层的函数f(case class/case object)。

我们回到PersistentQueue中看replayJournal的时候,发现它将调用replay后得到的一系列的case class转义成为在PersistentQueue中需要执行的各种命令——所以这个方法的名字叫做replay!就是回放的意思。

当系统重启的时候,打开每个queue之前都需要一段回放的时间,把文件系统中记录的当时的整个存取过程重新回放一次,通过回放来重建内存中的队列。回过来再看Journal.scala的时候,我们就更清晰的知道,文件存储的不是当时的队列状态,而是每一次系统执行的轨迹。所以,Journal对整个Kestrel消息队列的开销才会很小。

但是另外一个问题随之而来,如果记录所有的操作过程,那么这个文件不是只会增大,不会缩小么?为了解决这个问题,Journal.scala实现了一个叫做roll的机制。从PersisitentQueue中的add方法中,我们可以看到这样的代码:

if (keepJournal() && !journal.inReadBehind) {
if (journal.size > maxJournalSize() * maxJournalOverflow() && queueSize < maxJournalSize()) {
// force re-creation of the journal.
[...]

走读Kestrel,了解Scala(6)

回顾一下之前我们读过的两个文件,Kestrel.scala, QueueCollection.scala。Kestrel.scala是启动文件,并且通过一个actor,保持整个项目不会因为没有线程运行而退出,同时注册了一个acceptor,当建立起新的链接的时候,访问 KestrelHandler.scala(这个稍后我们再读)。QueueCollection.scala,维护一个PersistentQueue的队列,如果访问的queue_name不存在,则创建一个,如果存在,就对相应的QueueCollection进行操作。如果留心的话,我们还可以看到QueueCollection在启动的时候,queue_name的来源是一个文件目录。

我们就从这个入口继续往下,看看PersistentQueue是如何处理消息队列的并发请求的:

在前几篇文章里面,我们曾经提到过PersistentQueue有两个“类”,一个是object PersistentQueue,一个是class PersistentQueue。而object在scala是一个单例模式,也就是singleton。也可以看做是只有static类型的java类。现在让我们关注一下,看看class PersistentQueue和object Persistent之间的关系是怎样的。

刚开始的一段代码有点吓人:

class OverlaySetting[T](base: => T) {
@volatile private var local: Option[T] = None
def set(value: Option[T]) = local = value
def apply() = local.getOrElse(base)
}

我们先跳过去,直接往下看,看到这里:

def overlay[T](base: => T) = new OverlaySetting(base)
// attempting to add an item after the queue reaches this size (in items) [...]

走读Kestrel,了解Scala(4)

在Kestrel.scala的startup方法中,告诉我们接下来有两个走读的分支,一个是QueueCollection和PersistentQueue。另一个是KestrelHandle。KestrelHandle是和NioSocketAcceptor相联系的,所以可以想想KetrelHandle是用来处理链接……与之相关的还有memcache目录下的Codec.scala。

我们先忽略Kestrel.scala在startup中的几个语法细节,从相对比较简单的QueueCollection开始吧:

class QueueCollection(queueFolder: String, private var queueConfigs: ConfigMap) {
……
if (! path.isDirectory) {
path.mkdirs()
}
……
}

这是一个Scala的比较特殊语法。class本身就是一个建构函数。程序中queueFolder和queueConfigs是创建是必须的两个参数。class在Scala里面就支持一种建构函数。这里的参数也可以看做是整个class的变量,所以程序中的private只表示在类以外的地方,没有办法获取queueConfigs这个参数。

接下来是关于HashMap的创建:

private val queues = new mutable.HashMap[String, PersistentQueue]

我们知道HashMap是有两个类型,一个是键的类型,一个是值的类型。在queues里面,String就是键,PersistentQueue就是值。这跟Java/C++的模板累死,不需要特别的解释了。

然后是一个很多地方都用到的Count,在Count.scala里面有说明,就是一个统计技术的类,写法很干净,注意里面的函数定义,都没有用大括号。多看几遍就熟悉,Scala是怎么定义函数的了。显然,这看起来更像是命令,而不是函数体的声明。

经过两段比较好理解的代码之后,出现了一段恐怖的代码:

queueConfigs.subscribe { c =>
synchronized {
queueConfigs = c.getOrElse(new Config)
}
}

如果只是看这段代码,就有种喉咙被卡住,咽不下去又吐不出来的感觉。这段代码做了些什么,大家都能猜出来,郁闷的是,这到底遵循的是那个语法规范呢?咋代码就写得那么四不像呢?不过如果我们回过来看一下net.lag.configgy.configMap中subscribe的定义,似乎就能明白多一点:

def subscribe(f: [...]

走读Kestrel,了解Scala (2)

下载编译不多说,注意要用Java 1.6就可以了。我们先从简单的开始,我们在Kestrel的官方网站上看到一个压力测试的用例。如下文所示:

$ ant -f tests.xml put-many-1 -Ditems=5000000

先从测试案例开始,然后再看核心代码,入门会比较容易。在测试案例里面,我们比较容易猜出来,整个程序大概做了一些什么,通过test.xml所述,我们找到了net.lag.kestrel.load.PutMany,在src/test/net/lang/kestrel/load里面可以找到这个程序,PutMany.scala,相对来说ManyClients.scala的功能可能更全面一些,我们就拿它作为例子吧:

package net.lag.kestrel.load

import _root_.java.net._
import _root_.java.nio._
import _root_.java.nio.channels._
import _root_.java.util.concurrent.atomic._
import _root_.net.lag.extensions._

这段一点都不难懂,嗯……我们可以大胆地猜的 _ 在这里的涵义等同于 Java 里面的 *。事实上就是这样的,在后来,你会发现 _ 不仅仅代表的是 * ,它会出其不意地出现在很多地方,而在Scala中,它的涵义接近于default。

object ManyClients {
private val SLEEP = System.getProperty(”sleep”, “100″).toInt
private val COUNT = System.getProperty(”count”, “100″).toInt
……
}

类定义也很清楚,object 等同于 Java 中的 class。变量声明中Scala的格式和Java有点不同,需要说明一下的是var表示变量,而val表示常量,在声明的时候必须被赋值,不过这个常量是在创建的时候执行的结果。就好像例子中写的那样。注意toInt,这是一个Scala的特点,通过推断的方式来确定变量的类型,所有在有些时候,变量的类型声明就可以被省略了。

这种对变量类型的省略是Scala的核心理念——”Type Less, Do more”,有些事情编译器直到就好啦。

函数来了:

def put(socket: SocketChannel, queueName: String, n: Int) = {
[...]