编写lua脚本来扩展redis命令
为什么使用脚本
原有的命令集合不够丰富
redis命令是原子操作,避免竞态。虽然事务也可以保证原子性,但一些情况下无法使用事务完成,如根据get的值选择不同的操作
常用命令写成脚本,避免多种应用(不同语言等等)编写重复代码
lua脚本(实现频率限制) 1 2 3 4 5 6 7 8 9 10 11 local times = redis.call('incr' , KEYS[1 ])if times == 1 then redis.call('expire' , KEYS[1 ], ARGV[1 ]) end if times > tonumber (ARGV[2 ]) then return 0 end return 1
使用脚本 在命令行运行 EVAL "return redis.call('SET', KEYS[1], ARGV[1])" 1 foo bar
输出OK ,与 SET foo bar
一样
在php中使用 注意 :网上很多教程中使用的接口为
1 eval ($lua_script , array ('key, ' argv'), 1)
或者
1 evalSha ($sha , array ('key' , 'argv' ), 1 )
根据测试发现,最新版本的predis已经更改了接口函数,目前的调用方式为
1 eval ($lua_script , 1 , 'key' , 'argv' )
或者
1 evalSha ($sha , 1 , 'key' , 'argv' )
,一言不和就修改接口函数,这得坑到多少小朋友啊
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 <?php require_once ('./predis-1.0.3/autoload.php' ); function get_connection ( ) { try { $redis = new Predis\Client (array ( 'scheme' => 'tcp' , 'host' => '127.0.0.1' , 'port' => 6379 )); return $redis ; } catch (Exception $e ){ echo "Error: {$e->getMessage()} " ; return null ; } } function can_login ($ip ) { $lua_script = <<<LUA local times = redis.call('incr', KEYS[1]) if times == 1 then -- KEYS[1] redis.call('expire', KEYS[1], ARGV[1]) end if times > tonumber(ARGV[2]) then return 0 end return 1 LUA ; $redis = get_connection (); return $redis ->eval ($lua_script , 1 , 'visit:' .$ip , '60' , 5 ) == 1 ; } var_dump (can_login (ip2long ('127.0.0.1' ))); ?>