之前的文章提到过本站已启用了 SPDY 协议,只要使用最新的 Firefox, Chrome, Opera 浏览器访问就是使用的 SPDY 协议。那么什么是 SPDY 协议呢?和 HTTP 相比又有什么优点呢?下面就是我自己的一些理解:

HTTP 为什么慢

下面是我认为影响比较大的原因:

  • 请求是串行的,只有等上一个请求应答接收完之后才能发送下一个请求。如果有个请求应答时间很长,则会导致后续的请求都阻塞住。就算是在有 HTTP pipelining 的时候也好不到哪儿去,因为对于客户端同时提交的多个请求,服务器应答的时候必须按照同样的顺序进行应答,因此如果某个请求的应答时间很长,同样会导致后面的应答阻塞。
  • 连接建立的开销。现在的浏览器为了避免上面提到的问题,都会采用多连接的方式,在有丢包和延迟的情况下面,建立连接代价是比较高的。而且 TCP 都会有一个慢启动的过程,而网页一般都比较小,往往这条连接还没有(或刚刚达到)最佳的速度就把内容下载完成,请求其他内容时又要重新完成慢启动的过程。

其他原因,影响应该不是那么明显:

  • 只能有客户端主动发起连接,不能服务器进行推送。
  • 头部没有压缩。

SPDY 做了什么

详细的协议介绍可以直接看其协议文档,这里简单的说一下,SPDY 在一个 TCP 流中分出了多个 SPDY 通道(协议里面叫做 stream),每个通道完成一个请求和应答,并且这些通道是并行的。这样一来就一举解决了上面提到的问题:

  • 请求在不同的通道中,并且可以并行,那么浏览器就可以一起把需要的资源请求同时发给服务器,而服务器也可以同时处理这些请求,同时将应答回复给客户端。不会出现等待每个请求或者某个应答才能继续的问题。
  • 由于一条 TCP 就能有多个通道(现有协议规定可以创建 2G 个,客户端与服务端各可创建 1G 个),因此客户端和服务器就只需要一条 TCP 连接即可,因此就不会有建立连接的开销,也没有慢启动那些的问题。
  • SPDY 的头部总是压缩的,这样可以减少传输量。
  • SPDY 的通道双方都可以创建,因此服务器可以主动推送资源,不需要被动等待客户端请求。
  • 通道在创建的时候可以指定优先级,可以让重要的请求优先处理。

SPDY 现状

新闻上说 HTTP 2.0 的第一个草案就是直接拷贝 SPDY 的协议文档,因此 SPDY 很有希望成为以后的 HTTP 2.0 标准。

不过至少在成为标准之前,SPDY 还没有自己的独有端口,那如何使用该协议呢?Google 提出了在 HTTPS 之上使用 SPDY,只需要增加一个 SSL 的扩展协议 NPN,在 SSL 协商的同时协商出上层使用的协议,这样在 SSL 握手完成后,客户端与服务器就知道上层的应用是 SPDY 还是普通 HTTP,从而使用相应的协议进行处理。这样做的好处就是对用户透明,不需要显示使用类似 spdy:// 这样的前缀去指定使用 SPDY 协议,现有的网页也不需要修改,只需要对浏览器和服务端进行升级即可。


这是我这几天看 pagespeed 的一些笔记,由于我并不是搞 web 开发的,文中的内容只是我自己的一些理解,所以可能并不一定正确。

pagespeed 是 google 出品的一款 apache 模块,其中有多达 40 种的优化手段,可以帮助提升网站的加载速度。其主要优化方法有以下几种:

  • 减少页面的大小
  • 减少浏览器的连接数量
  • 提高浏览器渲染速度

减少页面的大小

在减少页面大小方面,pagespeed 提供了以下选项:

  • Collapse Whitespace
  • Combine Heads
  • Elide Attributes
  • Minify JavaScript
  • Optimize Image
  • Remove Comments
  • Remove Quotes
  • Rewrite CSS
  • Rewrite Style Attributes
  • Trim URLS

这些选项所做的基本上就是以下几点:

  • 对于 html, css, js 文件:
    • 去掉不必要的空格
    • 去掉注释
  • 单独对于 js 文件:
    • 类似 jsmin 方法压缩 js
  • 对于图片:
    • 将小图片进行内联,目的是为了减少连接数
    • 重新压缩图片,以减少大小
    • png 转换为 jpg,jpg 转换为 webp

