2012年5月3日

(译注:圣经记载:在远古的时候,人类都使用一种语言,全世界的人决定一起造一座通天的塔,就是巴别塔,后来被上帝知道了,上帝就让人们使用不同的语言,这个塔就没能造起来。 巴别塔不建自毁,与其说上帝的分化将人类的语言复杂化,不如说是人类自身心灵和谐不再的分崩离析。之所以后来有了翻译,不仅是为了加强人类之间的交流,更寄达了一种愿望,希望能以此消除人际的隔阂,获求来自心灵的和谐及慰藉。真正的译者,把握血脉,抚平创痕,通传天籁,开启心门。)

这是我写的旋风式的编程语言简介 —— 我本来为亚马逊开发者杂志本月的期刊写的,但是发现我写的东西没法见人。

首先,我偶尔一不小心口出脏话,或者对上帝不恭的话,所以在很官方很正式的亚马逊上发表是不合适的; 所以我就把它塞到我的博客里了,我的博客反正没人看的。除了你以外。是的,只有你会看,你好啊。

其次,这是一项进行中的工程,现在只是东打一耙西搞一下,还没有精加工过的。又一个把它写到博客里的很大的理由。不需要很好,或很完整。就是我今天想说的一些话。请随便!

我的旋风式简介会讲C、C++、Lisp、Java、Perl (我们在亚马逊用到的所有语言)、Ruby (我就是喜欢) 和 Python,把 Python 加进来是因为 —— 好吧,你看了就知道了,现在我可不说。

C

你必须懂C。为哈? 因为出于所有现实的理由,这个世界上你过去,现在,将来会用到的每一台计算机都是一台冯·诺曼机器,而C是一种轻量级的,很有表达力的语法,能很好的展现冯·诺曼机器的能力。

冯·诺曼架构就是你每天都用的计算机的架构的标准:一个 CPU,内存,硬盘,一条总线。多核计算机并没有带来本质上的变化。冯·诺曼机是一个很方便,很便宜,上世纪五十年代的实现图灵机的技术,图灵机是执行计算的最知名的抽象模型。

世上还有其他的计算的机器。比如,Lisp 机器,是上世纪 50 年代对 Lisp 计算模型的实现。Lisp 模型是基于 lambda 代数的一种计算语言表示法,后者是与图灵机同构的一种模型。不像图灵机,lambda 代数能被人类读和写。但是这二者是同等能力的。它们同样精确的表示了计算机能干什么。

Lisp 机现在不是很流行了,除了在跳蚤市场里。从谁更受欢迎来说,冯·诺曼机器赢了。还有一些其他的计算机,比如神经网络计算机,译者也不知道怎么翻的计算机(cellular automata),但是这些都不够大众化,至少现在是这样的。

所以你必须知道C。

还有一个你必须知道C的原因是,Unix 是用C写的。巧的是,Windows 也是。基本上所有的其他操作系统都是用C写的。因为这些操作系统都是冯·诺曼机的操作系统,你还能用别的吗? 任何跟C很不一样的东西都会跟硬件的实际能力相差太远而导致无法满足性能上的需要,至少对一个操作系统来说是这样—至少在上个世纪是这样,碰巧这些系统都是上个世纪的。

你还应该知道 Lisp。你不必用它来干实际工作,虽然它在很多 GNU 的软件里都会很用得着。尤其是,你应该学会 Scheme,Lisp 的一种小巧化的,纯洁的方言。GNU 的版本叫 Guile。

他们在麻省理工和加州伯克利教新学生一到两个学期的 Scheme,这些学生都对他们为哈要学这么奇怪的语言抓破脑袋。实话实说,作为第一门学习的语言,这是一个很烂的选择,第二门也是很烂。你应该学会它,最终,但不是作为第一门或第二门语言。

这是很难的哦。这是很大的一步。学会怎么用 Lisp 写出像C语言的程序是不够的,那没有意义。C 和 Lisp 一个就像红外线,一个就像紫外线,它们分布在光谱的最两端。它俩一个牛逼的地方刚好是另一个傻逼了的地方。

如果说,C是最靠近计算机是如何工作的语言模型,Lisp 就是最能反映计算(注意,这里没有了“机”字,计算机和计算是很不同的!译者注)是如何工作的模型。你不需要懂很多 Lisp,真的。紧咬 Scheme 就哦了,因为它是最简单最干净的。其他的 Lisp 已经发展成了很大,很复杂(很好很强大? 译者:-)的编程环境,就像 C++ 和 Java,要有很多库啊,工具啊等等之类。那些,你不需要知道。但是你应该能用 Scheme 写程序。如果你能够做出 The Little Schemer 和 The Seasoned Schemer 这两本书里的所有习题,你懂得就够多了,我认为。

但是对于你天天要做的编程工作,你应该基于以下条款选择你的语言:库,文档,工具支持,操作系统集成,资源,和一堆其他的东西。这些条款跟计算机如何工作关系很小,但是跟人类如何工作关系甚大。

人们还在用很直白的C语言写东西。很多东西。你应该懂C!

C++

C++是地球上最蠢的语言,即使是从蠢这个字的真正意义上出发。C++很无厘头。它不知道自己是什么东西。它没有自省(introspective,面向对象里的一个概念,译者注)。C也没有,但是C不是“面向对象”的,而面向对象很大程度上是关于要让你的程序知道它自己。对象就像演员。所以面向对象语言应该有运行时的自省机制,知道自己是个什么类的对象。C++不是这样的,真的,你不会那样用它。

关于C:写一个C的编译器是那么的简单,以至于你可以用C写一个关于C的工具,用起来就像是有内省机制。而 C++ 呢,基本上是不可解析的,所以如果你想写一个很牛逼的工具用来 —— 比如,告诉你你的虚函数的原型,或者帮你重构你的代码,你将不得不依赖别人的工具集,因为你自己在除非脑子进屎的情况下是根本不会去写一个 C++ 的解析器的。而市面上所有的 C++ 的解析器都很傻逼。

C++很蠢,你不能用蠢语言创造一个好系统。语言决定世界,蠢语言决定蠢世界。

所有的计算都基于抽象。你用低级的东西创造出高级的东西。但是你不能用分子创造出一个城市。尝试使用太低级别的抽象只会给你带来麻烦。

我们就惹上麻烦了 (是指亚马逊的员工,还是所有 C++ 的程序员? 我也不知道,译者注)。

理智的情况下,你用C写的最大的东东就是一个操作系统。而操作系统其实不是很大的,真的。它们看起来很大,但那是因为它们有很多应用软件,操作系统本身的内核是蛮小的。

你用 C++ 能写的最大的东东是…也是操作系统。好吧,或许稍微再大点儿。让我们说,再大三倍吧。或者 10 倍吧。但是操作系统内核最多也就,那啥,一百万行代码? 所以我说你能用 C++ 写的最大的系统大概也就是一千万行代码吧,再大的话就开始不行了,这玩意儿你没法控制了,就像恐怖片里的…

我说的一千万行是指如果你那时候还能让你的系统编译通过的话。

我们(在亚马逊,译者注)有五千万行 C++ 代码。不,现在还要更多了。我已经不知道有多少行了。上个圣诞节是五千万行,那是九个月前,而它以每季度八百万行的规模增长。增长率本身也增长,妈呀。

我们想在这个系统里干点啥好像要一万年。一个亚马逊工程师有一次这样描述我们的代码库:“一座很大的屎山,你见过的最大的山,每次你想修正一个 bug,你的工作就是爬到屎山的正中心去。”

伙计们,那哥们可是在四年前说的这话。他现在已经到更环保绿色的牧场上去了。真是太可惜了,他可是个实实在在的高手啊。

这都是 C++ 的错。别跟我争论。就是的。我们用的是世上最蠢的语言。这简直有点老板级的蠢,你说呢? (译者注,meta 在计算机术语里通常表示更高一个层次,比如,meta-language,比普通的 language 高一个层次,意思是关于语言的语言。哲学里应该会经常用到这个词。我不懂哲学,但是我觉得老板们总是比我们高一级,所以 meta-dump 我就翻译成老板级的蠢喽。:-)

说了以上这些难听的话,话得说回来了。用 C++ 写出漂亮的代码显然是可以的,我的意思是说,这样的代码应该大部分还是C,偶尔很有品味的,很有节制的用一点C++。但是这种代码几乎从来不会被写出来。C++是个很好玩的游乐场,而如果你把它玩儿得门儿清的话你会觉得自己特牛,所以你总是被诱惑把你知道的所有的东西都用上。但是那是很难做好的,因为从一开始这个语言就太狗屎了,最终,你会弄得一塌糊涂,即使你很能干。

我知道,我说的都是异端邪说,该被钉到十字架上的。随便吧。我在大学里的时候老喜欢 C++ 了,因为我那时候就只知道这一门语言。当我听到我的语言教授,Craig Chambers,绝对的厌憎C++,我想:“为啥呢? 我觉得它挺好的啊”。而当我听到 STL (标准模板库)的发明者被采访时说他恨 OOP (面向对象编程)时,我更是认为他肯定是磕药了。怎么会有人恨 OOP 呢,而这个人竟然还是 STL 的发明者?

