Loading...

Archive for the '技术文章' Category

    AD: 猛买网,精彩团购 | Jobsdigg | 很棒的男装店 | 网站地图

小心输入Gmail密码

最近在使用gmail时,会遇到原本登录的情况下还会提示输入用户名密码的情况。感觉蹊跷就检查了一下页面源代码,果然是钓鱼行为。源码如下:

我用gmail都是直接点击Google工具栏的按钮,但在家里和公司的电脑都会被劫持,特别地,家里在用Mac公司是Windows,不可能同时中了一样的木马。应该是运营商(两边的网络都是北京联通)做了手脚问题出在网络上。另外,通过搜索ndns01.com域名的相关信息,找到了今年7月的一篇帖子

解决方案:坚持使用https访问Gmail,而且坚持手动输入URL。

如果已经在这样的钓鱼页面输入过密码,建议立即修改密码,然后检查Gmail账户的过滤器设置,看看有无转发邮件到外部陌生邮箱的过滤器。

UPDATE:看到下方有朋友的留言,说电信线路也有此问题。看起来不像是单个运营商的作为,也许错怪北京联通了。

一段脚本让断掉的SSH Tunnel自动重连

平时上网用ssh代理,但合上屏幕时就会断掉。用了Macbook之后,随时随地都有shell可以用,于是写了一段脚本来检测ssh tunnel是否还存活。如果没有就启动一个新的连接。

脚本内容:

curl -s -I http://www.google.com/ –socks5 localhost:7070 > /dev/null
[ $? -gt 0 ] && ssh -fN username@hostname -D 7070

脚本很简单,就是使用curl通代理访问一下google(这里也可以是任意别的网站)。如果访问失败了,就重开一个ssh进程监听7070端口。

要使用重连的机制,需要先建立远程服务器对本机的信任关系,这样才可以免输密码。如果觉得搭建信任关系过程太繁琐,可以使用ssh-copy-id这个小工具。

上述两行脚本可以加入到~/.profile(有的也叫.bash_profile)中,这样每次打开屏幕都会做检查,每次开机登录后也会自动连接。如果愿意,也可以加在Crontab中,每隔几分钟跑一下。

这样设置之后,平时基本上不用关心代理了,它会安静地在后台一直跑着。

UPDATE: 我就知道是我老土了。 @murj 推荐autossh,就是类似原理,大家可以关注一下。

Macbook使用小记

今天不小心伸腿时把Macbook电源线踢掉了,电源线和本本是通过磁性吸附在一起,因此本本毫发无损。我第一次体会到这种小设计的大用处,于是觉得是时候写篇文章,谈谈自己使用Macbook半月多来的感受。

4月去香港时通过子宁认识了一个在那边读书的姑娘,等到学生机开卖,就拜托她买了一台最低配的Macbook Pro。图片就不贴了,官网到处都是。随本本还赠送一个iPod Touch,后者已经成为我坐地铁、开会时的游戏机了。

总体来说,对Macbook的感觉是“也就那样”。如果你是个乐于尝鲜的人,那它值得拥有;不然还是别买了,会后悔的。到手的Macbook有太多优点,比如文章开头的电源线设计,比如支持多点触摸的触控板…最令我欣喜的是电池续航能力很棒。但也有太多细节,需要花时间去习惯。

中文输入

默认的中文输入法很差。请教传教士tinyfool之后,装了SunPinyinFIT。目前在坚持用着FIT。但输入的流畅程度比Windows上的搜狗、谷歌拼音相差甚远。相信这种状况,短期内难以改变。

浏览器

默认的Safari用不习惯──总按错快捷键。后来装了Firefox,搭上熟悉的插件们,这才找回上网的感觉。有朋友总结说,现在操作系统的使用体验,很大程度上取决于浏览器的使用体验,深以为然。

系统相关

MacPorts是个好工具,虽然比不上apt-get那么犀利,但用它装点儿小软件不在话下。

缺少一个SecureCRT的替代品,后来直接用ssh+信任关系,也算好用。搭好了信任关系,想翻墙时、想上服务器时,都省得输密码。

