在 Discuz 中应用 MogileFS 分布式文件存储系统

Posted by David on 2009-05-10 in Web Development

本文讨论内容基于 Discuz 7, Red Hat Enterprise Linux AS 4, MogileFs Server 2.17, MogileFS Client 1.08, MogileFS Utils 2.14

MogileFS 安装和配置

安装
基本安装顺序是 mogilefs-server(服务端), MogileFS-Client(客户端), MogileFS-Utils(工具包)。安装 MogileFS 其实并不复杂,但有些耗时,因为大多数据时间都被用在安装依赖包上了。在安装 MogileFS 之前有几个包是必需的:

Sys-Syscall-0.22.tar.gz
Danga-Socket-1.56.tar.gz
String-CRC32-1.4.tar.gz
Gearman-1.07.tar.gz
Gearman-Client-Async-0.93
Net-Netmask-1.9015.tar.tar 
Perlbal-1.70.tar.tar

若在安装过程中还提示其它包不存在,可以根据提示到search.cpan.org搜索相应的包装上。具体安装步骤可以参考这里这里

安装成功后,会提示安装了以下文件,在下文的 MogileF S配置部分中会提到。

/usr/bin/mogilefsd
/usr/bin/mogstored
/usr/bin/mogdbsetup
/usr/bin/mogautomount
/usr/bin/mogtool
/usr/bin/mogadm

数据库配置
MogileFS 通过数据库来维护文件的存储节点和存储份数等相关属性。 创建一个数据库命名为 mogilefs,创建一个用户 mogile,设置密码为 mogile

# mysql
mysql> CREATE DATABASE mogilefs;
mysql> GRANT ALL ON mogilefs.* TO 'mogile'@'%';
mysql> SET PASSWORD FOR 'mogile'@'%' = PASSWORD('mogile');
mysql> FLUSH PRIVILEGES;
mysql> quit

然后通过 MogileFs 自带的工具创建表

# /usr/bin/mogdbsetup --dbhost=db.yourdomain.com --dbname=mogilefs --dbuser=mogile --dbpassword=mogile

tracker 配置
编辑 /etc/mogilefs/mogilefsd.conf

db_dsn DBI:mysql:mogilefs
db_user mogile 
db_pass mogile 
conf_port 6001 
listener_jobs 5

存储节点配置

  • 先编辑配置文件 /etc/moiglefs/mogstored.conf

    httplisten=192.168.0.7:7500 
    mgmtlisten=192.168.0.7:7501 
    docroot=/var/mogdata
        
  • 向系统添加存储节点主机,并查看是否添加成功。这里我们添加的主机名称是 file.yourdomain.com,IP为192.168.0.7

    # /usr/bin/mogadm --trackers=192.168.0.7:6001 host add file.yourdomain.com --ip=192.168.0.7 --port=7500 --status=alive
    # /usr/bin/mogadm --trackers=192.168.0.7:6001 host list
        
  • 向系统添加存储节点设备,并查看是否添加成功

    # mkdir /var/mogdata/dev1
    # /usr/bin/mogadm --trackers=192.168.0.7:6001 device add file.yourdomain.com 1
    # /usr/bin/mogadm --trackers=192.168.0.7:6001 device list
        

    这里的 1 代表 file.yourdomain.com 主机下的存储设备编号,对应存储目录为 /var/mogdata/dev1

  • 添加存储域。在 MogileFs 中,文件通过 KEY 来引用,KEY 在某个存储域下是唯一的。我们添加一个 bbs.yourdomain.com 域,论坛里的附件都存储到该域下。

    # /usr/bin/mogadm --trackers=192.168.0.7:6001 domain add bbs.yourdomain.com
    # /usr/bin/mogadm --trackers=192.168.0.7:6001 domain list
        
  • 添加存储类别。在 MogileFs 中可以设置不同的存储类别存储不同份数的文件副本。我们添加一个 attach 类,每个文件至少存储 2 份

    # /usr/bin/mogadm --trackers=192.168.0.7:6001 class add bbs.yourdomain.com attach --mindevcount=2
    # /usr/bin/mogadm --trackers=192.168.0.7:6001 class list
        

