RabbitMQ简介

RabbitMQ是一个很受欢迎的消息中间件,通过它可以很方便地实现异构子系统之间的通讯,还可以将不同子系统之间进行解耦。它用erlang开发,基本上是实现了AMQP 1.0标准的消息协议。

了解RabbitMQ首先要了解以下一些概念:Message,Producer、Exchange、Queue、Consumer

Message是一些简单的字符串, Producer(Publisher)是实际发布消息的角色

Queue,是实际存放消息的地方。顾名思义,消息从Queue一端放入,另一段由Consumer(Subscriber)取出,如果有多Consumer,每个consumer各自取出不同的消息进行处理。

当Producer发布消息的时候,首先是发布到Exchange,然后RabbitMQ根据Exchange的类型和逻辑来判断应该发送到哪个Queue中。所以Queue必须bind到特定的Exchange上才能获取到消息,绑定的时候可以提供一个routing_key来判断选择什么消息,Publisher在发出信息的时候就可以指定不同的routing_key来选择如何分发消息。

当使用最基本的队列模式的时候,可以不指定exchange,这时候会使用默认exchange来进行消息的发送。

Exchange和Queue都有自己的名字,多个Publisher可以发布到同一个Exchange,多个Consumer也可以订阅到同一个Queue。

RabbitMQ支持的Exchange方式有:

  • direct 直接投递
  • fanout 广播投递
  • topic 可以按照一个topic名字的模式进行匹配routing_key,例如topic.*可以匹配topic.paragraph和topic.paragraph.word,而topic.#
  • rpc Producer可以等待Consumer处理消息结束并把结果返回给Producer

Ruby下可以使用基于EventMachine的异步客户端amqp,或者是同步模式的bunny和carrot。

用bunny,以topic订阅为例:

publisher部分:

consumer部分

RabbitMQ集群和High Scalability

由于RabbitMQ实现的是AMQP,它非常强调一致性,而AMQP本身就是一种适用于金融行业的消息协议。根据CAP原理,一致性、高可用性和分区容忍性只能选两项,RabbitMQ提供了三种配置选项:

  • ignore:默认配置,发生网络分区时不作处理,当认为网络是可靠时选用该配置
  • autoheal:各分区协商后重启客户端连接最少的分区节点,恢复集群(CAP 中保证 AP,有状态丢失)
  • pause_minority:分区发生后判断自己所在分区内节点是否超过集群总节点数一半,如果没有超过则暂停这些节点(保证 CP,总节点数为奇数个)

由于使用Erlang开发,RabbitMQ可以非常方便的搭建集群,可以随时加入节点,这些节点之间是互相等同的,可以在客户端随机选择节点,或者使用诸如haproxy等进行负载均衡反向代理,以达到水平扩展的目的。

如何解决翻页条目重复问题?

之前做糗百和暴漫的时候,由于帖子更新速度很快,所以当用户看完一页帖子之后,翻到下一页,则会发现有一些帖子是跟前一页是重复的。

如果短时间内翻页就出现这种情况,或是每次翻页都会出现这种情况,就非常恼人了,那么如何能解决这个问题?

原理分析

首先我们分析一下问题出现的原理(请原谅我其实不懂数学):

  1. 用户访问的是按id顺序排列的页面:
    这种情况下是不会出现翻页重复的
    当用户在t0时间访问服务器请求第P(P>=1)页时,服务器的帖子列表是a0, a1, a2 … an的一个序列A,服务器根据每页e进行分页,得到了一个A'[p]a[(p-1)*e] .. a[p*e-1]
    对于服务器帖子A来说,下标是单调增加的,然后t1时间(t1>t0),A增加了一个a[n+1]的帖子,对于用户来说,即使t2时间进入p+1页,也可以得到A'[p+1]a[p*e] .. a[(p+1)*e-1]帖子,与之前是没有任何重复的,因为帖子的增加都是在最后的

  2. 用户访问的是按id倒序排列的页面:
    这种排序方式,是最新的排序在最前,目前大部分网站的帖子排序都是这样的

当用户在t0时间访问服务器请求第P页时,服务器的帖子列表是a1, a2 ... an的一个序列A,服务器根据每页e进行分页
由于是id倒叙,则得到了一个A'[p]a[n-(p-1)*e] .. a[n-p*e+1]的序列
下一页p+1页d的记录A'[p+1]则为a[n-p*e] .. a[n-(p+1)*e+1]的序列
假如t1(t1>t0)时间,A增加了一个a[n+1]的帖子,则P和P+1页的序列都会变了
P+1页则会变成A''[p+1]a[n+1-p*e] .. a[n+1-(p+1)*e+1]的序列
如果增加了m条记录,则有可能变成 Am[p] = a[n+m-p*e] .. a[n+m-(p+1)*e+1]

我增加一个起始点s的概念,在t0时刻,s=n,而在t1之后,s=n+m

所以,如果要顺序不变,则需要保证该用户会话中,起始点s始终为n

这时候,可以为每个用户存一个自定义的s,用起作为偏移的参考。(怎么存下面会讲到)

  1. 用户访问的是按某个字段(如热度)排序的页面:
    对于暴漫和糗百首页而言,实际并不是前两种排序,而是按其文章的打分来排序,很多其他网站有按点击率进行排序,分数的计算公式也会很不相同。

这种情况相当复杂,因为这个字段并非单调增长,而且经常会变化,我们先来看一下:

当用户在t0时间访问服务器请求第P页时,服务器的帖子列表是a1, a2 ... an的一个序列A,排序算法为f,f(A)得到此时其排序为S,其第P页的内容为S(a,p)
t1之后,f让A的排序变为了S’,其第P页的内容为S'(a, p)

