ip_conntrack: table full, dropping packet.

流量比较大的服务器上经常出现 “ip_conntrack: table full, dropping packet.” 这类的信息。
由于CentOS/Redhat默认的ip conntrack参数值比较低,要做一些调整:

在/etc/sysctl.conf
net.ipv4.ip_conntrack_max = 131072
在/etc/rc.local 加入:
echo 16384 > /sys/module/ip_conntrack/parameters/hashsize

然后
sysctl -p
echo 16384 > /sys/module/ip_conntrack/parameters/hashsize

这样就调高了ip conntrack table的大小和对应优化ip conttrack模块的hash 大小,对于网络应用链接多的场合适用。

要查看当前链接数:

cat /proc/sys/net/ipv4/netfilter/ip_conntrack_count

node.js-高效的javascript evented I/O framework

node.js是最近时期比较热门的主题。
这个基于V8的javascript io framework,由于是evented,性能很高,符合当前低炭高效的主旋律。
对于那些有大量并发链接的场景,不妨尝试一下。

node.js上手非常容易,因为javascript估计几乎每个web开发人员都或多或少知道。
node.js也证明了server side js是大有前途。当然,这里的所指的js开发者应该是那些真正熟悉
javascript的开发者,对于那些仅仅当作前端脚本的人来说,需要补课的地方还是非常多的。

在node.js基础上,派生了很多的framework和许多有趣的东西,这里列出一些node.js相关的资料:

node.js

关于nodejs的howto

nodejs的一些相关的项目和资料

Mongo DB Native NodeJS Driver

Non-blocking I/O, Event loops and node.js

MongoDB 1.4新特性以及如何使用Icc编译 (build with Icc v11.x)

mongodb 1.4.0正式发布,将生产环境的1.2.x升级到1.4.0. 从1.3.2开始我已经在开发环境和个别slave服务器中部署,
没有什么大的问题。
1.4有不少值得称赞的改进,对于开发人员来说,以下是需要关注的几个:

对数组字段的支持明显增强了。

$set ,$unset 都支持数组角标了,也就是可以直接更新或删除数组中的子记录了。
$elemMatch
这个对于使用embbed object来说是必须的。1.2系列的如果要查找embbed object的数组记录,必须匹配全部的属性字段,造成检索
嵌入的数组记录成为一个及其困难的事情。$elemMatch弥补了这个缺陷,基本上打消了我使用embbed array的顾虑。

$addToSet
给数组加入一个唯一的值。1.2版本要使用push模拟实现这个,需要在条件中使用$not,但这样当需要upsert的时候会有问题。所以我的解决方案是先pull再push

$
新增的$数组操作符,用于代表“匹配的数组的记录的下标值。一般是用于配合更新。目前只能应用到结果中的首个记录。

在复制方面,增加了–fastsync选项,适合用于使用起一个slave来快速备份数据。(我经常这么干,比较省心省事)。

=========
使用ICC编译MongoDB是比较麻烦的,它使用scons来做为build系统,相对于传统的autoconfig,automake来说,需要
读懂scons的配置文件。
我直接修改了SConstruct, 加入了几个选项。我对server和client,tool分别选用了不同的编译参数,由于scons的install命令有些问题,
我直接用unix的install来安装的,不具通用性。

SConstruct的patch

[root@nightsailer.com mongodb-src-r1.4.0]# cat mongodb-1.4-icc-build.patch
==============
— SConstruct.1.4 2010-03-27 21:47:22.503284322 +0800
+++ SConstruct 2010-03-27 22:23:48.040272478 +0800
@@ -253,6 +253,32 @@
dest=”sharedclient”,
action=”store”,
help=”build a libmongoclient.so/.dll”)
+#hack for icc compiler by n.s.(nightsailer.com)
+AddOption( “–icc”,
+ dest=”icc”,
+ type=”string”,
+ nargs=0,
+ action=”store” )
+
+AddOption( “–icc-cxxflags”,
+ dest=”icc-cxxflags”,
+ type=”string”,
+ nargs=1,
+ action=”store”,
+ help=”cxxflags for icc” )
+
+AddOption( “–icc-cppflags”,
+ dest=”icc-cppflags”,
+ type=”string”,
+ nargs=1,
+ action=”store”,
+ help=”cppflags for icc” )
+AddOption( “–icc-ldflags”,
+ dest=”icc-ldflags”,
+ type=”string”,
+ nargs=1,
+ action=”store”,
+ help=”ldflags for icc ” )

