2009年12月26日星期六

在 Heroku 上面架设 Redmine

如何在 Heroku 上架设 Redmine,可以参见这篇:如何在Heroku上架設免費、開放源碼的專案管理軟體:Redmine。这里补充几点我在尝试中遇到的问题和解决办法。

1. 基本环境构建

这里是 Ubuntu 的例子:

sudo apt-get install subversion git-core
确保 SVN 和 Git 已被安装。

sudo apt-get install ruby ruby-dev rubygems sqlite3 libsqlte3-dev rake
sudo gem install rails sqlite3-ruby heroku
部署 RoR 环境。


2. 创建数据库。

文中的 rake db:migrate RAILS_ENV="demo" 改为 rake db:migrate RAILS_ENV="production",而在此之前,需要对 config/database.yml 进行修改(假设你使用的是 SQLite 储存数据):
production:
  adapter: sqlite3
  database: db/test.db

然后:
rake db:migrate RAILS_ENV="production"
rake redmine:load_default_data RAILS_ENV="production"


3. RSA

安装: sudo apt-get install openssh-client

生成: ssh-keygen -t dsa

保存的位置是 ~/.ssh/id_dsa


4. 测试

Redmine 中内置了一个简单的 HTTP Server: ruby script/server webrick -e production

标签: ,

2009年12月24日星期四

Note on Non-Blocking Scripts Loading on Desktop Browsers

The presentation: http://stevesouders.com/docs/velocity-20090622.ppt

Now we have six ways to do loading Scripts without blocking, but not all work on all browsers, and not all work as we need.

XHR Eval

var xhrObj = getXHRObject();
xhrObj.onreadystatechange=function()
{
    if (xhrObj.readyState != 4) return;
    eval(xhrObj.responseText);
}

Script must have same domain as main page, and script must to be refactored.

XHR Injection

var xhrObj = getXHRObject();
xhrObj.onreadystatechange=function()
{
    if (xhrObj.readyState != 4) return;
    var se = document.createElement('script');
    document.getElementsByTagName('head')[0].appendChild(se);
    se.text = xhrObj.responseText;
}
xhrObj.open('GET', 'A.js', true);
xhrObj.send('');

Script must have same domain as main page.

Script in iFrame

Create a page contains the script, like A.html contains A.js, than modify your main page and add iframe like:

<iframe src="A.html" width="0" height="0" frameborder="0" id="frame1"></iframe>

But iframe must have same domain as main page, and your scripts in both main page and iframe have to refactor, though the functions in A.js exist in iframe, not main page:

// Access iframe from main page: call function createNewDiv()
window.frames<sup><a href="#fn0">0</a></sup>.createNewDiv();

// Access main page from iframe: add a div element to main page
parent.document.createElement('div');

Script DOM Element

var se document.createElement('script');
se.src = 'http://anydomain.com/A.js';
document.getElementsByTagName('head')[0].appendChild(se);
Script and main page domains can differ, and no need to refactor JavaScript.

Script Defer

<script defer="defer" src="A.js"></script>

In this trick script and main page domains can differ, no need refactor JavaScript. But only supported in IE, now landed in FF 3.1.

document.write Script Tag


document.write('<script type="text/javascript" src="A.js"></script>');

Parallel downloads for scripts, nothing else. But all document.writes must be in same script block, parallelization only works in IE.

We must ensure scripts ordered execution, cause some scripts have dependencies.

We must avoid scripts ordered execution, that make faster execution.

But when inline scripts dependencies on scripts file? Script DOM Element works only on FF and Opera. Five techniques cames:
  • Hardcoded callback in the end of script. Not very flexible, does’t work for 3rd party scripts.
  • Window onload event. Must use async technique that blocks onload event to ensure scripts loaded before onload event have been fired. Script in iframe does this across most browsers.
  • Timer. Not smart enough, load if interval too low, delay if too high.
  • Degrading script tags. From John Resig, but doesn’t work for 3rd party scripts.
  • Script onload event. That’s what we want.
Example for script onload:

var init = function ()
{
    // do something....
}
var se = document.createElement('script');
se.src = 'some.js';
se.onloadDone = false;
se.onload = function ()
{
    if (!se.onloadDone)
    {
        init();
    }
    se.onloadDone = true;
}
se.onreadystatechange = function ()
{
    if ('loaded' === se.readyState)
    {
        if (!se.onloadDone)
        {
            init();
        }
    }
}
document.getElementsByTagName('head')[0].appendChild(se);

What about multiple scripts that depend on each other, and inline code that depends on the scripts? We got two solutions: Managed XHR, DOM Element and Doc Write.

Managed XHR: base XHR Injection than maintains a queue additional, than inline code runs after all scripts in queue load finished. This solution works fine at all browsers, but all scripts must same domain as main page. See example in the presentation page 48 – 50.

