wordpress抽啥疯

wordpress 2.8系列升级太快了吧。才多长时间就匆忙升到2.8.2,而且还遗留了像visual editor正常安装下
都无法工作的问题。mu版本也跟风,rc没多久就轻松跳啊跳,跳到2.8.2了。只能说,太不靠谱了。
我现在对mu 2.8系列的稳定性打了个大大的问号,不知道是否有大规模部署的实例?

世道不好,观望为佳。

从doggy1.3的跳票谈起

今天抽空看了下blog的后台,发现有个朋友在我去年一篇帖子后留言说跳票了,已经是2009-7-22,
不禁汗颜。因为原来文中我是计划去年7月底能够发布Doggy 1.3的。

跳票的原因很多,杂事,乱事很多。更主要原因是没有最直接的动力。毕竟之前1.2内部用的都还凑合,
而之后相关产品开发陷入迟钝。另外,惰性使然。

1.3的代码其实早就完成了,但是却没有机会使用,最近1个月,由于几个产品出现了新的性能问题,
我也决心重新打开1.3的代码库。这1年来就很少接触PHP的开发工作,重新审视,也有了很多新的想法。
这样,原始的1.3的代码就仅停留在试验阶段,我有选择的将这部分代码合并到1.2中,并形成了新的1.3分支。
尽量保持一定的兼容性,做了一些妥协,因为还需要将很多产品升级,过于激进就缺乏实际了。

我很理解很多项目为什么要opensource,最主要的一个原因是作者没有精力而希望借助社区的力量让项目
的生命力得以延续。

但,现阶段,我不能开源。开源也需要勇气、精力和责任。首先,我没有足够的时间和精力解决使用者的种种问题,
从安装到调适。其次,我无法保证这个框架能够有广泛的适用性,更无法保证能够给用它的人带来什么好处,
现如今,较为流行的PHP框架不是少而是太多,较为熟悉的如ThinkPHP,FleaPHP, Kohana, Yii.这些都是很好的选择。
选择和学习一个framework,对于一个PHP程序员来说真的是一个折磨,我也不想再添乱。
我也建议如何选择framework:

如果你用1天时间还没有感受它的好处,扔掉它。
如果你用1周的时间还无法用它重构一个原有的中等规模的项目(比如blog,bbs,cms),扔掉它。

最多花1周的时间,太多了就亏本了。

PHP现如今有点要重复当年Java的道路,开发的方式似乎越来越“重”, 看看所谓官方的Zend Framework吧,
设计的成分到处都是…..

我用Doggy最大的一个理由是我懒,懒得花时间去学习一个新的framework。
我不怕它缺少某个功能,而是担心功能太多,杂而不精(从这个意义上,我
个人还是比较喜欢CI,比较简单,可以仅仅当作一个lib来使用)。 因此,这次首先是删除代码,我把这1年来,大家都没有使用过的模块全部删除,比如Threads(实现Java的threads功能),现在想,有点华而不实。
这样大概删除了3-5个模块,也增加了几个真正缺乏的模块,最主要的是模版引擎,实现类Django的模版,取代Smarty. 其他实用的是添加支持Flare,Redis,MongoDb的模块。这些是解决一些高并发场合的利器。

我希望,如果有天,doggy的运行性能能够远超同类的项目,我可以把它开源,因为值得折腾。
在此之前,还是集中精力,闭门造车吧。

MongoDb的用途

MongoDB是个好东西,唯一缺憾是目前的shard还比较初级,但可以在客户端做。

对于它的用途,目前初步的想法:

1. 实时的流量分析统计
使用Google统计固然好,但不够实时。使用MongoDb的特性倒是比较容易实现统计。

2. MogileFS的替代方案
对于mogileFS的替代方案,准备做2个尝试
1种是使用Flare构建,实际上Flare内置了一个fusefs,可以参考。
第二是用MogoDB搭建。最新的sf.net用mongo存储所有的项目页面和下载页面,算是一个成功的实例(它是使用TurboGear2实现的)。
mogo内置了gridFS的支持,虽然Perl不支持,但是PHP还是不错的,况且perl的gridfs实现也并不难。