那么对于用户而言,根据A的对应的热度的变化频率,则翻页是内容变化的概率会很大,并不一定会重复
换句话说,如果用户想得到一个较为稳定的视图,那么我们必须假设用户始终在t0时间的角度来看内容
假设t1对应A的初始状态,t2的变更操作C将A的排序属性变成A’,令S也变成S’
那么我们必须保留t1和t2时候A状态的快照,同时当用户访问网站的时候,记录其所绑定的快照。
这样才能令用户看到一致的结果。

这种情况也可以完全涵盖第二种情况,第二种情况其实是本例的特例

实现方法

说完了原理之后,必须阐述一下实现方法。

首先是比较简单的第2种情况,需要为用户会话绑定一个起始id,这个id可以保存在每个用户的cookie中,或者是保存在服务器端session中,也可以是在翻页的链接上加上一个起始id,比如/articles/latest/page/1?s=100

由于用户在同一时间看到的内容都是相同的,所以其实我们并不需要针对每个不同的用户保存单独的起点信息。

而对于上节描述第三种情况来说,其实早在很久之前数据库就有一种称之为“游标”cursor,可以实现类似的功能。但如今大部分网站都不会使用数据库游标,因为游标会大量消耗数据库服务器的资源。

另一种方式是使用业务层的游标方式。我们可以观察到,对于每一个不同的排列顺序S,产生一个特定的快照,将记录的顺序存下来。
有些网站比如hacker news,便利用了基于continuation的方式,保存了针对每一个排列顺序产生的快照,它的下一页的链接是这种形式的 https://news.ycombinator.com/x?fnid=Y7HN9XBN2kRzQlD5LklRh4 如果用户在页面长时间没有动作,再次点击这个链接的时候,便会显示为“expired link”,表示这个排序方式的快照已经失效了,业务层服务会在每次排名变更之后丢弃之前的排序方式。
但是对于国内大部分网站而言,使用这种简单粗暴的方式是不可行的,可以根据业务需求,保存不同版本的快照,每个快照内的翻页链接都加上快照的版本号。当快照过期之后,把旧的链接重新定向到最新的链接。

如何保存快照?当需要排序的元素数量较小,比如几千几万条,就可以使用单独的数据库表,或者是Redis的Sorted Set,甚至使用Google的leveldb单独做一个Service都可以。

第三种方式是直接进行客户端排序,每次用户新访问的时候,便将排序的顺序都下载到客户端,当服务器端排序方式发生变化的时候通知客户端是否要重新加载最新的排序方式。
由于现在的浏览器都支持JavaScript,还支持localStorage,所以网站已经做成了纯的Web Application,那么如果当记录数量不是很大,一次性下载几千个id,也是可以考虑的方式。但这并不适合直接从服务器端取页面的方式。

综合考虑的解决方案

由于大部分网站的大部分用户,在同一时刻看到的页面是一样的,所以很多网站都会有缓存。同时仔细考虑,对于缓存来说,每个快照S,都会直接对应一组页面缓存C,而客户端则会有另一组缓存C’。

过去由于缓存的页面并没有绑定到快照,排序更新后,缓存的更新也并非同步,有的页面可能根据新的排序更新了,有的可能还没有。
所以,只需要增加一个缓存绑定到排序快照的机制,可以直接将页面缓存视为排序的快照,而无需保存实际的排序方式。
于是我们便可以使用类似于id逆序的方式,为分页的页面缓存增加一个快照id,形如/articles/latest/page/1?s=100,同时当排序发生变化时,把快照id增加1
对于页面缓存的失效时间,则可以根据可容忍的程度进行设置。当请求的快照id 不存在时,则可以跳转至最新的快照id。

这样不用额外保存排序快照,对于网站而言,大部分用户同一时间能复用同一套缓存,提高了性能和效率。

当然,其实对于用户而言,是否真的需要非常准确、无重复的翻页?首先也要从产品设计的层面来考虑。

程序员,你调试过的最难的 Bug 是?

伯乐在线导读:调试 Bug?每个程序员工作中必须品。在 Quora 上有一个和 Bug 相关的热门问答帖:《What’s the hardest bug you’ve debugged? | 你调试过的最难 Bug 是?》。在众多回复中,Dave Baggett 的经历最让人惊叹,得到了 2400 多个顶。感谢@cugbabyebar 的热心翻译。

回想起这个bug,仍然让我有些痛苦。作为一个程序员,在发现bug时,你学会了首先在自己代码中找问题,或许在测试一万次之后,你会把问题归咎于编译器。只有在这所有的都不起作用之后,你才会把问题归咎于硬件。

这是我遭遇一个硬件bug的故事。

抛开别的不说,我曾为《Crash Bandicoot》写存储卡(读写)代码。对于一个自大的游戏程序员,这就像是在公园里散步一样轻松愉快,我认为只要几天就写完了。我中止调试六个礼拜。在此期间我做一些其他的事情,但我一直回来处理这个bug——几天内每天几个小时。这个bug实在烦人。

这个bug的症状是,当你需要保存你的进度时,代码会访问存储卡,而大部分情况下没有什么问题…但是偶尔读写会超时…没有任何明显的原因。一个短小的写入经常毁掉存储卡。玩家要保存进度,我们不仅不保存,还擦除他们存储卡上的全部东西。天哪。

过了一段时间,我们在Sony的制作人Connie Booth慌了。我们显然不能带着这个bug发布游戏,而六个星期之后我对于问题出在哪一点线索都没有。通过Connie我们向其他 PS1 开发者求助:有没有人出现过像我们这样的情况?没有。绝对没有任何人在存储卡系统上出现任何问题。

在你绞尽脑汁之后,你能做的唯一一个调试方法就是分而治之:一点点去除程序中的代码,直到留下的代码很少但你仍然出问题。像木雕一样去除没有问题的代码,留下的就是你的bug所在。