DOM Element and Doc Write: this solution depends on browser. Use Script DOM Element for Firefox and Opera, document.write for IE. But no benefit for Safari and Chrome, they don’t load scripts in parallel, rely on Safari 4 and Chrome 2. When scripts same domain, order preserved, no blocking (on all browsers). If scripts on different domain, all browsers order preserved, all browsers load scripts in parallel except Safari 3 and Chrome 1, and load script and image in parallel only on Firefox, Safari 4 and Chrome 2.

best to move inline scripts above stylesheets or below other resources

Because browsers download stylesheets in parallel with other resources that follow unless the stylesheet is followed by an inline script.

Positioning inline scripts

  • Remember inline scripts carry a cost.
  • Avoid long-executing inline scripts.
  • Don’t put inline scripts between stylesheets and other resources.

标签: , ,

为什么至今中国大陆用户还在使用 IE6?

  1. 组装机。这部分机器几乎都会安装盗版 Windows XP。
  2. 网吧。在多个机器上安装系统的方法,最广为被人使用的手段是 Ghost;中国大陆的网吧大部分都会预装大量的游戏,而对游戏兼容性最好的还是 Windows XP。
  3. 部分二手电脑用户。基于机器性能问题,Windows XP 是最为合适的操作系统,也相对容易使用。
上面三条都说到了 Windows XP。而安装 Windows XP 的方法也很简单:Ghost 版 Windows XP 光盘镜像,或者自定义化后、更便于使用的 Windows XP 安装光盘,在网上随处可得,不会因为捉了“番茄家园”的制作者而消失,但会因为这个事件而开始销声匿迹。而大量遗留在网上的这类光盘镜像,都已经广为被人使用,而其中的浏览器都是 IE6,或者 IE7,预装系统中带有 IE8 光盘镜像很少很少,甚至不被考虑。

同时,在国内也有很多基于 IE 内核的浏览器:傲游,360浏览器,腾讯TT,绿色浏览器,等等。这些都只是让用户获得更好的使用体验,并不在乎背后调用的是 IE6、IE7 或是 IE8。

如果要普及 IE8:
  1. 市场上出现大量自动安装 IE8 的 Ghost 版 Windows XP 光盘镜像和自定义化后、更便于使用的 Windows XP 系统光盘镜像。
  2. 基于 IE 内核的浏览器强制用户升级到 IE8。

或者上面说得有些乱,而这结论也只是基于我对周围那些计算机用户的观察,一家之谈,仅供参考。

标签:

柴静眼中的陈虻

由柴静最近一篇写到陈虻的文章,再找到此前她写的另一篇写陈虻的文章。

“我把它放在一个医学家面前,我说请你给我写三千字,他说行,你等着吧,他肯定写尼古丁含量,几支烟的焦油就可以毒死一只小老鼠,吸烟的人肺癌的发病率,是不吸烟人的多少倍,吸烟如何危害健康。还是这盒烟,我把他拿给一个搞美术设计的人,我说哥们请你写三千字,那哥们给你写,这个设计的颜色,它的民族化的特点,它的标识写出来。我给一个经济学家,他告诉你,烟草是国家税收的大户,如果全不吸烟的话,影响经济发展,还有烟草走私对经济影响。”

“你有自己认识事物的座标系吗?有几个?”——(我)最怕的该是没有自己认识事物的坐标系。以前的我铁定没有这东西,可以被网上的文字忽悠得人云亦云;做笔记的现今也不确定有这东西,让自己欣慰的进步是,可以同时接受观点对立的两个消息,尽管接受了其中一个而不接受另外一个。看黑泽明的《虾蟆的油》,摘下了里面的一句话,前几天翻笔记又碰见了,今天又想起来了:

我那时还年轻,对于这一点既不满,也深感不安,于是焦急也强迫自己要有自己的想法。……事情就是这样,很多人年轻时显示欲过强,这样反倒失去了自己。

“思想、你、你妈这是三个东西,现在你妈看不懂,这是铁定的事实,到底是这思想错了,还是你妈的水平太低,还是你没把这思想表达清楚?我告诉你,你妈是上帝,不会错。思想本身也不会错,是你错了,是你在叙述这个思想的时候,叙述的节奏、信息的密度和它的影像化程度没处理好,所以思想没有被传递。”

“结论是简单的,关系是深刻的。”
无论节目还是程序。话说,以英文来说,都是 program? 那么整个程序的追求,似乎就出来了?

“你必须退让的时候,就必须退让。但在你必须选择机会前进的时候,必须前进。这是一种火候的拿捏,需要对自己的终极目标非常清醒,非常冷静,对支撑这种目标的理念非常清醒,非常冷静。只有你非常清楚地知道你的靶子在哪儿,退到一环,甚至脱靶都没有关系。环境需要你脱靶的时候,你可以脱靶,这就是运作的策略,但你不能失去自己的目标。”

“宽容的基础是理解,你理解么?”


- 柴静,我以为我失去了他,但是没有
- 柴静,陈虻不死

标签: , , ,

2009年12月16日星期三

前端优化笔记

注:内容主要来自于日常阅读中读到的一些前端优化相关的文章,或者有所遗漏。


