高性能LAMP程序设计

July 18th, 2011

周六分享的PPT,一些比较common的大杂烩,看不到slides的同学在这里查看。

我为什么选择MongoDB

May 30th, 2011

大概在08年,那时候nosql的概念特别热,最早的那批开源项目好多参考google bigtable来设计,我也关注过其中的几个,比如hypertable,couchdb之类,阅读了一些相关的文档和博文,没有太跟进,那些开源项目的设计scope太大,想解决google都不一定很好解决的问题,事实上国内能真正碰到那种数据规模的人少,很少,极少;迁移的成本也很高,我们的项目大多构建在mysql+memcached上,关系型的操作很多,这种key-value或者类key-value的数据库不是特别合用;也觉得很难从那些产品中获得什么可预知的好处,不管是性能上的还是开发上的,所以也在那些项目上浅尝辄止。

mongodb是从09年开始关注,从最初的一些文档可以看出这家伙是来解决实际问题的,mongodb的作者们对mysql+memcached这套东西的优势和弊端看的非常清楚,项目的设计对我们这些lamp程序员来说,也有天然的亲和力,你可以很容易把mysql的那套东西照搬到mongodb,比如collection对于mysql里面的table,没有言必谈分布式,可以很容易在单机上开发测试,可以相信在mongodb早期的auto-sharding只是个噱头,事实上在早期的很多版本官方也不太推荐大家用。

我在选用一些技术的时候大多考虑这些方面:

1. 新的方案能不能解决目前项目中难以忍受的问题?

2. 新的方案是不是足够简单?见过很多的项目为了解决一个稍显复杂的问题引入一个更加复杂的方案,着实累人

3. 新的方案能不能很平滑的应用到项目中去?

4. 新的方案能不能被替换?不能被替换的方案都是烂方案

在mongodb之前,我碰到的问题是:

1. 项目的需求不断变化,数据库表结构需要不断调整来满足新的需求,FriendFeed用的是schema-less方法来解决这种问题,但是schema-less也有一些问题,在设计时候需要考虑动静分离,要不然为了更新某个小数据需要频繁的更新整个大数据块会比较烦躁,数据的一致性和有效性需要在代码中特别注意。

2. 多对多关系的处理,典型的例子是文章tag,一篇文章有多个tag,每个tag对应多篇文章,传统的做法是维护2张表,一张是tag表,里面是tag_id对应的tag_name,另外一张是文章对应tag表,里面维护3个字段,tag_id、文章id、tag被打的次数,然后当有用户新打tag的时候,你需要把新的tag和已有的tag比较,相同的+1,不存在的需要新建,然后置为1,删除什么的也是各种烦躁

mongodb文档型数据库设计可以很好的解决这些问题,schema less的设计可以把整片数据塞进去,可以很方便的对数据key建立索引,提高访问速度,tag处理也非常方便,直接把{tag_name:tag_times}数据丢进去就可以,非常方便。

mongodb支持大多数的mysql操作,原有代码稍作修改即可,可以很平滑的应用,对于一个MVC的应用来说,基本上小修改model层就可以,非常轻松,当然,如果一天mongodb不行了,再切换回去也不是什么困难的事情。

作为一个前中期的开源项目,mongodb也有一些问题需要注意:

1. 运维和监控工具不如mysql那么丰富

2. mongodb采用的mmap机制,在断电和某些异常情况下有可能丢失数据或者crash,好消息是1.8以后可以打开journal日志来避免此类问题

3. schema less是一把双刃剑,因为什么数据都可以往里面存,不像mysql那样会有字段的概念可以对数据有效性把最后一道关,需要在编写程序的时候特别注意一下数据有效性,当然用mysql也要注意

4. 没有auto increment id,这个很不爽,不过可以通过findandmodify来模拟出来

function autoIncrementId($domain, $collection = 'autoIncrementIds', $db = null)
{
    $result = $mongo->command(array(
        'findAndModify' => $collection,
        'query' => array('_id' => $domain),
        'update' => array('$inc' => array('val' => 1)),
        'new' => true,
        'upsert' => true
    ));
 
    if ($result['ok']) {
        return $result['value']['val'];
    }
 
    throw new Exception('Mongo: gen auto increment id failed');
}