窗口切换挺不习惯。一个应用程序的多个窗口没法用Command + Tab切,只能用Command + `来切换。其实如果窗口开得不多的话,没必要这样子。

键盘

键盘上没有Home/End/PgUp/PgDown/Insert/Delete这6个键,标着”delete”的键其实是”backspace”。当然,消失的按键都可以通过快捷键做到。比如 Home = Ctrl + A; End =  Ctrl + E。多用快捷键可以提高效率,但把原来一个按键改成两个的组合,恐怕只会降低效率。

iPhoto

在使用iPhoto以前,我都是自己把照片从相机里复制出来归档。但这个软件改变了我的习惯,它自动同步,并且可以把照片发到Flickr,而且会把相机里的照片按照时间段记录为一个事件,很棒。为什么当初我用Picasa时,就没喜欢上它呢?

XCode

拿到Macbook和Touch之后,尝试写App。好歹也得写个”hello world”出来吧。XCode的代码提示、自动完成很棒,如果用vim来写我真不知道该如何记住那么多又臭又长的类名方法名。当然,我觉得有语义的类名和方法名还是很好的,用Ruby久了应该也会养成这习惯。

今天刚看完《iPhone 3开发基础教程》,这本书有很多推友推荐过。用来入门,值得一看。

买了Macbook,手头的T61还是没法被替代,而且T61确实也不错。世界杯期间一边用T61看球一边用Macbook上网,很有趣。

写完文章想起在杭州读大学时,一个用着Macbook的MM找我们宿舍一哥们去修电脑,众人口水不已,YY许久。现在自己买了一台,好像也没啥。当年去修电脑的兄弟,也没有然后

LAMP最小优化

假如你在Linode入手了一个VPS,迅速地部署了Ubuntu,然后使用一大堆apt-get把LAMP服务都搭好了(这整个过程也就10分钟吧,可以更短)。此时可以算是“It works”,但还颇有一些地方需要调整。本文就在这种场景下,写一下此时可以做的最小优化,作用范围不仅限于Linode、Ubuntu,其他系统也可以参考。希望对一些朋友有帮助。

Linux

日志切分

如果没有日志切分,日志可能很快会把硬盘塞满,最后不得不手动清理。做日志切分推荐用logrotate,易于配置,一旦配置完成就会默默无闻地工作。

调整swapiness

swappiness用来控制使用系统swap的概率,ubuntu内核默认是60。建议修改为0,使系统尽可能使用物理内存而非swap。实际上,在上次Linode升级套餐后,我已经关掉了swap,系统运行得依然稳定。具体修改方法可以百度一下。

UPDATE: 经 @7id 提醒,swappiness参数更多的是降低磁盘io操作,对于内存不是特别小的情况,差别不大。详细请看下方留言。

Apache

启用压缩

启用压缩,可以减少传输的内容。对WebServer来说这几乎是必须的,但默认的Apache安装并未开启压缩。对于比较慢的连接,启用压缩会有更多好处。

调整MaxRequestsPerChild

MaxRequestsPerChild用于设置每个子进程在其生存期内允许伺服的最大请求数量。到达MaxRequestsPerChild的限制后,子进程将会结束。如果这个参数为0,Apache进程占用的内存会只增不减。一些使用Apache的VPS经常遇到iorate很高,可以尝试调整一下这个参数(ref)。

Mysql

关掉InnoDB

如果服务器用来放blog或是论坛,多数时候MyISAM就足够用了。此时可以把InnoDB关掉。my.cnf中的注释说:”You might want to disable InnoDB to shrink the mysqld process by circa 100MB.”。实测在VPS关掉Innodb时虽然没节约了100MB,但50M还是有的,内存珍贵,能省则省吧。

只需在my.cnf加入一行 skip-innodb 就可以把InnoDB的功能关掉。

PHP

开启输出缓冲

在使用mod_php时,如果不开Output Buffering,每一个输出都会使Apache向客户端发送数据,导致效率很低。使用fastcgi时,由于WebServer本身有buffer,影响并不大。但默认的apt-get安装,使用的就是mod _php,因此建议在php.ini里把Output Buffering打开。

以上只是一些最初级的调整,叫做“优化”都显得有点夸大。但在初期遇到问题时,调整这些地方往往可以很快见效。

除此之外,强烈建议用nginx换掉Apache。

用Racc编写解释器

在Twitter上看到盛大举办的校园牛人大赛,其中有“云计算脚本比赛”。看了下题目,开始悔恨大学里没好好学编译原理。但又手痒,就想用Racc试试。做题过程居然很顺利,从学习Racc到做完前三道题花了两三个小时,于是写一点有关Racc的示例,算是学习笔记。

以“云计算脚本比赛”第一题为例,要求执行如下脚本后,得到“Hello, 老赵”。

SET name = "老赵"
RETURN "Hello, " + name

简单来说,用Racc写成解释器,可以把上面的脚本,转化为Ruby代码并执行。

安装Racc:
gem install racc

创建一个level_1.y文件,定义一个JeffParser类。

class JeffParser
token T_SET T_RETURN T_STRING T_VAR
rule
  lines:
    | lines T_SET T_VAR '=' T_STRING {@local_vars[val[2]] = val[4] }
    | lines T_RETURN exp {result = val[2]}
  exp: exp '+' exp {result = val[0] + val[2] }
    | T_STRING
    | T_VAR {result = @local_vars[val[0]]}
end

先定义几个token。因为语法比较简单,只有 SET/RETURN/变量名/字符串四种。

在rule部分,格式是 token: token token … {action} 。中括号中的action 部分就是Ruby代码了,调试时可以把一些需要的变量打出来。val变量是由左边token的值构成的数组,0表示第一个token的值,依此类推。

之后是词法分析的部分。做的事儿是用正则表达式,把上面定义的4种token识别出来,并放到 @tokens 数组中。

---- header
require 'strscan'
---- inner
 def parse(str)
   @local_vars = {};
   @tokens = []
   s = StringScanner.new(str)
   until s.eos?
     case
     when s.scan(/SET/)
       @tokens << [:T_SET, s[0]]
     when s.scan(/RETURN/)
       @tokens << [:T_RETURN, s[0]]
     when s.scan(/".+"/)
       @tokens << [:T_STRING, eval(s[0])]
     when s.scan(/[a-z0-9]+/)
       @tokens << [:T_VAR, s[0]]
     when s.skip(/[ \t\r\n]/)
     else
       @tokens << [s.getch, nil]
     end
   end
   do_parse()
 end

 def next_token
   @tokens.shift
 end

保存后运行 `racc level_1.y ` 将生成一个名为level_1.tab.rb的文件,这就是我们想要的解释器了。

简单测试(假设脚本存放在level_1.file文件中):

ruby -r level_1.tab.rb -e \
  'print JeffParser.new.parse(open("level_1.file").read())'

输出:

Hello, 老赵

当然,题目还只做了一半。题目要求:需要用http访问并得到结果。可以在前面挂一个Webrick来做到,就和Racc没关系了。

后面几道题,也可以如法泡制。

–广告时间–

赵姐夫(@jeffz_cn)说这次的题目是他经手的,后悔没把blog的链接放上去,于是我帮他打个广告,欢迎大家猛点此处

HipHop实战

前几天HipHop着实火了一把,我也第一时间参照Guide在Ubuntu Server上编译好了HipHop。

之后,又打算把我的blog使用的wordpress编译出来,历经艰难险阻,终于编译成功。在此把一些心得分享出来。

HipHop是什么?

HipHop是Facebook新近开源的一款软件,它可以把php代码转换成c++代码,并将其编译。据称,编译后在性能上会得到较大提升。

一、编译HipHop

  1. 建议使用ubuntu,参照这个文档,可以非常快地装好依赖的库。
  2. 别拿Linode去折腾,内存太小,吃不消的。如果手头没有合适的环境,建议在RackspaceCloud开一个Cloud Server来做。用完就关掉,估计也就一两块钱的事儿。我是在一个虚拟机上编的。
  3. 运行完make之后,可执行文件藏在 $HPHP_HOME/src/hphp下面,一开始我居然没找到。

二、编译PHP项目

  1. 编译过程中遇到错误,只要进入/tmp/hphp_xxx这个临时目录,解决掉相应问题,在该目录重新运行make即可。
  2. 如果牵涉到修改php文件,则需要从头开始先生成cpp代码,再编译。
  3. 如果php项目中有重复的类定义,可能遇到“No rule to make target `cls/atomentry$1.h’,” 这样的错误。WordPress中就有好几处(>=3)。我的解决办法是,把重复定义的类去掉。
  4. 编译还会遇到类似“undefined variable eo_1”的错误。要解决此问题,可打开相应的cpp文件,在报错行的前一行加入:
  5. Variant eo_1;
  6. 编译时的参数–cluster-count建议开大点,如果太小,会导致生成少数个大cpp文件,编译时非常占内存。
  7. WordPress中不需要的主题、插件都可以删掉。惭愧的是,很早以前我写的一个插件,会导致编译出错。