载入一个页面,分为三个阶段:浏览器打开链接并发送请求,服务器作出响应、客户端开始接收数据直至服务器端的应答完成,浏览器完成页面的渲染。



浏览器打开链接并发送请求


减少 DNS 查询次数

  1. 优化使用域名及子域名的数量。在 Let's make the web faster 的系列文章中1,Google 推荐使用一个主域名,辅以四个域名用于平行下载可缓存的资源。但不够六个资源时,最好不要使用超过一个域名;每个域名分配的资源不足两个显得十分浪费。最好不使用超过五个域名。
  2. 尽可能使用 URL 路径而非域名:例子是,http://example.com/i-am-a-page.html/i-am-a-page.html
  3. 对于经常重复引用的文件,尽量处于同一个域名之下,例如脚本、样式文件,这样可以充分利用 DNS 的查询缓存。
  4. 对于 DNS 解析结果,IE 缓存 30 分钟,可以通过注册表中的 DnsCacheTimeout 来设置;Firefox 缓存 1 分钟,修改 about:config 中的 network.dnsCacheExpiration 可以更改默认值。

避免重定向的请求

发送重定向的请求有数种方式,服务器端返回 30x 并指定重定向地址,通过 meta 标签写 refresh 进行重定向,JavaScript 实现的重定向。无论哪种重定向,对 mobile 版本的网站更为致命。
对于 301/302 重定向,似乎更多是出于 SEO 的考虑,对此,应该考虑使用 Cononical Meta Tag


使用 GET 来完成 AJAX 请求

在 AJAX 中使用 GET 请求的原因是,GET 请求很多时候只占用一个 TCP 包,而对 URL 长度最为刻薄的 IE 也只是限制 URL 在 2000 字符以内;而 POST 请求则是,先发送响应头,再发送响应中的数据。


每一个请求都会包含该域名下的 cookie,怎么说也是一个不小的负担。谨慎地使用 cookie,减少 cookie 的数量和大小并不是一个坏主意。


根据浏览器的同源原则,向同一个域名的请求总会包含该域名下的 cookie,对于静态文件来说,cookie 是不必要的。除了最大化平衡下载页面中的元素以外,无 cookie 的请求也是为静态文件启用不同域名的理由。

另外参见 使用CDN



服务器作出应答,客户端开始接收数据


开启 HTTP 服务器中的 keepalives 选项

为免受限于浏览器全局的服务器连接数,keepalives 的超时时间可以考虑设得更短,如 5 - 10 秒。而静态文件和动态文件最好分开存在于不同的服务器;当近千个请求发送向静态文件服务器时或许仅是消耗了 10 M 内存,而你的动态文件服务器很轻易地就为每一个请求消耗 10M 内存了。


缓存 AJAX 请求的结果

AJAX 请求也是一个 HTTP 请求,即是说,是可以缓存的,expires/last-modified/cache-control/gzip 都可以用上。另一方面,对 IE 来说,对于指定了 cache-control/expires 的 AJAX 请求,在过期以前是不会重新向服务器发出请求,即使你使用 Ctrl+R 强制刷新页面。参见:Ajax Caching: Two Important Facts


将样式文件的引用放到页面的顶部

将样式文件的引用放在页面的头部,这样页面渲染将会是渐进式的;此外,CSS 文件是并行下载的;浏览器即使还没有完整载入页面,也会发起新的请求下载 CSS 文件。


使用 <link> 而非 @import 引用样式文件

对于 IE 来说,@import 的行为就等于把样式文件放在页面的后面;而这显然不是我们所期望的。


将脚本文件的引用放到页面的底部

和样式文件不同,默认情况下,脚本文件的下载并非并行模式,无论是否属于相同的域名源,载入脚本文件时总会阻止其他文件的并行下载。并行下载脚本的实现,见 Downloading JavaScript Files in Parallel

对于已经支持 HTML5 的浏览器,在 script 加上 defer 标签亦有同样的效果

最后,避免重复载入同一个脚本


尽早让页面离开缓存

这里的缓存指的是 Buffer。在 PHP 中就是在使用 ob_start() 之余,灵活地放置一些 flush()。例如,在页面头确定以后来一次 flush()。这也是实现渐进式渲染中的一个关键。


优化 CSS sprites

在 sprites 中水平地排放图片比垂直地排放图片更能减少生成图片的体积;而将相近颜色的图片合并为 sprites 可以让图片使用的颜色数目更少,甚至一张 256 色的 PNG8 也能满足需要;为了对移动设备更友好,不要在图片之间留太多的空隙,这对图片大小没有太多的影响,但对于客户端来说,这会是使用更少的内存。


避免通过 HTML 或者 CSS 代码修改图片大小

例如,在 <img> 标签中以 width 和 height 重定义图片大小并不是好主意,一张 500x500 的图片并不因此而缩减了大小。


