众所周之,服务器的设计永远不能依赖于单进程/单线程,由于PHP本质上是不支持多线程的,所以在开发过程中,只能把不同的逻辑或者用户分发到不同的进程之间进行处理(这是由系统自己调用分发的)。由此可知道在游戏中用户之间必须共享一些特定的公共数据,由于PHP进程之间不能够直接调用数据,所以就需要一个共公的数据库或者内存空间进行保存,因我的服务器采用的是WorkerMan进行开发的,所以用到的公共组件可以是GlobalData或者Redis组件,它是实质就是公有数据库服务。此处采用的是GlobalData组件。以下是实现代码:
class Events
{
private static $globalClient;
/**
* WORKER端启动的时候调用,用于初始化全局数据
* @param $worker
*/
public static function onWorkerStart($worker){
//连接进程共享数据服务器
self::$globalClient = new GlobalData\Client("127.0.0.1:2207");
//创建当前服务器
$server = new GameServer();
//保存共享数据,本函数只执行一次
self::$globalClient->add('server', $server);
}
/**
* 当客户端连接时触发
* 如果业务不需此回调可以删除onConnect
*
* @param int $client_id 连接id
*/
public static function onConnect($client_id)
{
//检查是否有更新,返回更新结果
//echo $client_id."\n";
echo "connect:".Gateway::getAllClientCount()."\n";
}
/**
* 当客户端发来消息时触发
* @param int $client_id 连接id
* @param mixed $message 具体消息
*/
public static function onMessage($client_id, $message)
{
do{
//获取服务器,当前服务器和保存的服务器环境
$server = self::$globalClient->server;
$oldServer = self::$globalClient->server;
//开始处理客户端请求,内部调用响应函数返回客户端
$t=$server->processClientRequest($client_id, $message);
}while(!self::$globalClient->cas('server', $oldServer, $server));
if($t){
for($i=0;$i<count($t);$i++){
$server->processServerResponse($t[$i][0],$t[$i][1],$t[$i][2],$t[$i][3],$t[$i][4]);
}
}
}
/**
* 当用户断开连接时触发
* @param int $client_id 连接id
*/
public static function onClose($client_id)
{
echo "exit!\n";
//当用户断开连接时
//获取服务器,当前服务器和保存的服务器环境
do{
//获取服务器,当前服务器和保存的服务器环境
$server = self::$globalClient->server;
$oldServer = self::$globalClient->server;
$server->disconnect($client_id);
}while(!self::$globalClient->cas('server', $oldServer, $server));
}
}
此处就是创建了一个GlobalData数据服务,里面存储的是游戏中公用的数据,封装在GameServer里面,每次都可通过GameServer进行调用。需要注意的就是因此方法的本质是IO阻塞式的,是一个原子操作,会影响到服务器的一定性能,在游戏中如果要达到正常通信,进行游戏分区是不很不错的方法的。