在这样的背景下挑战在于,视频游戏是很难去除某一部分的。在你删除模拟重力或者显示字符的代码后,如何运行游戏?

你必须做的是用一个假装做真正的事情,但实际上只是做很简单的不会出现bug事情的东西来替换掉整个模块。你必须写新的支撑代码来让这些玩意正常工作。这是一个缓慢而痛苦的过程。

长话短说:我做完了。我移除了大片大片的代码,相当多,只留下了初始化代码——就是准备游戏运行系统,初始化底层硬件等等。当然,我不能显示加载/保存菜单,因为我截除了所有的图像代码。但是我能够假装用户使用(不可见的)加载/保存屏幕并且请求保存,然后写入卡中。

我最终以一个带有这个bug的很少量的代码结束——但问题仍然随机出现!在大多数情况下没啥问题,但是偶尔会失效。基本上所有的Crash的实际代码都被移除了,但还是这样。这实在是莫名其妙:留下来的代码基本上都没做什么事。

在那时——估计是凌晨3点——一个想法蹦了出来。读写(I/O)涉及精确定时。无论是硬盘、存储卡、蓝牙发送器——随便啥——做读写的底层代码都是根据时钟来的。

时钟让不直接连接到CPU的硬件设备和cpu运行的代码同步。时钟决定了波特率——数据从一头传到另一头的速率。如果计时有什么问题,硬件或者软件或者两者都会乱七八糟的。这真的,真的很糟糕,并且通常导致数据损坏。

如果我们的初始化代码以某种方式弄乱了计时会怎么样?我又看了一遍测试程序中和计时有关的代码,并注意到我们将PS1上的可编程计时器设置到了1kHz(1000跳每秒)。这是比较快了,当PS1启动的时候,默认状态大概是100Hz。因此,大多数游戏将他们的计时器设置为100Hz。

这个游戏的带头(和除我外的唯一)开发者Andy,将计时器设置为1kHz,使得Crash的动作计算更加准确。Andy喜欢矫枉过正,如果我们要模拟重力,我们应该尽可能的提高精度!

然而如果提高计时器频率莫名其妙的干扰了整个程序的计时,故而将这个计时器设置到存储卡的波特率上会怎样呢?

我将计时器代码注释掉。然后我就无法复原这个bug了。但是这并不表示bug被修复了,这个问题是随机发生的。万一我只是运气好呢?

几天过去了,我还是在玩我的测试程序。Bug没有再出现。我回到全部的Crash代码中,修改了加载/保存代码,在访问存储卡之前将可编程计时器重置为默认设置(100Hz),之后设置回1kHz。从此之后没有发现问题再次出现。

但是…为什么?

我重新回到测试程序上,试着检测当计时器设置为1kHz时出现的那些错误的模式。终于,我注意到这些错误出现在使用PS1手柄的人身上。因为我自己很少这样做,所以我没有注意到(为啥我要在测试加载/保存代码的时候用手柄)。但是有一天我们的美工等我去完成测试(我确定那时候我在爆粗口),而他紧张的摆弄着手柄。卡损坏了。“等下,怎么回事?喂,再来一次!”

一旦我发现了这两件事是联系着的,就很容易重现bug:开始写入存储卡,动一下手柄,存储卡损坏。在我看来完全是硬件bug。

我去找Connie告诉他我的发现。她转述给设计过PS1的硬件工程师。她被告知:“不可能,这不可能是硬件问题。”我跟她说问一下我能不能直接和他说。

那个工程师给我打电话了,他用着他的烂英语,我用着我更烂的日语,我们争论一会。我最后说:“我给你一个30行的测试程序,让你在动手柄的时候能够出现这问题。”他答应了。他向我保证,这是浪费时间,而他正在一个新项目上很忙,但因为我们是Sony很重要的开发者,他会试的。

第二天晚上(我们在洛杉矶,而他在东京,所以对于我来说是晚上而他是到了第二天),他给我打电话,不好意思的向我道歉。这是个硬件问题。

我还是没有完全搞清楚问题到底在哪,但是我的印象中,从Sony总部的反馈听到的是,如果将可编程计时器设置到足够高的时钟频率,会影响到主板上时钟晶振附近的一些东西。这些东西之一就是存储卡的波特率控制器,同时也设置手柄的波特率。我不是搞硬件的,所以对于细节我相当模糊。

但是主旨是主板上两个独立部分的串扰,以及手柄接口和存储卡接口数据发送的结合在1kHz的时钟频率下会导致丢位,从而数据丢失,以致卡损坏。

这是我全部编程生涯中,唯一一次因为量子力学debug的问题。

原文链接: Dave Baggett 翻译: 伯乐在线 – CuGBabyBeaR
译文链接: http://blog.jobbole.com/50995/
[ 转载必须在正文中标注并保留原文链接、译文链接和译者等信息。]

程序员幽默

1、程序猿最烦两件事,第一件事是别人要他给自己的代码写文档,第二件呢?是别人的程序没有留下文档。

2、程序猿的读书历程:x 语言入门 —> x 语言应用实践 —> x 语言高阶编程 —> x 语言的科学与艺术 —> 编程之美 —> 编程之道 —> 编程之禅—> 颈椎病康复指南。

3、还没上大学的时候,高三暑假,跑到家那边的图书城想买传说中的C++的书,然后看到一本C#,我一看,嘿,这个++还写得挺艺术的,重叠起来了,于是把C#买了回来……

4、问:程序猿最讨厌康熙的哪个儿子。答:胤禩。因为他是八阿哥(bug)

5、有一天,程序猿们突然发现他们要涨的工资掉到井里啦!大家都很害怕,连忙一个吊着一个,从树上伸到井里去捞工资。正好他们摸到工资的时候,一个老程序员忽然兴奋的大叫:别蠢了,要涨的工资还好好的挂在天上呢!