如果你采用ColaPHP框架开发的话,这部分代码已经包含在里面了。

一个建议是如非必要,不要用mongodb里面独有的特性,用的越多,绑的越紧,这些特性包括auto-sharding之类。

另外一个好消息是国内第一本mongodb图书出版了《MongoDB权威指南》,这是一本手册性质的指南,译者是mongodb中国社区的负责人程显峰,对于不喜欢E文的同学了解mongodb大有裨益,我也在读。

我的SEO搜索引擎优化经验

April 20th, 2011

注意:我不是SEO从业者亦不是搜索引擎排名工程师,我尽量保证提到策略有效且合乎规则的,如有谬误,请略过或指正,我本人不推荐一些所谓的“黑帽”策略,因为能欺骗搜索引擎一时却不能长久,所以做垃圾站的或者想赚快钱的也可以忽略本文,本文的目标是希望和大家一起探讨如何创建一个既对用户又对搜索引擎友好的网站。

搜索引擎优化最值得参考的两篇文档:Google Search Engine Optimization Starter Guide百度搜索引擎优化指南,这是两份官方文档,非常值得仔细研读,网上其他文档充斥太多讹传和猜想(包括本文,虽然我极力想避免)。

一直觉得搜索引擎优化是一个符合马太效应的过程,需要慢慢的积累,表现好的网站越来越好,所以如果已经有一个好的网站尽量持久的运营,如果要换域名的话,原有的链接最好做301跳转到新域名的对应链接,这是搜索引擎推荐的做法,大家可以看看最近javaeye切换域名到iteye,基本的链接都做了301跳转,对用户来说也非常友好,robbin同学是个SEO高手,后面还会再提到他的另一个高明之处。

一、链接篇

传闻hao123站长李兴平曾说过“SEO没有什么技术,就是靠外链”,可见外链对SEO来说有多么重要的,我们把被链接的页面叫做子页面,链接到子页面的页面叫父页面,按照Google Pagerank的算法,子页面的父页面越多,子页面的权威性越高;子页面的父页面权威性越高,子页面的权威性也越高,有个域名是“www.miibeian.gov.cn”的网站,由于基本上全中国网站都链接到他了,不管网站做的怎么样,Google PR稳稳的为10。(不要试图访问这个网站,已废弃了,暴殄天物啊)

除了外链,站内链接也是一个非常重要的部分,作用虽没有外链那么作用大,胜在可以自己控制,添加一些相关链接,除了能够给其他页面提升权威性外,也能很好的提高用户粘性,降低跳出率。

链接里面有一个很重要的部分就是“锚文本”,什么是锚文本呢,比如这个链接 当知网 ,当知网三个字就是锚文本,锚文本对搜索引擎来说非常有用,因为搜索引擎很难获得某个页面的关键字,标题获取是最主要的来源,锚文本也是一个非常重要的因素,可以很简要的描述页面内容。

这里有个小故事,很多美国人对小布什不满意,于是有人发起了个倡议,在自己的博客上建立一个链接,链接的锚文本是”Loser”,链接的地址是小布什在白宫的页面,于是很多人用google搜索”Loser”,小布什的白宫页面稳稳的排在第一位,虽然小布什的白宫页面上没有”Loser”这个关键字,架不住从”Loser”跳过来的链接太多,也就壮烈了。(Google现在已经改进了算法,避免锚文本被人滥用)

网站添加链接时,尽量避免链接到作弊网站,因为”蛇鼠一窝”,搜索引擎很容易把链接到作弊网站的网站也当作作弊网站;需要特别注意的是开放了博客空间、留言板、BBS之类网站,很多spammer用灌水机贴了很多垃圾网址,要注意即时清除,否则容易被误伤,如果业务需求不能随便删除的话,可以参考现在微博的做法,提供一个短地址或者地址跳转服务,最不济的话,加上nofollow标记。

“链接堆砌”也不是一种好的做法,一段文字里面密集的链接到目标网站,现在的搜索引擎已经很容易识别了。

虽然之前百度的“左螺旋哥”风光无限,但”linkfarm”也不是一种好的做法,搜索引擎已经能够识别,作弊的方法虽然能短时间获得排名,一旦被K,基本万劫不复。