缩减页面以及各个文件的体积

  1. 使用 Gzip 压缩页面。这是最早被提出的优化手段之一。往响应头里面加上 Accept-Encoding: gzip, deflate,然后使用 deflate 的方式压缩(apache 2.x 的 mod_deflateNginx 中的 Http Gzip Module)。
    另外,脚本以及样式文件也推荐使用 Gzip 压缩。
  2. 对于 1KB 以下体积的文件,GZip 压缩的效果并不好。
  3. 压缩脚本和样式文件。对于精简脚本文件,推荐 Google 的 Closure-Compiler;而样式文件可以使用 YUI Compressor 或者 cssmin.js 进行处理。
  4. 对图片进行优化:选择更合适的图片格式,减少图片的体积。
  5. 对于图片来说,开启 Gzip 的作用不大。
  6. 设置一个体积很小的、可缓存的 favicon.ico。无论存在与否,浏览器都会向服务器请求 favicon.ico 这个文件。为了避免返回 404,最好还是让这个小图标存在,并确保:足够小的体积,甚至在 1kb 以下;设置一个遥遥无期的过期时间。
  7. 对于页面来说,100KB 以下的页面体积也属于一个 SEO 优化手段。
  8. 这主要是针对 iPhone 而非其他移动设备的优化;iPhone 仅能缓存不大于 25 KB 的单个文件,并且这是解压缩后的大小;对于一个页面的所有文件,iPhone 也仅是提供 475 - 500 KB 的缓存;更甚,页面之间是不共享缓存的,即使它们使用了同一个文件。或者对于 iPhone 来说,嵌入的样式和脚本值得考虑。

使用 HTML52

  1. 更短的 DTD: <!DOCTYPE html>
  2. 编码:<meta charset="utf-8">
  3. 对于 style 和 script 标签来说,type 属性可以忽略。
  4. 针对脚本载入而制定的新属性 async 和 defer。

优化服务器端的程序架构

好的架构有更良好的页面生成速度。对于 PHP,推荐阅读 The No-Framework PHP MVC Framework



浏览器完成页面的渲染


针对这一部分的优化手段较多,来自 Yahoo! 的 14 条优化准测3几乎都是针对这一部分的优化。而在页面渲染的过程中,会继续向服务器的发送请求(脚本、样式文件、图片等等),对于发送请求的优化见第一部分。


数量更少的请求

对于浏览器来说,读取两个小文件比读取相同大小的一个大文件更耗费时间。

  1. 将多个脚本、样式文件合并为一个。实际上读取两个小文件比读取等同大小的一个大文件更耗费时间。
  2. CSS Sprites。优化页面中的背景图片,将多个背景图片合成一个图片。参见 A List Apart 的文章:CSS Sprites: Image Slicing’s Kiss of DeathCSS Sprites2 - It’s JavaScript Time
  3. Data:URL

使用多个域名/子域名提高下载速度

子域名的启用可以让页面元素尽可能地平行化下载。例如,启用 static.yourdomain.com 为站点静态内容的域名,甚至更短的域名:ydcdn.cn。而利用多个域名/子域名优化的时候,需要考虑 DNS 的因素


减少 iframe 的数量

iframe 等同于向服务器发送一个新的页面请求。事实上为什么要使用 iframe?


消灭 404 页面

这一点是基于用户体验出发的;当然,设计出更有用的 404 页面也是一个挑战。;)


使用 CDN

对于 CDN,如果展开了,似乎能说的有很多。首先看这篇:Amazon CloudFront vs. Rackspace Cloud Files CDN Performance。而使用 CDN 服务也得讲究性能:CDN 服务器的 uptime、DNS 解析速度、单包延迟 (single-packet latency)、丢包率等等(Understanding CDN Performance)。

对于国内的 CDN 服务,甚至说,要对 CDN 有一个更全面的认识,请看这篇:看上去很美——国内CDN现状与美国对比


设置 expires 以及 cache-control 响应头

这是最早被提出的优化手段之一。推荐给静态文件设置一个牛年马月的过期日期,然后通过版本化的方式来更新文件。对于动态文件,则是通过 Cache-Control 这个响应头来控制缓存 4

这里对 Cache-Control 常见的值进行一些解释。

  • no-cache - 每一次强迫请求项目的缓存时,向源服务器验证缓存的可用性。
  • no-store - 标记任何时间都不建立缓存。
  • max-age=[seconds] - 指定缓存的存活时间。与 expires 响应头相似,但这个存活时间是相对于请求发出时的相对时间,并非绝对时间。[seconds] 为请求发出后、你期望缓存存活的秒数。
  • max-stale[=senconds] - 标记响应过期后还允许存活的秒数,若不指定一个确切的时间,则是表明缓存任何时候都是有效的。
  • s-maxage=[seconds] - 与 max-age 响应头的作用相近,但仅作用于分享缓存(如,代理缓存)。
  • public - 标记验证过后的响应为可缓存的;一般情况下,如果 HTTP 验证是必需的,那么响应自动标记为不可缓存的(对于分享缓存来说)。
  • private - 标记响应不能被可分享的缓存系统缓存;而私有缓存或许会缓存这个响应。
  • must-revalidate - 指定缓存必须严格遵守指定的存活时间信息。在特殊条件下,过期的响应是可以接受的;但这个响应标明,缓存必须遵守你指定的缓存规则。
  • proxy-revalidate - 于 proxy-revalidate 相似,但仅作用于代理缓存。