6、诸葛亮是一个优秀的程序猿,每一个锦囊都是应对不同的case而编写的!但是优秀的程序猿也敌不过更优秀的bug!六出祈山,七进中原,鞠躬尽瘁,死而后已的诸葛亮只因为有一个错误的case-马谡,整个结构就被break了!

7、生活中程序猿的真实写照、一款游戏一包烟,一台电脑一下午。一盒泡面一壶水,一顿能管一整天。

8、程序猿要了3个孩子,分别取名叫Ctrl、Alt 和Delete,如果他们不听话,程序猿就只要同时敲他们一下就会好的…

9、宪法顶个球!中国的法律都是.txt文件,不是.exe文件。

10、同事说,他在写i++的时候总觉的自己写的是 我艹………有木有同感????

11、程序员,年二十有二,始从文,连考而不中。 遂习武,练武场上发一矢,中鼓吏,逐之出。 改学IT,自撰一函数,用之,堆栈溢出。

12、《桃花庵–程序员版》写字楼里写字间,写字间中程序员; 程序人员写程序,又将程序换酒钱; 酒醒只在屏前坐,酒醉还来屏下眠; 酒醉酒醒日复日,屏前屏下年复年; 但愿老死电脑间,不愿鞠躬老板前; 奔驰宝马贵者趣,公交自行程序员; 别人笑我太疯癫,我笑自己命太贱; 但见满街漂亮妹,哪个归得程序员;

13、有一天某程序员去买肉,要了一公斤, 拿到公平电子秤上一称:”额。。怎么少了24克。。”

14、检验代码质量的唯一标准 = 代码review时骂的次数 / 代码review时间 。

15、杀一个程序员不需要用枪,改三次需求就可以了。

16、C++程序员看不起C 程序员, C 程序员看不起java程序员, java程序员看不起C#程序员,C#程序员看不起美工。周末了,美工带着妹子出去约会了,一群SX程序员还在加班。。。

17、问:如何生成一个随机的字符串?答:让新手退出VIM 。

18、“我给你出个脑筋急转弯,你说达芬奇密码的上面是什么?” “这。。太难了吧。。不知道。。。” “笨!达芬奇密码的上面就是达芬奇帐号啊,那达芬奇密码的下面是什么?”“我。。。这。。。还是不知道。。。”“是达芬奇验证码”。

19、随机函数可以帮你实现家庭和谐: Talk(){:top word(1)=”恩!”; word(2)=”好的!”;word(3)=”然后呢?”;word(4)=”有道理”;i=random(4); say word(i) goto top;}

20、程序员爱情观:爱情就是死循环,一旦执行就陷进去了;爱上一个人,就是内存泄漏–你永远释放不了;真正爱上一个人的时候,那就是常量限定,永远不会改变;女朋友就是私有变量,只有我这个类才能调用;情人就是指针用的时候一定要注意,要不然就带来巨大的灾难。

21、女同学们纷纷表示,这年头不找个程序员老公,还真是连节日低价购物权都没了。

22、Delphi象吉普车,什么路上都能开,却在啥路上也开不好;PB就象卡丁车,只能在固定线路上开,到室外就有些不稳;VC象跑车,你开得起却买不起,而且一旦发生故障,想修都找不到毛病在哪;Java象敞棚车,不管刮风下雨还是艳阳高照,都能照开不误;VB就是摩托车,骑的时间越长,你越痛恨它!

23、上联MYSQL明月三千里 下联: XHTML.信号他妈烂!

24、程序员的四大理想:南极有套房,澳大利亚有群羊,全世界电脑死光光,孩儿有个娘。

25、有一种崩溃叫密码输入有误;有一种惊慌叫做账号异地登陆;有一种感情叫隐身对其可见;有一种误会叫人机离线;有一种失落叫没有访问权限;有一种感情叫站点访问失败;有一种无奈叫bug无法复现。。。

26、黑体的锯齿,宋体的沧桑,崩溃的避头尾集。美工永远纠结于网站程序员的粗犷,就像MAC永远不懂PC的忧伤。。。。

27、程序猿追求MM不成,含泪追问:我在你眼里算什么?!MM答曰:真人版的windows优化大师……极客哥们莫伤心,小戴安慰递纸巾。

28、 据说有一位软件工程师,一位硬件工程师和一位项目经理同坐车参加研讨会。不幸在从盘山公路下山时坏在半路上了。于是两位工程师和一位经理就如何修车的问题展开了讨论。硬件工程师说:“我可以用随身携带的瑞士军刀把车坏的部分拆下来,找出原因,排除故障。” 项目经理说:“根据经营管理学,应该召开会议,根据问题现状写出需求报告,制订计划,编写日程安排,逐步逼近,alpha测试,beta1测试和beta2测试解决问题。” 软件工程说:“咱们还是应该把车推回山顶再开下来,看看问题是否重复发生。”

29、【高效的程序员】当世界末日还有5分钟就要到来的时候。程序员: 让我们在这最后的时刻作些什么吧!女友: 那好,让我们在做最后一次吧!程序员: 那剩下的4分50秒做什么啊?

30、【开发时间】项目经理: 如果我再给你一个人,那可以什么时候可以完工?程序员: 3个月吧!项目经理: 那给两个呢?程序员: 1个月吧!项目经理: 那100呢?程序员: 1年吧!项目经理: 那10000呢?程序员: 那我将永远无法完成任务。