最后在防火墙配置上述几个端口访问规则。现在可以启动 MogileFs,并查看进程与服务状态。

# /usr/bin/mogstored --daemon
# /usr/bin/mogilefsd -c /etc/mogilefs/mogilefsd.conf --daemon

# ps -fe |grep mogstored
# ps -fe |grep mogilefsd

# /usr/bin/mogadm --trackers=192.168.0.7:6001 check
# /usr/bin/mogadm --trackers=192.168.0.7:6001 stats

注:上述配置例子基于tracker,存储节点均在同一台主机,而实际使用上需要将存储节点分配到不同主机上才是真正意义上的分布式存储。甚至于 tracker 也可以是多个。

安装 MogileFS PHP 扩展

http://svn.usrportage.de/php-mogilefs/trunk/ 获取源代码编译安装:

$ phpize
$ ./configure
$ make install

将生成的 php-mogilefs.so 复制到 PHP 扩展目录,在 php.ini 添加一行扩展配置:

extension=php-mogilefs.so

重启 WEB 服务,用 phpinfo() 函数测试,如果显示结果如下图显示则表示安装成功
mogilefs
该扩展提供的方法和函数可以直接使用,不过为了方便维护,可以对之进行一下简单的封装。

改造 Discuz 的附件系统

接下来会应用到 WordPress 的 Hook 机制,详见WordPress 的 Hook 机制在 Discuz 二次开发中的应用

附件上传

在 Discuz 7 中附件的上传由include/post.func.php文件里的attach_upload()函数处理,我们在其中增加将附件添加到 MogileFS 的处理。由于 Discuz 7 支持远程上传附件到 FTP,并且可以设置成只上传到远程 FTP,这会删除上传到 WEB 服务器的原始附件,所以要在这之前处理。

// begin of hack
dz_do_action('mogilefs_attachment_upload', $attach, $target);
// end of hack

// 找到以下这行代码,在这之前添加以上代码
$attach['remote'] = !$swfupload ? ftpupload($target, $attach) : 0;

mogilefs_attachment_upload 函数用来处理往 MoigleFs 添加附件。在前面提到,MogileFs 通过 KEY 来引用文件,我采用的 KEY 的规则是:

attach_{attachment_md5}_{ext}
attach_{attachment_md5}_thumb_jpg

{md5} 是 Discuz 附件表 attachment 字段的 MD5 值,{ext} 是附件的扩展名。注意,Discuz 存储文件时会把部分可能不安全附件的后缀名更改为 attach。Discuz 中图片附件的缩略图均为 JPG 格式,所以 thumb_jpg 表示 JPG 格式缩略图的意思。

mogilefs_attachment_upload() 函数的处理流程:

  1. 判断 MogileFs 扩展是否加载,否则退出处理
  2. 获取 MogileFs 单个操作实例
  3. 根据传入的附件信息($target, $attach)生成 attach_key,然后往 MogileFs 提交文件
  4. 如果附件是图片,检查图片的缩略图是否存在,如果存在,则生成 thumb_key ,然后往 MogileFs 提交缩略图文件

附件删除

Discuz 7 中附件文件的删除由include/global.func.php文件里的dunlink()函数处理,我们在该函数结束之前添加 MogileFs 的文件删除处理:

dz_do_action('mogilefs_attachment_delete', $filename, $havethumb);

mogilefs_attachment_delete() 函数的处理流程:

  1. 判断 MogileFs 扩展是否加载,否则退出处理
  2. 获取 MogileFs 单个操作实例
  3. 根据传入的附件路径 $filename 生成 attach_key,然后通过 MogileFs 提供的方法删除文件
  4. 根据 $havethumb 判断是否存在缩略图,若存在则生成 thumb_key,然后通过 MogileFs 提供的方法删除文件