设置 Etags 或 Last-Modified 响应头

只能说,使用 Last-Modified 比使用 ETags 有更多的优势。这里得和 expires 和 cache-control 这两个响应头配合使用。


推迟下载页面中相对不那么重要的元素

将页面中的元素分解、剖析,那些相对不那么重要的元素可以暂缓下载和渲染,例如实现拖动和动画的脚本,无关大雅的图片或广告等等。这也是实现渐进式渲染的一个要素。


预读取部分页面元素

同样是实现渐进式渲染的一个步骤,但这一建议是将下一个页面要用到的东西提前缓存。除了有效地利用响应头(设置 expires 以及 cache-control 响应头设置 Etags 或 Last-Modified 响应头)以外,HTML5 的 client-side storage 亦在考虑之列。


减少 DOM 节点数量

另一种表述方式是,精简页面的 HTML 架构。一个反面学习教材是 Drupal,它的 div 嵌套太多了。


减少对 DOM 进行的操作

访问 DOM 节点很慢;缓存引用 DOM 的变量,让 DOM 脱离文档树进行更新,以及避免使用脚本为 DOM 修改布局,都属于作为减少访问 DOM 节点而衍生的手段。

在进行例如动画这般的操作之前,尽可能把元素从页面流中拖出,然后使用绝对定位来定位。


减少不必要的 DOM 嵌套深度

由于对 DOM 进行的每一次操作,都可能会造成目标元素的重新渲染(reflow),因此,在对 DOM 进行操作时,尽量通过修改 class 完成样式的替换,在进行大量操作之前将其从页面正常的流中拖出,而同理加入一个元素到页面之前、先完成对该元素的操作,也尽可能把多个对页面内的 DOM 操作简化,例如合并 DOM 操作,创建一个 DocumentFragment 对象缓存多个未插入页面的DOM。


更合理地为 DOM 节点注册事件

一个常见的手段是 event delegation,假设你有 10 个按钮并且使用一个 div 节点包括,为 div 节点注册事件比为 10 个按钮各自注册事件更好。相对于浏览器的 onload 事件,在 DOMContentLoaded 事件发生后进行对 DOM 的操作更佳,后者标明页面的所有 DOM 节点已经准备就绪,而前者还需等待其他元素、例如图片的载入完成。


优化脚本运行速度

这是直接能让用户体验到的。糟糕的脚本甚至能让浏览器假死、崩溃。Google 在 Let's make the web faster 系列中有一篇针对 JavaScript 优化 的文章。


避免使用 CSS expressions

CSS expressions 是 IE 的产物,或者对于 IE 实在没有好感的前端开发者来说,是能够轻易做到的。


避免使用 filters

又一个 IE 产物。最为常用的就是 AlphaImageLoader,用于修改 IE7 以前版本的透明 png 文件。filter 的坏处是,在图片下载时,会阻碍页面的渲染进程以及让浏览器假死,也会增加每一个元素对内存的需求。对于透明 png,更好的方案是使用 gracefully degrading PNG8。


优化样式文件

  • 减少样式的重复定义。例如,
    h1 { color: black; } p { color: black; } 改为 h1, p { color: black; }
  • 适当的使用 !important 有很好的效果。
  • 简化样式定义。去掉无用的样式定义。
  • 合理地分割和合并样式表:Google 的建议是分割为更小的文件,仅为特定页面定制的样式独立一个小文件。
  • 若非在页面载入时就使用到的样式,独立放置到一个样式表中,然后推迟这个样式表在 onload 事件以后载入。
  • 假使你需要用到脚本为页面更改样式,要确保这些代码在未需使用之时不会对页面应用这些样式。
  • 精简定义中的样式选择器。
  • 去掉不必要的样式定义。