亲不敬,熟生厌(语出圣经,译者注)。说的是在大多数情况下,跟一件事物熟悉了之后你就失去对它的膜拜尊敬了; 在计算机语言里情况不是这样的。光对一门语言熟悉不会导致你看轻这门语言。你必须成为另一门更优秀的语言的专家(才能让你明白原来那门语言有多么多的问题)。

所以如果你不喜欢我针对 C++ 大放厥词,请你去学另一门语言并成为一个专家(我推荐 Lisp),只有那时你才有足够的武器与我争论。然而,那时你将不会跟我争了。你上了我的当了。你也会跟我一样变得不喜欢 C++ 了,你或许会觉得我这个人很恶心,把你骗得不喜欢自己曾经的最爱了。所以或许你应该把我说的一切都忘了。C++挺好的其实,真的。它就是很棒棒(译者注,作者在这里用了 ducky,这是一个女性喜欢用的夸某物好的词,近来也为玻璃们喜爱)。忘了我说的话。C++不错的。

Lisp

(我打赌这一节会让你觉得惊讶,即使你已经关注我的博客有一阵了[译者注,作者也可能是说,即使你成为亚马逊的员工有一阵了])

亚马逊创业之初,我们有很多明星级的工程师。我不认识他们所有人,但是我认识几个。

比如?Shel Kaphan, 大拿。Greg Linden, 大拿。Eric Benson。即使在他加入亚马逊之前就已经有自己响亮的名气了。也是大拿。

他们写了 Obidos 服务器。是 Obidos 让亚马逊成功的。只是后来那些生产大便很拿手的工程师,网页开发者,搞前端的人 —— 这些人因为生产大便很拿手而总是能让经理们满意 —— 只是在后来这些人把 Obidos 搞糟了。(他们的大便)把整条河都堵了,打个比方说的话。但是 Obidos 是亚马逊最初的成功的一块关键的基石。

这些最早的牛人们在亚马逊神圣的代码库里只允许两种语言:C 和 Lisp。

你自己去想吧。

当然,他们所有人都使用 Emacs。靠,Eric Benson 是 XEmacs 的作者之一。这个世界上所有伟大的工程师都在用 Emacs[注1]。那种世界因你而不同级别的伟大。不是坐在你旁边的格子里的那哥们那种伟大。也不是 Fred,走廊尽头那哥们。我说的是我们这个行业里最伟大的软件开发者,那些能改变这个工业的面貌的人。像 James Gosling 们(Java 语言设计者),Donald Knuth 们(这个人没有听说过的话赶紧改行吧,别搞计算机了),Paul Graham 们[注2],Jamie Zawinski 们,Eric Benson 们。真正的工程师用 Emacs。你必须很有点聪明才能把 Emacs 用好,而如果你能成为一个 Emacs 大师的话它会给你难以置信的牛力。有机会的话你应该站到 Paul Nordstrom 的肩后看看他是怎么工作的,如果你不相信我的话。对那些一辈子都在用烂 Visual Studio 之类的集成开发环境的人来说,一定会大开眼界的。

Emacs 是那种你可以用 100 年的编辑器。

Shel, Eric, Greg,和其他像他们那样的人,我没有足够幸运能跟他们直接一起工作:他们禁止在这里使用C++,他们禁止使用 Perl(或者 Java,为完整起见)。他们是明白人。

现在我们都在用C++,Java 和 Perl 了,所有的代码都用这些语言。我们的前辈们已经到更环保的牧场上去了 (指没有大便的牧场,译者注)。

Shel 用 C 写了 Mailman,客服部的人把它用 Lisp 封装了一下。Emacs-Lisp。你不需要知道 Mailman 是什么东西。除非你是个 Amazon 的老员工,或许不是搞技术的,而且你曾经不得不让客户哈皮 (只有在这种情况下你才需要知道 Mailman,译者注)。不是间接的,因为你用 C++ 写的一个狗屎功能跑不起来了,让客户很生气,于是你不得不去搞定它以恢复客户的哈皮度。不,我是说直接的,意思是,你必须跟他们聊。我们可爱的,不识字的,呱呱其谈的,心地善良的,充满希望的,困惑的,能帮点小忙的,愤怒的,哈皮的客户们,真正的客户们,那些从咱们这里买东西的人,我们的客户们。(如果你必须跟他们打交道的话,)那你就会知道 Mailman 这个东西。

Mailman 是客服部的客户电子邮件处理软件,我们用了它有…四,五年? 反正是很长时间。它是用 Emacs 写的,所有人都爱死它了。

人们现在还很爱它。直到今天,我依旧不得不听我们一些非技术员工跟我长篇大论的叨叨他们是多么的怀念 Mailman。我可绝不是满嘴喷粪。上个圣诞节我参加了一个 Amazon 的派对,一个我不知道自己怎么会被邀请的派对,里面全是些西装笔挺的商务人士,谁都长得比我帅,比我光鲜。以及一些我在公司里曾经打过交道的人(这句不知道怎么译)。四个美女认出了我是在客服部里干的,把我包围了,跟我说了十五分钟她们是多么的怀念 Mailman 和 Emacs,而现在的亚马逊(我们用 JSP 花了好多年准备换掉 Mailman 的那一套软件)是怎么的不能满足她们,让她们觉得跟以前一样爽。

这一切都太梦幻了,我觉得她们可能是喝多了。

Shel 是个天才。Emacs 是天才。连非技术人员都爱 Emacs。我现在就是在 Emacs 里打这些文字。我绝不情愿在任何其他地方打字。这不只是关于让你的效率得到飞跃,通过那些地球上其他地方找不到的快捷键和文本编辑功能。我每分钟打一百三到一百四十个英文单词,在 Emacs 里,当我在写没有格式要求的文本的时候。我测过这个时间速度。自己写了一个测打字速度的 Emacs 应用。但我想跟你说的不只是这个。

Emacs 有的是一种你叫不出名字来的品质。

我们现在不用 Mailman 了。那是因为我们有一种叫得出名字的品质 —— 就是,烂。我们很烂。我们(当时)找不到 Emacs-Lisp 足够牛的人把 Mailman 继续搞下去。今天这应该不难了; 亚马逊现在到处都是 Emacs Lisp 的黑客。但是在那时候,客服部的人没法从别人那里得到帮助。于是他们就用他们当时手头有的资源去搞这件事。他们当时没有足够多的 Emacs-Lisp 的人。有一段时间,他们甚至找来 Bob Glickstein 当合同工,那个给 O’Reilly 写了那本 Gnu Emacs 扩展的书的家伙,坐在一个小办公室里给 Emacs 写 Mailman 的扩展。

客服应用部是 Amazon 的第一个两块比萨饼的团队(代表团队人数的增加,编者注)。这个团队是完全自立的。不管是那时还是现在。没人跟他们说话,没人帮他们。没有枪,没有炮,他们自己造。他们没有网页工程师,没有支持工程师。屁也没有。有的只是一堆骨灰级的工程师和一个能带新人的文化。这就是他们需要的一切了。

但他们最终不得不让 Mailman 光荣退休。妈哎。而我呢今天还听到人们说他们是多么的怀念它。甚至在派对上。

我想今天按人头比例来说,客服部仍然拥有比亚马逊任何其他团队更多的 Lisp 黑客。可能他们用到 Lisp 的机会不多了,但是 Eric Raymond 说过,即使你很少用 Lisp 写程序,学习 Lisp 会是意义深远的一个经历,能让你下辈子都成为一个更好的工程师。

卡尔,宗教现在已经不是大众的精神鸦片了。现在鸦片是集成开发环境了。(卡尔·马克思。这个人不知道的话应该打屁屁)。

Java

Java 是过去的 10 年中计算行业里发生过的最好的同时也是最坏的事。

一方面,Java 把你从 C++ 编程的很多枯燥易错的细节中解救出来了。没有数组越界了,没有 core dump 了。抛出来的异常能让你精确定位到出错的那一行代码,而且 99% 的时候都是正确的那一行出错了的代码。对象们在需要的时候能智能地把它们自己打印出来。等等等等。

另一方面,除了是一种语言,一个虚拟机,一个巨无霸的类库,一个安全模型,一个可移植的字节码格式,Java 还是一个宗教。邪教。所以你不能太相信对它太虔诚的人。想要招一个好的 Java 工程师是一项很有技术挑战的活。

但是总的来说,Java 是软件工程史上的一大进步。

从 C++ 到 Java 不只是语法上的改变。这是一种需要一段时间去好好体会的一种震撼性的世界观的转变。这有点像突然你被配了一个执行助理。你知道老总们为什么总是好像有时间去开会,总是知道公司现在运行的情况,总是写出很酷酷的文档吗? 老总们常常忘记其实他们不是一个人在战斗,他们都是两个全职的人,他们和他们的执行助理们。有一个执行助理把你从琐事中解救出来让你有时间去思考那些真的需要你去解决的问题; 没有的话你将不得不花一半的时间在那些无聊的世俗的事情上。切换到 Java 编程语言就把你变成了两个程序员 —— 一个处理那些你不需要关心的东西,另一个可以集中精力在问题本身上。这是一个很震人的改变,一个你应该很快就能习惯能喜欢上的改变。