我心里不是特确定的是它的能力,能够承载多大的数据量?
1T以上的数据是否可以承受? 如果不行,就需要考虑shard,这是目前一个短板。
此外,mogileFS的复制和冗余功能也需要考虑实现。

3. 整合服务的后端数据库
使用json可以将任何数据文档轻松存储,这对于聚合类的服务非常适合。
结合sphinx,可以轻松实现全文检索。

Redis – 有点神奇的data structures server

Redis 早期版本曾经测试过。由于缺乏我需要的PHP/Perl binding,后来就放弃了。
但Redis开发神速,目前已经接近1.0-rc1,据作者自言,可以用了。呵呵。

Redis的神奇之处在于对于value的处理,除了String类型,还增加了Set、List。这点是超越Memcached之所在。
Redis支持对list的pop/push,range原子操作,很容易实现类似Queue,Stack等的数据结构.为此,作者说,可以把redis看做data structures server。

安装

Redis使用posix c编写,所以安装直接make就完事。 运行可以直接用内置配置跑,还是非常方便的。如果一个软件是否能够快速安装测试部署,会得到更多人的青睐。尤其是像需要短时间测试体验类似的东西,要是配置麻烦,我直接就放弃了。

协议

Redis没有使用memcached协议,而是使用自己的一套规范

速度

Redis的速度很快,110000 SETs/second, 81000 GETs/second。和普通版的memcached相当。

命令
除了针对String的set/get外,redis支持针对Set/List的操作,如LPUSH,RPUSH。
这里有完整的命令列表

多Db
这是另一个有意思的东西,Redis支持不同的db空间,通过selectdb来切换到不同的db空间。从而实现数据的隔离。redis允许key在不同的db之间移动,也支持flush某个db。这个特性可以解决原来memcached中不支持tag/namespace的问题。

Sharding
和Flare不一样,Redis本身没有内置的sharding功能,而是需要通过client api来实现。比如和libmemcached一样通过实现consistent hashing来实现sharding。

Replication
仅支持简单master-slave。 slave支持只读查询,slave也可以通过上级slave来获得同步数据。

缺陷:
1. redis将所有数据放入内存中,而通过后台进程定时将数据dump到磁盘上,从而实现持久存储。这是redis为什么这么快的原因。因此,如果你的存储数据超过可用内存大小,就会导致OS使用swap文件。作者解释,鱼和熊掌不可兼得,既然要快,就多费点内存吧,反正现在内存也很便宜,呵呵。
2.由于只能通过client端实现sharding,而redis并不支持master之间的复制,因此如何实现动态节点添加和删除,只能由client端来实现。从这个方面说,redis还只能作为一个高级的memcached。

=====
我准备测试用Redis解决以下场景的问题:
1. 高速消息队列
2. 访客列表类
常见的最近来访啊,这篇文章谁看过啊,谁谁点评过啊,等等
3. SNS中常见的事件,最近发生了什么
4. 最近更新名单
虽然以上这些通过将数据结构unserialize为string后可以用memcached来实现,但既然redis是data structures server,那么会更加高效好用。
同时,这些数据也不是关键数据,即便丢失也无伤大雅,对于需要可靠保存的数据,要么使用flare,更为安全则应使用传统的数据库。

Flare-兼容Memcached协议的分布式(key/value store)键值存储系统

Flare, Green Lab开发的开源产品,底层使用TC, 完全兼容Memcached协议(ASCII,非binary)。主要有以下特性:

  • 支持Master/slave复制
  • 支持master 分区
  • 支持动态节点添加删除,可以failover
  • 持久存储

现在似乎大家都很关心好的key/value存储方案。作为传统数据库的补充。Memcached由于缺乏持久存储功能,因此无法作为一个可靠的key/value方案。
我之前关注过基于Memcached派生的一些方案,包括
memcachedb – 新浪团队的,bdb+memcached协议
tokyo tyrant – TC+memcached网络接口
LightCloud 在Tokyo Tyrant上实现了多节点的分布式管理。

repcached - 在memcached源码基础上增加复制功能
redist - 除了string,还允许存储list、set等类型