编译WordPress这个大玩意很需要耐心,我连续战斗了三个晚上,修改了多处代码,重编了无数次,终于泪流满面地看到编译成功的信息。

三、运行PHP项目

  1. 如果打算放到服务器上运行,还需要参考编译hiphop的教程把依赖的库先装好。可以用 ldd 命令查看依赖的库是否都满足了。
  2. scp到服务器之前,建议先压缩一下。我把WordPress编完将近80M,压缩之后只剩20M。
  3. 程序作为服务器启动后会有50多个线程,占用100M以上的内存。我没找到线程数这东西在哪里设置,小小一个blog,根本用不着这么多。
  4. 若打算长期使用而不是玩玩,可以参照这篇文章,使用nginx做一个反向代理。

值得一提的是,hiphop生成的中间代码(cpp代码),可读性相当好。

WordPress的一个彩蛋

昨晚不小心点了blog中某文章的“Compare Revisions”功能,深夜把我吓出一身冷汗。


      Wake up, 张磊...
      The Matrix has you...
      Follow the white rabbit.

把这消息发到Twitter(@blogkid),大家都和我说“Knock Knock”。

我想我是遇到彩蛋了(若不是就惨了),于是今天打算寻找一下出处。在目录中grep Matrix、wake up、rabbit都未果,猜到可能是做了加密,于是去翻代码。在Wordpress 2.92版本的wp-admin/revision.php中找到了线索:

54   if ( $left_revision->ID == $right_revision->ID ) {
55       $redirect = get_edit_post_link( $left_revision->ID );
56       include( 'js/revisions-js.php' );
57       break;
58   }

大意是,如果两个相比较的文章ID一致,就会包含js/revision-js.php这个文件。

wp-admin/js/revisions-js.php文件中,有一堆加密过的字符串。粗略判断,就是看到的这几行字了。下面可以自己触发这个彩蛋,只要构造一个这样的url:

http://you_blog_address/wp-admin/revision.php?action=diff&left=2414&right=2414

把后面的“2414”替换为blog上随便一个已有的文章ID,就能看到彩蛋出现啦。

PS:不知道是不是自己太没娱乐细胞了,初看到震惊了半晌。都是被逼的啊。

PS2:据说早在2.6版本就发现了这彩蛋

Rails中压缩存储文本数据

继续压缩话题。之前写了 Rails生成压缩的静态缓存,以及配置nginx以支持直接发送压缩文件两篇文章。今天谈的是在Rails中,将大段文本内容以压缩格式存在数据库。这也是从公司的一个项目中获得的灵感。

原始需求

手头有一个结构简单的文本库,可以看做是key=>value。约180万条数据,2.8G(确实不算大,见笑了)。

  1. 这个库查询频繁,CPU在IOWAIT耗时颇多。
  2. Linode服务器硬盘紧张,而CPU富裕。
  3. 若能通过压缩减小体积,备份/回导时也会更省时。

实施过程

整个实施过程做到了无缝转换,不需要停服务。