书写 HTML 时的奇技淫巧

  • 让相近样式定义之间的定义语句书写排序尽可能地一致,如按照字母表排序(原话:“Specify CSS key-value pairs in the same order where possible, i.e. alphabetize them”,来自于 Google 的 Let's Make the Web Faster 系列文章5)。
  • 让 html 代码中属性值的书写顺序尽可能地一致,例如按照字母表排序。Google 说,在他们的搜索结果页里所有链接的属性按照字母表顺序排序书写,Gzip 压缩时能减少 1.5% 的体积。
  • 保持一致的字母大小写。
  • 在 html 属性的书写中保持一致的引号风格,一致地使用单引号、双引号、或者甚至不使用任何引号。

不在页面内嵌入脚本以及样式

把样式和脚本分别独立成一个文件的好处不需再说。值得注意的是,嵌入的样式和脚本并非都是坏事,适当的使用有想不到的妙用。例如,平行下载脚本文件的方法就是在页面嵌入一段脚本。


渐进式渲染

更快的页面载入应该首先渲染用户可见区域的内容,然后渲染目前不可见的页面区域;其次,先载入和渲染轻量级的资源、如文字,然后载入和渲染如图片和视频那般的重量级资源。此外,使用表格布局、把样式表放置到页面底部都会致使部分浏览器的渐进式渲染失效。


为移动设备进行优化

  • 创建和提供移动版本,使用习惯上指向移动版本的 URL:m.example.com, wap.example.com, mobile.example.com
  • 为移动设备创建合适的界面。
  • 减少浏览时产生请求的数量和数据传输量。
  • 尽可能地启用那些新功能:程序缓存(application cache),CSS3。
  • 兼容更多常见的设备:不要嵌入 flash,慎重使用 JavaScript。

其他


测试

  • 经常在苛刻的网络条件下访问你的站点;这样能够更快地发现问题。Linux 2.6 内核中的 netem 和 HTB 模块,都可以结合命令行工具 tc 架设一个慢速网络代理。此外的工具包括,Firefox 的 Tamper Data,windows 下的 Fiddler,Mac OSX 中的 Charles。
  • 在内网/开发环境中对页面进行压力测试。
  • 建议用户为他/她的浏览器开启“流水作业”特性。例如 Firefox,在地址栏中输入 about:config,把 network.http.pipelining 设为 TRUE。

扩展阅读

  1. Let's make the web faster.
  2. Using HTML 5 for performance improvements.
  3. Best Practices for Speeding Up Your Web Site.
  4. Steve Souders, In Search of Speed: slides, zip, video.
  5. Improving Netflix Performance @ Velocity 2008.
  6. Aaron Hopkins, Optimizing Page Load Time.
  7. SproutCore Blog, How SproutCore Makes Your App Run Faster.
  8. Caching Tutorial for Web Authors and Webmasters.

标签: ,

2009年12月2日星期三

终极问题

看了 @ashchan 的『图书馆里的自由枪骑兵』,仿似闻到自由的气息,又似是找到了以后生活的样子。虽然到 @ashchan 的 Twitter 上,看到其提及 freelancer 的几个品质:懒惰,不爱束缚,不怕(没钱)喝西北风,偶尔善吹嘘(忽悠?)。外部条件:父母理解不会说你不求上进,老婆不会逼你买房。

不说外部条件。懒惰我是够可以的了,束缚我也受不了。没钱这点实在难受,尤其在大学以后,支出是指数级上升;我可以直白地承认我需要钱,虽然我希望过上梭罗那般的简单生活。不过另一方面说来,现在不可能再让你开荒自食其力,梭罗那种隐居生活已经接近幻想;倒是可以做点其他。例如从《全球通史》中得知,千万年前的石器时代,由于人均土地面积较大,每天只需花数小时觅食即可,每周的工作时间甚低,更多时间花费在玩乐上了。

这样也有另一个问题:理想。我想创建一个能改变人们生活的网站(或者程序?)。那么我就需要一个惊天地泣鬼神的想法,然后付诸实现。然而前一条是难以寻觅的,现实首先迫使你去面对。当然现在有另一个选择,创业。这点很不错,至少工作是不需要用你的学历/文凭来交换。而细节上还是有很多问题,至今我或许对人生思考了无数次,有了无数个答案,又推翻了无数个答案。若是小学作文书中那些范文作者,写着自己的理想,他们早就完成了对人生的安排,而我的答案至今未见。不过这样也好,人生之意义到成为我现在的终极问题了。

开支是迫使工作的一个缘由;而我发现我最大的开支,除去书和数码用品,似乎都花在吃喝玩乐上面了。或者一个人要象一个队伍:适应孤独,就像适应一种残疾。在踏入大学校门之前我就想着要孤独起来,就像住进了 Sputnik。我知道某些事情是在消耗我的生命力,不做就像空虚填满了全身。或者真的需要真正的绝望。

最近在看张五常的《经济解释》,看了就是觉得需要继续改进自己的思考能力。某次强迫症地想买《可乐牛奶经济学》给人看——那本也算是解释经济现象的书,我还没有看——最后没有成事,总不能强制他人爱上自己的喜好吧。最后自己买了一本英文的《Freakonomic》,有空才看。

不过有空这词,实在很微妙。我太多太多的东西没看,太多太多的东西没做,那么相对来说就是没有时间,那么就是没有空,或者需要的只是重新思考,给排一个优先级;那么优先级应该是按照什么定义呢,自己的喜好,完成任务所需的时间,还是任务的紧迫程度?然后做这些东西,真的接近了自己所想的了吗?对自己来说,终极问题的答案是什么?引一段话描述自己在不断绕的圈子好了:
對哲學家來說,一般人並不是真的「知道」他們宣稱他們知道的那些事情,他們只是把自己覺得荒謬而又無法反駁的東西掃到地毯下面不去面對。哲學家比較保守︰ 要合理地支持一個主張,忠實地相信它為真還不夠,你還得確定自己能夠對於檯面上所有反對這個主張的說法給出有道理的回應,而這類有道理的回應,當然不能是 「這個說法的結論很怪」或是「我就是不相信啦」。因此,哲學家認真地研究那些看起來挺有道理,結論卻跟大家相信的完全不同的論證,企圖找出它們的問題。
我不是哲学家,我却需要学习哲学来寻找我的答案。

还是继续思考我的终极问题好了。

标签:

Quote @ Dec. 2ed, 2009

經濟分析中的邊際生產理論指出,任何生產要素擁有者對生意額作出的貢獻會反映在他的收入上。

标签: ,

程序员应有的小习惯?

一个 ASP.NET/C# 程序员写了一篇东西,总结自己在五年职业生涯中的错误:没有使用适合的 ORM,没有及时学习和应用 Generics,重复制造轮子,过度编写文档,没有自动化打包,过度依赖视觉检查后进行除错,没有进行单元测试。一共七点。

对于 ORM 的争论似乎从来没有停止过;对于我这个只有四年非专业程序员来说,ORM 带来的性能问题是致命的,特别是对于 PHP 来说。或者这应该分别而论:对于类似 PHP 这般无状态、运行时编译的语言来说,我觉得不宜用 ORM。或者到了 N 台高性能服务器专门运行 PHP 线程、数据库已经分离至独立的服务器(群)中运行、大规模地部署缓存——这个时候 ORM 就不是瓶颈了;但我还是喜欢不来 ORM。或者我的程序员年龄到五岁的时候,我会喜欢上 ORM?

第二点说到 Generics,简单看了一下说明,指的是 C# 2.0 中的一个无类型数据结构。对于 PHP/Python/JavaScript 来说,三者都是无类型的;因此强类型的数组造成的复杂性我只能通过想象来了解它的复杂性。这里了解不多,该住嘴。

重复制造轮子,过度编写文档,没有自动化打包,没有进行单元测试,这四点都是常年讨论的问题了。

至于“过度依赖视觉检查后进行除错”——这里我自觉翻译得不好,原文是“rely on visual inspection and debugging too much”,大概指的就是因为模块之间的依赖性太大,无法分开测试;而整体编译后再通过输出报错到界面来检查错误是很高成本的。这一点恰恰和是否单元测试组成了一个很有趣的问题。

这文看看就好了;要检查自己写代码中的习惯,就有很多书和很多文章在讨论了;如果没有实践过,其中的选择还是各自的观点吧。

标签:

2009年12月1日星期二

Python 中多个 decorator class 的属性丢失问题

Stack Overflow 上有人问到在 Python 中,为一个函数指定多个 decorator class 时出现的属性丢失问题。其下就有人贴出了答案,在 decorator class 的 __init__ 中进行属性的复制:

class NullDecl (object):
    def __init__ (self, func):
        self.func = func

        for n in set(dir(func)) - set(dir(self)):
            setattr(self, n, getattr(func, n))
    def __call__ (self, * args):
        return self.func (*args)

    def __repr__(self):
        return self.func


上面贴出的这个答案,最后的 __repr__ 是提问者自己补充的,用于 debug。上述属性的复制看起来真像在 JavaScript 中实现继承的法子。 XD

标签: ,

新浪博客的优化经验

一个多月前的文章,新浪博客的优化经验:新版博客宣讲会【第五讲】:体验双倍提速博客生活

公开说明做了的数据库层优化有三个。第一个是对位运算的应用,利用位运算来优化二选一的属性判断; @xmpp 老师曾在 Twitter向他人解释这点优化,我感觉这个技巧主要是利用了位运算的速度优势,但实际操作可能需要做点实验,例如在 blob/char 之间进行选择,以及索引添加与否的影响。其次是优化 Slave Server,由于查询需要满足大量条件,而不在 Slave Server 镜像大字段来缩减 Slave Server 上数据的体积,也间接优化了查询速度。最后就是把 Slave Server 按照需要进行设置,例如 Slave A 针对应用 M 重新设置索引,Slave B 则是针对应用 N 设置索引,然后把来自 M 的查询固定导向到 A、把来自 N 的查询固定导向到 B,一个分库的小技巧。

接下来是对前端的优化。由于可以自定义样式和显示效果,每个用户的界面几乎都是独一无二的。这里说后端是 MDB,我不清楚这里说的 MDB 指的是 Microsoft Access。由于页面是 RIA 的设计(Rich Interface Application),静态页面缓存了,但大量动态内容是在静态页面载入后载入的,动态内容的载入速度受到程序和设计上限制。这里的优化,其实也是很常见的优化手段:把 JavaScript 中修改页面样式的代码改为修改元素属性,把动态修改样式改为通过修改元素的属性(通常是 class)完成,而把样式定义输出到一个可缓存的 CSS 文件。实际上这也是 JavaScript 的优化:由于样式修改操作变为属性修改操作,reflow 的次数减低了,对于用户来说又是一个大大的体验优化,Google 的 Let's make the web faster 系列文章中介绍过通过 DOM 减少 reflow 的这个技巧,不过就是没有说到这样也间接避免了出现以前新浪博客常见的白屏现象。

不够他们的总结写得不好,看起来就是容易看晕,不知道在说什么。

标签: , ,

九个常见的 IE Bugs

这篇东西算是对 IE Bugs 的总结和回顾;这些 bugs 大部分都存在于 IE6 当中。
  1. 元素居中。这个 bugs 是最为著名的 bug 了,解决办法是,为元素所在的容器(父元素添上 text-align: center。
  2. 阶梯式显示列表元素。用 Zen Coding 的语法写出现这个的 HTML 代码是:ul>li*n>a,n > 2。两个解决办法:将 li 设为浮动元素,后者改为 display: inline。
  3. 浮动元素的双倍边距。增加 display: inline 即可解决。
  4. 无法给元素设置数值较小的高度。原因或者是 IE 假定每个元素中都是有文字的,当然这是无责任的推测,推断自解决方案:一个是设定 font-size: 0,另一个是加上 overflow: hidden。
  5. 可翻页容器中,以相对位置定位的子元素显示错位。继续以 Zen Coding 说明 HTML 的关系: div#element>div#anotherelement。#element 限定高度并设置 overflow 为 auto;#anotherelement 是相对定位,但高度值比其父元素 #element 要大。解决办法就是把父元素 #element 也设置为相对定位。
  6. 固定定位元素的宽度问题。问题缘由于 padding 的设置;为固定定位元素设置 padding,其设定值也被计算到宽度中去,解决办法是 IE hacks。
  7. 为元素设定最小高度和最小宽度。方案一,通过 !important 来达到目的:设定最小值,然后声明为 auto 并添上 !important 的标记,最后设置一个值:
        #element {
            min-height: 150px;
            height: auto !important;
            height: 150px;
        }

    方案二,利用选择符的实现程度做 IE hacks。
  8. 固定宽度浮动元素的宽度异常。解决办法是设置 overflow: hidden。
  9. 列表元素之间的空白。与上述第二条的 HTML 架构一模一样:ul>li*n>a,n > 2。方案一是为 a 元素设置一个固定宽度,方案二是设置 a 元素为浮动并添上 clear: left,方案三是设置 li 为 display: inline。
总结是,IE sucks。大部分问题都是盒子模型的理解问题,解决方案都是那几个突破口。不过,针对 IE 实现的 IE hacks 最好还是用<--[if lt IE 7]><[endif]--> 来插入额外的样式表,IE hacks 对其他浏览器来说也算是一个异数。

标签: ,

2009年11月27日星期五

Quote: Embodiment 6

情感是认知中不可分割而又必不可少的一部分。我们所做、所想的一切都伴随着情感,大部分情况我们都意识不到这一点。另一方面,情感影响着我们的想法,不断引导我们感受一切(and serve as constant guides to appreciate behavior),引导我们走出困苦,迈向美好。

然而,我们在谈论产品可用性的时候,是不能把美学和感知从行为中分离出来。更重要的是,行为不仅是我们做什么,更是让我们感知什么——在交往和产品中直接展现情感。

后一段的原文在这里,感觉翻译得还是不太畅顺:However, the products we talk about usability, aesthetics and perception can't be detached from the acting. Most importantly, the action not just we do, but we make how we feel and know - the expression of emotions bearing on human communication and products directly.

在这一点,苹果领先太多了。

一个 ed2k 的 wrapper

不知道有多少人和我一样,专门有一个机器用于下载和储存;装的是 Ubuntu,通过 ssh 登录后使用 ed2k 来下载 VeryCD.com 上的资源。不过有一个问题,ed2k 只能一个一个链接地添加,复制-粘贴从来就是一个体力活,而且传递给 ed2k 的参数必须以引号包围,如 ed2k 'ed2k://some_link' 才能完成添加下载任务的工作。

为此我写了一个脚本,给 ed2k 加一个 wrapper,传递一个无需引号引用、 VeryCD.com 上任意页面的网址,然后就自动把该页面上所有的 ed2k links 添加到 amuled 的下载列表中。这个脚本用的是 Python,其中使用到了 BeautifulSoup 解析 HTML。后来发现 BeautifulSoup 并不好使,会出现莫名其妙的错误,就改用了 libxml2dom,事实上这个东西比 BeautifulSoup 更好使。

这个脚本也有一个问题,就是不能选择性地下载页面中的某些文件。为了解决这个问题,这天我给这个脚本修改了一下,也可以接受一个文件名作为参数;而可以通过建立一个临时的文本文件,每一行就是一个 ed2k link,保存后把文件名当作参数传递给这个脚本则可。

脚本比较简单,45行代码也不多,可以再缩减几行,不过为了可读性,就没有这般干。在使用这个脚本之前,需要确保为你的 python 装上 libxml2dom。

libxml2dom 的下载地址:http://www.boddie.org.uk/python/libxml2dom.html
脚本保存在 pastein.com 上:http://pastebin.com/f7e4b1cde

标签: , ,