就像 Jamie Zawinski (Netscape 牛人,开发 Mozilla 浏览器,好像学历是高中毕业?)在他著名的“Java 真烂(java sucks)”那篇文章里说的:“先说那些好东西:Java 没有 free() 函数。我必须一开始就承认,其他的东西都没什么了不起。(没有 free)是能让我原谅其他所有东西的特性,不管其他东西有多烂。讲完这一点后,我的文章里其他一切几乎都完全没有重要性了。”

Jamie 的文章写在 1997 年,按 Java 年来算的话是很早以前了,跟他写这篇文章时比,Java 已经有很大的改善; 一些他抱怨的东西甚至已经被 fix 了。

但是大多数还是没有被 fix。Java 作为一门语言还是有点烂。但就如 Jamie 指出的,Java“是今天为止最好的语言。我的意思是说,它是今天市面上那些烂得底儿掉地一堆语言比起来有那么一点能被我接受。”

真的,你应该读读他那篇文章

Java 几乎每一方面都很好,除了它的语言本身,而这是 JWZ 抱怨的主要对象。但那是一个很大的抱怨。再好的库也救不了一个烂语言。相信我:你可能比我知道多得多的东西,但是我知道好兵救不了烂将。在 Geoworks 搞了五年汇编语言都会了我这个道理。

跟 C++ 比,Java 作为一个语言还过得去。好吧,别扯了,Java 要好很多。因为它有(内建)的字符串。哥们,你说一个没有内建的字符串的语言是人用的吗。

但是 Java 跟 C++ 比少了一些好东西,比如(函数调用时)传引用,栈上的对象,typedef,宏,以及运算符重载。一些时不时地会很称手的东西。

哦,还有多重继承,我现在老了,反而挺欣赏了的多重继承。如果你认为我这个观点僵硬不灵活的家伙是多态教义很好的反例的话,我倒是可以给你举几个为什么你需要多态继承的好例子,或者至少像 Ruby 那样的 mixin 或者自动的派遣。下次问问我白龙马的事情。今天我要告诉你为什么 Java 的 interface 是个烂货。

几年前 Gosling 自己都说,如果一切都能重来的话,他不会搞出个 interface 的概念。

但是那正是 Java 的问题。当 James 说出那句话的时候,人们被雷到了。我甚至能感觉到那股雷劲儿,能感觉到 Sun 公司市场部和法务部的鸟人是多么想把 James 灭口,然后告诉大家他没那么说过。

Java 的问题就是人们都被那帮人搞的广告效应蒙住了眼。C++,Perl,任何流行语言都有这个问题。这是很严重的,因为如果没有一些说大话吹牛逼的广告,一个语言是不会流行起来的。所以如果一个语言的设计者说他的语言没有被设计得很完美的话,就是赶紧用枪射击这胡说八道的家伙并关闭会议的时候了。

语言们需要放点儿卫星才能活,我只希望人们不要被卫星耀瞎了眼。

我学了面向对象编程, 我自己也对此大吹大擂。当我加入亚马逊时,我不能告诉你我有什么智慧或者经验,但我可以给你背诵出所有关于 OOP 的魔咒。多重继承是邪恶的,因为大家都这么说; 运算符重载是邪恶的,诸如此类。我甚至有点模糊地知道为什么是邪恶的,但实际上不知道。后来我明白了,这些都不邪恶,不是烂玩意儿,烂的是开发者,是我。我现在还是烂,但是希望每年都不烂一点起来。

上礼拜我碰到一个来面试的,他告诉我多继是邪恶的,因为,比如,你可以从头,胳膊,腿,躯干多重继承出一个人来。他既是对的,又是错的。那样的多继情形当然邪恶,但那都是因为他自己太邪恶了。那样继承出来的“东西”远远就能看见有多蠢,如果他还把这玩意儿弄进门来那就更邪恶了。

不良开发者,占了这世上开发者的大多数,他们能用你扔给他们随便什么语言写出不良的代码。

说了这些,还是得说回来,多继不是请客吃饭那么轻松的事儿; mixin 看起来是更好的解决方案,但是还没人完美的解决这个问题。但我还是认为 Java 比 C++ 好,即使它没有多继。因为我知道不管我的出发点是多么好,某一天我还是会被一堆不懂怎么写好代码的人包围,让他们用 Java 比用 C++ 会带来更少的伤害。

此外,Java 除了语言本身外还有老多其他的重要有用的东西。且 Java 语言本身也在进化,虽然像冰川一样慢,所以我们还是能看到希望。Java 正是我们应该在亚马逊推荐使用的语言。

你就是得小心点儿,因为和其他任何语言一样,你能很容易找出一堆人,他们很懂一门语言及其编程环境,但对品味,计算或者其他任何重要的东西却一无所知。

当你有怀疑时,还是雇那种会好几门语言的 Java 程序员,那种厌憎 J2EE/EJB 之类松松跨跨的所谓框架的,那种使用 Emacs 的。这都是一些实战经验。

Perl

Perl,怎么说呢?

Perl 是个老朋友。老老朋友。我开始写 Perl 代码的时候,可能是 1995 年。而它为我很好的服务了差不多 10 年的时间。

它就像你骑了十万二十万英里的老自行车,你心里永远有一块地方装着它,虽然现在你已经换了一辆更加现代化的只有五磅重的自行车,而且这一辆也不像老的那辆顶得你屁眼疼了。

Perl 受欢迎原因有仨:

  1. 用 Perl 你很快就能搞定你的问题。而这是最终的衡量标准。
  2. Perl 有世上最好的市场推广。你可以写一本介绍他们市场推广有多绝的书。Sun 公司砸大笔钱给 Java 推市场,Perl 在受欢迎程度来说能跟 Java 齐头并进,但 Perl 纯粹是依靠 Larry Wall 和他那帮哥们的三寸不烂之舌做市场。哈佛商学院的人应该去研究 Perl 的市场是怎么做出来的。真的让人瞠目结舌。
  3. 直到差不多,呃,现在,Perl 没有真正的竞争者。

有比 Perl “好”的语言。操,有很多比 Perl 好的语言,如果你定义“好”为“不是给疯子用的”的话。Lisp, Smalltalk, Python,妈呀,我可能可以列出二三十种比 Perl “好”的语言。从这些语言不像这个夏天在台湾街头爆了肚皮的抹香鲸这个角度来说。鲸鱼肠子到处都是,汽车上,机车上,行人身上。这就是 Perl。让人着迷,真的。

但是 Perl 有很多很多好的特性,直到最近,都是其他语言没有的。它们弥补了 Perl 肠子在外的不足。你可以从爆了肚皮的鲸鱼可以做很多有用的东西出来,比如香水。这很有用。Perl 也是这样。

当其他的那些语言(尤其是 Lisp 和 Smalltalk)都想假装操作系统并不存在,列表(Lisp 的)和对象(Smalltalk 的)就是把屎搞出来的唯一存在,Perl 却走了截然相反的路子。Larry 说:Unix 和字符串是搞出屎来的唯一存在。

对很多任务来说,他绝对是正确的。所以 Perl 绝对是 Unix 系统管理和字符串处理的史上最强语言,除了一个,刚出来的一个,从哥斯拉(电影哥斯拉看过没)之地出来的一个。我一会儿会讲到那一个。

可惜,Larry 太太太太在意 Unix 系统管理和字符串处理以致他压根忘了列表和对象,等他明白过来想改正的时候已经晚了。实际上,在 Perl 早期的…好吧,对鲸鱼肠子我实在不想用“设计”这个词,就说生命周期中吧,他犯的几个关键错误让把列表和对象加进来变得如此尴尬,以致 Perl 已经进化成一个真正的 Rube Goldberg 机器,至少当你想在 Perl 里用列表和对象的时候。(Rube Goldberg 是一漫画家,常画一些很复杂的机器,但只完成简单的工作,比如一个小球滚过很多关卡,最后把门打开。译者注)。

列表和对象也他妈的是很重要的,Larry!(farging 应该是作者不想说 fucking 那么直白,译者注)

Perl 没法表达列表因为 Larry 一早犯了一个悲剧性的愚蠢的错误,把列表全抹平。于是(1, 2, (3, 4))魔术般地变成(1, 2, 3, 4)。不是说你会想让它这样工作,而是 Larry 刚好那天在搞一个这样会更方便的问题。于是 Perl 的数据结构从此就变得爆炸了的鲸鱼了。

今天你看 Perl 的书,小教程或 PPT 的时候,不花三分之一的时间在“引用”上是不可能的。这就是 Larry 可怜的,坏了的,Goldberg (漫画家,想起来没? 译者注)式的对他那抹平列表的疯狂错误的解决方案。但是 Perl 的市场宣传做得那么难以置信地好以致它让你觉得这是你身上发生过的最好的东西。你可以对任何东西取它的引用。这很有趣!闻起来也很香(说肠子呢,译者注,呵呵)!

Perl 不能支持面向对象编程因为 Larry 压根不相信这玩意儿。这可能没什么大不了; 我也不是很确定我是不是信这个 OOP。但是那么为啥他又要试着把对象加进 Perl 呢? Perl 的面向对象是个半成品,且在 Perl 社区里没多少人重视。它就是不像字符串处理或 Unix 集成那样充满灵感。