除了上述软件,还有一些用Erlang,Java的方案,我并没有考虑。首先,我的使用场景还没有大到那个地步,我希望选择一个性价比更高的中低端方案 ;-)

这其中,最关注的是TT(Toyko tyrant)。 但TT令我不太满意的是对于memcached的协议的兼容性并不够好,比如不能正确处理过期,也不能处理flag。导致客户端无法正常解压缩和unserialize,虽然在client端都能处理,不过当时是打算作为一个session storage,无法自动过期就比较麻烦,解决方案是通过在TT端执行lua可以进行gc处理,总体感觉不够完满。
memcachedb 对于存储定长字段不错,但不固定长度记录的存储似乎不够好,也没有现行的例子。
repcached 仅仅是支持mm复制,但无法正面解决持久存储的问题。

redist 当初测试时缺乏我需要的api。不过目前已经有了PHP PECL版本的api。 redist的复制机制现在也有了很大的改进。redist的应用实例不多。但是,我认为它和mongoDb一样,值得持续关注。

flare 则是最后选择的。经过测试,虽然速度上没有其他那么快,但其良好的扩展能力让我非常满意。此外,对于memcached协议的兼容性也做的很好。
还有1个特性,flare的key可以超过256bytes,而value可以超过1mb。 传统memcached由于内存分配策略因此有上述限制。
flare的底层存储是可以扩展的,目前使用的是tc(tokyo cabinet)。
从这个结构上看,倒是一个tt的很好替代品。

flare的运行性能还是不错,目前在GREE中得到实际应用,摘自项目网站描述:

Flare is running at GREE (one of the major SNS services in Japan) w/ 6 masters and 6 slaves (over 2,000M keys), and 500-1,000 qps (load average is nearly 0.00…).

看起来很诱人。因为同样TT超过100M的key,性能会直线下降。plurk的lightclound通过多节点的负载均衡解决了这个问题。(btw,plurk.com目前是被和谐中,要想取得lightclound源码还需要穿墙,唉)

以下是在我的osx上进行的安装测试。

1. 首先下载源码

http://labs.gree.jp/Top/OpenSource/Flare/Download-en.html

2. 使用port安装boost

$ port
install boost

3. 安装tc

http://tokyocabinet.sourceforge.net/

4. 编译flare
tar zxvf flare-1.0.8.tgz
./configure –with-boost=/opt/local –prefix=/opt/flare
make install

flare编译后只有2个文件
flarei,flared
分别是index server和node server.
很清爽啊。

5.运行
flare sources下有一个etc目录,中间分别有index server, node server的配置。
可以直接复制到/opt/flare/etc
然后使用
flared –daemonize -f
flarei –daemonize -f

就可以跑起来了。

6.概念
Flare有几个概念,需要理解
index server
这是索引服务器,用于控制node server的状态
注意,client并不直接和index server进行交互。
node server
这是实际存储节点. node有3种role:
master/slave/proxy
master是主节点,slave是分流节点,用于同步复制master
proxy则将client的请求转发到当前合适的节点(包括master/slave)。
有的人不太理解为什么要用proxy,是否多此一举,而实际测试表明,
通过proxy转发的请求的确要比直接connect到实际节点速度差很多。
这是因为,flare是一个集群,其中的node server是可以动态添加,删除的。
当某个node down后,index server会检测到,并标志其state为down。
此外,master 支持partition,因此通过proxy节点,client无需处理这些复杂问题。
根据作者的建议, proxy node应该和client本地运行,这样可以减少多余的tcp请求。

下面是我写的一个脚本,用于搭建一个典型的测试场景:
1个index server
2个master node,启用partition
1个slave,启用balance

由于flare把ip:port作为一个node,因此只需要1个ip,不同的port就可以快速实现测试需要的环境。