31、一个程序员对自己的未来很迷茫,于是去问上帝。“万能的上帝呀,请你告诉我,我的未来会怎样?”上帝说:“我的孩子,你去问Lippman,他现在领导的程序员的队伍可能是地球上最大的”。于是他去问Lippman。Lippman说:“程序员的未来就是驾驭程序员”。这个程序员对这个未来不满意,于是他又去问上帝。“万能的上帝呀,请你告诉我,我的未来会怎样?”。上帝说:“我的孩子,你去问Gates,他现在所拥有的财产可能是地球上最多的”。于是他去问Gates。Gates说:“程序员的未来就是榨取程序员”。这个程序员对这个未来不满意,于是他又去问上帝。“万能的上帝呀,请你告诉我,我的未来会怎样?”。上帝说:“我的孩子,你去问侯捷,他写的计算机书的读者可能是地球上最多的”。于是他去问侯捷。侯捷说:“程序员的未来就是诱惑程序员”。这个程序员对这个未来不满意,于是他又去问上帝。“万能的上帝呀,请你告诉我,我的未来会怎样?”。上帝摇摇头:“唉,我的孩子,你还是别当程序员了”。

32、面试官:“熟悉哪种语言”。应聘者:“JAVA”。面试官:“知道什么叫类么”。应聘者:“我这人实在,工作努力,不知道什么叫累”。面试官:“知道什么是包?”。应聘者:“我这人实在 平常不带包 也不用公司准备了”。面试官:“知道什么是接口吗?”。应聘者:“我这个人工作认真。从来不找借口偷懒”。面试官:“知道什么是继承么”。应聘者:“我是孤儿没什么可以继承的”。面试官:“知道什么叫对象么?”。应聘者:“知道,不过我工作努力,上进心强,暂时还没有打算找对象。”。面试官:“知道多态么?”。应聘者:“知道,我很保守的。我认为让心爱的女人为了自已一时的快乐去堕胎是不道德的行为!请问这和C#有什么关系?”。

33、IT工程师=加班狂+程序员+测试工程师+实施工程师+网络工程师+电工+装卸工+搬运工+超人,有同感的转走。

34、 用一句话总结了HTML,CSS,JS的关系。HTML是名词,JS是动词,CSS是形容词和副词。

35、我是个程序猿,一天我坐在路边一边喝水一边苦苦检查bug。这时一个乞丐在我边上坐下了,开始要饭,我觉得可怜,就给了他1块钱,然后接着调试程序。他可能生意不好,就无聊的看看我在干什么,然后过了一会,他幽幽的说,这里少了个分号。。。分号。。。分号。。。

36、女友对程序员说:“紫禁城占得地方好大呀!”程序员:“杀死那个子进程……”

37、从前有个全国管理系统,是孙中山做的设计,老蒋做的实现,结果老毛写了个病毒,趁着日本黑客对系统做攻击的当口,拿到了管理员权限,把原来那批程序员给隔离了。老邓接手以后,重构代码,出了个2.0版,为了开发速度,遗留了一堆BUG没处理。人们纷纷质疑:是不是核心构架太单一,双核会不会好点?

38、一程序员家的水管坏了,他打电话叫来一个水管工修理。 水管工鼓捣了一个小时,终于把管子修好了,他递给程序员一张600元的帐单。 “600元!”程序员愤怒地说:“我当程序员一天都赚不了这么多钱!” “是啊。”水管工平静地说,“我当程序员的时候也是。”

39、十年前,女:“对不起,我不会喜欢你的,你不要再坚持了,就好比让 Linux 和 Windows 同时运行在一台PC机上,可能吗?”男生听后默默走开,十年后,在一次虚拟技术大会上,我听到一名虚拟技术开发程序员给我讲述了这个故事。

40、程序猿问程序媛:”为什么要离开我,我做得还不够好吗?” 媛说:”别傻了,我们根本就是两个世界里的人,就像在JS里永远都无法调用JAVA类一样,我们之间也是不可能的。” 猿沉默了很久,转身离开了。一个月之后,他在开源社区公布了dwr的完整代码。

41、【程序员被提bug之后的反应】1.怎么可能; 2.在我这是好的,不信你来看看; 3.真是奇怪,刚刚还好好的; 4.肯定是数据问题; 5.你清下缓存试试; 6.重启下电脑试试; 7.你装的什么版本的类库(jdk) 8.这谁写的代码; 9.尼玛怎么还在用360安全 浏览器 ; 10.用户不会像你这么操作的。

42、敲一夜代码,流两行老泪;用三种语言,唯四肢受罪 ; 待五更鸡鸣,遇骤雨初歇;遂登门而去,伫十里长亭;欲望穿泪眼,无如意郎君;借微薄助力,愿寻得佳偶;成比翼双鸟,乃畅想云端;卷情网之内,做爬虫抓取;为连理桂枝,容数据分析;思千里子规,助框广天地; 念茫茫人海,该如何寻觅?

43、早晨一女生抱着一堆书进了阅览室,结果警报响了,大妈让女生看看是哪本书把警报弄响了,那女生把书倒出来,准备一本一本的测。大妈见状急了,把书分成两份,第一份过了一下,响了。又把这一份分成两份接着测,三回就找到了,大妈用鄙视的眼神看着女生,仿佛在说O(n)和O(log2n)都分不清。

44、发现程序员经常熬夜有三个弊端:第一,记忆力越来越差;第二,数数经常会数错;第四,记忆力越来越差。

45、医院回来的程序猿一脸的苦逼样。程序媛:怎么了?程序猿:得了类风湿性关节炎了,我怕会遗传给下一代啊。程序媛:谁说类风湿性关节炎能遗传的?程序猿一脸诧异:类不是继承的吗?

46、知道JAVA程序员和C程序员的差别吗?食堂里,吃完饭就走的是JAVA程序员,吃完饭还要自己 收拾的那就是是C程序员。至于为什么会这样、大家都明白(因为JAVA自带垃圾回收机制、、、C需要手动释放内存)←这就是原因

47、计算机系的男同学追班里一女同学,结果此女总是躲躲闪闪。 男的看没戏,就另找了一个去追,结果这女的不满意了,质问这男的为啥抛弃她。 男的问:“请教一个电脑问题,如果你点击一个程序,总是提示‘没有响应’,怎么办?” 女的说:“马上结束任务。” 男的:“对,我也是这样想的。”