这每一个优化选项在开启了 gzip 压缩的服务器上面效果可能都不是很明显,不过一点一点的积累之后效果也是可以的。对于那些没有对 js 压缩过的网页,可能 Minify JavaScript 效果是最好的。

减少浏览器的连接数量

一般来说,在网页方面,减少连接数要比减少文件大小效果要好。

因为在有 gzip 压缩的情况下面,页面都是比较小的,而且能减少的大小也是很小的。有时候减少文件大小并不能减少传输的包个数,比如:4K的文件优化后为 3K,其实都需要 3 个 TCP 包才能传输完成。

而一个请求的代价则会多很多,首先,请求头部与应答头部的开销;其次,多一个请求可能会多一个 TCP 连接,新建 TCP 连接的开销。

在减少浏览器连接数上,pagespeed 提供了以下选项:

  • Combine CSS
  • Flatten CSS Imports
  • Inline CSS
  • Combine JavaScript
  • Inline JavaScript
  • Move CSS Above Scripts
  • Configuration file directive to shard domains
  • Sprite Images

看名字就能猜到其功能。

对于 css:主要就是将页面中的多个 css 合并为一个,将小的 css 文件内联到页面中,将 imports 的 css 也进行内联。

对于 js:主要也是将多个 js 合并为一个,将小的 js 文件进行内联。

对于 css 中的背景图片,Sprite Images 可以将多个背景图片合并为一个。

提高浏览器渲染速度

提高浏览器渲染速度其实是比较难的,不过 Google 自己就是做浏览器的,清楚的知道什么样的网页可以更快的进行渲染。可以使用的选项如下:

  • Convert Meta Tags
  • Defer JavaScript
  • Inline Preview Images
  • Lazyload Images
  • Move CSS to Head
  • Optimize Image
  • Rewrite Style Attributes

感觉比较有效果的是 Inline Preview Images 和 Lazyload Images。

Inline Preview Images 打开之后先加载低画质的图片,待高画质图片下载完成之后在加载高画质图片。

Lazyload Images 则只加载显示区域的图片。淘宝的页面应该就是使用类似这个的技术。

缓存改进

其实这个可以归为减少浏览器连接数,不过我还是把他单独列出来了,主要思想就是把资源尽可能的缓存在浏览器,这样浏览器访问页面的时候就直接使用本地缓存而不用向服务器发送请求了。提供的选项如下:

  • Extend Cache
  • Extend Cache PDF
  • Local Storage Cache
  • Outline CSS
  • Outline Javascripts

Extend Cache 修改资源的名称,在原有名称后面加上该资源的 hash 值,然后在资源的 http 应答头部中将缓存时间设置很长(比如 1 年),那么后续的页面如果还有引用该资源,则不会向服务器进行请求。如果资源有改动,那么 hash 值就会改变,因此浏览器会认为是不同的资源,然后发送请求获取新资源,当然引用资源的 html 页面是不能被缓存的。

Outline CSS/Javascripts 将页面中内联的 CSS/JS 进行外联,这样的好处是:CSS/JS 可以被缓存,如果多个页面用到同一个资源的话,可以节省传输量;如果 html 经常改动,那么 CSS/JS 的缓存也能减少传输量;如果 CSS/JS 很长,外联可以使 html 文件变小,可以让浏览器先加载 html 进行显示。当然外联的坏处也是显然的:增加了浏览器的请求数。


在我的 google 相册里发现了以前上传的 E17 截图,时间大概在 2005 到 2006 之间。可以看到那时候的 E17 就已经很漂亮了,最近 E17 终于发布了,贴上来怀念下以前折腾的岁月。

E17

E17

E17

E17


eyerest 是通过定时锁定屏幕,提醒用户休息的小软件。代码在 github上:https://github.com/zlbruce/eyerest,有需求的可以自行取代码编译,上面有写依赖关系。

以前用 Windows 的时候,同事介绍了一款软件 EyeFoo,能够定时提醒休息。对于我这种在电脑前一坐就是一天的人来说非常好用。可是公司电脑换 Gentoo 后就没有找到对应的软件,于是用剩余时间搞了这么一个东西,基本上满足我自己的需求了。