第一步:将文本字段类型由TEXT修改为BLOB;新增一个is_gzip字段用于标识是否经过压缩,默认为0。

* 需要注意,BLOB和TEXT最长支持65535字节

第二步,修改Rails相应的Model。代码示例(压缩content字段):

class TextData < ActiveRecord::Model
  def content
    if self.is_gzip
      Zlib::Inflate.inflate(super)
    else
      super
    end
  end

  def content=(info_content)
    super Zlib::Deflate.deflate(info_content, 9)
    self.is_gzip = true
  end
end

再一次感叹super的便捷。修改代码后,需要重新部署一下Rails应用。

第三步,创建一个rake任务,用来批量做转换。关键部位只消这么做:

text_data.content = text_data.content
text_data.save

第四步,在做过修改的表上做一次optimize table,以释放那些不需要的空间。

效果观察

  1. 经如上处理,2.8G数据只剩1.4G。如果用了Gzip而不用Deflate,有可能更小。
  2. IOWAIT未见明显下降。压缩前,单条记录理论上可以通过一次IO完成。
  3. 备份的速度确实有所提升,意料之中。

更多信息

  1. 这种思路同样适用于PHP、Python以及其他语言下的Web应用,只是在Rails中搞起来更为轻松。
  2. Mysql提供了Compress/Uncompress两个函数,但是不建议直接使用。一方面会增加数据库服务器计算的压力;另一方面,如果用了主从,每个服务器都得算一次。
  3. Zlib::Deflate.deflate 的第二个参数是压缩的level。我用随机数据测试,Level 9压缩后的体积比Level 1 小10%。
  4. 如果用了Sphinx之类的从数据库导出数据的全文检索引擎,此法需慎用。

榨干服务器:让进程运行在指定的CPU