48、一个程序员的吐槽:即要被当做修电脑的,也要被当作做网站的;即要被当作杀毒的,也要被当作盗号的。我要告诉大家,其实我们只是写代码的。

49、如果一个足球界的人“猝死”了,会被怀疑和赌球有关;如果一个官员“猝死”了,会被怀疑和贪腐有关;如果一个农民”猝死”了,会被怀疑和拆迁有关;而如果一个程序员猝死了,那他真的猝死了。

50、老婆是操作系统,一但安装卸载十分麻烦;小秘是桌面,只要你有兴趣可以天天更换;情人是互联网,风光无限花钱不断;小姐是盗版软件,用时记着先杀毒。

51、前台美女三宝:你好,找谁,倒饮料。产品经理三宝:山寨,改版,再推倒。项目经理三宝:进度,流程,做报表。团队经理三宝:团建,开会,评绩效。程序员三宝:闷骚,加班,修电脑。

52、对于程序员来说、没老婆不悲催。悲催的是、没老婆、控制台还不停的提示你Error:could not find the object

53、假如生活欺骗了你,不要悲伤不要心急。《代码大全》会一直陪伴着你……

54、有时候真觉得有些事情如同char*一般,从开始就注定,无法改变。

55、洛阳亲友如相问,就说我在敲代码。

56、”如果你ctrl+alt+del,蹦出任务管理器,你从上到下扫一眼,所有的进程你都认识,知道他们是干什么的,并且知道关掉有什么后果,而且你还能从CPU和内存占用的数字跳动上清楚的知道电脑现在什么状态,那么你应该没有女朋友”………..你妹啊

57、用IE6的吃方便面都没有调料包,你知道不知道……

58、普通青年用IDE(Visual Studio, Eclipse, XCode);文艺青年用VIM, Emacs;二逼青年将IDE设置成VIM模式。

59、程序员换IDE相当于搬家,换主力语言相当于改嫁,换操作系统相当于参加FBI证人保护计划…

60、有两个程序员钓鱼,其中一个钓到一条美人鱼,这个美人鱼上半身是美女,下半身是鱼,于是这个程序员 就吧她放了,另一个问他:Why,他回答说:没有API

61、阿里小米皆自主,百度排名最公平;京东全网最低价,当当爱国很理性;用户体验看新浪,网易从来少愤青;豆瓣从来不约炮,人人分享高水平;从不抄袭数腾讯, 开放安全三六零。

62、编程夜当午,手握小滑鼠。谁知编程辛,行行皆“心”苦;头昏不觉晓,使劲揉眼角。夜夜太辛苦,睡眠知多少;

63、热火朝天的办公室,一精壮青年一边啃着馒头,一边看着眼前产品,愁眉紧锁的他陷入了沉思:产品下一步应该怎么走?如何保证代码质量?如何缩短项目时间?如何控制项目成本?一个个难题需要他思索,抉择。此时,传来项目经理的吆喝:“程旭元,先别敲代码了!给我修下电脑……”

64、原来《人月神话》不是本奇幻小说! 原来《代码大全》不是一堆开源代码! 原来《鸟哥的Linux私房菜》不是教你做菜! 原来《边城》不是教你写代码的! 原来《深入浅出HTML》不是教你How to Make Love

65、文艺程序员写代码追求让别人看懂,普通程序员追求让自己看懂,2B程序员则追求让编译器能看懂;半年后看自己当初写的代码,文艺程序员不知道是自己写的但很容易看懂,普通程序员知道是自己写的但是不太容易看懂,2B程序员埋头看了半天后拍着桌子吼到:“这是哪个SB写的程序!”

66、我真的想让这个世界变得更好,但是他们不给我源代码……

如果像招聘程序员那样招聘木匠

面试官: 您是木匠了,对吧?
木匠: 是的,我就是干这个的。

面试官: 您做木匠有多长时间了?
木匠: 10 年。

面试官:嗯,很好。那现在我有一些技术问题想问问您,看看您是否适合我们团队。可以吧?
木匠: 没问题,问吧。

面试官: 首先,我们目前正在做很多棕色房子的构件。不知您之前建过很多棕色的房子么?
木匠: 我是个木匠,我当然会建房子。不过人们更愿意刷自己喜欢的颜色。

面试官: 嗯,我理解,不过您是否告知,您有多少建棕色房子的经验呢?
木匠: 囧,我真不知道啊。房子建好了,我又不关心刷什么颜色的漆。或许有六个月的经验吧

面试官:六个月?噢,我们在找有更多棕色房子经验的人了,但是否允许我再问问其他问题呢?
木匠:嗯,没问题。但粉刷是粉刷,你应该懂的。

面试官: 好。能聊聊胡桃木么?
木匠:您想了解什么呢?

面试官: 您做木匠时,经常用胡桃木的木材么?
木匠: 当然了,胡桃木、松树、橡树、桃花心木都用过。

面试官: 那您用胡桃木的时间,又多少年呢?
木匠:天哪,我真的不知道。难道我应该要统计胡桃木的数量么?

面试官:嗯,您估算一下吧。
木匠:好不,那我就说有一年半时间在用胡桃木。

面试官:那您认为您是处于一个什么级别?普通胡桃木木匠,还是胡桃木大师么?
木匠: 胡桃木大师?什么是胡桃木大师啊?当然了,我以前是用过胡桃木的。

面试官:那您不是胡桃木大师了?
木匠: 这个,我就是个木匠,所以我用过很多类型的木材了,你知道的,木材都是有区别的,如果您是一位出色的木匠……

面试官:嗯嗯,我懂。但我们目前在用胡桃木了,这没问题吧?
木匠: 胡桃木很好的!不管您要什么。我是一个木匠。

