discuzx2.5高并发时生成模板缓存的bug.

用discuzx2.5搭建了一个论坛,清除缓存后经常出现白屏或错误,再次清除缓存后又正常了。


排查了很久发现是模板缓存大小为 零k,或没生成完整。

discuz生成模板缓存的函数部分parse_template。

if(!@$fp = fopen(DISCUZ_ROOT.$cachefile, 'w')) {
 			$this->error('directory_notfound', dirname(DISCUZ_ROOT.$cachefile));
 		}

		$template = preg_replace("/\"(http)?[\w\.\/:]+\?[^\"]+?&[^\"]+?\"/e", "\$this->transamp('\\0')", $template);
		$template = preg_replace("/\<script[^\>]*?src=\"(.+?)\"(.*?)\>\s*\<\/script\>/ies", "\$this->stripscriptamp('\\1', '\\2')", $template);
		$template = preg_replace("/[\n\r\t]*\{block\s+([a-zA-Z0-9_\[\]]+)\}(.+?)\{\/block\}/ies", "\$this->stripblock('\\1', '\\2')", $template);
		$template = preg_replace("/\<\?(\s{1})/is", "<?php\\1", $template);
		$template = preg_replace("/\<\?\=(.+?)\?\>/is", "<?php echo \\1;?>", $template);
	

 		flock($fp, 2);
 		fwrite($fp, $template);
 		fclose($fp);

生成模板缓存时用了flock锁,高并发时,要排队获取锁。

如同时100个并发,假设没并发每个脚本的执行时间为0.5秒,flock锁没加防堵塞锁flock($fp, LOCK_EX | LOCK_NB),所有获取同一个文件锁时要排队等候,

排第一的执行完成花0.5秒,排第二的要等第一个释放锁后才能获取到,所有执行完成要花1秒,如此类推排100的就要花50秒时间才能完成,从而导致长时间

独占锁,甚至PHP脚本超时。


用flock($fp, LOCK_EX | LOCK_NB),在锁定时不堵塞,就是程序尝试去获取文件锁,没获取到就返回false不堵塞和排队等待。

		//start---在锁定时不堵塞,没获得锁返回false。LOCK_NB与线程有关,有时不起作用。 date:2013-05-02
                $canWrite = flock($fp, LOCK_EX | LOCK_NB);
		if($canWrite){
 		    fwrite($fp, $template);
 		    flock($fp, LOCK_UN | LOCK_NB);
 		}
 		fclose($fp);

但测试发现, LOCK_NB有时也要等待,查了一下资料,LOCK_NB与线程有关,用nginx+php-fpm时,LOCK_NB也会堵塞。

http://stackoverflow.com/questions/5524073/lock-nb-ignored




参考了smarty的源码,他们的模板生成的解决办法是生成临时文件,然后rename的方式代替flock的。

每个并发的程序都生成一个临时文件,然后重命名为模板的名字。

/**
	 * 写临时文件然后rename的方式代替flock().
	 * 
	 * @param string $cachefile
	 * @param string $contents
	 * @return boolean
	 * @date 2013-05-02
	 */
	private function tmp_template($cachefile,$contents){
		$cachefile = DISCUZ_ROOT.$cachefile;
		$_dirname = dirname($cachefile);		
		
		// write to tmp file, then rename it to avoid file locking race condition
		$_tmp_file = tempnam($_dirname, 'wrt'); //建立一个具有唯一文件名的文件
		
		if (!($fp = @fopen($_tmp_file, 'wb'))) {
			$_tmp_file = $_dirname . DIRECTORY_SEPARATOR . uniqid('wrt');
			if (!($fp = @fopen($_tmp_file, 'wb'))) {
				return false;
			}
		}
		fwrite($fp, $contents);
		fclose($fp);
		
		if (DIRECTORY_SEPARATOR == '\\' || !@rename($_tmp_file, $cachefile)) {
			// On platforms and filesystems that cannot overwrite with rename()
			// delete the file before renaming it -- because windows always suffers
			// this, it is short-circuited to avoid the initial rename() attempt
			@unlink($cachefile);
			@rename($_tmp_file, $cachefile);
		}
		@chmod($cachefile, 0777);
		return true;
	}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值