当然了,Perl 还有其他很多怪怪的特性。比如它的“上下文”,这是 Larry 要有N个变量名字空间的喜剧式决定的一个恐怖片式的产物。这些空间由 sigil 来区分(就是 Perl 里变量名前面的‘$’,‘@’,‘%’字符),看着像是从 shell 脚本里拷贝来的。在 Perl 里,所有的运算符,所有的函数,所有的操作其行为都是六取一的随机的,取决于当前的“上下文”。没有一些规则或助记法能帮你搞定这些特定操作在特定上下文里的特定行为。你得把它们全记在脑子里。

想要个例子? 这儿有一个:在一个值量(scalar,对应于 vector,向量)上下文里对一个哈希取值你得到一个字符串,里面是个分数,分子是目前已分配的键,分母是总共有多少个桶。鲸鱼肠子,我告诉你。

但就像我说的—直到最近,没啥能像 Perl 那样把屎搞定。

Ruby

每过 15 年左右,一门语言就会被更好的代替。C被 C++ 代替,至少对大应用开发而又需要性能和数据类型的人们来说。C++ 被 Java 代替,而 Java 无疑在 7 年后又会被更好的东西代替—好吧,我说的是完全代替 C++ 的 7 年后,这到目前为止还没有发生,主要是因为微软能在 Java 霸占桌面系统之前狙击它。但是在服务器上的应用而言,C++ 的阵地已经慢慢让给 Java 了。

Perl 有一天也会消逝。那是因为一门新的语言 Ruby 刚刚终于被翻译成英语了。没错,它是在日本发明的,这么多地儿,没想到日本人搞出来了,还以为他们只是硬件和制造上占有名气,而不是他们的软件业,所以大家都跟你一样惊奇。为什么呢,大家可能都在想。但是我认为这都是跟打字有关。我根本不能想象他们以前能打字打得足够快,英文字母只有 26 个,他们却有上万个字。但是 Emacs 几年前支持多字节字符了,所以我猜他们现在打字速度他妈的快多了。(所以能搞出 Ruby 来了,译者猜作者是这个意思) (是的,他们也用 Emacs —— 事实上日本人负责了 Emacs 多字节支持的大部工作,而且搞得坚不可摧。)

不管怎么样,Ruby 从 Perl 那里偷师了所有的好东西; 实际上,Matz, Ruby 的作者(Yukihiro Matsumoto,如果我没记错的话,但是他外号“Matz”),觉得他从 Perl 那里偷的有点太多了,他的鞋上也粘了些鲸鱼肠子。但是只是一丢丢。

最重要的是,Ruby 拿来了 Perl 的串处理和 Unix 集成,一点没改,就是说语法都是一样的,于是乎啥也不说了,你就拥有了 Perl 最好的那部分。这是个不错的开局,特别是如果你不把 Perl 剩下的东西也拿进来的话。

但是之后 Matz 还从 Lisp 那里拿来的最好的列表处理,Smalltalk 和其他语言那里拿来了最好的面向对象,CLU 那里拿来了最好的迭代器,以及基本上是每个人每个事的最好的东西。

而他让这些东西全部都跑起来,跑得那么顺,你都不会注意到这些东西在那儿。我比其他任何语言都快就学会了 Ruby,我总共会三十到四十门语言; 而我花了大概三天时间就能用 Ruby 比 Perl 还流畅地工作了,当了八年的 Perl 黑客后。这些东西是这么的和谐你都能自己猜它们是怎么工作的,而且大多数时候你都能猜对。漂亮。有趣。靠谱。

如果把语言比成自行车,那么 AWK 就是一辆粉系的儿童自行车,前面有个白色小框,还插块小旗,Perl 就是沙滩车(还记得那有多酷吧? 唉。),而 Ruby 则是一辆七千五美金的钛合金山地自行车。从 Perl 飞跃到 Ruby 意义不下于从 C++ 到 Java 的飞跃。却没有任何缺陷,因为 Ruby 几乎是 Perl 功能的一个超集,而 Java 却拿掉了一些人们想要的东西,且没有真正的提供一个替代品。

下次我会写更多关于 Ruby 的东西。我先需要灵感。去读读 Lucky Stiff 的(poignant) guide to Ruby 吧。那本书是一本有灵感的书。真的,读一下。超赞。我不理解产生它的那种头脑,但它很有趣,很犀利,且全是关于 Ruby 的。好像。你会看到的。

Python

啊,Python 怎么说呢,一个不错的语言,这么多年来一直旁边在等待它的机会? Python 社区很长时间以来是那些勇敢地吞下红药片从 Perl 骇客帝国中醒来的人的避难营。

啊,有点像 Smalltalk 的人们,他们永远在等待替代C++,没想到半路杀出 Java 一下把它们操翻了,漂亮地,永久地。哎哟。Ruby 正在对 Python 做着同样的事,现在,今天。可能会在一夜之间吧。

Python 本来可以统治世界,可惜它有两个致命缺陷:空格,和冷淡。

空格很简单,就是说 Python 是用缩进来表达代码块之间的嵌套。它强制你必须按一定格式把所有的东西缩进,他们这样做是为了让所有人写的代码看上去一样。不料蛮多程序员讨厌这点,因为他们觉得自己的自由被拿走了; 感觉就像 Python 侵犯了宪法赋予他们的可以随便缩进格式和全写在一行上的权利。

Python 的作者,Guido Van Rossum,也在早期犯过一些很傻的技术错误 —— 没有像 Larry 的失误那么严重,但是还是有几个。比如,最早 Python 没有字面变量范围,但它同时也没有动态变量范围,而动态变量范围可能会有它一些问题,但它还是有用的。Python 却没有这些,只有全局的和本地(函数)的两种范围。所以即使它是一个真正的 OO 系统,类甚至不能访问它们自己的动态成员变量。你必须给成员函数传“self”参数,一大堆 self 参数很快就会把你搞疯掉,即使你不在意空格问题。

等等之类。

但在我看来,Python 不行其实是因为冷淡。这阻止了它成为首选脚本语言,或者首选一切语言。靠,人们现在还在用 Tcl 作嵌入解释执行器,虽然 Python 比 Tcl 好得不要太多 —— 除了,我说,这个冷淡问题。

(此处开始我不知所云。呵呵,这样吧,把原文贴在最后面。译者注)

What’s the frost thing, you ask? Well, I used to have a lot of exceptionally mean stuff written here, but since Python’s actually quite pleasant to work with (if you can overlook its warts), I no longer think it’s such a great idea to bash on Pythonistas。The “frost thing” is just that they used to have a tendency to be a bit, well, frosty。Why?

Because they were so tired of hearing about the whitespace thing!

I think that’s why Python never reached Perl’s level of popularity, but maybe I’m just imagining things。

Coda

That was the ADJ article I really wanted to write。Or at least something like it。For some reason, though, my true feelings only seem to come out during insomniac attacks between 3am and 6am。Time for bed!2 hours ’til my next meeting。

注1,Eric 告诉我当时几乎全是 Jamie Zawinski,当他们在 Lucid 工作的时候。

注2,我写了这个之后很多人告诉我 Paul Graham 是用 VI 的,想不到。

注3,为了有据可查,我个人根本不介意空格问题。我认为因为这个而不喜欢 Python 是很傻的。我只是说有一堆比例让人惊奇的其他工程师讨厌空格问题。

这才是我真正想给亚马逊开发者杂志写的文章。或者至少是这样的。出于某些原因,我的真感情好像只有在我凌晨三点到六点失眠的时候都会流露。该睡觉了!我下个会议再过两小时就开始了。

(发布于 2004 年 9 月,小小的更新在2006年3月28日)

转载自:http://blog.jobbole.com/18587/

posted @ 2012-05-03 18:00 李少宏 阅读(214) 评论(1) 编辑
  2012年4月29日

国内最大的NOSQL 聚集地:http://blog.nosqlfan.com

redis命令全集:http://redis.io/commands

Redis命令参考简体中文版 v2.4.1(v2.6已经出来了,这个网站上也有):http://redis.readthedocs.org/en/2.4/

Linux 下 Redis 安装详解: http://www.oschina.net/question/12_18065

Redis内存存储结构分析:http://www.searchtb.com/2011/05/redis-storage.html

这个网站使用了redis:www.gongju.com

测试效果:ce.gongju.com/www.cnblogs.com

新浪微博也用了redis: weibo.com

posted @ 2012-04-29 11:20 李少宏 阅读(1092) 评论(0) 编辑
  2012年4月27日

  相信大家对众多选择判断语句都感到很厌烦,一大堆的

if something:do something()

else:

do something, 

或者是

switch(something):

  case st1:do1

  case st2:do2

  ...

  case stn:don