由于附件的上传中可能出现错误,Discuz 7 会做回滚处理删除本次上传出现错误之前已经正常上传的附件,而这些文件同样已经被上传到 MogileFs,故也要做删除处理:
修改include/post.func.php文件里的upload_error()函数和ftpupload_error()

function upload_error($message, $attacharray = array()) {
  if(!empty($attacharray)) {
    foreach($attacharray as $attach) {
      @unlink($GLOBALS['attachdir'].'/'.$attach['attachment']);
      // begin of hack
      dz_do_action('mogilefs_attachment_delete', $attach['attachment'], true);
      // end of hack
    }
  }
  showmessage($message);
}

function ftpupload_error($source, $attach) {
  @unlink($source);
  if($attach['thumb']) {
    @unlink($source.'.thumb.jpg');
  }
  // begin of hack
  dz_do_action('mogilefs_attachment_delete', $source, $attach['thumb']);
  // end of hack
  showmessage('post_attachment_remote_save_error');
}

附件读取
附件的读取需要在 Discuz 后台打开“下载附件来路检查”选项,因为附件的读取操作是 hook 在attachment.php文件上的。

  • 缩略图处理

    // begin of hack
    dz_do_action('mogilefs_attachment_fetch', $attach['attachment'], true);
    // end of hack
    
    //找到下面这行代码,在此之前添加上述代码
    getlocalfile($attachdir.'/'.$attach['attachment'].'.thumb.jpg');
        
  • 其他附件处理
    先删除attachment.php中的以下代码

    if(!$attach['remote'] && !is_readable($filename)) {
      showmessage('attachment_nonexistence');
    }
        

    然后将以下代码添加到getlocalfile()函数的起始处,接管该函数读取本地附件的处理。

    // begin of hack
    dz_do_action('mogilefs_attachment_fetch', $filename);
    // end of hack
        

mogilefs_attachment_fetch() 的处理流程

  1. 判断 MogileFs 扩展是否加载,否则退出处理
  2. 获取 MogileFs 单个操作实例
  3. 根据传入参数判断读取的是缩略图还是原附件,生成 thumb_key 或 attach_key
  4. 通过 MogileFs 扩展提供的方法读取文件内容
  5. 输出

当然,此外还有一些细节处理,如文件大小,文件头响应输出等,不再赘述。

Tags: , .

Comments

  1. 1 Chen

    不愧是做技术的,厉害。

  2. 2 David

    @Chen: 这还只是初级的应用而已。

  3. 3 洒水车

    好复杂啊 看不懂

  4. 4 Feerbook

    看上去,有点复杂,不懂

  5. 5 布衣

    怎么实现 WordPress首页每个分类只出现一篇文章

    要实现排序。就是最新发布的放在前面。。。

    所以。现有的那个函数没法用的。因为没办法排序。

    怎么查询数据库才能 每个分类查询出来最新的一篇文章呢?

    比如。我有十个分类。

    然后 调用 每个分类的最新文章。然后这十篇文章按照发布时间排序。

    具体怎么实现呢|

    请大神赐教。万分感谢

  6. 6 David

    @布衣: 已经邮件回复给你了

  7. 7 lgpzll

    我怎么总也配置不成功呢,怎么回事呢

  8. 8 lgpzll

    Unable to find MogileFS::Admin module. Please ensure that you have the
    module installed in a location in your search path. Or, add a search path
    to mogadm:

    mogadm –lib=/path/to/lib

    Or add it to the configuration file:

    lib = /path/to/lib

  9. 9 David

    @lgpzll: 检查一下 MogileFS-Utils 有没有安装正确

  10. 10 you are good

    很详细 顶一个

Leave a Reply

You can use these XHTML tags:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>