对于链接这种事情,建议还是一五一十的慢慢做,当然,有资源的写一些软文投递到一些优质的网站发表也大有裨益。

二、标题篇

页面标题是我认为SEO所有因素中的最重要的那个,一个好的标题应该应该包含关键字,比如本文的标题包含”搜索引擎优化”、”SEO”这两个关键字,标题不宜过长,忌关键词堆砌,标题太长的话,对于每个关键字的权重就降低了,另外,对于搜索引擎来说,页面标题的长度也是有限制的,太长的标题直接容易被切掉,大家可以看一下这个例子,这不是一个好的标题,重要的关键字放最后还被截掉了。

个人觉得一个好的标题设计可以参考这种格式:标题_频道名称_网站名称,好的标题”短小精悍”,切实把握用户心理需求,很久以来,我的一篇关于MySQL 分页的文章一直排在Google的第一位

页面的meta信息,包括description和keywords,对排序作用不大,对生成内容摘要有用,所以如果可以的话还是添加上去,免得搜索引擎把不重要的文字当作摘要,影响用户点击。

三、URL篇

url目录不要太深,/a/b/c/d/e/f.html这样的链接权重会有一些影响,另外迷宫型的url有可能会被认为是作弊/a/b/c/d/a/b/c/d/e.html

url设计尽可能精简,尽量不包含一些不能被系统自动识别为url的字符,方便用户转帖

尽可能的使用静态URL,虽然说搜索引擎已经能索引动态url,不管对用户还是搜索引擎来说,静态的url更好一点

同一篇内容,只有一个url,尽量避免无效的参数,有些网站为了统计的需要添加类似foobar.html?ref=xxx这样的参数着实不好,流传出去,一个页面多个地址

不同类型的内容url应该有区别,比如列表页/list/9527,内容页/view/3306

关于目录和子域名的选择,子域名的权重会比目录高一些,注意平衡,一般内容较少时用目录,忌滥用子域名,子域名越多,不太容易积累每个域名的权重,还不如重点维护好几个关键子域名

好的内容和差的内容一定要从子域名或子目录区分开,百度百科和百度贴吧两者的权重差别就很大,好的内容和差的内容(比如UGC的内容)混在一起,容易被误伤

url中包含英文关键字,对google来说甚佳,baidu来说不太清楚,url中多个关键字建议以”-”隔开,没有太多的证据证明中文关键字做urlencode对seo有效,不过中文拼音缩写对用户来说可能会更友好一点

对于时效性比较强的内容,可以用日期作为目录,现在的搜索引擎已经可以获取页面内容的时间

好的url设计应该能够会意,这篇URL设计准则值得读一下

四、内容篇

原创的内容更受搜索引擎青睐,搜索引擎对采集的内容也有一定的容忍度,一个好的网站最好不要大量采集,免得自己的优质内容被误伤,搜索引擎判断是否原创内容的方法有很多,比如页面生成时间、来源链接等等,不过很悲剧的是国内很多网站转帖不带来源地址,如果转帖网站的权重比原网站高的话,首先被搜索引擎索引到,很容易排在前面,所以我们经常会看到一些聚合类的网站搜索结果靠前

内容的阅读体验也非常重要,包括网站的打开速度、页面停留时间、跳出率等等,这些指标有的是从各个搜索引擎提供的统计系统中获得,有的是通过用户行为获取,比如页面停留时间,在搜索结果页中点第一个链接和第二个链接之间间隔,很有可能就被当作是第一页面停留的时间,当然这个有些不准,因为很多人喜欢打开多个,不过搜索引擎应该有办法来区分

页面的内容应该要和标题呼应,应用好<h1>……<h6>之类的标签突出你的标题和核心内容,html标签很多是有语义的,如果可以的话,建议按照规范使用

专题性的内容是搜索引擎的最爱,在搜索一些技术关键字的时候,很容跳到javaeye的关键字内容聚合页,这也是robbin高明之处

内容页面设计尽可能的清晰,方便搜索引擎索引需要的内容,如果搜索引擎给页面生成段落或者目录信息,恭喜你,说明你的网站权重已经不错了。

内容结构尽量扁平,可以按照这种结构规划:首页、列表页、内容页