$./start_flare.sh
#启动index server,监听 127.0.0.1:12120
flarei –daemonize -f /opt/flare/etc/flarei.conf
#proxy
flared –daemonize –data-dir /opt/flare/var/data/d0 –index-server-name 127.0.0.1 –index-server-port 12120 –server-name 127.0.0.1 –server-port 12121
#master1 node
flared –daemonize –data-dir /opt/flare/var/data/d1 –index-server-name 127.0.0.1 –index-server-port 12120 –server-name 127.0.0.1 –server-port 12122
#slave node
#flared –daemonize –data-dir /opt/flare/var/data/d2 –index-server-name 127.0.0.1 –index-server-port 12120 –server-name 127.0.0.1 –server-port 12123
#master2
#flared –daemonize –data-dir /opt/flare/var/data/d3 –index-server-name 127.0.0.1 –index-server-port 12120 –server-name 127.0.0.1 –server-port 12124
echo “waiting dameon startup…”
sleep 3
echo “set nodes role….”

# 当某个node加入时,默认是proxy role,因此需要修改这些role
# 通过telnet到index server,可以执行这些命令,我们在脚本中可以使用
# nc来自动执行

# node role的命令格式:
# node role server port master|slave|proxy balance partiion

echo “node role 127.0.0.1 12122 master 1 0″|nc 127.0.0.1 12120
# 设置为slave, balance 为2,给于2倍read权重
echo “node role 127.0.0.1 12123 slave 2 0″|nc 127.0.0.1 12120
# 第2个master node,将partition设置为1,表明这是第2个partion,允许
#将部分数据从原来的master 同步过来
echo “node role 127.0.0.1 12124 master 1 1″|nc 127.0.0.1 12120
sleep 2
echo “stats nodes”|nc 127.0.0.1 12120

STAT 127.0.0.1:12121:role proxy
STAT 127.0.0.1:12121:state down
STAT 127.0.0.1:12121:partition -1
STAT 127.0.0.1:12121:balance 0
STAT 127.0.0.1:12121:thread_type 16
STAT 127.0.0.1:12122:role master
STAT 127.0.0.1:12122:state down
STAT 127.0.0.1:12122:partition 0
STAT 127.0.0.1:12122:balance 1
STAT 127.0.0.1:12122:thread_type 17
STAT 127.0.0.1:12123:role slave
STAT 127.0.0.1:12123:state prepare
STAT 127.0.0.1:12123:partition 0
STAT 127.0.0.1:12123:balance 0
STAT 127.0.0.1:12123:thread_type 18
STAT 127.0.0.1:12124:role master
STAT 127.0.0.1:12124:state prepare
STAT 127.0.0.1:12124:partition 1
STAT 127.0.0.1:12124:balance 1
STAT 127.0.0.1:12124:thread_type 19
END

stats nodes可以列出当前各个节点的状态信息
注意, 有些节点的state是prepare,表明正在进行同步复制。

我进行了一个简单的测试, 分别读写1M 记录,
测试代码


?php
$host = $argv[1];
$port = $argv[2];
set_time_limit(0);
echo "connect to $host $port \n";
$memcached = new Memcached();
$memcached->addServer($host,$port);

$loop = 1000000;
echo "start set tests...\n";
$start =microtime(true);
for ($i=0; $i < $loop; $i++) {
    $memcached->set(md5("k$i"),md5("k$i"));
}
$end = microtime(true);
echo "ok.\n";
$total1 = $end-$start;
echo "start get tests...\n";
$start =microtime(true);
for ($i=0; $i < $loop; $i++) {
    $memcached->get(md5("k$i"));
}
$end = microtime(true);
$total2 = $end-$start;
echo "ok.\n";
echo "set time:$total1 ",$loop/$total1,' rps',"\n";
echo "get time:$total2 ",$loop/$total2, ' rps',"\n";
?>

当单节点执行set/get操作时,速度和memcached
的ascii模式基本一样,rps只略低几百个。
但是使用proxy 节点后,只有原来的一半。

============
推荐的Flare的适用场景:
1. 最正统的就是key/value storage。我自己将flare作为doggy中dhash的后端实现。
2. Session Storage。 比如PHP,可以直接适用memcached的session_handler。由于是持久的,因此解决原来memcached的尴尬和困扰。
3. 其他的基于key/value的扩展方案

Flare可以直接替代Memcached么?
答案是否。Flare的优势和本质是分布式key/value的持久存储,而不是作为一个cache。作为一个caching方案,要比memcached差很多。
尤其是启用了binary protocol后,memcached的优势很明显。