面试官:咱能聊聊黑色的胡桃木么?
木匠:怎么了?

面试官:虽然我们现在已经有了几位用过胡桃木的木匠,但他们都没有用过黑色的胡桃木。您有黑色胡桃木的经验吧?
木匠:当然了,有一些。如果我有更多的经验,是会更好吧。

面试官: 好的。请稍等,我查一下收件箱……
木匠:嗯,您查吧。

面试官:好了,今天还有件事要问您。我们在用 Rock 5.1 敲钉子。您用过 Rock 5.1 么?
木匠: [囧] 自从 Craftsman 买了一个石场,我知道很多木匠开始用石头敲钉子,但是你知道,说实话,我用射钉枪用的更多。铁锤也用过。我发现我用石头,经常伤到我手指,我手上其他地方也受过伤,因为石头太大了。

面试官:但是其他木匠都在用石头了。您是说石头不起作用么?
木匠:没有啊,我没说石头没用,确切地说,我认为我的射钉枪更好。

面试官:嗯。我们的建筑师全部都在用石头,并且他们都喜欢用石头。
木匠: 额,他们当然喜欢,但我整天都要敲钉子,这个…… 我需要工作,如果您想用我的花,我肯定愿意用石头。我尽量别禁锢我的思维吧。

面试官: 好的,我们还有其他一些应聘者,我还要……到时候会通知您。
木匠:嗯,感谢您。很荣幸与您会面。

第二天:

电话响了……

面试官:您好?
木匠:您好。还记得我么?您之前在找有黑色胡桃木经验的木匠,我是您之前面试过的那个木匠。我打电话是想看看你们是否已确认人选了。

面试官:嗯,实际上我们已有人选了。总体来说,我们喜欢您的经验,不过我们决定,还是雇用有很多棕色房子经验的木匠。
木匠:这样啊?那我就丢掉这份工作了,就因为我没有足够多的棕色(房子)经验?

面试官:嗯,这是部分原因,另外部分原因是,我们找到了薪资要求更低的候选人。
木匠:真的吗,那那位有多少经验呢?

面试官:其实他根本就不是木匠,他是一位汽车销售,不过他卖过很多棕色的汽车,并且他常和胡桃木内饰打交道。
木匠: [卡] (木匠同学挂了)

见李开复初印象

前几天去了趟北京,王尼玛让我跟手机app的开发人员深入交流一下,并要和创新工场的一些其他团队进行一些合作。

不巧的是我去的那天,李开复老师有事外出,当时说是我今天回来也见不着了。

昨天下午王尼玛神秘兮兮地让我和王尼美过去找人,一开始没注意,等到了办公室才知是大名鼎鼎的李开复老师来了,要跟我们暴走漫画的核心团队成员聊聊。

见到李开复老师第一眼的印象感觉文质彬彬,但比照片上感觉更瘦更高,上身着粉红色衬衫——刚巧我也是——下身黑色西装裤。

办公室不大,大约有十个平米多,中间是一张很大的L形办公桌,我们和开服老师隔着办公桌面对面坐着。左手边就是飘窗。办公桌上收拾地非常干净一丝不苟,靠窗有台iMac还有台Dell,边上的线都卷地很好。桌上还摞着一些书,有关于产品设计战略的等等,中间还有很多盒名片,名片整齐地排列着。还有个泡茶的壶,感觉是茶室这种地方用的。但老师本人用的是一个巨大的运动型水杯,好像有1L的容量。

右手边有一个简单的书柜,书柜上放了与家人的合影以及女儿的照片。书柜脚边是几扎牌子叫火山岩的纯净水。

左边飘窗后面还有三四盆盆植物,只认识一个虎尾兰。后面还有个一直在轰鸣的大约一米高半米长宽白色外壳的不知道是什么机器。

李开复老师对暴走漫画的情况提出了很多的意见和建议,也根据当前的互联网的形势给出了自己的见解和看法,并且也介绍了很多创新工场内可以交流合作的团队和个人。谈吐温文尔雅,确实非常配得上“老师”的称号。

程序员幽默:他们是如何过河的?

C程序员
每当有大问题,上帝都会跟C程序员进行磋商。
C程序员可以直接在水上行走。

VB 程序员
VB 程序员每天都会跟上帝吃午饭。
他是游泳奥运冠军。

Turbo Pascal 程序员
Turbo Pascal 程序员偶尔跟上帝交谈过几句。
他游泳很不错。

Fortran 程序员
Fortran 程序员曾和上帝碰过几次面。
他可以努力让自己漂浮在浅水中。

QBASIC 程序员
QBASIC 程序员认识哪个是上帝。
不过他连在自己的浴缸里不溺水都有困难。

LOGO 程序员
Logo 程序员唯一了解上帝的地方在于,“上帝”这个单词对他来说足够简短能念出来,但他拼写这个单词还有问题。
他需要其他人把他抬过河。

汇编语言程序员
其实汇编语言程序员就是上帝。
当他想过河的时候,他会分开水面。

Forth 程序员
Forth 程序员从来不会看他遇到的河流一眼因为这违背了他虔诚的信仰,于是他过河都是走桥的。

各种奇怪的中文编程语言(转)

1、易语言

这语言是本文中最不奇怪的语言了……所以不做介绍,只放上一段Hello World。

.版本 2
.程序集 程序集1
.子程序 _启动子程序, 整数型
标准输出 (, “你好,世界!”)
返回 (0)

2、丙正正

丙正正是啥?答曰C++。。。C排第三个,所以是丙,代码实例如下:

空 象棋檔::設定註解(字元 *s,整數 n)
{
若(n >= 最大註解數)
對於(;最大註解數 <= n;最大註解數++)
註解[最大註解數]=NONE;
若(s==NULL 或 字串長度(s)==0)
傳回;
若(註解[n]!=NONE)
刪除 註解[n];
註解[n]=新 字元[字串長度(s)+1];
字串複製(註解[n],s);
}

