方案基于shadowsocks-libev的ss-redir功能,根据网上获取的一个路由表作为判定条件,将访问国外IP的流量转发至代理服务器。 不过ss-redir只支持TCP的转发,UDP的是不行的,因此要解决DNS问题,因为DNS默认是UDP。

要解决DNS解析问题,确保国内站点利用国内DNS解析,国外站点转发至代理服务器解析。显然在客户端完美实现比较困难,于是乎有个退而求其次的判断方法,同时对国内DNS和国外DNS(走代理)发起DNS请求,根据返回的IP地址判断采用哪个的结果(也就是我们用到的ChinaDNS用的方法):

1、国内DNS返回结果为国内IP,国外DNS返回结果同样为国内IP或者返回失败,则采用国内DNS解析结果;

2、国外DNS返回结果为国外IP,国内DNS返回结果同样为国外IP或者返回失败,则采用国外DNS解析结果;

3、国内DNS返回国内IP,国外DNS返回国外IP,则如果国内IP不在黑名单列表(GFW的DNS污染IP黑名单)中,采用国内IP,否则采用国外IP;

4、不符合上述结果的,返回失败。

另外对于某些客户端软件可能会使用UDP方式进行一些通讯,比如说外服游戏的客户端,这类软件,如果是PC上你还是老老实实用Proxifier之类的软件转发至本地的shadowsocks客户端吧,如果是手机上还是要老老实实开shadowsocks软件,并且要使用VPN方式。不过这类比较非常少见,大部分情况,被墙的服务还是采用TCP的。

介绍完以上需要预先了解的内容后 让我们开始正题。

继续阅读

配置一台基于openWRT的路由器使用shadowsocks并智能穿墙

本文开始写作时中使用的路由器为TP-LINK WR841N(D)V7(with openwrt 12.09 稳定版),到这次更新时已经换为了水星 4530R (with openwrt trunk)

在路由器上使用shadowsocks的优势

  1. 效率比较高,在我的不严谨测试中效率比ipsec和pptp都略高。
  2. 目前为止比较稳定(较少受到干扰)而且比较安全。
  3. 服务器端和客户端的配置都相对来说比较简单,不容易出错。
  4. 路由器下面的所有设备都可以0配置自动穿墙,你懂的。
  5. 相比goagent(基本阵亡?默哀)而言,要求的包都很小。

劣势

  1. 暂时没有发现。(是的。本来的问题我貌似解决了。)

本文的基本目的是在openwrt上使用pdnsd通过tcp查询规避DNS污染,通过iptables转发到端口的功能转发特定流量给跑在路由器上的shadowsocks来访问某些特定IP段达到一定程度无视某墙的存在的目的。

本文分为三个部分:

  1. 相关包的安装和shadowsocks的配置
  2. pdnsd的配置
  3. 使用iptables对流量进行重定向

在openwrt上安装相关的包并配置shadowsocks

其中shadowsocks我们使用shadowsocks-libev-polarssl(openssl的lib比较大,塞不下……),推荐从这里获取:
http://sourceforge.net/projects/openwrt-dist/files/shadowsocks-libev/

然后在路由器端刷新opkg缓存包并安装shadowsocks:

opkg update
opkg install shadowsocks-libev-polarssl_1.4.5_ar71xx.ipk

我们还需要安装额外的包,其中我们使用pdnsd(如果后面直接配置dnsmasq转发请求给opendns则不需要)来净化部分国外域名解析,用iptables-mod-nat-extra实现iptables流量转发到端口的功能:

opkg update
opkg install pdnsd  
opkg install pdnsd  iptables-mod-nat-extra

我们需要编辑shadowsocks的配置信息 /etc/config/shadowsocks.json(新版默认的配置文件移动到了/etc/shadowsocks.json 不过在后面启动的时候指定就好了,无影响。):
格式如下:

{
    "server":"[服务器IP地址]",
    "server_port":[服务器端口],
    "local_port":[本地端口,稍后iptables会用到],
    "password":"[密码]",
    "timeout":600,
    "method":"[加密方式]"
}

在12.09上shadowsocks会因为缺少libpolarssl.so.3而无法启动,我们可以使用ln“欺骗”一下shadowsocks:

注:我目前一切切换到trunk版本,不知道新版是否还存在这个BUG,请自行测试能否启动。

ln -s /usr/lib/libpolarssl.so /usr/lib/libpolarssl.so.3

配置pdnsd对某些域名进行净化