尽量避免用JS/Flash/iframe之类的来展示主体内容,目前只有google宣称能够解析js内容,baidu明确表示不会解析js内容,iframe是被所有搜索引擎抛弃的

五、工具篇

Google网站管理员工具是我认为最好的SEO工具,方便查看网站的搜索引擎表现状况

站长之家的站长工具也是一个非常不错的辅助工具,方便查看pr、外链之类的属性

百度指数百度火爆地带Google Adwords关键字工具对于优化关键字来说很有参考意义

善用robots.txt和sitemap对于优化网站搜索引擎表现也大有裨益

后记:

搜索引擎优化是一个漫长的过程,涉及的因素很多,同样的木桶原理在这里也适合,本文的内容大多来自google和百度那两篇官方指导文档,大多经过实践验证,欢迎交流fuchaoqun#gmail.com (#换成什么,你懂的),可以交换一些网站推广方面的资源。

ColaPHP 1.1GA发布

March 19th, 2011

距离1.0GA发布有近3个月的时候了,ColaPHP现在来说已经比较稳定了,版本的发布周期会控制在3个月左右,1.1GA主要的变化有:

  • 引入Widget机制,方便页面片管理
  • 全局配置管理优化,配置调用Api微调,更好、更方便的满足大量配置信息管理
  • 其他优化和bugfix

下载ColaPHP 1.1GA,也可以访问ColaPHP项目主页:http://code.google.com/p/colaphp/

使用fastcgi_cache加速你的Nginx网站

January 1st, 2011

很久以前在TW上挖了个坑,说nginx的fastcgi_cache是被大家忽视的一大金矿,今天把这个坑填上,顺祝大家新年快乐。

对于变化不太频繁的数据,大家都比较喜欢存Memcached以减少数据库的读取,但还是会有语言解析运行上的消耗(比如运行PHP,Python等),当然这个时间很短,记得OP上有个同学说P字头的语言,效率都不高,如果能省去,当然最好。(已经用上Squid等的可以忽略本文)。

还有一个问题就是很多时候一个页面由多个数据片断组成,为了提高页面速度,要么分别缓存,要么整体缓存(所谓的Page Cache),其实都比较麻烦,增加和减少数据片断的时,大多需要调整。

最后一个问题,所有的数据都存Memcached是否经济?服务器资源足够多的无所谓,捉襟见肘的就要考虑了,当然,生成静态页面是一种方法,需要维护,还是比较累。

好吧,nginx的fastcgi_cache可以解决上面的那些问题,比较squid等的好处是简单,不需再要去维护另外一个系统,适合不那么大的网站。

关于Nginx fastcgi_cache,基础的可以参看Nginx官方文档http://wiki.nginx.org/HttpFcgiModule,下面是一个典型的做法是:

fastcgi_temp_path /data/ngx_fcgi_tmp;
fastcgi_cache_path /data/ngx_fcgi_cache levels=2:2 keys_zone=ngx_fcgi_cache:512m inactive=1d max_size=40g;
fastcgi_cache_valid 200 301 302 1d;
fastcgi_cache_use_stale error timeout invalid_header http_500;
fastcgi_cache_key $request_method://$host$request_uri;

注意一定要加上$request_method作为cache key,否则如果HEAD类型的先请求会导致后面的GET请求返回为空,全局定义一个缓存空间,配置文件名为,fastcgi_cache.conf,然后在vhost配置里面加上:

fastcgi_cache ngx_fcgi_cache;
include fastcgi.conf;

大概解释下各个参数的含义:

fastcgi_temp_path:生成fastcgi_cache临时文件目录

fastcgi_cache_path:fastcgi_cache缓存目录,可以设置目录哈希层级,比如2:2会生成256*256个字目录,keys_zone是这个缓存空间的名字,cache是用多少内存(这样热门的内容nginx直接放内存,提高访问速度),inactive表示默认失效时间,max_size表示最多用多少硬盘空间,需要注意的是fastcgi_cache缓存是先写在fastcgi_temp_path再移到fastcgi_cache_path,所以这两个目录最好在同一个分区,从0.8.9之后可以在不同的分区,不过还是建议放同一分区。

fastcgi_cache_valid:定义哪些http头要缓存

fastcgi_cache_use_stale:定义哪些情况下用过期缓存