我的Linode十分繁忙,在跑一些密集操作数据库的Rake任务时尤其如此。但我观察发现,Linode服务器的4核CPU,只有第1个核心(CPU#0)非常忙,其他都处于idle状态。

不了解Linux是如何调度的,但目前显然有优化的余地。除了处理正常任务,CPU#0还需要处理每秒网卡中断。因此,若能将CPU#0分担的任务摊派到其他CPU核心上,可以预见,系统的处理能力将有更大的提升。

两个名词

SMP (Symmetrical Multi-Processing):指在一个计算机上汇集了一组处理器(多CPU),各CPU之间共享内存子系统以及总线结构。 [更多...]

CPU affinity:中文唤作“CPU亲和力”,是指在CMP架构下,能够将一个或多个进程绑定到一个或多个处理器上运行。[更多...]

一、在Linux上修改进程的“CPU亲和力”

在Linux上,可以通过 taskset 命令进行修改。以Ubuntu为例,运行如下命令可以安装taskset工具。

# apt-get install schedutils

对运行中的进程,文档上说可以用下面的命令,把CPU#1 #2 #3分配给PID为2345的进程:

# taskset -cp 1,2,3 2345

但我尝试没奏效,于是我关掉了MySQL,并用taskset将它启动:

# taskset -c 1,2,3 /etc/init.d/mysql start

对于其他进程,也可如此处理(nginx除外,详见下文)。之后用top查看CPU的使用情况,原来空闲的#1 #2 #3,已经在辛勤工作了。

二、配置nginx绑定CPU

刚才说nginx除外,是因为nginx提供了更精确的控制。

conf/nginx.conf中,有如下一行:

worker_processes  1;

这是用来配置nginx启动几个工作进程的,默认为1。而nginx还支持一个名为worker_cpu_affinity的配置项,也就是说,nginx可以为每个工作进程绑定CPU。我做了如下配置:

worker_processes  3;
worker_cpu_affinity 0010 0100 1000;

这里0010 0100 1000是掩码,分别代表第2、3、4颗cpu核心。

重启nginx后,3个工作进程就可以各自用各自的CPU了。

三、刨根问底

  1. 如果自己写代码,要把进程绑定到CPU,该怎么做?可以用sched_setaffinity函数。在Linux上,这会触发一次系统调用
  2. 如果父进程设置了affinity,之后其创建的子进程是否会有同样的属性?我发现子进程确实继承了父进程的affinity属性。

四、Windows?

在Windows上修改“CPU亲和力”,可以通过任务管理器搞定。

* 个人感觉,Windows系统中翻译的“处理器关系”比“CPU亲和力”容易理解点儿

—————–

进行了这样的修改后,即使系统负载达到3以上,不带缓存打开blogkid.net首页(有40多次查询)依然顺畅;以前一旦负载超过了1.5,响应就很慢了。效果很明显。

cURL使用心得

cURL

cURL是我在Linux上经常用的一个小工具,我理解它是一个“客户端”。今天记录一下我的使用心得。达人请忽略。

cURL是一个利用URL语法在命令行方式下工作的文件传输工具。它支持很多协议:FTP,  FTPS,  HTTP, HTTPS, GOPHER等。[更多...]

场景一:测试域名绑定

我常需要在开发环境中,测试某台服务器上的Web Server是否正确绑定了域名。比如,我希望在服务器192.168.1.10上绑定www.blogkid.net。但需要修改hosts才能看到效果,这活儿很累人。

所谓“域名绑定”,就是把host映射到对应的目录。如果手头有cURL,可以使用 -H 参数,在请求头信息中多写一个 Host 字段。就可以测试是否配置正确了。

# curl -H "Host: www.blogkid.net" http://192.168.1.10/

场景二:查看头信息

响应头信息中包含了很多东西。除了HTTP版本和响应代码,还有Server、Content-Type、Content-Length等信息,如果有写入Cookie的操作,也会体现在头信息中。

使用cURL的 -I 参数,就可以看到这些头信息。比如淘宝的:

# curl -I http://www.taobao.com/
HTTP/1.1 200 OK
Date: Sun, 14 Feb 2010 08:57:35 GMT
Server: Apache
Set-Cookie: abt=b; expires=Sun, 28-Feb-2010 08:57:35 GMT; path=/; domain=www.taobao.com
at_catetype: b (咦,这是什么?)
Set-Cookie: _lang=zh_CN:GBK; Domain=.taobao.com; Path=/
Cache-Control: max-age=3600
Expires: Sun, 14 Feb 2010 09:57:35 GMT
Vary: Accept-Encoding
Content-Type: text/html; charset=GB2312
Content-Language: cn

我昨天也修改了一下我服务器的server信息,大家感兴趣可以 curl -I http://www.blogkid.net/ 看看。

这里插一句,不建议把使用Web服务器的版本暴露出来(其实服务器信息也最好隐藏掉,或者把Apache伪装成nginx什么的 :P )。免得突然爆出漏洞时,措手不及,被人利用。

场景三:跟踪URL跳转

如果遇到了一个多次跳转的URL,可以先用curl的 -L 参数看看,这个URL最终跳转到了什么地方。-L 参数最好配合 -I 使用,不然cURL会把最后一次请求获得的数据输出到控制台。

没有合适的URL拿来做例子,意会一下吧 :D

场景四:发送压缩的请求

cURL提供了一个 –compress 参数,可以用来发送支持压缩的请求。但使用了–compress之后,虽然传输过程是压缩的,cURL的输出还是解压之后的,难以看到效果。

我一般用 -H 参数,自己写一个 Accept-Encoding 字段在头信息中。

curl -H "Accept-Encoding: gzip" http://www.blogkid.net/

如果直接运行上面的命令,会得到一堆乱码,因为cURL输出的内容,是压缩后的数据。不妨在后面接一个gunzip试试。

curl -H "Accept-Encoding: gzip" http://www.blogkid.net/ | gunzip

使用gunzip解压之后,信息又被还原了。前几天我写的压缩话题(12),就用了类似的方法来测试。

场景五:忽略证书错误

平日上网,遇到证书错误一定要小心。但我在工作中,经常需要用自签的假证书搭建开发环境。cURL在遇到证书错误时罢工,使用 -k 参数就可以让它不做证书校验。