目录

之前公司有个SNS方向的项目,由于有过Elgg的经验,所以选定继续采用Elgg作为基础来进行二次开发。项目中也更深入了解了Elgg的原理和特性,在性能优化和分布式扩展上积累了一些经验,分享记录一下,希望对有兴趣的童鞋有所帮助。

1. Elgg是什么

简单来说,Elgg是一套开源的基于PHP开发的社交网络平台套件,最初接触到Elgg的时候是2008年国外InfoWorld组织的一个开源软件评选,Elgg被评为best open source social networking platform。Elgg实现了社交网络常见的基本功能:关注、好友、微博、信息流、日志、群组等等。Elgg最有特点的是它的插件结构,除了极少数核心功能以外,大部分功能都是通过插件的形式实现,也就意味着各个功能模块都可以被重载替换或者停用,很方便开发者分别对各个功能进行修改更新,而不影响其他模块的正常运行。

此外,Elgg本身对object metadata和attribute的实现形式在编程开发上提供了很大的简化,开发人员可以用类似NoSQL的赋值语法$object->anything = anything 添加和修改任意的metadata,而不用去考虑扩展数据库表的字段等问题。

当然,这些优点在某些程度上反而变成了Elgg的缺点,开发上的便利对性能会有一定的影响,这些我会在以后的文章里具体解释。

2. Elgg的分布式部署架构

Elgg本身是典型的LAMP架构,很明显,单节点部署的模式不能满足高可用、负载均衡等等已经不算很新的要求,于是有了下面这样简单的分布式部署架构:

最前端用HA实现负载均衡,接一系列可以水平扩展的Web Server运行Elgg程序,后面接一个共用的Database (MySQL)。为满足可用性要求,MySQL也需要配置Master/Slave结构。

LAMP结构的这类应用,压力瓶颈最容易集中在数据库一层,因此在单台数据库能够支撑之前,master/slave的结构够用,能保证服务稳定运行,如果压力进一步增大,则首先需要考虑实行读写分离,在应用中分别配置读数据库源和写数据库源(需要修改一定的代码),如果嫌麻烦,也可以使用mysql自带的一个mysql-proxy组件来实现读写分离,分散数据压力。如果数据量增长到更大的规模,则需要考虑进行分表了。当然,优化不是一步到位就可以搞定的,等到你必须要进行分表的时候,你的业务的访问量应该已经大到相当惊人的规模了:) 在那之前,倒不用过多考虑这样的问题,还是先想想怎么把业务做到那么大访问规模再说:)

3. Elgg+Nginx+Php-fpm

传统的LAMP架构中都是指的Apache服务器,对web服务器领域有所了解的同学一定都知道Nginx,是比apache更高效更灵活的选择,所以我们首先将web server这一层换成nginx,再接一个Php-fpm后台程序用来解析PHP代码。由于Elgg本身只提供了为apache准备的htaccess文件,需要编辑nginx的配置文件,下面给出一个elgg的nginx配置示例:

server {
    listen   80; ## listen for ipv4
    server_name  localhost;
    root   /opt/web;
        index  index.php index.html index.htm;
    access_log off;
        error_log  /var/log/nginx/elgg.error.log;
    location ~* favicon.ico {
                log_notfound  off;
        }
    rewrite action/([A-Za-z0-9-\/]+)$ /engine/handlers/actionhandler.php?action=$1 last;
        rewrite pg/([A-Za-z0-9-]+)/(.)$ /engine/handlers/pagehandler.php?handler=$1&page=$2 last;
        rewrite pg/([A-Za-z0-9-]+)$ /engine/handlers/page_handler.php?handler=$1 last;
        rewrite cache/(.)$ /engine/handlers/cachehandler.php?request=$1 last;
        rewrite services/api/([A-Za-z0-9-]+)/(.)$ /engine/handlers/service_handler.php?handler=$1&request=$2 last;
        rewrite export\/([A-Za-z]+)\/([0-9]+)/?$ /engine/handlers/exporthandler.php?view=$1&guid=$2 last;
        rewrite export\/([A-Za-z]+)\/([0-9]+)/([A-Za-z]+)\/([A-Za-z0-9]+)/$ /engine/handlers/export_handler.php?view=$1&guid=$2&type=$3&idname=$4 last;
        rewrite xml-rpc.php /engine/handlers/xml-rpc_handler.php last;
        rewrite mt/mt-xmlrpc.cgi /engine/handlers/xml-rpc_handler.php last;
        rewrite tag/(.+)/?$ /engine/handlers/page_handler.php?handler=search&page=$1 last;
        rewrite rewrite.php$ /install.php last;
        if (!-e $requestfilename){
                rewrite ([A-Za-z0-9-]+)/(.)$ /engine/handlers/pagehandler.php?handler=$1&page=$2 last;
                rewrite ([A-Za-z0-9-]+)$ /engine/handlers/page_handler.php?handler=$1 last;
        }
        location ~ .php$ {
                fastcgi_pass   127.0.0.1:9000;
                fastcgi_index  index.php;
                include        fastcgi_params;
        client_max_body_size  200m;
        proxy_set_header   Host             $host;
                proxy_set_header   X-Real-IP        $remote_addr;
                proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
        fastcgi_buffer_size 1024k;
        fastcgi_buffers 32 1024k;
        fastcgi_busy_buffers_size 2048k;
        fastcgi_temp_file_write_size 2048k;
    }
    location ~* .(css|js) {
        expires  30d;
    }
    location ~*  .(bmp|ico|gif|jpg|jpeg|png)$
    {
        expires  30d;
    }
}
这里有些细节说明一下:上面这个配置 php-fpm在本地的9000端口上提供服务,如果需要的话,可以配置一组php-fpm的upstream server, 然后配置nginx使用这一组服务器,真正做到水平横向扩展。此外,nginx本身有缓存静态文件的能力,对css,js和图像等静态内容,设置了缓存和失效时间,提高访问效率。

以上是在架构和配置上对Elgg进行扩展的基本介绍,后续我会深入Elgg的代码,从数据库访问、ache缓存、引擎启动优化等等各个方面解释对我们对Elgg进行的改造和优化,还有一些自己对这个架构的思考。