# — environment setup —

@@ -303,7 +329,7 @@

justClientLib = (COMMAND_LINE_TARGETS == ['mongoclient'])

-env = Environment( MSVS_ARCH=msarch , tools = ["default", "gch"], toolpath = ‘.’ )
+env = Environment( MSVS_ARCH=msarch , tools = ["default","gch"], toolpath = ‘.’,ENV = os.environ )
if GetOption( “cxx” ) is not None:
env["CC"] = GetOption( “cxx” )
env["CXX"] = GetOption( “cxx” )
@@ -357,6 +383,8 @@
for x in GetOption( “extralib” ).split( “,” ):
env.Append( LIBS=[ x ] )

+#hack for icc compiler by n.s.(nightsailer.com)
+icc_build = not GetOption(“icc”) is None
# —— SOURCE FILE SETUP ———–

commonFiles = Split( “stdafx.cpp buildinfo.cpp db/common.cpp db/jsobj.cpp db/json.cpp db/lasterror.cpp db/nonce.cpp db/queryutil.cpp db/cmdline.cpp shell/mongo.cpp” )
@@ -689,6 +717,16 @@
if GetOption( “gdbserver” ) is not None:
env.Append( CPPDEFINES=["USE_GDBSERVER"] )

+ #hack for icc compiler by n.s.(nightsailer.com)
+ if icc_build:
+ print ‘***************WARN:This build only valid for ICC v11.x EM64T ****************’
+ if GetOption( “icc-cxxflags” ) is not None:
+ env['CXXFLAGS'] = GetOption(“icc-cxxflags”)
+ if GetOption( “icc-cppflags” ) is not None:
+ env['CPPFLAGS'] = GetOption(“icc-cppflags”)
+ if GetOption( “icc-ldflags” ) is not None:
+ env.Append( LINKFLAGS = GetOption(“icc-ldflags”) )
+
# pre-compiled headers
if False and ‘Gch’ in dir( env ):
print( “using precompiled headers” )
=============