fastcgi_cache_key:定义fastcgi_cache的key,示例中就以请求的URI作为缓存的key,Nginx会取这个key的md5作为缓存文件,如果设置了缓存哈希目录,Nginx会从后往前取相应的位数做为目录。

fastcgi_cache:用哪个缓存空间

这样就可以了,基本上可以work,但还没完,如何手动清除缓存?有个Nginx的第三方扩展可帮你做到:https://github.com/FRiCKLE/ngx_cache_purge/,如果对大多数第三方扩展无爱,写个清除的脚本也非常简单,以PHP为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
function purgeCache()
{
    $url = $this->post('url');
 
    if (empty($url) || !Cola_Com_Validate::url($url)) {
        exit('请输入正确的URL。');
    }
 
    $md5 = md5($url);
    $cacheFile = $this->_cacheRoot . '/' . substr($md5, -2, 2) . '/' . substr($md5, -4, 2) . '/' . $md5;
 
    if (!file_exists($cacheFile)) {
        exit('缓存不存在。');
    }
 
    if (@unlink($cacheFile)) {
        echo '清除缓存成功。';
    } else {
        echo '清除缓存失败。';
    }
}

核心是第11行,直接找到缓存文件,然后删掉就可以,这个脚本有个副作用,手动清除之后,缓存失效,但Nginx后面还会自己清除一遍,然后报个unlink失败的日志,不过无关紧要了。

淡定,文章还没完,要不就成标题党了,Nginx fastcgi_cache缓存很不错,但我只想在某些页面用fastcgi_cache,很简单,有两种方法,一是在location中定义fastcgi_cache,这样只有满足一定规则的url才会用上cache,其他的就不会了;另外一种方法是在你不需要缓存的页面上,输出禁止缓存的头信息,用ColaPHP的话,直接$this->response->disableBrowserCache(); 具体代码:

header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");

这样就告诉Nginx,这个页面不需要缓存。

好吧,要淡定不要D疼,还有最后一个问题,如果页面中只有一小部分内容不可以缓存,可以用Nginx fastcgi_cache吗?比如某个内容页,大部分内容可以缓存,但希望把用户的登录信息更新上去。答案是肯定的,可以直接输出用户未登录的页面样式,等页面加载完毕之后,通过ajax异步更新用户信息:

$().ready(function() {
    initUser();
})

码完收工。

Tags: ,

ColaPHP 1.0GA发布

November 27th, 2010

从2008年底开始,经历了4个alpha版本、5个beta版本,趁着今天下午去Beijing Open Party,ColaPHP发布1.0GA版本,这是ColaPHP一个重要的里程碑版本,相信我,这只是开始….

感兴趣的同学可以到http://code.google.com/p/colaphp/下载1.0GA的版本,当然,欢迎你下午和我一起交流。

Tags:

ColaPHP In Action

August 6th, 2010

看不到Slide的同学,可以直接点这里

Tags:

Mediawiki扩展编写实战

July 22nd, 2010

Wikipedia大家都很熟悉,而Mediawiki则是Wikipedia背后的功臣,整个Wikipedia都构建在mediawiki之上,mediawiki的稳定性和高效性值得信赖,同时Mediawiki非常易于扩展,可以通过Extension的方式添加非常多的功能,而且Mediawiki的Extension社区也非常活跃,大家可以到Mediawiki Extension目录下去下载自己需要的扩展程序。

上周末,帮朋友写了一些Mediawiki的扩展,立即被Mediawiki的强大扩展性折服,主要实现的功能有:增加Google Analytics统计、自定义标题、增加Google Adsense广告之类,写Mediawiki的扩展,最好的参考是Mediawiki扩展手册:http://www.mediawiki.org/wiki/Manual:Extensions