谁看了都会烦。不仅增加了巨大的代码量,而且不能很好地进行逻辑封装。

  其实看到标题,高手就知道怎么做了。是的,就是用数组,列表或字典来替代这些选择判断语句。怎么处理呢?

  比如我要对不同类型的书籍进行不同的处理,用 python 来实现

  有个 read_book: 方法

  read_book(book):

    ret = None

    if book == 'python':

       ret = python()

    elif book == 'C#':

      ret = CSharp()

    elif book == 'C':

      ret = C()

    elif book == 'C++':

      ret = CPlus()

    elif book == 'java':

      ret = java()

    elif book == 'lua':

      ret = lua()

    return ret

我认为读者看到一大堆if,都不会想看下去了,这样的代码实在太烂了。好,让我们来修改一下,我这里是用字典来实现的,请看我是怎样用字典来实现的:

read_book(book):

  book_dict = { 'python': python(), 'C#': CSharp(), 'C':C(), 'C++':CPlus(), 'java':java(),'lua':lua() }

  return book_dict[book]

看到没,两行就解决了!!不仅节省了大量代码,而且思路也更加清晰。

  如果书籍有多种分类呢?比如上面的是程序书籍,如果我想看人文书籍呢?这就涉及到了 一Key 对 多Key 的问题,其实也很好解决。

read_book(cataglog, book):

  book_dict = {'promgram': { 'python': python(), 

               'C#': CSharp(), 

               'C':C(), 

               'C++':CPlus(), 

               'java':java(),

               'lua':lua() } 

         'renwen': { 'zhexue': zhexue(),

               'shici': shici(),

               'sanwen': sanwen(),

               'guwen':guwen() }

        }

  return book_dict[catalog][book]

如果用if,就要使用两层嵌套,如果有多个嵌套,那代码看起来岂不是很恶心!!

我这里是用字典实现的,因为字典的索引好操作,很直观。如果用数组或列表,就可以用多维数组,但这个不好操作,要先遍历一遍数组或列表,对于数据量不大的时候,跟if判断语句差不多;如果数据量很大,可以别去遍历,先进行检索 Key,再去查找相应的函数。

其实我这里有个不足之处,就是每调用一次read_book函数,都要把所有的函数(执行这些python(),CSharp(),C())执行一次,这样会大大削弱程序性能。如果函数不复杂,也影响不了多少;如果函数很复杂呢?别担心,还是有解决办法的。

read_book(cataglog, book):

#注意,我用双引号把函数变成字符串

  book_dict = {'promgram': { 'python': 'python()'

                'C#': 'CSharp()'

                'C':'C()'

                'C++':'CPlus()'

                'java':'java()',

                'lua':'lua()'

         'renwen': { 'zhexue': 'zhexue()',

               'shici': 'shici()',

               'sanwen': 'sanwen()',

               'guwen':'guwen()' } 

        }

  return eval(book_dict[catalog][book])

大家注意到没,我把函数转成字符串,这样就不用执行全部函数了,只在最后用 eval() 执行一次函数,是不是很爽啊?!!!

推荐几个用python写的网站:
  python站:http://simple-is-better.com/news/552

 

   站长工具界新秀 站长工具 www.gongju.com, 这里有个很好用的网站测速工具:ce.gongju.com/cnblogs.com

posted @ 2012-04-27 15:54 李少宏 阅读(130) 评论(1) 编辑

对于网站访客行为和心理分析,国外有专门的研究。

 

Jakob Nielsen,著名的Web可用性领域专家曾经有一个著名的 0.1~1.0~10秒理论,用于评价网页速度算不算足够快的标准。作者引用如下,并给出作者的理解:

— 0.1秒:访客在网页UI中的时间敏感下限。例子,你的网站某个操作比竞争对手快0.05秒,访客是感受不出来的。如果快了0.2秒,用户是可以感受到的。

— 1.0秒:访客在网页UI中的流畅体验上限。例子,如果你的网页的某个操作的等待时间在0.1~1秒之间,用户在时间上感觉是“流畅”的,如果超过1.0秒,用户就会有明显等待感。

— 10秒:访客在网页uI中的专注忍耐上限。例子,你的网站某个操作需要用户等待10秒以上,用户往往会丧失耐心,或者放弃或者产生不舒适感。

 

另一些研究资料显示,基于目前的网络访问速度,访客对于网页打开速度的满意标准是2秒,访客最常忍耐时间是6~8秒,如果12秒还无法打开一个页面,90%以上的访客就会关闭网页走人。一些抽样调查显示,访客对于速度快的网站感觉更可信、更有趣。网页速度慢,会让访客产生挫败感。

 

你可以用上面的数据来评估自己的网站够不够快。当然,如果你是一个较真的人,可以使用工具帮你判断网站实际打开速度。站长工具:http://www.gongju.com。它这里有个: http://ce.gongju.com/www.cnblogs.com。可以很直观看出网站到底快不快,还可以跟其他网站进行对比

posted @ 2012-04-27 14:31 李少宏 阅读(399) 评论(1) 编辑

1、图片为什么会影响网站性能?它不是I/O读取吗?

  答:我们说到有关图片影响性能的问题,一般来说都是出自于我们的大部分访问页面中图片往往比html代码占用的流量大,在同等网络带宽的情况下,图片传输需要的时间更长,由于传输需要花很大开销在建立连接上,这会延长用户client端与server端的http连接时长,这对于apache来说,并发性能肯定会下降,除非你的返回全部是静态的,那就可以把 httpd.conf 中的 KeepAlives 为 off ,这样可以减小连接处理时间,但是如果图片过多会导致建立的连接次数增多,同样消耗性能。另外我们提到的理论更多的是针对大型集群的案例,在这样的环境下,图片的分离能有效的改进架构,进而影响到性能的提升,要知道我们为什么要谈架构?架构可能为了安全、为了资源分配、也为了更科学的开发和管理,但是终极目都是为了性能。另外在RFC1945的HTTP协议文档中很容易找到有关Mime Type和Content length部分的说明,这样对于理解图片对性能影响是很容易的。

 

2、对于大型的站点来说,他的数据库和 Web Server 一般都是分布式的,在多个区域都有部署,当某个地区的用户访问时会对应到一个节点上,如果是对社区内的帖子实时静态化,有更新时再重新静态化,那么在节点之间如何立刻同步呢?数据库端如何实现呢?如果用户看不到的话会以为发帖失败?造成重复发了,那么如何将用户锁定在一个节点上呢,这些怎么解决?

  答:对于将一个用户锁定在某个节点上是通过四层交换来实现的,一般情况下是这样,如果应用比较小的可以通过程序代码来实现。大型的应用一般通过类似LVS和硬件四层交换来管理用户连接,可以制定策略来使用户的连接在生命期内保持在某个节点上。

静态化和同步的策略比较多,一般采用的方法是集中或者分布存储,但是静态化却是通过集中存储来实现的,然后使用前端的proxy群来实现缓存和分担压力。

 

3、一般对于一个中型网站来说,交互操作非常多,日PV百万左右,如何做合理的负载?

  答:交互如果非常多,可以考虑使用集群加Memory Cache的方式,把不断变化而且需要同步的数据放入Memory Cache里面进行读取,具体的方案还得需要结合具体的情况来分析。

 

4、如果一个网站处于技术发展期,那么这些优化手段应该先实施哪些后实施哪些呢?或者说从成本(技术、人力和财力成本)方面,哪些先实施能够取得最大效果呢?

  答:先从服务器性能优化、代码性能优化方面入手,包括webserver、dbserver的优化配置、html静态化等容易入手的开始,这些环节争取先榨取到最大化的利用率,然后再考虑从架构上增加投入,比如集群、负载均衡等方面,这些都需要在有一定的发展积累之后再做考虑比较恰当。

 

5、关于图片服务器的问题

  答:台球网站故人居 9tmd.com 也使用了图片服务器架构上的分离,目前是仅仅是架构上分离,物理上没有分离,由于没有钱买更多的服务器:),大家可以看到故人居上的图片连接都是类似img.9tmd.com或者img1.9tmd.com的URL。

也就是说是一个apache提供的服务吧,这样的话对于性能的提高也很有意义吗?还是只是铺垫,为了方便以后的物理分离呢?因为目前 台球网站故人居 9tmd.com只有一台服务器,所以从物理上无法实现真正的分离,暂时使用虚拟主机来实现,是为了程序设计和网站架构上的灵活,如果有了一台新的服务器,只需要把图片镜像过去或者同步过去,然后把img.9tmd.com的dns解析到新的服务器上就自然实现了分离,如果现在不从架构和程序上实现,今后这样的分离就会比较痛苦:),一般来说ie或者firefox都有从同一个域名建立链接数的限制,如果把图片分到单独的域名,那么可以加大同一个浏览器的并发链接数,也相应的加快了网页的显示,这也是一种性能的提高

 

6、如何能在素材上传时直接传到图片服务器上呢?

  答:通过samba或者nfs实现是比较简单的方法。然后使用squid缓存来降低访问的负载,提高磁盘性能和延长磁盘使用寿命。

 
posted @ 2012-04-27 11:52 李少宏 阅读(82) 评论(1) 编辑

  

