Squid 2.6配置的注意事项

前一阶段Squid推出了2.6版,在缓存机制上有了更好的统一,将反向代理和正向代理统一在了一起,但是也有一些问题。
第一是反向代理配置方式改变了,去掉了httpd_accel_*这些配置,而改用http_port和cache_peer等配置来完成,例如:

有了这两条,就可以实现反向代理了
第二如果Apache有身份验证的话,Squid 2.6可能并不会将Authorization的HTTP头发送给Apache,所以我在Squid 2.6后配置Apache + Subversion就无法使用身份验证——这个目前还没办法解决。
第三是Squid 2.6的基于IP的反向代理服务器的配置改变,也就是Squid 2.5中的httpd_accel_host virtual,在Squid 2.6的方法是命名多个parent、originserver的cache_peer,然后配合cache_peer_domain来指定对不同域名发送到不同的上游服务器;

需要注意的是,每个cache_peer都必须至少有一个cache_peer_domain分配到其上,才能真正实现类似virtual的配置。

Cache_Lite的检测缓存失效

PEAR组件Cache_Lite是一个很实用的文件缓存组件,我在PHP编程中常常用到,现在我常需要检测某个id的缓存是否已经失效了,而Cache_Lite本身并未提供这样的功能,因此我写了这样一个函数:

使用也很简单,应该一眼就能看明白。当然也可以通过继承Cache_Lite,并添加一个方法来完成。

Currying的翻译

函数Currying,是我所一直不能确定的英文翻译之一 ,另外还有一个Web的翻译。

函数Currying的意思就是将参数和函数关联起来,变成一个新的函数。比如一个二元参数f a b

当给出f 1的时候,应该返回什么呢?一般的语言中,要么是将b设为一个默认值,要么就是直接出错,而当有了Currying,那么f
1其实返回一个函数g x = f 1 x。
当然这个描述也不够好。

其实其命名是为了纪念一个逻辑学家 Haskell Curry 的――函数式语言Haskell也是为纪念这个人,当然这个东西并不是他第一个发现。

既然作为姓名,数学中可能不会进行翻译,就叫做Curry化,或者柯里化,不知道还有什么更好的翻译方法,能较为直接地体现其真正的含义。

给“Web”一个准确的翻译

一般提到“web”这个词,大家都知道它是“网”的意思,而且还是“蜘蛛网”。在it界,大家一般都知道是“网络”的意思,但是很奇怪的是,“net”也一样是“网”,同样表示“网络”的意思,这就很奇怪了。到底两者应该如何翻译?

我查了金山词霸,web的定义是:

n. 环球网
n. 网, 蛛丝, 蹼, 翼手, 织物, 圈套, 卷筒纸

而net的定义是:

n. 网, 网络, 网状物, 净利, 实价

而且在计算机中,一般等同于network这个词:

n. 网络, 网状物, 广播网

而如今在国内,始终没有办法很好地区分这两个词,而往往涉及到web的时候,都会忽略不翻译,比如web appliaction,一般不会翻译成网络应用,而是说成web应用,web service,也往往翻译成web服务,而不是网络服务。web 2.0更没有人去翻译它了,绝不会看到有人说成是“网络2.0”。为什么不翻译,就是因为net和web有区别,但直接翻译过来,比如web service,是一种软件技术,但说网络服务,很多人会和network service,也就是诸如拨号上网、宽带之类的相混淆。

在学校上课的时候,学过几门和网络有关的课,比如“网络工程基础”,“网络编程基础”等课,回忆一下其中讲过的tcp/ip的协议栈,里面是这么讲的:

  1. 应用层
  2. 传输层
  3. 互连层
  4. 网络接口层

而我记得很清楚,网络工程中,基本没有涉及到很多应用层的东西,网络编程基础,一般教授的是传输层的编程,至于应用层,也仅仅教授了应用层很底层的知识。

再考虑网络工程这个词,英文是network engineering,而网络工程考虑的问题多数是硬件、如何组网,可以说这些是互联网比较底层、基础的东西。

反观web 2.0的定义,可以发现,其中多数是有关最终用户的内容,是直接和人打交道的。
我们现在就可以下结论了,net是我们最宽泛的“网络”概念的底层基础,而web则是指顶层的应用,甚至包含了人与人利用web进行的交互。