回头想了想,这东西比较适合我这种使用 Linux 办公的人,因为软件不会检查是否有全屏软件在运行,如果刚好时在玩游戏的话。。。不过如果只是上上网,听听音乐,看看电影什么的,这个软件还是有作用的。

为了健康,还是应该每隔一段时间站起来走两步,放松下眼睛和身体,别老是坐着。


主要是看到了换种方式使用星际译王这篇文章,里面是用的 awesome。这两天在 xmonad 折腾了一下,虽然没有像原文中那样好用,不过也算是可用了。

xmonad 没有自己的通知窗口,这里只能用 notify-send 来发送翻译结果。基本思路很简单,就是按照以下步骤来:

  1. 获取选中的文字,或者是输入的文字,这里使用 dmenu 获取用户输入
  2. 调用 sdcv 获得翻译后的文字
  3. 调用 notify-send 显示翻译结果

折腾了两天原因主要是卡在第三步上了,一共遇到两个问题。

第一个问题是只显示了一个标题,不显示翻译内容。在我手动将翻译结果一行一行的使用 notify-send 显示后,发现只要带有 < 号的都不可以;后来才知道我用的 notification-daemon 可以使用 html 式标签对显示内容进行格式化。而 sdcv 输出的结果有 html 的一些特殊字符,导致解析失败。

第二个问题是乱码,显示的中文都变成了乱码,但是在终端中手动发送中文字符是没有问题的。后来看了 xmonad-contrib 的源代码才知道我使用的 safeSpawn 函数会使用 encodeString 对输入的参数进行编码。暂时不清楚为什么会对参数编码,简单的在配置文件中定义一个 mySafeSpawn 函数搞定。

下面是配置方法,首先,引入一些模块:

import Data.Char
import XMonad.Util.XSelection
import System.Posix.Process (createSession, executeFile, forkProcess)

定义快捷键,我这里使用 modMask + s 对选中的文件进行翻译,使用 modMask + Shift + s 键弹出 dmenu,对输入的文字进行翻译:

...
myKeys conf@(XConfig {XMonad.modMask = modm}) = M.fromList $

    -- launch a terminal

    [ ((modm .|. shiftMask, xK_Return), spawn $ XMonad.terminal conf)
      ...
      , ((modm              , xK_s),      getSelection  >>= sdcv)
      , ((modm .|. shiftMask, xK_s),      getDmenuInput >>= sdcv)
      ...
    ]
...

其中 getSelection 是获取选中文字的函数,包含在 XMonad.Util.XSelection 模块中;getDmenuInput 函数是用来获取 dmenu 输入的文字的,这里过滤了不可见字符:

getDmenuInput = fmap (filter isPrint) $ runProcessWithInput "dmenu" ["-p", "Dict: "] ""

sdcv 函数完成调用 sdcv 取得翻译结果,并调用 notify-send 进行发送:

sdcv word = do
    output <- runProcessWithInput "sdcv" [word] ""
    mySafeSpawn "notify-send" [word, trString output]

这里使用了 mySafeSpawn 去执行 notify-send 命令,mySafeSpawn 是从 XMonad.Util.Run 里面拷贝过来的,只是去掉了 encodeString 的调用:

mySafeSpawn :: MonadIO m => FilePath -> [String] -> m ()
mySafeSpawn prog args = io $ void_ $ forkProcess $ do
    uninstallSignalHandlers
    _ <- createSession
    executeFile prog True args Nothing
        where void_ = (>> return ()) -- TODO: replace with Control.Monad.void / void not in ghc6 apparently

trString 函数是用于将翻译后结果中的 html 特殊字符进行转义的,我这里只对<>&三个符号进行了转义,如果你使用的是 xfce4-notifyd 这样的服务端,应该就不需要转义了:

trString = foldl (\s c -> s ++ (trChar c)) ""

trChar c
    | c == '<' = "&lt;"
    | c == '>' = "&gt;"
    | c == '&' = "&amp;"
    | otherwise = [c]

修改完后,使用 modm + q 重启 xmonad,在浏览器或者终端中选中一个单词,然后按 modMask + s 应该就能再通知窗口中显示翻译的结果了。不过与我参考的原文相比较,还是有不完美的地方,那就是不能使用快捷键关闭翻译窗口,只能点一下鼠标,或者等待超时自动关闭。不过暂时先这样了。