compile.sh
============
#!/bin/bash
#dirty,hard compiler for n.s.(nightsailer.com)
COMMON_CXXFLAGS=’-fp-model source -unroll2 -axSSE2,SSE3,SSE4.1,SSE4.2 -xSSE3 -static-intel -fpic -fno-strict-aliasing’
CPPFLAGS=’-I/opt/local/include -I.. -pthread’
CXX=icc
VERSION=1.4.0
DEST=/opt/mongodb-$VERSION
BIN_TOOLS=”mongodump mongorestore mongoimport mongoexport mongofiles mongosniff mongostat mongobridge”
BIN_SERVER=”mongod mongos”
BIN_SHELL=”mongo”
LIB_CLIENT=”libmongoclient.a”
build() {
CXXFLAGS=”-O3 -ip -fno-inline-functions $COMMON_CXXFLAGS”
CPPFLAGS=’-I/opt/local/include -I.. -pthread’
scons –release -Q –extrapath=/opt/local –cxx=$CXX –icc –extralib=’tcmalloc_minimal’ –icc-cxxflags=”$CXXFLAGS” –icc-cppflags=”$CPPFLAGS” $*
}
build_tools() {
CXXFLAGS=”-O3 -ipo $COMMON_CXXFLAGS”
scons –release –extrapath=/opt/local –cxx=$CXX –icc –extralib=’tcmalloc_minimal’ –icc-cxxflags=”$CXXFLAGS” –icc-cppflags=”$CPPFLAGS” -c all
scons –release –extrapath=/opt/local –cxx=$CXX –icc –extralib=’tcmalloc_minimal’ –icc-cxxflags=”$CXXFLAGS” –icc-cppflags=”$CPPFLAGS” $BIN_TOOLS mongoclient
CXXFLAGS=”-O2 -ip $COMMON_CXXFLAGS”
scons –release –extrapath=/opt/local –cxx=$CXX –icc –extralib=’tcmalloc_minimal’ –icc-cxxflags=”$CXXFLAGS” –icc-cppflags=”$CPPFLAGS” mongosniff
scons –release –extrapath=/opt/local –cxx=$CXX –icc –extralib=’tcmalloc_minimal’ –icc-cxxflags=”$CXXFLAGS” –icc-cppflags=”$CPPFLAGS” mongostat
scons –release –extrapath=/opt/local –cxx=$CXX –icc –extralib=’tcmalloc_minimal’ –icc-cxxflags=”$CXXFLAGS” –icc-cppflags=”$CPPFLAGS” mongobridge
}
build_shell() {
CXXFLAGS=”-O2 -ip $COMMON_CXXFLAGS”
scons –release –extrapath=/opt/local –cxx=$CXX –icc –extralib=’tcmalloc_minimal’ –icc-cxxflags=”$CXXFLAGS” –icc-cppflags=”$CPPFLAGS” -c $BIN_SHELL
scons –release –extrapath=/opt/local –cxx=$CXX –icc –extralib=’tcmalloc_minimal’ –icc-cxxflags=”$CXXFLAGS” –icc-cppflags=”$CPPFLAGS” $BIN_SHELL
}
build_server() {
CXXFLAGS=”-O3 -ipo -static-libgcc $COMMON_CXXFLAGS”
scons –release –static –extrapath=/opt/local –cxx=$CXX –icc –extralib=’tcmalloc_minimal’ –icc-cxxflags=”$CXXFLAGS” –icc-cppflags=”$CPPFLAGS” -c $BIN_SERVER
scons –release –static –extrapath=/opt/local –cxx=$CXX –icc –extralib=’tcmalloc_minimal’ –icc-cxxflags=”$CXXFLAGS” –icc-cppflags=”$CPPFLAGS” $BIN_SERVER
}