这不就是翻译么。。。

3、习语言

另一个类C语言……全中文关键字:

包含 “习语言系统.接口”

整数类型 主函数()
{
文件类型 *fp;
整数类型 ch; 【注意这里要定义为整数,以便接受文件结束符号】
如果((fp=文件打开(“练习13-2.文本”,”写+”))==空指针)
{
格式输出(“无法打开文件:练习13-2.文本! “);
暂停();
退出(1);
}
格式输出(“输入一串字符:n”);
ch=输入字符();
若符合条件 (ch != 宏_换行键 ) 重复
{
文件写字符(ch,fp);
ch=输入字符();
}
文件复位(fp);
ch=文件读字符(fp);
若符合条件(ch!= 宏_文件结束 ) 重复
{
输出字符(ch);
ch=文件读字符(fp);
}
格式输出(“n”);
文件关闭(fp);
暂停();
返回 0;
}

不由的想说这些字打起来肯定很费劲……

4、PerlYuYan

这个汉语编程是基于Perl语言的,不过它的语法号称是文言文!看如下一段代码

!/usr/local/bin/perl

use Lingua::Sinica::PerlYuYan;
用警兮用嚴。
印道
1..1
哉兮

印編曰雜申雜申矣
又曰ok矣
又曰1矣

亂曰
國無人莫我知兮 又何懷乎故都
既莫足與為美政兮 吾將從彭咸之所居

它其实是什么呢……如下翻译

用警兮用嚴。 use warnings; use strict;
印道 print ”
1..1 1..1
哉兮 “;
印編曰雜申雜申矣 print sprintf ‘%s %s’
又曰ok矣 ,’ok’
又曰1矣 ,’1′
亂曰 END

本文末,贴一段最恶心的PerlYuYan代码。。征求翻译达人:

!/usr/local/bin/perl

The Sieve of Eratosthenes – 埃拉托斯芬篩法

use Lingua::Sinica::PerlYuYan;

用籌兮用嚴 井涸兮無礙。
印曰最高矣 又道數然哉。
截起吾純風 賦小入大合。
習予吾陣地 並二至純風。
當起段賦取 加陣地合始。
陣地賦篩始 繫繫此雜段。
終陣地兮印 正道次標哉。
輸空接段點 列終註泰來。

/Autrijus/

发布糗事百科旧版代码“冇问题”

在一些开源项目的激励之下,为了能与广大同行进行更深入的探讨,经过糗事百科官方许可我发布了这份由我维护的旧版代码,并取名“冇(máo)问题”。

博聆网暴走漫画的很多思路也是从这份代码中延续过来的。比如SuperCache的概念便是其中之一。

目前这份代码已经经过我的清理,可以运行在Ruby 1.8.7和JRuby以及Rubinius的1.8模式,亦可运行于Windows平台下。

起源

我曾与2008年和糗事百科创始人“黑衣大葛格”王坚在某个Ruby on rails群里认识,由于当时帮他维护秘密和糗事百科的同学因为私人原因无法继续,所以来寻找一个技术合伙人一起把网站经营下去。当时我们相聊甚欢,产生了很多奇思妙想,于是我也开始帮他接管这套代码。

那时候糗事百科和秘密还是在不同的代码和数据时中的,而且糗事百科也只相当于一个留言板。之后我清理重构了这套代码,将两个网站合并到了一套系统中,并使这套系统可以增加新的站点,且站点可以自定义主题等等一系列功能。此后根据根据黑老大的要求,开发完了手机版网站,在手机市场爆发之前站住了脚跟。其后还开发了诸如群众审核等特色的功能。并且通过页面纯静态化并异步加载动态信息的做法,既极大提高了服务器的负载能力,也保证了诸如SEO等要求。

2011年我退出团队之后,接手的技术合伙人由于并不擅长Ruby on rails,所以将网站代码迁移到了PHP和Python上,于是这部分代码也闲置了。其后我断断续续维护过这部分代码。并将从中所得经验,应用于博聆网和暴走漫画之中。

由于“冇问题”是黑老大先前开发“秘密”和“糗事百科”网站时所使用的团队的代号,所以这次发布的代码使用了这个名字作为代号。

目标

我希望能在将来把“冇问题”发展成一个具有高可定制性的微型论坛系统。
但由于过去人手不够时间不足,代码中还存在着很多问题,尚缺很多文档、基本没有测试代码,这部分内容我经验不足希望同行们指点。
接下来将现在的代码升级至Rails 3.2,保证系统能运行于MRI、JRuby和Rubinius平台之上,并提供良好的性能和用户体验。

欢迎大家Clone、Fork这份代码:

https://github.com/qiushibaike/moumentei

SuperCache的lock选项性能测试

上次说到了SuperCache的lock选项可以解决DogPileEffect。那么效果究竟如何,我做了个简单的测试:

我使用了Ruby 1.9.3-p392,Rails 3.2.12,只建了my_controller.rb,其中模拟了一个10秒慢速请求,为了测试方便,缓存时间只设置为5s:

服务器端使用了unicorn,启动5个worker,测试端开100个并发,50000次请求。

不使用lock:

使用lock

结果很明显,启用了lock之后,SuperCache的长达10s的请求数明显更少,使得平均响应速度有明显的提升。

在实际的生产环境中,一般长达10s的请求很少,只缓存5s的请求也很少,但是通常长时间的请求伴随着都是较高的数据库负载,一旦大量出现,容易拖垮整个服务。通过这种机制,就可以明显地让缓存失效时负载曲线平滑。

暴走漫画的服务器使用了这个机制之后,使得平均响应速度从300ms左右降低至了200ms左右。