Redis是一個輕量級的key-value型資料存放區系統,相對於memcache/memcached,提供了更多的冗餘性,並且可以將儲存的資料轉存到磁碟檔案中,以便下次啟動時恢複。同時Redis提供了集合、列表等資料結構。
結合以上特性,採用Redis作為聊天室的資料存放區解決方案,可獲得較高的執行效率。直接貼服務端代碼了,需要php_redis擴充支援,沒有使用者系統沒做,這個程式目前是掛到一個phpwind論壇裡的,直接從全域變數讀取使用者和使用者組。介面部分在此省略。完整示範可參考http://bbs.91d2.cn/,點擊快捷操作欄右下角的“論壇聊天室”即可開啟。
<?phpdefine('DISABLE_REFRESH_LIMIT', 1);require_once('global.php');header('Content-Type: text/html; charset=UTF-8');$redis = new Redis();$redis->connect('127.0.0.1',6379);$if_init = get_string('chat_init');if(!$if_init){if(get_string('chat_init_lock')){die;}set_string('chat_init_lock', '1');set_array('chat_channel', array('GLOBAL'));set_array('chat_motd', array('GLOBAL' => 'Hello!'));set_array('chat_online', array());set_string('chat_init', '1');set_string("0_name", '系統');set_string("0_flag", 'SA');}if(!$winduid){echo 'chatLogin();';die;}set_string("{$winduid}_name", $windid);set_string("uid_{$windid}", $winduid);$channel_list = get_array('chat_channel');$channel_motd = get_array('chat_motd');$my_channel = get_string("{$winduid}_in");$my_index = get_string("{$winduid}_index");if(!$my_channel || !in_array($my_channel, $channel_list)){$my_channel = $channel_list[0];set_string("{$winduid}_in", $my_channel);$my_index = 0;}$channel_index = get_string("{$my_channel}_index");$channel_ca = get_array("{$my_channel}_ca");if ($winddb['groupid'] == 3 || $winddb['groupid'] == 4 || $winddb['groupid'] == 22){$user_flag = 'SA';}else if(in_array($winduid, $channel_ca)){$user_flag = 'CA';}else{$user_flag = 'U';}set_string("{$winduid}_flag", $user_flag);InitGP(array('action', 'first'));if(!$my_index || $first){if ($my_index == 0){send_message(0, 'MOTD: ' . $channel_motd[$my_channel], 'info', $my_channel, $winduid);}$my_index = $first ? ($channel_index - 10) : ($channel_index + 1);if($my_index < 0){$my_index = 0;}set_string("{$winduid}_index", $my_index);}if($action == 'ui'){require_once PrintEot('chat_ui');}if($action == 'channel'){echo "chatChannelListBegin();\r\n";foreach($channel_list as $channel_name){echo "chatHandleChannelList('" . mb_convert_encoding($channel_name, 'UTF-8', 'GBK') . "');\r\n";}echo "chatChannelListEnd();\r\n";}if($action == 'send'){InitGP(array('content', 'receiver'));if (!$content || !$receiver){echo '0';die;}if ($winddb['postnum'] < 100){send_message(0, '您的發貼數不足100,沒有發言許可權', 'warn', $my_channel, $winduid);}else{if (get_string("{$winduid}_block") > $timestamp){send_message(0, '您已被禁言,無法發言', 'warn', $my_channel, $winduid);}else{$content = htmlspecialchars($content);send_message($winduid, stripslashes(mb_convert_encoding($content, 'GBK', 'UTF-8')), 'info', $my_channel, $receiver);}}echo '0';}if($action == 'get'){$channel_ca_list = '';if ($user_flag == 'SA' || $user_flag == 'CA'){foreach ($channel_ca as $ca){$channel_ca_list .= get_string("{$ca}_name") . ',';}$channel_ca_list = mb_convert_encoding($channel_ca_list, 'UTF-8', 'GBK');}echo "chatHandleStatus('" . mb_convert_encoding($my_channel, 'UTF-8', 'GBK') . "', '" . mb_convert_encoding($windid, 'UTF-8', 'GBK') . "', '{$user_flag}', '{$channel_ca_list}');";for(; $my_index <= $channel_index; $my_index ++){$message = get_array("{$my_channel}_{$my_index}");if ($message['receiver'] == 'all' || $message['receiver'] == $winduid){$message['content'] = addslashes(mb_convert_encoding($message['content'], 'UTF-8', 'GBK'));$sender_flag = get_string("{$message[author]}_flag");$sender_flag == 'SA' && $message['content'] = '<span style="color:red;font-weight:bold">' . $message['content'] . '</span>';$sender_flag == 'CA' && $message['content'] = '<span style="color:blue">' . $message['content'] . '</span>';$message['author'] = addslashes(mb_convert_encoding(get_string("{$message[author]}_name"), 'UTF-8', 'GBK'));echo "chatHandleMessage('{$message[type]}', '" . $message['author'] . "', '{$message[content]}', {$message[date]});\r\n";}}set_string("{$winduid}_index", $my_index);}if($action == 'block'){InitGP(array('username', 'timespan'));$username = mb_convert_encoding($username, 'GBK', 'UTF-8');if ($user_flag == 'SA' || $user_flag == 'CA'){set_string(get_string("uid_{$username}") . '_block', $timestamp + $timespan * 3600);send_message(0, '禁言使用者[' . $username . ']成功', 'warn', $my_channel, $winduid);echo '0';}else{send_message(0, '許可權不足', 'warn', $my_channel, $winduid);echo '-1';}}if($action == 'setchannel'){InitGP(array('option', 'channel', 'channel_name', 'channel_ca'));if ($option == 'join'){$channel = mb_convert_encoding($channel, 'GBK', 'UTF-8');if (in_array($channel, $channel_list)){set_string("{$winduid}_index", 0);set_string("{$winduid}_in", $channel);echo '0';}else{send_message(0, '頻道不存在', 'warn', $my_channel, $winduid);echo '-1';die;}}else{if ($user_flag != 'SA'){send_message(0, '目前只有全域管理員可以建立頻道', 'warn', $my_channel, $winduid);echo '-1';die;}if (!$channel_name){send_message(0, '請輸入頻道名', 'warn', $my_channel, $winduid);echo '-1';die;}$channel_name = mb_convert_encoding($channel_name, 'GBK', 'UTF-8');$channel_ca = mb_convert_encoding($channel_ca, 'GBK', 'UTF-8');if (in_array($channel, $channel_list)){send_message(0, '頻道名重複', 'warn', $my_channel, $winduid);echo '-1';die;}$channel_list[] = $channel_name;$channel_motd[$channel_name] = 'Hello!';set_array('chat_channel', $channel_list);set_array('chat_motd', $channel_motd);set_array("{$channel_name}_ca", explode(',', $channel_ca));send_message(0, '頻道建立成功', 'warn', $my_channel, $winduid);}}if($action == 'setmotd'){InitGP(array('motd'));$motd = mb_convert_encoding($motd, 'GBK', 'UTF-8');if ($user_flag == 'SA' || $user_flag == 'CA'){$channel_motd[$my_channel] = $motd;set_array('chat_motd', $channel_motd);send_message(0, 'MOTD設定成功', 'warn', $my_channel, $winduid);echo '0';}else{send_message(0, '許可權不足', 'warn', $my_channel, $winduid);echo '-1';}}if($action == 'delchannel'){if ($user_flag == 'SA' || $user_flag == 'CA'){unset($channel_list[array_search($my_channel, $channel_list)]);unset($channel_motd[$my_channel]);set_array('chat_channel', $channel_list);set_array('chat_motd', $channel_motd);remove("{$my_channel}_ca");set_string("{$winduid}_index", 0);set_string("{$winduid}_in", 'GLOBAL');send_message(0, '頻道刪除成功', 'warn', 'GLOBAL', $winduid);echo '0';}else{send_message(0, '許可權不足', 'warn', $my_channel, $winduid);echo '-1';}}if($action == 'setca'){InitGP(array('username', 'option'));if ($user_flag == 'SA' || $user_flag == 'CA'){$username = mb_convert_encoding($username, 'GBK', 'UTF-8');$uid = get_string("uid_{$username}");if (!$uid){send_message(0, '使用者沒有登入', 'warn', $my_channel, $winduid);echo '-1';die;}if ($option == 'add'){if (!in_array($uid, $channel_ca)){$channel_ca[] = $uid;send_message(0, '增加管理員[' . $username . ']', 'warn', $my_channel, $winduid);}}else{if (in_array($uid, $channel_ca)){unset($channel_ca[array_search($uid, $channel_ca)]);send_message(0, '取消管理員[' . $username . ']', 'warn', $my_channel, $winduid);}}set_array("{$my_channel}_ca", $channel_ca);echo '0';}else{send_message(0, '許可權不足', 'warn', $my_channel, $winduid);echo '-1';}}function remove(){global $redis;$redis->delete($name);}function set_string($name, $val){global $redis;$redis->set($name, $val);}function get_string($name, $val){global $redis;return $redis->get($name);}function append_string($name, $val){global $redis;return $redis->append($name, $val);}function set_array($name, $val){global $redis;$redis->set($name, serialize($val));}function get_array($name){global $redis;return unserialize($redis->get($name));}function send_message($author, $content, $type = 'info', $channel = 'GLOBAL', $receiver = 'all'){global $redis, $timestamp;if (strlen($content) > 200){$content = substr($content, 0 , 200);}while(get_string("{$channel}_lock")){usleep(100);}set_string("{$channel}_lock", 1);$channel_index = get_string("{$channel}_index");$channel_index = $channel_index + 1;set_array("{$channel}_{$channel_index}", array('author' => $author,'content' => $content,'type' => $type,'receiver' => $receiver,'date' => $timestamp));set_string("{$channel}_index", $channel_index);$clean_index = $channel_index - 100;remove("{$channel}_{$clean_index}");$author_name = get_string("{$author}_name");append_string("{$channel}_record", get_date($timestamp, 'Y-m-d H:i:s') . " [{$type}] {$author_name} -> {$receiver}: {$content}\r\n");check_channel_record($channel);set_string("{$channel}_lock", 0);}function check_channel_record($channel){global $timestamp;$lastsave = get_string("{$channel}_lastsave");if($timestamp - $lastsave > 600){set_string("{$channel}_lastsave", $timestamp);save_record($channel);}}function save_record($channel){global $db, $timestamp;$record = str_replace(array("\\", "\"", "'"),array("\\\\", "\\\"", "\\'"),get_string("{$channel}_record"));set_string("{$channel}_record", '');if(strlen($record)>0){$db->update("INSERT INTO `pw_chatrecord` (`channel`, `time`, `content`) VALUES ('$channel', '$timestamp', '$record')");}}