转载请保留出处:俊麟 Michael’s blog (http://space.itpub.net/7311285/viewspace-97)

        我在CERNET做过拨号接入平台的搭建,而后在Yahoo&3721从事过搜索引擎前端开发,又在MOP处理过大型社区猫扑大杂烩的架构升级等工作,同时自己接触和开发过不少大中型网站的模块,因此在大型网站应对高负载和并发的解决方案上有一些积累和经验,可以和大家一起探讨一下。


  一个小型的网站,比如个人网站,可以使用最简单的html静态页面就实现了,配合一些图片达到美化效果,所有的页面均存放在一个目录下,这样的网站对系统架构、性能的要求都很简单,随着互联网业务的不断丰富,网站相关的技术经过这些年的发展,已经细分到很细的方方面面,尤其对于大型网站来说,所采用的技术更是涉及面非常广,从硬件到软件、编程语言、数据库、WebServer、防火墙等各个领域都有了很高的要求,已经不是原来简单的html静态网站所能比拟的。

  大型网站,比如门户网站。在面对大量用户访问、高并发请求方面,基本的解决方案集中在这样几个环节:使用高性能的服务器、高性能的数据库、高效率的编程语言、还有高性能的Web容器。但是除了这几个方面,还没法根本解决大型网站面临的高负载和高并发问题。

  上面提供的几个解决思路在一定程度上也意味着更大的投入,并且这样的解决思路具备瓶颈,没有很好的扩展性,下面我从低成本、高性能和高扩张性的角度来说说我的一些经验。

 

1、HTML静态化
  其实大家都知道,效率最高、消耗最小的就是纯静态化的html页面,所以我们尽可能使我们的网站上的页面采用静态页面来实现,这个最简单的方法其实也是最有效的方法。但是对于大量内容并且频繁更新的网站,我们无法全部手动去挨个实现,于是出现了我们常见的信息发布系统CMS,像我们常访问的各个门户站点的新闻频道,甚至他们的其他频道,都是通过信息发布系统来管理和实现的,信息发布系统可以实现最简单的信息录入自动生成静态页面,还能具备频道管理、权限管理、自动抓取等功能,对于一个大型网站来说,拥有一套高效、可管理的CMS是必不可少的。

  除了门户和信息发布类型的网站,对于交互性要求很高的社区类型网站来说,尽可能的静态化也是提高性能的必要手段,将社区内的帖子、文章进行实时的静态化,有更新的时候再重新静态化也是大量使用的策略,像Mop的大杂烩就是使用了这样的策略,网易社区等也是如此。目前很多博客也都实现了静态化,我使用的这个Blog程序WordPress还没有静态化,所以如果面对高负载访问,www.toplee.com一定不能承受 

  同时,html静态化也是某些缓存策略使用的手段,对于系统中频繁使用数据库查询但是内容更新很小的应用,可以考虑使用html静态化来实现,比如论坛中论坛的公用设置信息,这些信息目前的主流论坛都可以进行后台管理并且存储再数据库中,这些信息其实大量被前台程序调用,但是更新频率很小,可以考虑将这部分内容进行后台更新的时候进行静态化,这样避免了大量的数据库访问请求。

  在进行html静态化的时候可以使用一种折中的方法,就是前端使用动态实现,在一定的策略下进行定时静态化和定时判断调用,这个能实现很多灵活性的操作,我开发的台球网站故人居(www.8zone.cn)就是使用了这样的方法,我通过设定一些html静态化的时间间隔来对动态网站内容进行缓存,达到分担大部分的压力到静态页面上,可以应用于中小型网站的架构上。故人居网站的地址:http://www.8zone.cn。顺便提一下,使用这个站长工具(http://www.gongju.com可以测试网站性能速度)

 

2、图片服务器分离
  大家知道,对于Web服务器来说,不管是Apache、IIS还是其他容器,图片是最消耗资源的,于是我们有必要将图片与页面进行分离,这是基本上大型网站都会采用的策略,他们都有独立的图片服务器,甚至很多台图片服务器。这样的架构可以降低提供页面访问请求的服务器系统压力,并且可以保证系统不会因为图片问题而崩溃。

  在应用服务器和图片服务器上,可以进行不同的配置优化,比如Apache在配置ContentType的时候可以尽量少支持,尽可能少的LoadModule,保证更高的系统消耗和执行效率。

  我的台球网站故人居8zone.cn也使用了图片服务器架构上的分离,目前是仅仅是架构上分离,物理上没有分离,由于没有钱买更多的服务器:),大家可以看到故人居上的图片连接都是类似img.9tmd.com或者img1.9tmd.com的URL。

  另外,在处理静态页面或者图片、js等访问方面,可以考虑使用lighttpd代替Apache,它提供了更轻量级和更高效的处理能力。

 

3、数据库集群和库表散列
  大型网站都有复杂的应用,这些应用必须使用数据库,那么在面对大量访问的时候,数据库的瓶颈很快就能显现出来,这时一台数据库将很快无法满足应用,于是我们需要使用数据库集群或者库表散列。

  在数据库集群方面,很多数据库都有自己的解决方案,Oracle、Sybase等都有很好的方案,常用的MySQL提供的Master/Slave也是类似的方案,您使用了什么样的DB,就参考相应的解决方案来实施即可。

  上面提到的数据库集群由于在架构、成本、扩张性方面都会受到所采用DB类型的限制,于是我们需要从应用程序的角度来考虑改善系统架构,库表散列是常用并且最有效的解决方案。我们在应用程序中安装业务和应用或者功能模块将数据库进行分离,不同的模块对应不同的数据库或者表,再按照一定的策略对某个页面或者功能进行更小的数据库散列,比如用户表,按照用户ID进行表散列,这样就能够低成本的提升系统的性能并且有很好的扩展性。sohu的论坛就是采用了这样的架构,将论坛的用户、设置、帖子等信息进行数据库分离,然后对帖子、用户按照板块和ID进行散列数据库和表,最终可以在配置文件中进行简单的配置便能让系统随时增加一台低成本的数据库进来补充系统性能。

 

4、缓存
  缓存一词搞技术的都接触过,很多地方用到缓存。网站架构和网站开发中的缓存也是非常重要。这里先讲述最基本的两种缓存。高级和分布式的缓存在后面讲述。

  架构方面的缓存,对Apache比较熟悉的人都能知道Apache提供了自己的mod_proxy缓存模块,也可以使用外加的Squid进行缓存,这两种方式均可以有效的提高Apache的访问响应能力。

  网站程序开发方面的缓存,Linux上提供的Memcached是常用的缓存方案,不少web编程语言都提供memcache访问接口,php、perl、c和java都有,可以在web开发中使用,可以实时或者Cron的把数据、对象等内容进行缓存,策略非常灵活。一些大型社区使用了这样的架构。

  另外,在使用web语言开发的时候,各种语言基本都有自己的缓存模块和方法,PHP有Pear的Cache模块和eAccelerator加速和Cache模块,还要知名的Apc、XCache(国人开发的,支持!)php缓存模块,Java就更多了,.net不是很熟悉,相信也肯定有。

 

5、镜像
  镜像是大型网站常采用的提高性能和数据安全性的方式,镜像的技术可以解决不同网络接入商和地域带来的用户访问速度差异,比如ChinaNet和EduNet之间的差异就促使了很多网站在教育网内搭建镜像站点,数据进行定时更新或者实时更新。在镜像的细节技术方面,这里不阐述太深,有很多专业的现成的解决架构和产品可选。也有廉价的通过软件实现的思路,比如Linux上的rsync等工具。

 

6、负载均衡
  负载均衡将是大型网站解决高负荷访问和大量并发请求采用的终极解决办法。

  负载均衡技术发展了多年,有很多专业的服务提供商和产品可以选择,我个人接触过一些解决方法,其中有两个架构可以给大家做参考。另外有关初级的负载均衡DNS轮循和较专业的CDN架构就不多说了。

6.1 硬件四层交换
  第四层交换使用第三层和第四层信息包的报头信息,根据应用区间识别业务流,将整个区间段的业务流分配到合适的应用服务器进行处理。 第四层交换功能就象是虚IP,指向物理服务器。它传输的业务服从的协议多种多样,有HTTP、FTP、NFS、Telnet或其他协议。这些业务在物理服务器基础上,需要复杂的载量平衡算法。在IP世界,业务类型由终端TCP或UDP端口地址来决定,在第四层交换中的应用区间则由源端和终端IP地址、TCP和UDP端口共同决定。

  在硬件四层交换产品领域,有一些知名的产品可以选择,比如Alteon、F5等,这些产品很昂贵,但是物有所值,能够提供非常优秀的性能和很灵活的管理能力。Yahoo中国当初接近2000台服务器使用了三四台Alteon就搞定了。

6.2 软件四层交换
  大家知道了硬件四层交换机的原理后,基于OSI模型来实现的软件四层交换也就应运而生,这样的解决方案实现的原理一致,不过性能稍差。但是满足一定量的压力还是游刃有余的,有人说软件实现方式其实更灵活,处理能力完全看你配置的熟悉能力。

  软件四层交换我们可以使用Linux上常用的LVS来解决,LVS就是Linux Virtual Server,他提供了基于心跳线heartbeat的实时灾难应对解决方案,提高系统的鲁棒性,同时可供了灵活的虚拟VIP配置和管理功能,可以同时满足多种应用需求,这对于分布式的系统来说必不可少。

  一个典型的使用负载均衡的策略就是,在软件或者硬件四层交换的基础上搭建squid集群,这种思路在很多大型网站包括搜索引擎上被采用,这样的架构低成本、高性能还有很强的扩张性,随时往架构里面增减节点都非常容易。这样的架构我准备空了专门详细整理一下和大家探讨。