install_mongodb() {
mkdir -p $DEST/bin
mkdir -p $DEST/include/mongo
mkdir -p $DEST/include/mongo/util
mkdir -p $DEST/include/mongo/db
mkdir -p $DEST/include/mongo/client
mkdir -p $DEST/lib64
install *h $DEST/include/mongo/
install -m 0644 util/*.h $DEST/include/mongo/util/
install -m 0644 db/*.h $DEST/include/mongo/db/
install -m 0644 client/*.h $DEST/include/mongo/client/
install -m 0755 -s $BIN_SERVER $BIN_TOOLS $BIN_SHELL $DEST/bin
install -m 0644 $LIB_CLIENT $DEST/lib64
install -m 0644 distsrc/* $DEST
}
build_tools
build_server
build_shell
install_mongodb
==================

在CentOS/Redhat 5.4上使用openswan搭建数据中心之间的VPN

最近由于主数据中心的流量过大,为了分流和成本,在国外购买了几个独立主机。这样就需要在2个数据中心之间进行
数据的同步,包括mysql,mongodb,以及反向代理等。通过在使用IPSec创建VPN从而将2个子网连接在一起是
最合适的方案。

在最初曾经考虑过使用OpenVPN,但是由于我们的应用涉及到较大的数据传输,OpenVPN的性能相对于IPSec的方案还是差,
我们也无法通过为openvpn部署硬件加速。出于成本考虑,最后选择纯软的vpn。

首先尝试使用CentOS自带的IPSec进行配置,不过很快失败。RedHat的文档描述的很不清楚,此外,配置比较繁琐,
按照其说明配置也无法连通,实在没有耐心,于是改用openswan来部署。

在部署openswan之前,先要简单画出2个子网的拓扑图,只要分出“左”《-》“右” 即可:

A(192.168.8.x/211.x.x.x) +————+ B(192.168.9.x/174.xx.xx.xx)

按照上图,A为left 节点,B 为 right节电。192.168.8.x和 192.168.9.x分别为2个网络的网段。
在2个网段分别选择一个主机作为网关,这样分别设置这个2个网关就可以了。
实际为:
gate_a(192.168.8.70/211.x.x.x) , gate_b(192.168.9.1/174.x.x.x)

这2个host都需要有2个网卡,分别是链接内网和公网,记住上面的内网和公网ip。

下面开始安装:(gate_a)

1. 安装openswan
gate_a.nightsailer.com> yum install ipsec-tools
gate_a.nightsailer.com> yum install openswan

注意:下面这步很关键,CentOS-5.4有个bug,安装openswan时自动创建cert db并不正确,所以首先需要
重新创建,否则下一步生成key的时候就会报错。

gate_a.nightsailer.com>certutil -N -d /etc/ipsec.d

2. 现在,为gate_a生成密钥:
gate_a.nightsailer.com>ipsec newhostkey –configdir /etc/ipsec.d/ –output /etc/ipsec.d/keys.secrets

3.显示输出gate_a 作为left的公钥:
gate_a.nightsailer.com>ipsec showhostkey –left
ipsec showhostkey nss directory showhostkey: /etc/ipsec.d
# rsakey Axxxx
leftrsasigkey=0sA………

将eftrsasigkey=0sA….. 这行输出记下,后面备用

4. 修改/etc/ip_sec.conf

# /etc/ipsec.conf – Openswan IPsec configuration file
#
# Manual: ipsec.conf.5
#
# Please place your own config files in /etc/ipsec.d/ ending in .conf

version 2.0 # conforms to second version of ipsec.conf specification

# basic configuration
config setup
# Debug-logging controls: “none” for (almost) none, “all” for lots.
# klipsdebug=none
# plutodebug=”control parsing”
# For Red Hat Enterprise Linux and Fedora, leave protostack=netkey
protostack=netkey
nat_traversal=yes
virtual_private=
oe=off
# Enable this if you see “failed to find any available worker”
nhelpers=0

#You may put your configuration (.conf) file in the “/etc/ipsec.d/” and uncomment this.
include /etc/ipsec.d/*.conf

5. 现在为这个vpn单独创建一个conf文件,放到/etc/ipsec.d/nightsailer.com_vpn.conf

conn nightsailer_vpn
#左节点的公网ip(gate_a)
left=211.x.x.x
#左节点的内网网段
leftsubnet=192.168.8.0/24
#左节点的网关的内网ip(可选)
leftsourceip=192.168.8.70
#左节点的id,可以是ip,也可以是域名:
# leftid=@gate_a.chinavisual.com
# 建议最好用ip,域名需要反向解析,dns没配好很容易出问题
leftid=211.x.x.x
# 上一步显示的gate_a作为left的公钥
leftrsasigkey=0sAQO…
leftnexthop=%defaultroute
#以下参数含义和上面的一样
right=174.x.x.x
rightsubnet=192.168.9.0/24
rightsourceip=192.168.9.1
rightid=174.x.x.x
rightrsasigkey=0sAQOp….
rightnexthop=%defaultroute
#是否在ipsec启动时自动启用这个链接
#auto = add ( 若这个选项,则需要手动up vpn链接)
auto=start

现在,登录到gate_b,重复上面的步骤1-5。

注意,第3步略有不同,因为gate_b是right节点,所以要显示它的right 密钥:
gate_b.nightsailer.com>ipsec showhostkey –right
ipsec showhostkey nss directory showhostkey: /etc/ipsec.d
# rsakey Axxxx
rightrsasigkey=0sA………

将rightrsasigkey=0sA….. 这行输出更新/etc/ipsec.d/nightsailer.com_vpn.conf(包括gate_a上的)

现在分别启动ipsec
gate_a.nightsailer.com>/etc/init.d/ipsec start
gate_b.nightsailer.com>/etc/init.d/ipsec start

好了,现在在gate_a和gate_b上相互ping,就可以ping通了。

网关通了,那么还需要再各自子网添加一个路由:
A网段添加:
route add -net 192.168.9.0 netmask 255.255.255.0 gw 192.168.8.70 eth1
#添加静态路由
在 /etc/sysconfig/network-scripts/route-eth1 追加:
192.168.9.0/24 via 192.168.8.70 dev eth1

B网段添加:
route add -net 192.168.8.0 netmask 255.255.255.0 gw 192.168.9.1 eth1
在 /etc/sysconfig/network-scripts/route-eth1 追加:
192.168.8.0/24 via 192.168.9.1 dev eth1

一切就大功告成了。

快速恢复mysql master-master同步

系统出现错误导致2个Master之间出现不一致。需要立刻同步。
由于错误出现的时间超过了binlog的保留范围(已经被清除),所以只能重新做。

1. 用mmm_control 关闭对应节点
mmm_control set_offline db11
mmm_control set_offline db2

2. 重置master/slave(db2,db11)
reset master;
reset slave;
slave stop

3. 复制最新的master到后备master(from db2 to db11)
使用 mk-parallel-dump和mk-parallel-restore 安全,快速。
xtrabackup 也可以,但不够快速和安全,且需要各个节点配置完全一样。

4. 重新启用replication
db2:
change master to master_host=’192.168.8.11′,master_user=’xxxxxx’,master_password=’xxxx’, master_log_file=’mysql-bin.000001′,master_log_pos=0;
db11:
change master to master_host=’192.168.8.2′,master_user=’xxxxx’,master_password=’xxxx’, master_log_file=’mysql-bin.000001′,master_log_pos=0;

both:
slave start
show master status\G
show slave status\G

5. set db online
mmm_control set_online db2
mmm_control set_online db11

完毕。

令我发狂IE cookie的问题

这个问题基本上可以令人发狂。在 6.0 sp3 091208-2036以后的版本(其他版本也可能有问题)。
如果使用php的set_cookie 如果设置的过期时间比较短,比如180秒后过期,那么cookie将无法创建。
而之前的IE版本以及如Firefox,Chrome,Safari等均没有这个现象。

发现这个原因,是我折腾了一晚上,并且特意弄了台windows,用QQ远程在一个网友的机器上反复折腾,
当临近崩溃的最后一秒发现的。

血泪的教训。 cookie的过期时间最好在1个小时以上,通过将过期时间写入值存入cookie,然后判断,不要依赖浏览器,尤其是
狗屎的IE6.

听说国外有人给IE6举办了葬礼,什么时候国内能有呢,那对于web开发真是个福音。不过,IE7/8对于网银的支持实在
是糟糕透顶,对于我这种mac用户,IE乃至windows的唯一用途就是上网银。。。所以,我的虚拟机用的还是IE6.。。
天大的讽刺!

还好,招商银行支持iphone,方便多了。

PHP:call_user_func_array 导致的诡异事件

最近,在升级原创榜时,发现了Doggy中Dt模版的一个诡异现象。当访问某些页面时,不定期出现Gateway timeout.
PHP没有报任何错误,也没有任何错误日志或者信息输出。我一度认为是模版的Tag有问题,因为当尝试
删除某些Tag后会解决这个问题。好景不长,更多的页面也冒出此类错误,然而当我把模版内部解析缓存关闭后,则不会出现此类错误。 于是转头跟踪Dt模版的内部调用。Dt模版的设计中,当模版第一次被解析后,将模版转换成Doggy_Dt_NodeList对象,并将它缓存到内部cache中。当模版下次被调用时,如果模版没有变动,则直接调用NodeList的render方法,这样可以减少模版解析的时间。然后,当从缓存中被反序列的Doggy_Dt_NodeList,却无法正确执行filter。看下面的一段代码:

if (isset(Doggy_Dt::$filters[$name])) {

foreach ($args as $i => $argument) {
# name args
if (is_array($argument)) {
foreach ($argument as $n => $arg) {
$args[$i][$n] = $this->resolve($arg);
}
}
else {
# resolve argument values
$args[$i] = $this->resolve($argument);
}
}
array_unshift($args, $object);
$object = call_user_func_array(Doggy_Dt::$filters[$name], $args);
}

罪魁祸首是:call_user_func_array(Doggy_Dt::$filters[$name], $args)。

此处调用的call_user_func_array导致无法autoload class。于是php就悄无声息的挂了。

说是诡异,就是因为,如果不是反序列出来的,则能够正常的调用。
此外,在官方手册中,对于call_user_func_array还有一段注释:

Note: Callbacks registered with functions such as call_user_func() and call_user_func_array() will not be called if there is an uncaught exception thrown in a previous callback.

也就是说,如果之前call_user_func_array()导致了未知的异常,那么后续的call_user_func_array则不会被调用。

后来检查,的确模版中也有错误的filter tag。 但,奇怪的是只有是反序列(unserialize)回来的才会重复此类
现象。 因此,未必是完全如注释所说的原因。
我猜测,有可能是call_user_func_array的错误导致了反序列后的对象无法正确再次调用call_user_func_array,至少在call_user_func_array中无法正确autoload class。

解决方法,其实也是修复上面代码的一个bug,在call_user_func_array前增加is_callable判断。
if (isset(Doggy_Dt::$filters[$name]) && is_callable(Doggy_Dt::$filters[$name])) {

}
else {
Doggy_Log_Helper::warn(‘unkown filter:’.$name);
}

于是,世界就太平了。

MongoDB practice:基于MongoDB的好友消息动态的实现思路(How to build activity-streaming with MongoDb)

好友动态是SNS最常见的功能。在设计“视觉中国原创榜”的好友动态时,也遇到如何实现的问题。和普通的SNS不同,
视觉中国原创榜用户不仅仅关注好友的动态,而且也要关注自己的作品和自己曾经关注过的作品的动态。
这样,就需要给用户分别push 3种不同的动态: 我的作品的动态,我关注过的作品(包括收藏过,评论过,评分过)的动态,
以及我关注的人(followed)的动态,未来还有好友的动态。这些动态,用户都应该可以取消关注。如何实现?
使用传统的数据库会面临很多问题,比如如何sharding。 幸运的是,我们用的MongoDB,这给我们解决问题带来了极大的方便。

首先,对每个用户,分别设计以下 collection

//by nightsailer.com
activity_streaming.feed, 属性分别为:
$schema = array(
_id, 用户id
followed_art => array(), 用户关注的作品id列表
followed_people=>array(), 用户关注的人id列表
my_art => array() 用户的作品id 列表
)
//by nightsailer.com
activity_stream.user:
_id
type=> 动态steam的类型(我关注的作品,我的作品,我关注的人)
stream_target => 对应动态stream的对象(作品id,人id)
stream => array() FIFO数组,存放activity的DBRef
time => int 最后一次activity的时间戳

activity_stream.site
_id uuid
type: 动态类型
data: hash 动态数据
time

activity_stream.queue
同 activity_stream.site
存放待处理的动态队列

我们使用异步处理方式,按照以下流程:
1. 当用户某些动作产生一个事件后,将事件push到activity_stream.queue, 通知worker进行处理
2. worker 被唤醒,从activity_stream.queue中提取未处理的事件。
3. worker 将事件放入activity_stream.site,作为全站动态广播
4. worker 从activity_streaming.feed中反向查找事件的作品或作者是否有对应的人,如果有,则将此事件id
push到activity_stream.user 的对应fifo数组中。
(这是MongoDB最兴奋的地方,可以直接查询数组中的值,只要对数组进行索引)

这样,用户可以:

- 从activity_stream.user 中删除某个事件
- 从activity_stream.feed 中删除某个关注的对象(实现类似忽略这个人的动态,忽略这个作品的动态等等)
- 当用户关注好友后,将其加入activity_stream.feed
- 当用户上传作品后,将作品加入activity_stream.feed
- 当用收藏、评分、评论后,将其作品加入activity_stream.feed

以上是MongoDB实践的第二篇,待续。

MongoDB practice: My Perl GridFS Wrapper

简单写了一个Perl版本的GriFS的wrapper:

package CZone::GridFS;
use strict;
use MongoDB::GridFS;
use Path::Class;
use Digest::file qw(digest_file_hex);
use Digest::MD5 qw(md5_hex);
use IO::File;
use Data::Dumper;
use Any::Moose;

has database => (
isa => ‘MongoDB::Database’,
is => ‘ro’,
required => 1
);

has _gridfs => (
isa => ‘MongoDB::GridFS’,
is => ‘ro’,
lazy => 1,
builder => ‘_build__gridfs’,
);

has _file_collection => (
isa => ‘MongoDB::Collection’,
is => ‘ro’,
lazy => 1,
builder => ‘_build__file_collection’
);

sub _build__gridfs {
my $self = shift;
return $self->database->get_gridfs;
}

sub _build__file_collection {
my $self = shift;
return $self->database->get_collection(‘fs.files’);
}

sub get_bytes {
my ($self, $id ) = @_;
my $file = $self->_gridfs->find_one({_id => $id });
my $bytes;
my $fh = new IO::File \$bytes,’>';
$file->print($fh);
return $bytes;
}

sub store_file {
my ($self, $file_path) = @_;
my $file = file($file_path)->absolute;
return undef unless -e $file;
my $md5 = digest_file_hex($file,’MD5′);
my $fh = $file->open(‘r’) or return undef;
return $self->_store_fh($fh,$md5);
}

sub _store_fh {
my ($self,$fh,$md5) = @_;
# $grid_file isa MongoDB::GridFS::File
my $grid_file = $self->_gridfs->find_one({ ‘md5′ => $md5});
if ($grid_file) {
$self->_inc_refs($grid_file->info->{_id});
return $grid_file->info->{_id};
}
else {
my $oid = $self->_gridfs->insert($fh,{
refs => 1,
md5 => $md5,
});
return $oid;
}
}

sub store_bytes {
my ($self, $bytes) = @_;
my $md5 = md5_hex($bytes);
my $fh = new IO::File \$bytes,’<';
# my $fh = FileHandle->new;
# $fh->open(\$bytes,’<');
return $self->_store_fh($fh,$md5);
}

sub unlink {
my ($self, $id ) = @_;
$self->_dec_refs(MongoDB::OID->new(value =>”$id”));
}

sub _inc_refs {
my ($self,$id) = @_;
$self->_file_collection->update({_id => $id },{ ‘$inc’ => { refs => 1}});

}

sub _dec_refs {
my ($self,$id) = @_;
$self->_file_collection->update({_id => $id },{ ‘$inc’ => { refs => -1}});
}

sub gc {
my $self = shift;
$self->_gridfs->remove({refs => 0});
}

no Any::Moose;
__PACKAGE__->meta->make_immutable;
1;

__END__

这是从czone项目中的PHP代码移植过来的。
方便将gridfs中的文件读写到scalar中。同时,通过检查存储文件的md5值,并记录相同文件的引用计数,相同文件只存储一个copy,节省空间。(BSON格式对于空间的需求是非常大的)

I’m back.

域名转移完毕. 现在是新域名 nightsailer.com了.

wordpress中需要更新settings中的WordPress address (URL)和Blog URL.
然后使用301将night9.cn 重新定向到 nightsailer.com.

« Previous PageNext Page »