我采用的基本思路是通过pdnsd使用TCP协议向国外的上级DNS查询而避过DNS污染,然后在本地提供一个1053端口的DNS供dnsmasq使用。如果全局使用pdnsd转发的国外DNS会导致国内某些网站或者服务访问较慢,不推荐。
另外一个思路是使用非标准端口查询,那么就可以不需要配置pdnsd,直接在dnsmasq配置中将相关域名查询请求转发给支持非标准端口的DNS就行了,目前已知的是opendns支持5353端口和443端口。(即在dnsmasq段配置将127.0.0.1#1053 替换为 208.67.222.222#5353 或 208.67.222.222#443注意:未测试,仅理论分析)。

提醒一下:最近对Google的干扰已经全面升级,单纯解决DNS污染没法愉快的撸youtube了。

修改pdnsd的配置文件 /etc/pdnsd.conf
注意关注中文注释部分,如果复制记得把中文注释删掉。。。其他部分如果您需要,再自行修改:

# 这一段全局配置需要修改:

global {
    # debug = on;
    perm_cache=1024;
    cache_dir="/var/pdnsd";
    run_as="nobody";
    server_port = 1053;    # !!!使用 1053 作为 dns 端口, 默认是 53,一定要修改否则会跟默认dnsmasq冲突
    server_ip = 127.0.0.1;  #我们只需要处理本机转发的DNS查询,所以不需要更改
    status_ctl = on;
    query_method=tcp_only; # !!!最重要的配置, 只使用 tcp 查询上级 dns
    min_ttl=15m;
    max_ttl=1w;
    timeout=10;
}

#……

# 自行增加下面这一段,pdnsd默认是没有提供上游DNS服务器的(默认配置文件中用各种注释方式把自带的注释掉了):

server {
    label= "googledns";           # 这个label随便写
    ip = 8.8.8.8; # 这里为上级 dns 的 ip 地址,要求必须支持TCP查询,相关说明见后文注解
    root_server = on;        # 设置为 on。
    uptest = none;           # 不去检测 dns 是否无效.
}
        # …… 后面不需要修改的就不贴出来了。

注:DNS地址如果不愿意倒腾可以使用Google Public DNS。如果需要使用其他DNS,请参考:http://public-dns.tk/ ,为了配合后面的重定向,建议使用与VPS地区相同的DNS,譬如我现在使用的服务器在日本,这里的DNS同样使用日本的DNS,一定程度上可以提高连接速度。

启用pdnsd,并设置为开机启动:

/etc/init.d/pdnsd enable
/etc/init.d/pdnsd start

设置dnsmasq对特定域名使用本地的pdnsd进行解析:
为了保持配置文件整洁,建议在 /etc/dnsmasq.conf 最后加入:

conf-dir=/etc/dnsmasq.d

然后新建目录 /etc/dnsmasq.d ,在里面加入一个conf,名字任选。譬如 /etc/dnsmasq.d/fuckgfw.conf ,下面是我的文件内容,你可以按自己需要整理自己的:

#Google and Youtube
server=/.google.com/127.0.0.1#1053
server=/.google.com.hk/127.0.0.1#1053
server=/.gstatic.com/127.0.0.1#1053
server=/.ggpht.com/127.0.0.1#1053
server=/.googleusercontent.com/127.0.0.1#1053
server=/.appspot.com/127.0.0.1#1053
server=/.googlecode.com/127.0.0.1#1053
server=/.googleapis.com/127.0.0.1#1053
server=/.gmail.com/127.0.0.1#1053
server=/.google-analytics.com/127.0.0.1#1053
server=/.youtube.com/127.0.0.1#1053
server=/.googlevideo.com/127.0.0.1#1053
server=/.youtube-nocookie.com/127.0.0.1#1053
server=/.ytimg.com/127.0.0.1#1053
server=/.blogspot.com/127.0.0.1#1053
server=/.blogger.com/127.0.0.1#1053

#FaceBook
server=/.facebook.com/127.0.0.1#1053
server=/.thefacebook.com/127.0.0.1#1053
server=/.facebook.net/127.0.0.1#1053
server=/.fbcdn.net/127.0.0.1#1053
server=/.akamaihd.net/127.0.0.1#1053

#Twitter
server=/.twitter.com/127.0.0.1#1053
server=/.t.co/127.0.0.1#1053
server=/.bitly.com/127.0.0.1#1053
server=/.twimg.com/127.0.0.1#1053
server=/.tinypic.com/127.0.0.1#1053
server=/.yfrog.com/127.0.0.1#1053

使用iptables对流量进行重定向

之前犯了一个错误,采用了默认流量重定向,特定流量(亚太流量)穿透的思路。这样相对来说有很多不必要的流量被重定向到了shadowsocks的服务器端,尤其是在本路由下跑PT的情况。这几天想了下,为什么不只定向某些流量呢。

下面脚本的思路是所有流量默认穿透,只有符合条件的流量才被重定向。这样显得“智能”多了。

您可以直接复制下面的脚本,跟我一样保存为/usr/bin/ss-black.sh,注意运行前要给它运行权限:

chmod +x /usr/bin/ss-black.sh

以下为脚本内容:

#!/bin/sh

#create a new chain named SHADOWSOCKS
iptables -t nat -N SHADOWSOCKS

#Redirect what you want

#Google
iptables -t nat -A SHADOWSOCKS -p tcp -d 74.125.0.0/16 -j REDIRECT --to-ports 1080
iptables -t nat -A SHADOWSOCKS -p tcp -d 173.194.0.0/16 -j REDIRECT --to-ports 1080

#Youtube
iptables -t nat -A SHADOWSOCKS -p tcp -d 208.117.224.0/19 -j REDIRECT --to-ports 1080
iptables -t nat -A SHADOWSOCKS -p tcp -d 209.85.128.0/17 -j REDIRECT --to-ports 1080

#Twitter
iptables -t nat -A SHADOWSOCKS -p tcp -d 199.59.148.0/22 -j REDIRECT --to-ports 1080

#Shadowsocks.org
iptables -t nat -A SHADOWSOCKS -p tcp -d 199.27.76.133/32 -j REDIRECT --to-ports 1080

#1024
iptables -t nat -A SHADOWSOCKS -p tcp -d 184.154.128.246/32 -j REDIRECT --to-ports 1080

#Anything else should be ignore
iptables -t nat -A SHADOWSOCKS -p tcp -j RETURN

# Apply the rules
iptables -t nat -A PREROUTING -p tcp -j SHADOWSOCKS

注1:以前的暴力重定向所有流量(除亚洲流量以外)的版本在这里:https://gist.github.com/reee/fe174cfd8985273bc478
注2:如果需要添加你自己需要访问的域名很简单。首先使用dig或者nslookup获取域名对应的正确IP,然后借助APNIC的IP WHOIS工具:(http://wq.apnic.net/apnic-bin/whois.pl) 可以轻松的获得大部分IP段。以facebook为例:

dig获取的正确IP为:173.252.110.27。
通过APNIC查询到173.252.64.0/18均属于facebook。则添加

iptables -t nat -A SHADOWSOCKS -p tcp -d 173.252.64.0/18 -j REDIRECT --to-ports 1080

到上面脚本 倒数第二条前面就可以了。

然后就是见证奇迹的时刻:

#设置路由:
/usr/bin/ss-black.sh
#启动shadowsocks
/usr/bin/ss-redir -c /etc/config/shadowsocks.json &

其他问题

查看iptables的NAT表来检查路由表是否已经成功加载:

iptables -t nat --list

停止服务器:

killall ss-redir  # 关闭shadowsocks。
/etc/init.d/firewall restart # 清除流量重定向配置。

参考文章:
openwrt 上通过 pdnsd 和 dnsmasq 解决 dns 污染
https://github.com/haohaolee/shadowsocks-openwrt

在WordPress 2.7或更高版本中更改用户密码,步骤如下:

1. 进入管理界面的用户选项

2. 点击用户列表中自己的用户名进行编辑

3. 在“配置”界面的最下方“新密码”区域,在两个输入框中输入新密码。输入框下方深色背景的方框显示了新密码的强度。

4. 点击“更新配置”按钮

之后新密码会立即生效。

通过邮件自动发送新密码

如果记得自己的用户名与用户资料中的电子邮件账号,可以使用WordPress的“忘记密码”功能:

访问自己网站的登录界面(类似于http://yoursite.com/wordpress/wp-login.php)
点击“忘记密码”
被带往一个界面,输入用户资料中自己的用户名、邮箱地址
稍等片刻,新密码就会被发送到所填的邮箱了
获得新密码后,再次登录,在“配置”页面将密码改为自己能够记住的密码
通过MySQL命令行更改密码

1. 获取密码的MD5散列表

访问md5 散列表生成器,或者….
用Python生成一个密码
在Unix上:echo -n | md5sum
2. 使用”mysql -u root -p”命令——登录MySQL

3. 输入mysql密码

4. 使用”use (数据库名)”命令——选择WordPress数据库

5. 使用”show tables;”命令——查找一个名称中含有”users”的表名

6. 使用”SELECT ID, user_login, user_pass FROM (查找到的表名)”命令——帮助给出内部运行状态

7. 使用”UPDATE (查找到的表名) SET user_pass=”(MD5 字符串)”命令,其中ID = (id#-需要重设密码的用户)——实际更改密码

8. 使用”SELECT ID, user_login, user_pass FROM (查找到的表名)” 命令——确定已经更改密码

9. (输入Control-D,退出mysql客户端)

如果MySQL版本较新(5.x),可以直接用MySQL计算MD5散列表:

1. 跳过上文介绍的第一步

2. 将第七步的命令改为:

使用”UPDATE (查找到的表名) SET user_pass=”(新密码)”命令,其中 ID = (id#-需要重设密码的用户)——实际更改密码
通过phpMyAdmin更改密码

适用于用phpMyAdmin访问数据库的用户。注意:使用phpMyAdmin时,风险自担。如果对自己的能力有怀疑,请向他人寻求帮助。WordPress对数据丢失概不负责。

登录phpMyAdmin,点击数据库:

之后会出现一个数据库列表,点击WordPress数据库

过去几天,一系列莫名其妙的小故障导致中国的微博服务中断,引起了用户的不安,人们担心政府将整顿这种广受欢迎的、类似于Twitter的社交网络工具。

首先,门户网站搜狐网的微博服务周末一度无法接入。之后, 新浪微博的标志旁边周一打上了“beta”符号(意为测试版),网易和腾讯的微博服务上也出现了这个符号。然后用户于周二晚报告说网易的微博服务突然“处于维护中”,账号也无法接入了。


同时,继网易微博停运、搜狐微博曾短暂中断之后,新浪微博也开始进行内部整顿,总编辑陈彤发出内部通知,“肯定要被删除的微博,通知监控部门,需要封杀的用户,拿不准的再请示我”。
新浪微博方面表示没有接到类似的指令。

分析人士表示,新浪此举或预示着微博正遭遇到一些监管压力。从本周开始,新浪微博页面增添的“测试版”字样正体现出对压力的某种妥协姿态。

新加坡中文报纸《联合早报》在其网站上报道称搜狐微博暂停服务是由于审查合规问题。这一报道致使用户推测微博这一愈发受到欢迎的互联网应用程序或许有受到限制或停用的危险。

搜狐网的客服代表证实于周五晚至周一期间关闭了搜狐微博,但说是为了维护而不是由于政府的命令。

新浪网营销中心副总经理刘奇否认网站打出“测试版”符号与政府命令或其它竞争者有关,并说用户不必担心微博服务会被关闭。他说自去年8月启用以来,微博网站严格来说一直处于试用阶段,目前正在为其一周年纪念日策划再次启用仪式。

尽管如此,几家网站的微博服务同时出现“beta”版本符号仍让许多用户焦虑,特别是由于过去中国的网站与政府进行不公开谈判造成网站停用时,曾以“网站维护”充当借口。

随着越来越多的知名网站受到屏蔽,加上谷歌决定停止审查其中文搜索引擎等受到广泛宣传的事件让公众意识到审查的存在,中国的互联网用户对政府的互联网控制措施越来越心存警惕。

去年,Twitter被中国屏蔽,与之类似的中文服务“饭否”也被关闭。尽管这两个网站在主流网民中并不普及,但由新浪网、搜狐网和网易网

启动的微博网站却开始蓬勃发展。作为最受欢迎的微博网站,新浪微博目前的用户超过500万人。这些网站均依据自我审查制度受到网站的监管。

在此之前,政府支持的中国社科院发表报告指出了社交网站的危险性。这份于7月7日发表的报告以Facebook(也被中国屏蔽)为例指出社交网站可被用作颠覆和侵犯用户隐私的工具,并称这些网站被西方国家情报机构所利用.

最后等待中国微薄集体水产的一天…那天所有人回归Facebook

首先插件的路径改变了
插件放置于 source/plugin/目录下
数据库读取的写法也发生了改变:
使用了类的静态方法好处显而易见不需要原来 $GLOBALS[‘db’] 直接在任何地点拿出来就能用
DB::table(‘forum_attachment’);   //这里会返回 pre_ forum_attachment
DB::fetch_first($query)              //这里对应原来DZ7的 $db->
fetch_first($query)
DB::query($query)                    //对应原来DZ7的 $db-> query ($query)
DB::fetch($query)                     //对应原来DZ7的 $db-> fetch ($query)
关于嵌入点:
Discuz!X1  由于有众多模块 .. 所以插件平台在原来DZ7.2 的基础上进行了升级
具体的写法:
class plugin_bigqi_com {
function plugin_bigqi_com(){
//这个写法会被插件执行函数runhooks() 运行时调用执行…
return ‘this is globals plugin_bigqi_com’;
}
function global_footer(){
// 这个是全局嵌入点的…所有的页面包括首页论坛群组空间等等
return ‘this is globals global_footer’;
}}
//下面这里就重要了..
// DISCUZ! X 的插件运行机制有些改动… 由于Dx 使用了模块化包含方式..
//所以就有必要强调嵌入点是哪个模块下的
//模块的定义在每个最外层文件的最上部如:forum.php
// define(‘CURSCRIPT’, ‘forum’); 定义了模块名称 forum
//另外原来DISCUZ 7.2 使用的执行脚本常量由原来的 CURSCRIPT 改为 CURMODULE
//所以大家在写 forum 嵌入点的时候判断模块下执行的脚本就要用 CURMODULE 来判断
//另外嵌入点的具体写法如下:
//只有用插件名+下横线+模块的名称对原来的类进行继承扩展才能让模块内的嵌入点显示
class plugin_bigqi_com_forum extends plugin_bigqi_com {
function index_top(){
return ‘this is forum index_top’;
}
}
// 另外 原来的 DZ7.2 使用的引用模板的写法不变只是插件的模板目录的名称改变了
由templates 改成了 template
例如:
source/plugin/bigqi_com_picrollshow/template/
插件模板引擎的写法 保持不变 我这里写个示例
// include template(‘bigqi_com:picrollshow’);
// return $return;

首先插件的路径改变了
插件放置于 source/plugin/目录下
数据库读取的写法也发生了改变:
使用了类的静态方法好处显而易见不需要原来 $GLOBALS[‘db’] 直接在任何地点拿出来就能用
DB::table(‘forum_attachment’);   //这里会返回 pre_ forum_attachmentDB::fetch_first($query)              //这里对应原来DZ7的 $db->fetch_first($query)DB::query($query)                    //对应原来DZ7的 $db-> query ($query)DB::fetch($query)                     //对应原来DZ7的 $db-> fetch ($query)
关于嵌入点:
Discuz!X1  由于有众多模块 .. 所以插件平台在原来DZ7.2 的基础上进行了升级
具体的写法:
class plugin_bigqi_com {         function plugin_bigqi_com(){                   //这个写法会被插件执行函数runhooks() 运行时调用执行…                   return ‘this is globals plugin_bigqi_com’;         }         function global_footer(){                   // 这个是全局嵌入点的…所有的页面包括首页论坛群组空间等等                   return ‘this is globals global_footer’;         }}
//下面这里就重要了..// DISCUZ! X 的插件运行机制有些改动… 由于Dx 使用了模块化包含方式..//所以就有必要强调嵌入点是哪个模块下的//模块的定义在每个最外层文件的最上部如:forum.php// define(‘CURSCRIPT’, ‘forum’); 定义了模块名称 forum//另外原来DISCUZ 7.2 使用的执行脚本常量由原来的 CURSCRIPT 改为 CURMODULE//所以大家在写 forum 嵌入点的时候判断模块下执行的脚本就要用 CURMODULE 来判断//另外嵌入点的具体写法如下://只有用插件名+下横线+模块的名称对原来的类进行继承扩展才能让模块内的嵌入点显示class plugin_bigqi_com_forum extends plugin_bigqi_com {         function index_top(){                   return ‘this is forum index_top’;         }
}
// 另外 原来的 DZ7.2 使用的引用模板的写法不变只是插件的模板目录的名称改变了由templates 改成了 template例如:source/plugin/bigqi_com_picrollshow/template/插件模板引擎的写法 保持不变 我这里写个示例// include template(‘bigqi_com:picrollshow’);// return $return;

RoR的部署方案可谓五花八门,有Apache/Fastcgi方式的,有Nginx/Mongrel方式的,还有lighttpd/Fastcgi方 式,也有人使用HAProxy/Mongrel,各种部署方式都是众说纷纭,让人搞不清楚哪种方式更好一些。我的这篇文章就是希望结合我们运营 JavaEye网站一年多以来的经验(通过统计Rails的production.log,JavaEye网站目前每天处理超过70万200 OK状态的Ruby动态请求,应该是国内目前负载量最大的RoR应用了),为大家剖析RoR部署方案的优劣,帮助大家选择适合自己生产环境的RoR部署方 式。

在讨论部署方案之前,先让我们看一下RoR网站部署的简单架构:

浏览器的HTTP访问请求首先达到Web服务器,充当Web服务器的一般是Lighttpd/Apache/Nginx,如果访问请求包含静态资 源,那么Web服务器就会直接从本地硬盘读取静态资源文件,例如图片,JavaScript,CSS等等,返回给客户端浏览器;如果访问请求是动态请求, 那么Web服务器把URL请求转发到后端的FastCGI/Mongrel来处理,等到FastCGI/Mongrel处理完请求,将生成的页面数据返回 给Web服务器,最后Web服务器把页面数据发送到客户端的浏览器。

从RoR的部署方式来看,主要由前端的Web服务器和后端的应用服务器构成:前端的Web服务器可以使用Apache,Lighttpd,Nginx和Litespeed,后端的应用服务器可以使用FastCGI和Mongrel,下面我们分门别类的介绍和剖析:

一、介绍Web服务器

Web服务器的主要作用有两点:一是处理静态资源,二是将动态请求分发到后端应用服务器,然后接收后端应用服务器生成的页面数据,将其返回浏览器,充当了一个信息沟通的桥梁作用,在本文当中我们重点分析后者的作用。

1、Apache 2.2

Apache是全球互联网使用最广泛的Web服务器,但在处理静态资源文件上却不是性能最优秀的Web服务器,不过一般情况下,静态资源的访问并不是RoR网站的瓶颈,因此也不必过于在意这一点。

Apache 2.2既支持HTTP Proxy方式连接后端的Mongrel应用服务器,也可以通过mod_fastcgi/mod_fcgid来连接FastCGI应用服务器:当以 HTTP Proxy方式连接Mongrel的时候,Apache接收Mongrel返回的页面数据的buffer size最大只能开到8KB(默认是4KB或者8KB),因此当页面数据超过8KB的时候,可能需要Apache和Mongrel之间发生多次交互;当以 mod_fastcgi方式连接FastCGI应用服务器的时候,接收返回数据的Buffer size仍然只有8KB而已,如果使用mod_fcgid,那么buffer size为64KB,有了很大的改善。

2、Nginx

Nginx是俄国人出品的轻量级Web服务器,在处理静态资源方面,据说性能还略微超过Lighttpd,但是Nginx在性能消耗方面略微比Lighttpd要高一些。

Nginx内置了良好的HTTP Proxy和FastCGI支持,因此即可以连接Mongrel,也可以连接FastCGI服务器,在这两种情况下,Nginx默认的接收应用服务器返回 数据的Buffer Size也只有区区的8KB,但是你可以自行设置更大Buffer Size。

3、Lighttpd

Lighttpd是全球互联网排名第五的Web服务器,也是近两年来上升最快的Web服务器,特别是很受一些著名Web 2.0大网站的欢迎,例如wikipedia的某些服务器,youtube的视频服务器,在国内,豆瓣网站和JavaEye网站都是Lighttpd的绝 对拥护者。在处理静态资源方面,Lighttpd性能远远超过Apache。

Lighttpd既支持HTTP Proxy连接Mongrel,也支持FastCGI方式,但是Lighttpd的FastCGI支持在所有流行的Web服务器当中可能是最优秀的,所以 用FastCGI的网站都很喜欢Lighttpd。Lighttpd在接收后端应用服务器返回数据的方式上和Apache/Nginx有非常大的区别:

Apache/Nginx是针对每个应用服务器连接分配固定Size的Buffer,而且默认只开8KB,这个Size对于现在网页动辄 50-100KB的情况来说,显得过于保守,如果应用服务器的返回数据无法一次填满Web服务器的Buffer,那么就会导致应用服务器和Web服务器之 间多次数据传输,这对于RoR网站的性能会造成一些相关的影响,我们在后面会详细的分析。

Lighttpd并不针对应用服务器的每个连接分配固定的Buffer,而是尽可能的把应用服务器返回的数据一次性接收下来,因此无论应用服务器返回多大的数据量,Lighttpd都是照单全收,胃口非常惊人。

4、Litespeed

Litespeed是一个商业收费的Web服务器,静态资源处理能力据它自己的评测数据比Lighttpd略高。Litespeed也同时支持 HTTP Proxy连接Mongrel和FastCGI连接应用服务器。此外Litespeed专门为单机运行的RoR开发了一个lsapi协议,号称性能最好的 RoR通讯协议,比HTTP Proxy和FastCGI都要好。但是lsapi的运行方式有很大缺陷:因为lsapi不是web server启动的时候启动固定数目的ruby进程,而是根据请求繁忙程度,动态创建和销毁ruby进程,貌似节省资源,实则留下很大的黑客攻击漏洞。只 要黑客瞬时发起大量动态请求,就会让服务器忙于创建ruby进程而导致CPU资源耗尽,失去响应。

由于Litespeed在运行RoR方面并没有表现出比Lighttpd优越之处,而且还是收费软件,企业版本售价在双核CPU上面每年收费 499美元,并且也不开源,因此我们就不再把关注点放在Litespeed上面。当然Litespeed收费也不是白收的,它提供了非常好用的基于Web 的服务器管理界面,以及非常多的安全性方面的设置参数。

5、HAProxy

HAProxy并不是一个Web服务器,他既不能处理静态资源,也不能充当浏览器和应用服务器之间的缓冲桥梁,他只是充当了一个请求分发的软件网 关作用。ThoughtWorks公司的RubyWorks选择使用HAProxy + Mongrel Cluster的方式来部署RoR应用,不能不说是一个愚蠢的方案。这种方案其实相当于把n个Mongrel应用服务器捆绑起来,直接充当Web服务器, 而Mongrel毕竟是一个Ruby写的服务器,无论是网络IO能力,还是静态资源的处理速度,无法和真正的Web服务器相提并论,让Mongrel直接 处理静态资源和调度网络IO,会造成服务器资源毫无必要的极大开销,因此HAProxy也不在我们的考虑之列。

二、分析应用服务器的处理方式

无论是Mongrel还是FastCGI,都能够良好的运行Rails服务器,但是他们在和Web服务器之间的数据传输方式上存在一些差别,而正是这些差别,对部署方式有重大的影响:

1、Mongrel

Mongrel本身可以直接充当Web服务器,但在这种情况下性能并不会好。因为Mongrel只有HTTP协议的解析部分是用C语言编写的,其 余所有代码都是纯Ruby的。在处理静态资源下载上面,Mongrel的实现方式非常低效率,他只是简单的以16KB为单位,依次读入文件内容,再写出到 网络Socket端口,其性能远远比不上传统的Web服务器调用操作系统的read()和write()库实现的静态文件下载速度,如果和现代Web服务器实现的sendfile方式的“零拷贝”下载相比,简直就是望尘莫及。

Mongrel使用了Ruby的用户线程机制来实现多线程并发,并且使用了一个fastthread补丁,改善了Ruby用户线程的同步互斥锁问 题。但是Ruby并不是本地线程,我们也不要对Mongrel的网络IO负载能力抱有什么不切实际的幻想。同时Rails本身也不是线程安全的,因此 Mongrel在执行Rails代码的过程中,完全是加锁的状态,那和单进程其实也没有太大差别。

因此,当我们使用Mongrel的时候,一般会在前端放置Web服务器,通过HTTP Proxy方式把请求转发给后端的Mongrel应用服务器。在这种情况下,Mongrel只处理动态请求,在运行Rails框架生成页面数据之后,把数 据返回给Web服务器就可以了。但是在这种部署方案下,有一个很重要的细节被我们忽视了,Mongrel运行Rails生成的页面数据是怎么返回给Web 服务器的呢?通过仔细钻研源代码我们可以搞清楚Mongrel处理Rails请求的细节:

1) Mongrel接收到请求以后,启动一个ruby线程解析请求信息

2) 加锁,调用Rails Dispatcher启动Rails框架

3) Rails处理完毕,创建一个StringIO对象,把Rails生成的页面数据写入到StringIO中

4) 解锁,把StringIO的数据flush到Web服务器

这个StringIO对象其实很重要!它充当了一个输出缓冲区的作用,我们设想一下,当Mongrel作为独立的Web服务器的时候,如果 Rails生成的页面比较大,而客户端浏览器下载页面的速度又比较慢,假设没有这个StringIO对象,会发生什么问题? Rails线程在执行render方法的时候就会被挂住!同步互斥锁没有解锁,Mongrel再也无法处理下一个动态请求了。

当Mongrel仅仅作为应用服务器的时候,这个StringIO仍然很重要,为什么?我们前面提到过了,Apache/Nginx的接收缓冲区 都只开了8KB,如果页面比较大,Mongrel就没有办法一次性把数据全部推给Web服务器,必须等到Web服务器把接收缓冲区的8K数据推到客户浏览 器端以后,清空缓冲区,才能接收下一个8KB的数据。这种情况下,Mongrel必须和Web服务器之间进行多次数据传输,才能完成整个Web响应的过 程,显然没有一次性把页面数据全部推给Web服务器快。如果Web服务器使用Lighttpd的话,情况会不一样。当Mongrel把StringIO的 数据flush出去的时候,Lighttpd是一次性全部接收下来了,不需要多次交互,因此Lighttpd+Mongrel的RoR网站的实际速度要快 于Apache/Nginx+Mongel。

Mongrel使用StringIO对象缓存输出结果,在某些特殊的情况下会带来很大的安全隐忧。我们假设使用服务器端程序控制带权限的文件下 载,某用户下载的是一个100MB的文件,该用户使用了多线程下载工具,他开了10个线程并发下载,那么每个线程Mongrel在响应之后,都会把整个文 件读入到内存的StringIO对象当中,所以总共会创建出来10个StringIO对象保存10份文件内容,所以Mongrel的内存会一下暴涨到 1GB以上。而且最可怕的是,即使当用户下载结束以后,Mongrel的内存都不会迅速回落,而是一直保持如此高的内存占用,这是因为Ruby的GC机制 不好,不能够及时进行垃圾回收。

也许你会觉得不太可能下载100MB那么大的附件,但是以JavaEye网站为例,圈子的共享文件最大允许10MB,只要用户在多台机器上面,每 台机器开100个线程下载圈子共享文件,每个Mongrel的内存占用都会立刻超过1GB,用不了几分钟,服务器的物理内存就会被耗尽,网站失去响应。这 个缺陷非常容易被别有用心的黑客利用,攻击网站。这也是JavaEye网站为什么始终不用mongrel的原因之一。

通过上面的剖析,我们知道Mongrel在使用Lighttpd的时候,可以达到最快的RoR执行速度,但是Lighttpd当前的1.4.18 版本的HTTP Proxy的负载均衡和故障切换功能有一些bug,因此一般很少有人会使用这种方式。大多数人都会采用Mongrel搭配Apache2.2或者 Nginx,但是正如我们上面做分析的那样,Apache/Nginx的Buffer Size实在是一个很讨厌的限制,特别是Apache只能最大开8KB的Buffer,因此我建议使用Nginx搭配Mongrel,并且把Nginx的 Proxy Buffer Size设置的大一些,比如说设置为64KB,以保证大多数页面输出结果可以一次性flush到Web服务器去。

2、FastCGI

很多人对FastCGI谈虎色变,仿佛FastCGI就是内存泄漏,性能故障的罪魁祸首,又或者嫌弃FastCGI太古老了,已经被淘汰掉的技术 了,其实这是一个很大的误解。FastCGI本质上只是一种进程间通讯的协议,虽然是一个比较古老的协议,但是还是比HTTP协议年轻多了,HTTP协议 不是照样现在很流行吗?

在PHP/ASP/JSP流行之前,FastCGI曾经非常普及,只不过那个时代的FastCGI程序是用C语言编写的,写起来太费劲,而PHP /ASP/JSP相比之下,写起来就太简单了,所以FastCGI就渐渐被丢到了历史的故纸堆里面。但是最近两年来,由于Ruby和Python的快速 Web开发框架的强势崛起,FastCGI仿佛又咸鱼翻身了。

当我们以FastCGI方式运行Rails应用服务器的时候,每个FastCGI进程都是单线程运行的,考虑到Rails本身不是线程安全的,所 以和Mongrel运行Rails的实际效果是一样的,都是每个进程只能跑一个Rails实例。但是FastCGI在Rails生成页面数据返回给Web 服务器的方式和Mongrel截然不同:

前面我们说到Mongrel自己开了输出缓冲区,而FastCGI则完全不开任何缓冲区,当Rails执行render方法的时 候,FastCGI实际执行的是FCGI::Stream.write方法调用,直接把数据写给Web服务器了。此时如果Web服务器是 Apache/Nginx,会发生什么?

如果我们使用mod_fastcgi模块,那么Apache的接收缓冲区就是8KB;

如果我们使用mod_fcgid模块,那么Apache的接收缓冲区就是64KB;(mod_fcgid是中国人开发的取代mod_fastcgi的开源项目,在Apache社区很受欢迎,谁敢说中国人只是开源“消费”国?)

如果我们使用Nginx服务器,那么默认的接收缓冲区就是8KB,但是可以改得更大;

如果页面数据比较大,超过8KB,会怎么样? FastCGI进程被挂在render方法上!必须等到Web服务器的缓冲区清空,把页面数据全部接收下来以后,FastCGI进程才能结束本次 Rails调用,处理下一个请求!所以千万别用Apache/Nginx搭配FastCGI应用服务器,否则你的RoR应用会死的很难看。根据我个人的测 试数据表明,同样的测试负载,Apache搭配70个FastCGI进程挂掉,但是Lighttpd搭配30个FastCGI进程轻松跑完!

当FastCGI搭配Lighttpd的时候,我们知道Lighttpd会一次性照单全收FastCGI送过来的页面数据,所以FastCGI进 程并不会被挂住。如果我们对比一下Lighttpd搭配Mongrel和FastCGI会发现,Lighttpd搭配FastCGI性能最好,为什么呢?

Mongrel首先自己会用StringIO缓冲页面数据,然后推送给Lighttpd以后,Lighttpd也在内存当中缓冲了一份页面数据, 造成了毫无必要的double buffer的开销。这自然不如FastCGI不做任何缓冲,直接推给Lighttpd性能来得高,内存消耗少了。

我们的方案分析到这里,大家应该自己心里有结论了,Lighttpd+FastCGI是性能最佳,服务器资源消耗最少的RoR部署方案,事实上目 前RoR网站部署使用最多最流行的也是Lighttpd+FastCGI方式,而JavaEye网站,自然也是这种方式的部署。因此我们可以对各种方案进 行一个性能优劣的排队:

Lighttpd+FastCGI > Lighttpd+Mongrel > Nginx+Mongrel > Apache+Mongrel > Ngignx+FastCGI > Apache+FastCGI

其中Lighttpd+FastCGI是性能最佳方案,而Apache+FastCGI是性能最差方案。

有些细心的同学可能会产生一个新的疑问?你说到底,之所以Lighttpd跑RoR性能最好,还是在于Lighttpd接收数据不限定缓冲区的大 小,而Apache/Nginx限定了缓冲区大小所至。那为什么Nginx要限制呢?Lighttpd如果不限制的话,会不会导致Lighttpd内存爆 掉?

Nginx限制Proxy Buffer Size其实也有道理,因为Nginx并不是为RoR量身打造的Web服务器,Nginx最广泛的用途还是高负载大访问量的代理服务器,在Nginx主要 的应用场合,如果不做这样的限制,那Nginx端的资源消耗就相当高了,有可能会拖累所代理的服务速度。

Lighttpd主要用途之一就是提供高性能的FastCGI支持的Web服务器,所以必须为FastCGI量身打造。Lighttpd端承担的 负载越高,就越能有效的加快FastCGI执行速度。其实我们稍微心算一下,假设Lighttpd后面挂1000个FastCGI进程,每个 FastCGI进程同时送过来50KB的页面数据,Lighttpd就是全部吃下来,也不过只消耗50MB的内存而已,而事实上1000个FastCGI 进程足以支撑每日上千万的大网站了。

只有当我们使用服务器端程序控制大文件下载的时候,有可能造成Lighttpd内存暴涨,例如某个用户使用100个线程并发下载JavaEye圈 子的共享文件,在没有特殊处理的情况下,Lighttpd将全部吃下100个FastCGI进程送过来的10MB数据,就会立刻暴涨1GB的内存。这种情 况怎么办呢?其实我们也有办法让Lighttpd一点内存都不吃, 请看我写的另外一篇文章:RoR网站如何利用lighttpd的X-sendfile功能提升文件下载性能

可能很多人看了我的文章,对结论觉得很诧异,既然Lighttpd+FastCGI这样好,为什么那么多人都推崇Mongrel,否定FastCGI呢?我想,不外乎几个原因:

一、Lighttpd+FastCGI配置起来比较专业,而Mongrel配置简单

尽管我当初第一次搭建Lighttpd+FastCGI环境没费什么周折,但是我观察到非常多的Ruby程序员很难成功搭建一个 Lighttpd+FastCGI的环境出来,很多人连Lighttpd都无法独立的运行起来。这也许是因为很多程序员习惯了Windows开发环境,对 于Unix上面通过源代码编译安装的方式过于陌生造成的。而我从97年开始使用Unix,至今已有10年历史,因此搭建这样简单的系统,对我来说不造成什 么障碍。

而Mongrel就简单了,gem install mongrel安装完毕,mongrel_rails start启动,哪个人不会?毕竟绝大多数开发人员和部署人员不是高手,他们熟悉哪种方式,自然就会推崇哪种方式。

二、Mongrel可以独立作为Web服务器运行,开发环境和部署环境统一

一般来说,程序员肯定是尽量保持开发环境和部署环境的一致性,避免部署到生产环境出现不测的后果。既然在开发环境熟悉了Mongrel,当然更加愿意在生产环境使用Mongrel,而不愿意碰没有接触过的Lighttpd。

三、Mongrel支持HTTP协议,因此不论监控还是集成其他服务都比较简单,容易玩出更多的花活。

HTTP协议要比FastCGI协议普及的多,因此通过HTTP方式的监控工具,群集管理工具,集成其他服务的工具都是一抓一大把。而支持 FastCGI的第三方工具就少得可怜了。你要玩很多花活出来,用FastCGI的话,就难免得自己开发相应的工具,那当然不如使用Mongrel方便 啦。

下午在安装调试WordPress的时候做了个固定链接(/%postname%),但是通过WP内部的URL居然不能访问 经过多方查找资料才发现问题原因。WP 由于使用的是UTF8字符集.但是URL访问提交过来的URL中文字符是GBK 所以根据这个GBK的字符串查找文章标题 肯定是找不到.. 所以要对这个GBK字符串转换成UTF8

经过多方查找,终于知道了不用插件只需简单的修改两句代码就可以支持汉字链接了。
以下是修改方法

修改wp-includeclasses.php文件:

1、找到

$pathinfo = $_SERVER['PATH_INFO'];

修改为

$pathinfo = mb_convert_encoding($_SERVER['PATH_INFO'], 'UTF-8', 'GBK');

2、找到

$req_uri = $_SERVER['REQUEST_URI'];

修改为

$req_uri = mb_convert_encoding($_SERVER['REQUEST_URI'], 'UTF-8', 'GBK');

(以上两处修改都在函数parse_request()内)

经过修改之后 WordPress URL里就可以支持中文字符串了

绝对能勾起食欲的下饭菜:剁椒鱼

剁椒鱼:

材料:福寿鱼一条(重约500克),剁椒、生姜、葱、香菜、芝麻油、生抽、胡椒粉各少许;

做法

1.先将鱼收拾干净(可请卖鱼的老板将鱼宰杀好,自己弄可是很麻烦滴!绝对能勾起食欲的下饭菜:剁椒鱼),鱼肚子里面有一层黑色的膜,一定要将其清洗干净,再将鱼的两边各斜划两刀;

2.葱切花,香菜切段,生姜切丝,取一部份姜丝切成末;

3.将姜末放入鱼肚子中,这样可以大大降低鱼的腥味;

4.锅内放油,待油烧热后放入鱼,小火将其煎至两面微黄;

5.放入适量的盐,洒入料酒,加入少许胡椒粉、生姜与适量的剁辣椒;

6.再加入两小碗水,大火将其烧开后滴入几滴芝麻油,转小火煮5分钟左右;

7.放入葱花、香菜与少量生抽即可。

绝对能勾起食欲的下饭菜:剁椒鱼 绝对能勾起食欲的下饭菜:剁椒鱼 绝对能勾起食欲的下饭菜:剁椒鱼

绝对能勾起食欲的下饭菜:剁椒鱼 绝对能勾起食欲的下饭菜:剁椒鱼 绝对能勾起食欲的下饭菜:剁椒鱼 

绝对能勾起食欲的下饭菜:剁椒鱼 绝对能勾起食欲的下饭菜:剁椒鱼 绝对能勾起食欲的下饭菜:剁椒鱼

经验分享:

1.烧鱼之前先将锅子烧热后用生姜擦一遍,可防止沾锅;

2.在油里加入少许盐也可防止沾锅;

3.在鱼身上抹上少许淀粉,也可防止沾锅.

4.鱼下锅之前要将锅子充分地烧热,热油下锅,这样也可防止沾锅.

以下的文章主要介绍的是MySQL update 语句的实际用法,我们首先是以单表的UPDATE语句来引出实现MySQL update 语句的实际方案,以下就是文章的详细内容描述,望你看完之后会有收获。

单表的MySQL UPDATE语句:

	
  • UPDATE [LOW_PRIORITY] [IGNORE] tbl_name  
  • SET col_name1=expr1 [, col_name2=expr2 ...]  
  • [WHERE where_definition]  
  • [ORDER BY ...]  
  • [LIMIT row_count] 
  • 多表的UPDATE语句:

    	
  • UPDATE [LOW_PRIORITY] [IGNORE] table_references  
  • SET col_name1=expr1 [, col_name2=expr2 ...]  
  • [WHERE where_definition] 
  • UPDATE语法可以用新值更新原有表行中的各列。SET子句指示要修改哪些列和要给予哪些值。WHERE子句指定应更新哪些行。如果没有WHERE子句,则更新所有的行。如果指定了ORDER BY子句,则按照被指定的顺序对行进行更新。LIMIT子句用于给定一个限值,限制可以被更新的行的数目。

    MySQL UPDATE语句支持以下修饰符:

    如果您使用LOW_PRIORITY关键词,则UPDATE的执行被延迟了,直到没有其它的客户端从表中读取为止。

    如果您使用IGNORE关键词,则即使在更新过程中出现错误,更新语句也不会中断。如果出现了重复关键字冲突,则这些行不会被更新。如果列被更新后,新值会导致数据转化错误,则这些行被更新为最接近的合法的值。

    如果您在一个表达式中通过tbl_name访问一列,则UPDATE使用列中的当前值。例如,以下语句把年龄列设置为比当前值多一:

    	
  • MySQL> UPDATE persondata SET ageage=age+1; 
  • MySQL UPDATE赋值被从左到右评估。例如,以下语句对年龄列加倍,然后再进行增加:

    	
  • MySQL> UPDATE persondata SET ageage=age*2, ageage=age+1; 
  • 如果您把一列设置为其当前含有的值,则MySQL会注意到这一点,但不会更新。

    如果您把被已定义为NOT NULL的列更新为NULL,则该列被设置到与列类型对应的默认值,并且累加警告数。对于数字类型,默认值为0;对于字符串类型,默认值为空字符串(”);对于日期和时间类型,默认值为“zero”值。

    UPDATE会返回实际被改变的行的数目。MySQL_info() C API函数可以返回被匹配和被更新的行的数目,以及在UPDATE过程中产生的警告的数量。

    您可以使用LIMIT row_count来限定UPDATE的范围。LIMIT子句是一个与行匹配的限定。只要发现可以满足WHERE子句的row_count行,则该语句中止,不论这些行是否被改变。

    如果一个UPDATE语句包括一个ORDER BY子句,则按照由子句指定的顺序更新行。

    您也可以执行包括多个表的UPDATE操作。table_references子句列出了在联合中包含的表。以下是一个例子:

    	
  • SQL>UPDATE items,month SET items.price=month.price  
  • WHERE items.id=month.id; 
  • 以上的例子显示出了使用逗号操作符的内部联合,但是multiple-table UPDATE语句可以使用在SELECT语句中允许的任何类型的联合,比如LEFT JOIN。

    注释:您不能把ORDER BY或LIMIT与multiple-table UPDATE同时使用。

    在一个被更改的multiple-table UPDATE中,有些列被引用。您只需要这些列的MySQL UPDATE权限。有些列被读取了,但是没被修改。您只需要这些列的SELECT权限。

    如果您使用的multiple-table UPDATE语句中包含带有外键限制的InnoDB表,则MySQL优化符处理表的顺序可能与上下层级关系的顺序不同。在此情况下,语句无效并被 回滚。同时,更新一个单一表,并且依靠ON UPDATE功能。该功能由InnoDB提供,用于对其它表进行相应的修改。

    目前,您不能在一个子查询中更新一个表,同时从同一个表中选择。