概念区分清楚了,那么如何给web一个准确的翻译,金山词霸给出的“环球网”读起来也不是很顺口,我现在也没有什么十分好的答案,希望大家能给我出个主意。

回忆我的Linux使用历程兼介绍Fedora Core 5 Test

我以前实验过很多Linux分版,也经历过很多次失败,比如安装Linux不当导致硬盘分区丢失等等.虽然我现在还不能说是高手,但已经可以熟练应用Linux了. 如果按照使用的时间排序,

  • 1999-2000 (完全不知如何应用)
    • BluePoint (现在在网上还能找到它的地址,但已经打不开了,http://bluepoint.com.cn/)
    • Lindows(国外有一个商业Linux发行版,也叫Lindows,做的相当不错,现在改名叫Linspire了)
    • RedFlag 3.0
    • Mandrake 8.0 是我买的一套正版,还送了一套字处理软件,可惜好像被我扔了,这是当时唯一用起来比较舒服的分版,可惜当时不能上网,很多软件都没有,所以最后还是放弃了
    • RedHat 7.0
  • 2002-2004上 (开始熟练应用Linux)
      RedHat 9.0 有友好的安装界面,已经相当易用,当时同学买了一套正版,很多人都使用

    • VMWare WorkStation,虽然不是Linux,但不得不提,因为这个软件,使得我们不用重启就可以在Windows和Linux之间切换,而且在虚拟机的环境中实验Linux更加安全
  • 2004下 至今 (完全适应使用Linux进行开发作业,而且可以完全抛弃商业软件使用开源软件)
    • Fedora Core 2,3,4 Fedora Core系列一直都是我十分喜欢使用的,
    • Ubuntu Linux (Linux for human beings)秉承了Debian的丰富的软件包,容易使用,易用扩展,缺点是速度慢(不知道为什么)
    • Gentoo Linux 直接从源码进行构建,提供极大的自定义空间,也能更好地为自给的机器进行优化,可惜不适合初学者,我当时学习Gentoo的时候,安装编译整个系统花了好几天时间
    • Mandrake 2005 Mandrake也提供了很好的界面,提供和KDE更好的集成,现在是和Connectiva合并为Mandriva
    • Arch Linux 是针对i686优化的分版,十分简洁,系统的一些机制也很简单(简陋?)可惜安装并不容易,缺乏文档支持,并不如Gentoo好

现在我很期待Fedora Core 5的发布,因为他有很多振奋人心的特性,内核是针对i686优化的,使用了Gnome 2.12(界面效果更好),一些控件加入了动画特效,如淡入淡出,窗体的显示也可以与Windows媲美

而且加入了最近的FreeType,已经可以进行仿粗体的渲染,以前使用Linux经常头疼如何配置中文字体粗体、斜体的问题,都已经得到了解决了,同时直接提供了SCIM作为输入法基础,这是迎和广大宽字符集用户需求的。其他的新我,比如使用GCC4.4,并集成了ECLIPSE 3.1的支持。

据说开发小组打算把所有的包都针对i686优化,如果那样,真是不得了。

你知道Lua语言吗?

Lua语言,也许几年前大家都没有听说过,他是巴西人开发的一个十分高效的动态语言,针对的就是嵌入在宿主程序中做扩展应用。

说他高效,是和很多其他动态语言相比,如JavaScript(Spidermonkey实现或JScript实现)、Python、Ruby等等,无论在执行速度还是内存占用上,都体现出很大优势。(见测评

而今天也许很多人都听说了,因为暴雪公司开发魔兽世界旗帜鲜明地应用了Lua语言来运行扩展脚本。

听朋友说,WOW的扩展开发社区,乍一看,就以为是Lua社区。而Lua也有一个十分活跃的社区,并不比Python、Perl的差。(http://lua-users.org/)

我在2002年的时候,也是因为研究游戏编程才了解了Lua语言,当时看到介绍说他的高效,适合做游戏开发的Scripting,然后我就十分喜欢这个语言。之后我和Nicholas翻译了他的Reference Manual,不过一直没翻译完。剩下的转交给luachina去完成了。

本来我很想推广一下这门语言,即使拿来做研究也很好。Lua使用了一个Clean C来实现他的所有功能,所谓Clean C,就是一个十分精简的C语言,比ANSI还精简,这样可以达到很好的兼容性,Lua可以很方便的在各种C/C++语言编译器上完成编译,虽然他本身源代 码只包含了Unix的Makefile。而脚本的执行性能更是值得去好好研究。不过似乎大家都不感兴趣,不过好在WOW的成功,也把Lua带给了中国的开 发人员。

Lua本身并不支持基于类的面向对象,和JavaScript一样是基于原型的,有一些人实现了Lua的扩展可以模拟基于类的编程,不过我并不赞成这样,原因可以看我前面介绍Self的Blog

Self 语言

不知道各位是否研究过Self语言,我研究了一下,虽然连Self都没有能够运行起来,但是从中已经学习到了很多新的概念,而且对面向对象的精髓有了更进一步的领悟。

我希望和大家分享我学习到的内容,所以我翻译了两篇文章,大家可以看:
Self:简洁的威力
克隆的反攻

如何优化JavaScript脚本的性能

作者:ShiningRay @ Nirvana Studio

随着网络的发展,网速和机器速度的提高,越来越多的网站用到了丰富客户端技术。而现在Ajax则是最为流行的一种方式。JavaScript是一种解释型语言,所以能无法达到和C/Java之类的水平,限制了它能在客户端所做的事情,为了能改进他的性能,我想基于我以前给JavaScript做过的很多测试来谈谈自己的经验,希望能帮助大家改进自己的JavaScript脚本性能。

语言层次方面

循环

循环是很常用的一个控制结构,大部分东西要依靠它来完成,在JavaScript中,我们可以使用for(;;),while(),for(in)三种循环,事实上,这三种循环中for(in)的效率极差,因为他需要查询散列键,只要可以就应该尽量少用。for(;;)while循环的性能应该说基本(平时使用时)等价。

而事实上,如何使用这两个循环,则有很大讲究。我在测试中有些很有意思的情况,见附录。最后得出的结论是:

  • 如果是循环变量递增或递减,不要单独对循环变量赋值,应该在它最后一次读取的时候使用嵌套的++--操作符。

  • 如果要与数组的长度作比较,应该事先把数组的length属性放入一个局部变量中,减少查询次数。

举例,假设arr是一个数组,最佳的遍历元素方式为:

或者,如果无所谓顺序的话:

局部变量和全局变量

局部变量的速度要比全局变量的访问速度更快,因为全局变量其实是全局对象的成员,而局部变量是放在函数的栈当中的。

不使用Eval

使用eval相当于在运行时再次调用解释引擎对内容进行运行,需要消耗大量时间。这时候使用JavaScript所支持的闭包可以实现函数模版(关于闭包的内容请参考函数式编程的有关内容)

减少对象查找

因为JavaScript的解释性,所以a.b.c.d.e,需要进行至少4次查询操作,先检查a再检查a中的b,再检查b中的c,如此往下。所以如果这样的表达式重复出现,只要可能,应该尽量少出现这样的表达式,可以利用局部变量,把它放入一个临时的地方进行查询。

这一点可以和循环结合起来,因为我们常常要根据字符串、数组的长度进行循环,而通常这个长度是不变的,比如每次查询a.length,就要额外进行一个操作,而预先把var
len=a.length
,则就少了一次查询。

字符串连接

如果是追加字符串,最好使用s+=anotherStr操作,而不是要使用s=s+anotherStr

如果要连接多个字符串,应该少使用+=,如

应该写成

而如果是收集字符串,比如多次对同一个字符串进行+=操作的话,最好使用一个缓存。怎么用呢?使用JavaScript数组来收集,最后使用join方法连接起来,如下

类型转换

类型转换是大家常犯的错误,因为JavaScript是动态类型语言,你不能指定变量的类型。

1.
把数字转换成字符串,应用"" + 1,虽然看起来比较丑一点,但事实上这个效率是最高的,性能上来说:

("" + ) > String() > .toString() > new String()

这条其实和下面的“直接量”有点类似,尽量使用编译时就能使用的内部操作要比运行时使用的用户操作要快。

String()属于内部函数,所以速度很快,而.toString()要查询原型中的函数,所以速度逊色一些,new String()用于返回一个精确的副本。

2.
浮点数转换成整型,这个更容易出错,很多人喜欢使用parseInt(),其实parseInt()是用于将字符串转换成数字,而不是浮点数和整型之间的转换,我们应该使用Math.floor()或者Math.round()

另外,和第二节的对象查找中的问题不一样,Math是内部对象,所以Math.floor()其实并没有多少查询方法和调用的时间,速度是最快的。

3.
对于自定义的对象,如果定义了toString()方法来进行类型转换的话,推荐显式调用toString(),因为内部的操作在尝试所有可能性之后,会尝试对象的toString()方法尝试能否转化为String,所以直接调用这个方法效率会更高

使用直接量

其实这个影响倒比较小,可以忽略。什么叫使用直接量,比如,JavaScript支持使用[param,param,param,...]来直接表达一个数组,以往我们都使用new Array(param,param,...),使用前者是引擎直接解释的,后者要调用一个Array内部构造器,所以要略微快一点点。

同样,var foo = {}的方式也比var foo = new Object();快,var reg = /../;要比var reg=new RegExp()快。

字符串遍历操作

对字符串进行循环操作,譬如替换、查找,应使用正则表达式,因为本身JavaScript的循环速度就比较慢,而正则表达式的操作是用C写成的语言的API,性能很好。

高级对象

自定义高级对象和DateRegExp对象在构造时都会消耗大量时间。如果可以复用,应采用缓存的方式。

DOM相关

插入HTML

很多人喜欢在JavaScript中使用document.write来给页面生成内容。事实上这样的效率较低,如果需要直接插入HTML,可以找一个容器元素,比如指定一个div或者span,并设置他们的innerHTML来将自己的HTML代码插入到页面中。

对象查询

使用[""]查询要比.items()更快,这和前面的减少对象查找的思路是一样的,调用.items()增加了一次查询和函数的调用。

创建DOM节点

通常我们可能会使用字符串直接写HTML来创建节点,其实这样做

  1. 无法保证代码的有效性

  2. 字符串操作效率低

所以应该是用document.createElement()方法,而如果文档中存在现成的样板节点,应该是用cloneNode()方法,因为使用createElement()方法之后,你需要设置多次元素的属性,使用cloneNode()则可以减少属性的设置次数——同样如果需要创建很多元素,应该先准备一个样板节点。

定时器

如果针对的是不断运行的代码,不应该使用setTimeout,而应该是用setIntervalsetTimeout每次要重新设置一个定时器。

其他

脚本引擎

据我测试Microsoft的JScript的效率较Mozilla的Spidermonkey要差很多,无论是执行速度还是内存管理上,因为JScript现在基本也不更新了。但SpiderMonkey不能使用ActiveXObject

文件优化

文件优化也是一个很有效的手段,删除所有的空格和注释,把代码放入一行内,可以加快下载的速度,注意,是下载的速度而不是解析的速度,如果是本地,注释和空格并不会影响解释和执行速度。

总结

本文总结了我在JavaScript编程中所找到的提高JavaScript运行性能的一些方法,其实这些经验都基于几条原则:

  1. 直接拿手头现成的东西比较快,如局部变量比全局变量快,直接量比运行时构造对象快等等。

  2. 尽可能少地减少执行次数,比如先缓存需要多次查询的。

  3. 尽可能使用语言内置的功能,比如串链接。

  4. 尽可能使用系统提供的API,因为这些API是编译好的二进制代码,执行效率很高

同时,一些基本的算法上的优化,同样可以用在JavaScript中,比如运算结构的调整,这里就不再赘述了。但是由于JavaScript是解释型的,一般不会在运行时对字节码进行优化,所以这些优化仍然是很重要的。

当然,其实这里的一些技巧同样使用在其他的一些解释型语言中,大家也可以进行参考。

参考

附录1

由于是以前做过的测试,测试代码已经不全,我补充了一部分如下:

附录2

代码1:

代码2:

代码3:

在firefox下测试这两段代码,结果是代码2优于代码1和3,而代码1一般优于代码3,有时会被代码3超过;而在IE
6.0下,测试压力较大的时候(如测试10000次以上)代码2和3则有时候优于代码1,有时候就会远远落后代码1,而在测试压力较小(如5000次),则代码2>代码3>代码1。

代码4:

代码5:

上面两段代码在Firefox和IE下测试结果都是性能接近的。

代码6:

代码7:

代码8:

代码9:

这四段代码在Firefox下6和8的性能接近,7和9的性能接近,而6,
8 < 7, 9;

最后我们来看一下空循环

代码10:

代码11:

最后的测试出现了神奇的结果,Firefox下代码10所花的时间与代码11所花的大约是24:1。所以它不具备参考价值,于是我没有放在一开始给大家看。

JavaScript的类型转换

我阅读了birdshome的在JavaScript中也玩变量类型强行转之后,在文章后面留言。也许我是表达不清楚,所以我想澄清一些情况,首先我反对使用“强制类型转换”这个词。

首先我们可以看C中间的强制类型转换,只能存在于基本类型之间,像整数可表示类型和浮点数类型之间,比如(int)2.45,这是可以的,但是他们和字符串之间都没有强制转换的可能,因为字符串实际是一个指针。因此不可能出现(char *)2.45就可以获得"2.45"这个字符串的情况,反之也不能。
而在C++中,我们可以创建一个类,并且重载强制转换操作,来完成这种情况,那么就必须要求有相应的对象,但是对于基本类型,这也是不可以的。

然后,因为JavaScript在对象模型上主要参考了Java,我们可以再参考一下Java的类型转换:在Java中,基本类型之间的强制转换也不是这样的,比如,整数要转换成字符串,必须使用Integer.toString()静态方法或者String.valueOf()静态方法,把字符串转换为整数,必须使用Integer.valueOf()

可见,不能把JavaScript中的类型转换看作为“强制类型转换”。

在JavaScript中,Double类型和Int类型都是看作为Number对象,因此无论是typeof 1还是typeof 1.0,都是返回number。这样我们可以不用去管是Int还是Double类型,让JavaScript解释引擎内部去处理。

  • 如果要把Number转换成String,可以使用NumbertoString()方法,(象(1).toString()括号必须或者 1 .toString()空格必须,否则会编译出错,如果是变量则无需),或者调用String()函数,以及直接进行字符串连接(字符串变量或常量后直接进行“+”操作)这几种方法都会自动调用解释引擎内部的 NumberToString()函数,或者根据进制调用其它函数,基本类似。
  • 如果要把String转换成Number,可以使用Number()函数,他会自动判断String中是整数还是浮点数,然后内部使用相应的数据类型,另外可以使用全局函数parseInt()和parseFloat(),他们根据你的要求进行转换。同样的,他们在解释引擎内部机制上是使用了 StringToNumber,StringToInt等等内部的函数。
  • 而如果是Double转换成Int,必须使用Math.floor()函数(截尾取整)或者Math.round()(四舍五入)。使用parseInt只能增加运算量,先从Double到String再到Int
  • Int转换成Double,无须考虑任何问题,直接把Int当成Double进行运算

注:NumberString函数是特殊的函数,在JS引擎中,他会自动判断是作为构造函数调用还是普通调用,所以既可以使用new关键字,也可以作为函数直接调用。

关于JS的参考手册,微软有一个CHM十分不错,指南、API参考都有,是Windows脚本技术,中文版,我在MSDN上下载的。而关于JS解释引擎的,我参考的是Netscape的Spidermonkey,现在由Mozilla组织维护。

让人无语的标示符名起法

同学今天做软件测试,让我帮他看代码,代码说是以前研究生留下来的……
我一看到他的变量名,我就无语了,什么yuju, denglu……
怎么用拼音呢??反而增加了理解的难度。我也看到很多人说,用拼音做标示符是很差的习惯……
其实,直接输入中文不就行了么……

Java也不是不可以用中文作标示符,他支持Unicode….
如果只是想让中国人看,那还是用中文吧,或者写在注释里面