月份:2012年3月

memcache一致性hash的php实现

有段时间没认真写博客了,最近在看一些分布式方面的文章,所以就用php实现一致性hash来练练手,以前一般用的是最原始的hash取模做分布式,当生产过程中添加或删除一台memcache都会造成数据的全部失效,一致性hash就是为了解决这个问题,把失效数据降到最低,相关资料可以google一下!

php实现效率有一定的缺失,如果要高效率,还是写扩展比较好
经测试,5个memcache,每个memcache生成200个虚拟节点,set加get1000次,采用一致性哈希分布效率比原生单台速度相差5倍,效率有待优化
实现过程:

  1. memcache的配置 ip+端口+虚拟节点序列号 做hash,使用的是crc32,形成一个闭环。
  2. 对要操作的key进行crc32
  3. 二分法在虚拟节点环中查找最近的一个虚拟节点
  4. 从虚拟节点中提取真实的memcache ip和端口,做单例连接

代码如下:


redis做共享锁机制

给出部分主代码

感谢HaKeem的提醒,程序发现一个bug,以前的代码会发生无法解锁的问题,放出修正后的代码
如果是redis2.2以上版本可以在lock逻辑中加上watch命令,锁定单个key

	/**
	 * 加锁  【新的加锁算法,采用redis做锁,redis失效情况下,返回 变量redisErrReturn 设定的值】
	 * @param string $key 唯一标识
	 * @param int $expire
	 * @return
	 */
	public function lock($key, $expire = 5) {
		try {
			list($key, $lockIdKey) = $this->_getLockKey($key);
			//抢锁,第一个线程抢到,并把过期时间写入锁
			if (Common::getQueue()->setnx($key, Common::getTime() + $expire)) return true;
			//没有抢到锁的线程,判断锁是否异常死锁,如果锁没有过期,返回false
			if (Common::getQueue()->get($key)  > Common::getTime()) return false;
			//锁因异常死锁并过期的情况下,多个并发线程再次抢锁,getset命令到过期时间,如果未过期,表示锁已被其它线程抢得,返回false
			if (Common::getQueue()->getset($key, Common::getTime() + $expire) > Common::getTime()) return false;
			Common::getQueue()->set($lockIdKey, $this->_lockId);
			return true;
		} catch (RedisException $e) {
			// 当捕捉到redis异常时,锁中断,返回变量redisErrReturn
			$this->_lockStatus = false;
			return $this->redisErrReturn;
		}
	}
	
	/**
	 * 给锁进行续期,延长锁的生效周期
	 * @param string $key
	 * @param int $expire       延时锁失效的秒数
	 * @param int $triggerTime  触发续期倒计时间
	 * @return
	 */
	public function updateExpire($key, $expire = 3, $triggerTime = 2) {
		if (!$this->_lockStatus) return true; //当redis失效,中断锁
		list($key, $lockIdKey) = $this->_getLockKey($key);
		$lockId = Common::getQueue()->get($lockIdKey);
		if ($this->_lockId === null || $lockId != $this->_lockId) return false;
		$time = Common::getQueue()->get($key);
		if ($time - $triggerTime <= Common::getTime()) return Common::getQueue()->set($key, $time + $expire);
	}
	
	/**
	 * 解锁
	 * @param string $key
	 * @return
	 */
	public function unlock($key) {
		if (!$this->_lockStatus) return true; //当redis失效,中断解锁
		list($key, $lockIdKey) = $this->_getLockKey($key);
		$lockId = Common::getQueue()->get($lockIdKey);
		if ($this->_lockId === null || $lockId != $this->_lockId) return false;
		Common::getQueue()->del($key);
		Common::getQueue()->del($lockIdKey);
		return true;
	}
	
	/**
	 * 清除死锁产生的无用key 默认清除24小时以前的无用key
	 * @param int $cleanTime  默认24小时
	 */
	public function cleanLockKey($cleanTime = 86400) {
		if ($cleanTime < 3600 || !$keys = Common::getQueue()->keys('PwLock:*')) return false;
		$i = 0;
		foreach ($keys as $value) {
			list($time, $lockId) = explode('|', Common::getQueue()->get($value));
			if (Common::getTime() - $time > $cleanTime) {
				if (Common::getQueue()->del($value)) $i++;
			}
		}
		return $i;
	}
	
	private function _getLockKey($key) {
		return array('PwLock:' . $key, 'PwLock:' . $key . ':lockId');
	}