Mediawiki的扩展主要有Tag Extension、Parser Functions、Hooks、Special Pages、Skins、Magic Words,对应的中文是:标签扩展(自定义wiki标签,比如xxxx)、解析扩展(和标签类似,不过呈现方式稍有不通,为{{#foo : bar}})、钩子、特殊页面、皮肤、魔术关键字,我这里演示的是Parser Functions和Hooks,其他的差不多类似。

一、增加Google Analytics统计和Google Adsense广告

原理很简单,我们在页面显示之前,把Google Analytics和Google Adsense的代码append到要显示的内容即可,代码:

<?php
/**
 * 安全设置,防止恶意调用
 */
if (!defined('MEDIAWIKI')) {
	die( 'This file is a MediaWiki extension, it is not a valid entry point' );
}
 
/**
 * 扩展的基本信息
 */
$wgExtensionCredits['other'][] = array(
	'path'           => __FILE__,
	'name'           => '插件名称',
	'version'        => '1.0',
	'author'         => '作者',
	'descriptionmsg' => '简要说明',
	'url'            => '作者地址',
);
 
/**
 * 注册一个钩子,在页面显示之前,处理页面显示内容
 *
 * 全部钩子列表:http://www.mediawiki.org/wiki/Manual:Hooks
 *
 */
$wgHooks['BeforePageDisplay'][] = 'dzBeforePageDisplay';
function dzBeforePageDisplay(&$out, &$skin) {
    /**
     * 在LocalSettings.php定义$wgDangZhiAppendHtml
     * 把要添加的Google Analytics和Google Adsense代码放里面
     */
    global $wgDangZhiAppendHtml;
 
    // 页面添加HTML
    $out->addHTML($wgDangZhiAppendHtml);
 
    // 记得返回true,收工
    return true;
}

短短几行代码,完成添加Google Analytics统计和Google Adsense广告代码,当然,你还可以添加任意你想添加到网页最后的代码,可以查看示例网站的源代码,Google Analytics的代码布在每个页面上。

二、自定义文章标题

自定义文章标题对搜索引擎来说非常重要,Mediawiki默认的标题只能和内容条目一样,缺乏灵活性,这里的原理是增加一个{{#title: 标题内容}}的标签,在页面显示之前替换成网页标题,示例网站的首页就自定了HTML标题。

 
/**
 * 还是第一示例中的显示前处理函数,这里是部分内容
 */
function dzBeforePageDisplay(&$out, &$skin) {
 
    // 如果页面中存在{{#title: 标题内容}},那么指定文章标题
    preg_match('/\{\{#title: ?(.*)\}\}/U', $out->mBodytext, $matches);
    if ($matches) {
        $out->mBodytext = str_replace($matches[0], '', $out->mBodytext);
        $out->mHTMLtitle = $matches[1];
    }
 
    return true;
}

三、文章在新窗口打开

Mediawiki是外国人做的东西,国外少有默认新窗口打开链接的习惯,国人的习惯可不是这样,让Mediawiki默认新窗口打开的做法非常的简单。

/**
 * 还记得这个函数吧
 */
function dzBeforePageDisplay(&$out, &$skin) {
    // 新窗口打开链接
    $p = array('/(<a href=\"\/index.php\?title=.+\")/U', '/(<a href=\"\/wiki\/.+\")/U');
    $r = '$1 target="_blank"';
    $out->mBodytext = preg_replace($p, $r, $out->mBodytext);
 
    return true;
}

在页面输出之前,给需要的a标签加个target=”_blank”的属性即可。

四、解析HTML标签

Mediawiki为了安全考虑,不允许在内容中使用非安全HTML标签,我们可以使用一种加密HTML的方法来达到安全性和易用性的平衡。在内容中增加{{#html: 加密码 | html内容}},其中加密码是系统密钥和html内容的md5值,如果符合则显示。

/**
 * 初始化Parser Functions
 */
$wgHooks['ParserFirstCallInit'][] = 'dzParserSetup';
$wgHooks['LanguageGetMagic'][] = 'dzMagicSetup';
 
/**
 * 设置解析函数
 */
function dzParserSetup(&$parser) {
    $parser->setFunctionHook('html', 'dzParseHtml');
    // 可以设置多个,$parser->setFunctionHook('foo', 'dzParseFoo');
	return true;
}
 
/**
 * 初始化Parser关键字
 */
function dzMagicSetup(&$magicWords, $langCode) {
    $magicWords['html'] = array(0, 'html');
    // 可以设置多个,$magicWords['foo'] = array(0, 'foo');
    return true;
}
 
/**
 * 解析HTML
 */
function dzParseHtml($parser, $param1 = '', $param2 = '') {
    // 在LocalSettings.php定义HTML加密密钥
    global $wgHtmlSalt;
 
    $key = md5($wgHtmlSalt . $param2);
 
    if ($key == $param1) {
        return array($param2, 'noparse' => true, 'isHTML' => true);;
    }
 
    return 'bad html';
}

Mediawiki的扩展写起来还是相当容易,文中提到的效果可以在这里体验,老外写的东西对Extension都很重视,wordpress也是这样,不似国内的某些项目,稍做修改都得hack代码,hack的坏处是升级的时候容易悲剧。

Tags: ,

ColaPHP 0.8beta发布

July 3rd, 2010

代号:Mini,bugfix版本,改善框架易用性,代码重构。

下载ColaPHP 0.8beta,阅读ColaPHP文档,访问ColaPHP项目

ColaPHP 0.8beta已经很稳定,可以在生产环境使用。

Tags:

利用Sphinx实现实时全文检索

June 22nd, 2010

Sphinx 0.9.9及以前的版本,原生不支持实时索引,一般的做法是通过主索引+增量索引的方式来实现“准实时”索引,最新的1.10.1(trunk中,尚未发布)终于支持real-time index,查看SVN中文档,我们很容易利用Sphinx搭建一个按需索引(on demand index)的全文检索系统。

参考文章:http://filiptepper.com/2010/05/27/real-time-indexing-and-searching-with-sphinx-1-10-1-dev.html

首先,从sphinxsearch的SVN下载最新的代码,编译安装:

svn checkout http://sphinxsearch.googlecode.com/svn/trunk sphinx
cd sphinx/
./configure --prefix=/path/to/sphinx
make
make install

编译没问题的话,在sphinx安装目录下的etc,建立sphinx.conf的配置文件,记得一定指定中文编码方面的配置搜索,否则中文会有问题:

index rt {
    # 指定索引类型为real-time index
    type = rt
    # 指定utf-8编码
    charset_type  = utf-8
    # 指定utf-8的编码表
    charset_table  = 0..9, A..Z->a..z, _, a..z, U+410..U+42F->U+430..U+44F, U+430..U+44F
    # 一元分词
    ngram_len = 1
    # 需要分词的字符
    ngram_chars   = U+3000..U+2FA1F
    # 索引文件保存地址
    path = /path/to/sphinx/data/rt
    # 索引列
    rt_field = message
    # 索引属性
    rt_attr_uint = message_id
}
 
searchd {
    log = /path/to/sphinx/log/searchd.log
    query_log = /path/to/sphinx/log/query.log
    pid_file = /path/to/sphinx/log/searchd.pid
    workers = threads
    # sphinx模拟mysql接口,不需要真正的mysql,mysql41表示支持mysql4.1~mysql5.1协议
    listen = 127.0.0.1:9527:mysql41
}

启动sphinx服务:

/path/to/sphinx/bin/searchd --config /path/to/sphinx/etc/sphinx.conf

插入几条数据看看:

ubuntu:chaoqun ~:mysql -h127.0.0.1 -P9527
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 1.10.1-dev (r2351)
 
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
 
mysql> INSERT INTO rt VALUES (1, 'this message has a body', 1);
Query OK, 1 row affected (0.01 sec)
 
mysql> INSERT INTO rt VALUES (2, '测试中文OK', 2);
Query OK, 1 row affected (0.00 sec)
 
mysql>

测试全文检索:

mysql> SELECT * FROM rt WHERE MATCH('message');
+------+--------+------------+
| id   | weight | message_id |
+------+--------+------------+
|    1 |   1643 |          1 |
+------+--------+------------+
1 row in set (0.00 sec)
 
mysql> SELECT * FROM rt WHERE MATCH('OK');
+------+--------+------------+
| id   | weight | message_id |
+------+--------+------------+
|    2 |   1643 |          2 |
+------+--------+------------+
1 row in set (0.01 sec)
 
mysql> SELECT * FROM rt WHERE MATCH('中');
+------+--------+------------+
| id   | weight | message_id |
+------+--------+------------+
|    2 |   1643 |          2 |
+------+--------+------------+
1 row in set (0.00 sec)
 
mysql> SELECT * FROM rt WHERE MATCH('我');
Empty set (0.00 sec)
 
mysql>

简单方便,码完收工。

Tags: , , ,