总结:
  对于大型网站来说,前面提到的每个方法可能都会被同时使用到,Michael这里介绍得比较浅显,具体实现过程中很多细节还需要大家慢慢熟悉和体会,有时一个很小的squid参数或者apache参数设置,对于系统性能的影响就会很大,希望大家一起讨论,达到抛砖引玉之效。

posted @ 2012-04-27 11:39 李少宏 阅读(1491) 评论(4) 编辑
  2011年12月8日

   什么是Google网页级别
   通常我们所说的PR值就是Google搜索引擎评价网站的核心因素全称为PageRank,这是由Google创始人开发出的一套用于网页评级的系统,是Google搜索排名算法中的一个组成部分,级别从1 ~10级,10级为满分, PR值越高说明该网页在搜索排名中的地位越重要,也就是说,在其他条件相同的情况下,PR值高的网站在Google搜索结果的排名中有优先权。网页别由此成为Google所有网络搜索工具的基础。
   1.google网页级别概述:
   当从网页A链接到网页B时,Google就认为“网页A投了网页投B一票”。Google根据网页的得票数评定其重要性。然而,除了考虑网页得票数(即链接)的纯数量之外,Google还要分析投票的网页。“重要”的网页所投出的票就会有更高的权重,并且有助于提高其他网页的“重要性”。重要的、高质量的网页会获得较高的网页级别。Google在排列其搜索结果时,会考虑每个网页的级别。当然,如果不能满足用户的查询要求,网页级别再高也毫无意义。因此,Google将网页级别与完善的文本匹配技术结合在一起,为用户找到最重要、最有用的网页。Google所关注的远不只是关键词在网页上出现的次数,它还对该网页的内容(以及该网页所链接的内容)进行全面检查,从而确定该网页是否满足用户的查询要求。
   2.PR值算法原理
   (1)如果一个网页被多次引用,那么它可能是很重要的;一个网页虽然没有被多次引用,但是被重要的网页引用。那么它也可能是很重要的;一个网页的重要性被平均地传递到它所引用的网页。这种重要的网页称为权威网页。
   (2)假定用户一开始随机地访问网页集合中的一个网页,然后跟随网页的链接向前浏览网页,不回退浏览,那么浏览下一个网页的概率就是被浏览网页的PageRanK值。 
   3.影响PR值的因素
   PR值体现为从0~10的11个数值。由于Google市场拓展进程的原因,中文网页的PR值整体上低于英文网页。对中文网站来说,拥有3级PR是基础,4级PR算达标,5级PR可谓良好,而6级、7级PR就算相当优秀的网站。当然,由于PR最直接的影响因素是来自链接,因此这种评级并不代表内容的级别水准,网站内容质量对PR的影响是间接的、长期的。根据PR值的算法原理,可知影响一个网站(首页) PR值的因素主要包括:
   1)网站的导入链接质量
   根据前文对高质量导入链接的分析,则获得高PR值需要获得来自以下网站的链接:
   (1)加入搜索引擎分类目录。
   (2)与已经加人目录的网站交换链接。
   (3)获得来自PR值不低干4并与你的主题相关或互补的网站的链接。
   (4)链接出现在流量大、知名度高、频繁更新的重要网站上(如新闻源)。 
   (5)交换链接的对方网站具有很少导出链接。 
   (6)与内容质量高的网站链接(PR值上升潜力大)。
   2)导出链接数量 
   根据PR计算原理,由于“一个网页的重要性被平均的传递到所引用的网页”,因此反过来看,一个页面内过多的导出链接将潜在引起该页PR值的流失。但从内容的角度看,适当数量的与主题有关联的导出链接给搜索引擎带来良好的印象。因此,一个页面,尤其是首页的导出链接数量的把握,应该兼顾到PR值和关键词内容二者之间的平衡,即控制导出链接数量,以不超过10个为宜。
   3)搜索引擎收录一个网站的页面数量 
   在探讨SEO的时候,人们会过多地关注核心关键词排名以及首页的表现情况,却往往忽略了一个极其重要的问题,即搜索引擎对一个网站收录的页面数量。后者在SEO中也有着极其重要的意义。一个用核心关键词查询排名不佳的网站,可能由于被抓取大量网页而在用户使用其他关键词查询时,网页获得前三甲排名。由于用户搜索时使用的关键词具有分散性,使得这种情况往往给网站带来级大访问量。
   正因为收录页面的数量直接影响访问量,因此对PR值的影响也是很大的。被收录页面越多,主页PR越高。需要强调的是:此处所指的数量是指被收录数与网站页面总数的比值,而不是收录页面的绝对数值。比如一个拥有50个页面的网站被收录了10个页面,网站的被收录比值是1/5,一个拥有5000个页面的网站被收录了500个页面,比值是1/10,结果是收录10个页面比收录500个页面的网站还更具排名优势。但对各个搜索引擎来说,究竟达到多大的比值算是良好,目前还没有相关的数据。
   4)首页PR的高低
   尽管对PR的界定是针对网页而不是网站,但由于每个网页都依托某一网站而存在,首页是推广的核心页面,所以网站首页PR高低对内页各PR 也产生直接影响。一般而言,一个网站各页面的PR值呈现以下走向:首页>一级页面>二级页面>三级页面……每深入一级,PR降低1~2个档次。更深层次可能就无法列入评级范围了。当然,如果一个深层内页有很多外部或内部链接,情况则另当别论。  
   5)文件类型 
   Google可以索引到的网页和文件类型包括:pdf ,asp,jsp,hdml,shtml,xml,sfm,doc,xls,pptrtf,wks,lwp,wri,swf。做成PDF格式的文件往往都是网站比较重要的内容或文章。因此Google默认PDF格式文件的PR值默认为3,高于一般的HTML文件。同理,Google给予XML、PS、Word、PowerPoint、Excel等类型页面的PR也比普通网页文件高。因此,对于网站比较有价值的内容,建议做成PDF格式。
   6) PR值的更新
   Google每个月进行一次彻底的数据更新(Dance),更新后的网站排名和导入链接都会有某些变化,但其PR更新时间一般要延迟至彻底数据更新后三个月左右,而且网站的PR值相对稳定,要上升或下降一级PR是很不容易的事情。
   4.PR值的作用 
   由于网页级别的高低直接受链接的影响,而外部链接仅是SEO的一个方面,因此PR的高低只能反映出SEO的部分效果。一个网页如果拥有高PR值能够说明的仅是:
   (1)网页比较重要,拥有了排名靠前的优先权,并不能与排名靠前直接画上等号。
   (2)具有获得高质量导入链接的谈判优势。
   (3)搜索引擎会加快对网页数据的更新。 
   正因为具有排名靠前的优先权和谈判优势,使得高PR值网站往往最终确实产生高排名,业内不少人士就干脆直接将PR直与SEO画等号,这不仅不正确,也不利千SEO实施。因为PR值不高的网站同样有可能获得好的排名,而PR值高的不见得有理想的排名。并且,在这样的思想指导下,人们往往片面追求链接效应,忽略了SEO对于内容,结构、关键词等方面的分析和改进,而后者才是用户和搜索引擎长期关注的焦点。
   近期几个月时间PR更新了几次,而且好像特别优待新站,今天PR值又有些波动,渐渐的大家感觉到PR值并不是那么重要了,SEO专家也建议大家平常心对待Google PR值,当然如果网站优化做的好,同时又获得很理想的PR值,那是最后不过了。

 

  
   作者: SEO专家
   原载: http://www.seozj.com  
   版权声明:原创作品,允许转载,转载时请务必以超链接形式标明文章原始出处 、作者信息和本站声明。否则将追究法律责任。

posted @ 2011-12-08 10:25 李少宏 阅读(87) 评论(0) 编辑
  2011年11月15日
from pymongo import * # 导包
con = Connection(...) # 链接
db = con.database # 链接数据库
db.authenticate('username', 'password') # 登录
db.drop_collection('users') #删除表
db.logout() # 退出
db.collection_names() # 查看所有表
db.users.count() # 查询数量
db.users.find_one({'name' : 'xiaoming'}) # 单个对象
db.users.find({'age' : 18}) # 所有对象
db.users.find({'id':64}, {'age':1,'_id':0}) # 返回一些字段 默认_id总是返回的 0不返回 1返回
db.users.find({}).sort({'age': 1}) # 排序
db.users.find({}).skip(2).limit(5) # 切片

db.users.find({}, {}, 10, 20) # 第二种写法 切片 未测试

 

collection 

 属性:full_name,name,database
insert(self, doc_or_docs, manipulate=True, safe=False, check_keys=True, **kwargs):
  • 当manipulate=True 时,_id 会被自动添加到 dict,并返回些 _id(or list(_id)),
    为False 时,返回 None
  • 当 safe 为 True 时,会自动判断to_save 是否为 dict,如果检查出错,会提示出错信息,会等待数据库的响应,而不会执行插入数据库
  • doc_or_docs 允许插入一条或多条记录