linux tar分卷压缩

分卷压缩一个目录:如linux
在linux目录的上层目录:
#tar cvf linux|split -b 2m (已2M大小分卷压缩)
#cat x* > linux.tar (合成分卷压缩包)
或者
#tar czvf linux.tar.gz linux/
#tar czvfp – linux.tar.gz | split -b 2m
#cat x* > linux.tar.gz


Mac OS Lion 下编译安装Nginx 1.0.12 + PHP 5.3.10 + Mysql 5.5.18 + Xdebug + PHPUnit [转]

在mac下编译安装了最新版本namp的环境, 记录一下安装过程, 其实总体和linux下没有太多区别,另外port是个好东西。

mysql编译安装:

1. 建立mysql数据存储目录, 权限设置为mac os默认存在的_mysql权限:

mkdir -p /var/mysql/data/
chown -R _mysql:_mysql /var/mysql/

2. 下载mysql, 我下的版本为mysql-5.5.18, 执行以下跨平台编译命令:

cmake -DCMAKE_INSTALL_PREFIX=/usr/local/server/mysql -DMYSQL_DATADIR=/var/mysql/data -DWITH_INNOBASE_STORAGE_ENGINE=1 -DWITH_MEMORY_STORAGE_ENGINE=1 -DWITH_MYISAM_STORAGE_ENGINE=1 -DSYSCONFDIR=/etc/ -DWITH_SSL=yes -DDEFAULT_CHARSET=utf8 -DDEFAULT_COLLATION=utf8_general_ci -DWITH_READLINE=on

3. 编译, 安装:

make
sudo make install

4. 改变mysql目录的权限:

sudo chmod +w /usr/local/server/mysql
sudo chown -R _mysql:_mysql /usr/local/server/mysql

5. 创建库软链接:

sudo ln -s /usr/local/server/mysql/lib/lib* /usr/lib/

6.  copy配置文件到etc目录:

cp /usr/local/server/mysql/support-files/my-large.cnf /etc/my.cnf
[client]
default-character-set = utf8

[mysqld]
character-set-server = utf8
default-storage-engine = MyISAM
basedir = /usr/local/server/mysql
datadir = /var/mysql/data
log-error = /var/mysql/mysql_error.log
pid-file = /var/mysql/mysql.pid

7. 建立初始数据表:

sudo /usr/local/server/mysql/scripts/mysql_install_db --basedir=/usr/local/server/mysql --datadir=/var/mysql/data --user=_mysql

8. 设置root密码:

sudo /usr/local/server/mysql/bin/mysqladmin -u root password 'mysql'

9.启动mysql:

sudo /usr/local/server/mysql/bin/mysqld_safe --user=_mysql &

10. 测试安装是否成功:

/usr/local/server/mysql/bin/mysql -u root -p -S /tmp/mysql.sock

 

php编译安装:

1. 编译安装php:

./configure –prefix=/usr/local/server/php –with-config-file-path=/usr/local/server/php/etc –enable-fpm –with-openssl –with-zlib –enable-mbstring –with-mcrypt –with-mysql=/usr/local/server/mysql –with-mysql-sock=/tmp/mysqld.sock –with-mysqli=/usr/local/server/mysql/bin/mysql_config –enable-sockets –without-iconv –with-curl=/opt/local/bin/curl

2. 复制php.ini-development到编译时指定的php配制目录:

sudo cp php.ini-development /usr/local/server/php/etc/php.ini

3. 复制phpfpm的配置文件到其配制目录:

sudo cp php-fpm.conf.default php-fpm.conf

4. 重命名phpfpm执行文件为正常名称:

sudo mv php-fpm.dSYM php-fpm

5. 启动:

sudo /usr/local/server/php/sbin/php-fpm

6. 安装xdebug

1) 进入http://xdebug.org/find-binary.php网址, 输入phpinfo返回的html源码后其会自动生成安装步骤, 按照其方法编译安装, 最后变更php.ini配制文件指定xdebug.so扩展路径。

7. 安装phpunit

1) 升级pear.

sudo /usr/local/server/php/bin/pear upgrade

2) 安装phpunit.

sudo ./pear config-set auto_discover 1
sudo ./pear install pear.phpunit.de/PHPUnit

3) 测试phpunit是否安装成功.

/usr/local/server/php/bin/phpunit

#有输出帮助信息则为正常。


nginx编译安装:

1. 编译安装:

./configure --user=_www --group=_www --prefix=/usr/local/server/nginx --with-http_stub_status_module --with-http_ssl_module --with-http_sub_module
make
sudo make install

2. 修改nginx配置:

sudo vim /usr/local/server/nginx/conf/nginx.conf

#指定程序运行权限:
user _www _www;
#在http内添加一条server信息:
server {
    listen 80; 
    server_name localhost;
    root /var/www;

    location ~ \.php$ {
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
        include fastcgi_params;
    } 
}

3.启动:

sudo /usr/local/server/nginx/sbin/nginx

#没有任何返回则说明执行成功。

 

参考文章:

lnap最新方案   http://www.yunwei8.com/nginx/


xcode4.3开启gcc/g++

真的坑爹,今天才开始玩MAC OX,装了个最新版本的10.7.3,只能装XCODE 4.3 这个月刚发行的版本。
安装时发现没有install过程,直接双击就进入开发环境了。而且装完后没有gcc 等各种编译工具,在TERMINAL下各种命令不识别,想装ruby的各种开发工具,都不行了。

查了半天才发现:
Apple announced Xcode 4.3 for OSX Lion and 4.4 for OSX Mountain Lion last week. The major difference is that Xcode no longer provide an installer which is good thing because you now could update Xcode with AppStore in the future, plus it is much easier to carry the development environment with you. However, there is a little problem with this new version of Xcode, is that all command line toolsets and compilers are not visible in terminal.

解决方案一:
A simple fix is to update your PATH env:
export PATH=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin:/Applications/Xcode.app/Contents/Developer/usr/bin:$PATH
Please be noted that clang does not reside in /Developer/usr/bin, it is now in /Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
Now you could access to gcc, g++, git or any toolsets bundled with Xcode. For your convenience, it is recommended to include it in your .bash_profile.
解决方案二:
You can install these additional tools directly in Xcode :
Preferences > Downloads > Command Line Tools > Install


于 PHP 安装使用 Google LevelDB extension

LevelDB (leveldb – a fast and lightweight key/value database library) 是 Google 开发非常快速的 key-value 储存的函式库, 效能看起来相当不错: LevelDB Benchmarks, 且 LevelDB 的资料, 都会经过 Snappy 压缩, 所以资料也会比较小.

注: 下述安装环境为 Debian / Ubuntu Linux

于 PHP 增加 Google LevelDB 的 Extension

有 LevelDB 的 Source code, 再来找 PHP 的 ext 是否有人写, 于是就找到: leveldb for php

也正好找到此篇有人已经有做过编译: 从原始码编译 Google LevelDb 的 PHP 扩展, 下述步骤摘录自此篇.

安装、编译步骤
  1. BUILD=/usr/local/
  2. # 编译安装 LevelDB
  3. svn export http://leveldb.googlecode.com/svn/trunk/ leveldb
  4. cd $BUILD/leveldb
  5. make -j8 OPT=”-O2 -DNDEBUG -fPIC”
  6. # 编译安装 php-leveldb ext
  7. git clone git://github.com/arraypad/php-leveldb.git
  8. cd $BUILD/php-leveldb
  9. phpize
  10. ./configure –with-leveldb=$BUILD/leveldb
  11. make -j8
  12. make test
  13. make install
  14. # 于 Apache 的 PHP 设定 leveldb.so (extenstion)
  15. vim /etc/php5/cli/conf.d/leveldb.ini # 内容如下述
    extension=leveldb.so
  16. /etc/init.d/apache2 restart # 到此就可以开始使用 LevelDB 囉~

LevelDB 于 PHP 的操作与使用范例

范例可于 php-leveldb 的 tests 里面找到: basic.phpt (下述参考整理自此档案)

范例
<?php
if (!extension_loaded('leveldb')) {
    die('skip leveldb not loaded');
}

$path = '/tmp/leveldb.test';
$db = new LevelDb($path);

echo "* setting (foo=bar): \n";
var_dump($db->set('foo', 'bar')); // bool(true)

echo "* getting (foo): \n";
var_dump($db->get('foo')); // string(3) "bar"

echo "* delete (foo): \n";
var_dump($db->delete('foo')); // bool(true)

echo "* getting (foo): \n";
var_dump($db->get('foo')); // bool(false)
?>