save(self, to_save, manipulate=True, safe=False, **kwargs)
  • to_save,只接受一个 dict (字典)
  • 当 to_save 里包含 _id 时,为update (可能update 多条)
drop(self) 
  • 删除些 collection (集合及数据一起删除)
remove(self, spec_or_id=None, safe=False, **kwargs)
  • romve 一条记录
  • 删除操作不可还原
  • 当 safe 为 True 时,会自动判断参数的正确性,如果有异常,会等待数据库的响应,而不会执行删除操作 
  • spec_or_id ,为一完整doc 或指定了 _id 的字典
find_one(self, spec_or_id=None, *args, **kwargs)
  • spec_or_id ,为一完整doc 或指定了 _id 的字典
  • 返回一条符合条件的记录
 find(self, *args, **kwargs)
  • 参数必需是一个 doc 原型(eg:{“name”:"abc"})
  • 返回 Cursor ,并非(result collection)
  • spec 查询条件
  • fields 返回的字段清单,但_id 为默认返回
  • skip 省略的记录数
  • limit 一次返回的最大记录数
  • timeout 如果为True ,超时会自动断开
  • snapshot 快照模式,True 时保证返回结果不重复
  • tailable 保持tailable 游标的开启,并记录上次的最后位
  • sort 排序字段
  • max_scan 允许查询的最大数量 ( version **>= 1.5.1**)
  • as_class 指定查询结果的类型,默认为 ~pymongo.connection.Connection.document_class
  • network_timeout 指定超时时间
count(self) 返回总记录数
create_index(self, key_or_list, deprecated_unique=None, ttl=300, **kwargs)
  • 为一个或多个key创建索引
  • key 必须为字符串类型
  • eg: create_index("mike")
  • eg: create_index([("mike", pymongo.DESCENDING), ("eliot", pymongo.ASCENDING)])
ensure_index(self, key_or_list, deprecated_unique=None, ttl=300, **kwargs)
  • 检查是否存在些索引,如果不存在则创建,存在则返回None
drop_indexes(self) 删除所以索引
drop_index(self, index_or_name) 删除指定索引
index_information(self) 返回索引信息
rename(self, new_name, **kwargs) 重新指向一个collection
distinct(self, key) 返回所有key 不重复的记录
 

01      mongo –path

02      db.AddUser(username,password) 添加用户

03      db.auth(usrename,password) 设置数据库连接验证

04      db.cloneDataBase(fromhost) 从目标服务器克隆一个数据库

05      db.commandHelp(name) returns the help for the command

06      db.copyDatabase(fromdb,todb,fromhost) 复制数据库fromdb—源数据库名称,todb—目标数据库名称,fromhost—源数据库服务器地址

07      db.createCollection(name,{size:3333,capped:333,max:88888}) 创建一个数据集,相当于一个表

08      db.currentOp() 取消当前库的当前操作

09      db.dropDataBase() 删除当前数据库

10      db.eval(func,args) run code server-side

11      db.getCollection(cname) 取得一个数据集合,同用法:db['cname'] or db.cname

12      db.getCollenctionNames() 取得所有数据集合的名称列表

13      db.getLastError() 返回最后一个错误的提示消息

14      db.getLastErrorObj() 返回最后一个错误的对象

15      db.getMongo() 取得当前服务器的连接对象get the server connection object

16      db.getMondo().setSlaveOk() allow this connection to read from then nonmaster membr of a replica pair

17      db.getName() 返回当操作数据库的名称

18      db.getPrevError() 返回上一个错误对象

19      db.getProfilingLevel() ?什么等级

20      db.getReplicationInfo() ?什么信息

21      db.getSisterDB(name) get the db at the same server as this onew

22      db.killOp() 停止(杀死)在当前库的当前操作

23      db.printCollectionStats() 返回当前库的数据集状态

24      db.printReplicationInfo()

25      db.printSlaveReplicationInfo()

26      db.printShardingStatus() 返回当前数据库是否为共享数据库

27      db.removeUser(username) 删除用户

28      db.repairDatabase() 修复当前数据库

29      db.resetError()

30      db.runCommand(cmdObj) run a database command. if cmdObj is a string, turns it into {cmdObj:1}

31      db.setProfilingLevel(level) 0=off,1=slow,2=all

32      db.shutdownServer() 关闭当前服务程序

33      db.version() 返回当前程序的版本信息

34       

35      db.linlin.find({id:10}) 返回linlin数据集ID=10的数据集

36      db.linlin.find({id:10}).count() 返回linlin数据集ID=10的数据总数

37      db.linlin.find({id:10}).limit(2)返回linlin数据集ID=10的数据集从第二条开始的数据集

38      db.linlin.find({id:10}).skip(8) 返回linlin数据集ID=10的数据集从0到第八条的数据集

39      db.linlin.find({id:10}).limit(2).skip(8) 返回linlin数据集ID=1=的数据集从第二条到第八条的数据

40      db.linlin.find({id:10}).sort() 返回linlin数据集ID=10的排序数据集

41      db.linlin.findOne([query]) 返回符合条件的一条数据

42      db.linlin.getDB() 返回此数据集所属的数据库名称

43      db.linlin.getIndexes() 返回些数据集的索引信息

44      db.linlin.group({key:…,initial:…,reduce:…[,cond:...]})

45      db.linlin.mapReduce(mayFunction,reduceFunction,

46      )

47      db.linlin.remove(query) 在数据集中删除一条数据

48      db.linlin.renameCollection(newName) 重命名些数据集名称

49      db.linlin.save(obj) 往数据集中插入一条数据

50      db.linlin.stats() 返回此数据集的状态

51      db.linlin.storageSize() 返回此数据集的存储大小

52      db.linlin.totalIndexSize() 返回此数据集的索引文件大小

53      db.linlin.totalSize() 返回些数据集的总大小

54      db.linlin.update(query,object[,upsert_bool])在此数据集中更新一条数据

55      db.linlin.validate() 验证此数据集

56      db.linlin.getShardVersion() 返回数据集共享版本号

57      db.linlin.find({‘name’:'foobar’}) select * from linlin where name=’foobar’

58      db.linlin.find() select * from linlin

59      db.linlin.find({‘ID’:10}).count() select count(*) from linlin where ID=10

60      db.linlin.find().skip(10).limit(20) 从查询结果的第十条开始读20条数据 select * from linlin limit 10,20 ———-mysql

61      db.linlin.find({‘ID’:{$in:[25,35,45]}}) select * from linlin where ID in (25,35,45)

62      db.linlin.find().sort({‘ID’:-1}) select * from linlin order by ID desc

63      db.linlin.distinct(‘name’,{‘ID’:{$lt:20}}) select distinct(name) from linlin where ID<20

64      db.linlin.group({key:{'name':true},cond:{'name':'foo'},reduce:function(obj,prev){prev.msum+=obj.marks;},initial:{msum:0}})

65      select name,sum(marks) from linlin group by name

66      db.linlin.find('this.ID<20′,{name:1}) select name from linlin where ID<20

67      db.linlin.insert({'name':'foobar’,'age':25}) insert into linlin ('name','age’)values('foobar',25)

68      db.linlin.insert({'name':'foobar’,'age':25,’email’:'cclove2@163.com’})

69      db.linlin.remove({}) delete * from linlin

70      db.linlin.remove({'age':20}) delete linlin where age=20

71      db.linlin.remove({'age':{$lt:20}}) delete linlin where age<20

72      db.linlin.remove({'age':{$lte:20}}) delete linlin where age<=20

73      db.linlin.remove({'age':{$gt:20}}) delete linlin where age>20

74      db.linlin.remove({‘age’:{$gte:20}}) delete linlin where age>=20

75      db.linlin.remove({‘age’:{$ne:20}}) delete linlin where age!=20

76      db.linlin.update({‘name’:'foobar’},{‘$set’:{‘age’:36}}) update linlin set age=36 where name=’foobar’

77      db.linlin.update({‘name’:'foobar’},{‘$inc’:{‘age’:3}}) update linlin set age=age+3 where name=’foobar’

posted @ 2011-11-15 10:27 李少宏 阅读(326) 评论(0) 编辑
  2011年11月1日
摘要: 如果不知道dict中是否有key的值,那么最好用dict.get(key)如果用dict[key]这个读取会报KeyError异常,根据不同情况用不同的方式阅读全文
posted @ 2011-11-01 10:52 李少宏 阅读(176) 评论(0) 编辑
  2011年10月20日
摘要: @修饰符:在模块或者类里面修饰函数或者方法。。只可以在模块或类定义层内对函数进行修饰,不允许修修饰一个类。 一个修饰符就是一个函数,它将被修饰的函数做为参数,并返回修饰后的同名函数或其它可调用的东西。例子:def s(fn): def saygood(*args): print "Good"*3 return fn(*args) return saygood@sdef u(a, b): print a**2+b**2if __name__ == "__main__": u(3, 4) u(3, 5)程序运行结果:GoodGoodGood25GoodGoo阅读全文
posted @ 2011-10-20 15:54 李少宏 阅读(247) 评论(0) 编辑

公告