一、配置 dev.php
/*################ REDIS CONFIG ##################*/
'REDIS' => [
'host' => '127.0.0.1',//ip地址
'port' => '6379',//端口
'auth' => '123456',//密码
'POOL_MAX_NUM' => '2',
'POOL_MIN_NUM' => '1',
'POOL_TIME_OUT' => '0.1',
],
二、配置 EasySwooleEvent.php
本次使用了 Process与 redis 相结合,一定要有这两个配置
getConf('MYSQL'));
$config->setMaxObjectNum(20);//配置连接池最大数量
DbManager::getInstance()->addConnection(new Connection($config));
//创建一个协程调度器
$scheduler = new Scheduler();
$scheduler->add(function () {
$builder = new QueryBuilder();
$builder->raw('select version()');
DbManager::getInstance()->query($builder, true);
//这边重置ORM连接池的pool,避免链接被克隆岛子进程,造成链接跨进程公用。
//DbManager如果有注册多库链接,请记得一并getConnection($name)获取全部的pool去执行reset
//其他的连接池请获取到对应的pool,然后执行reset()方法
DbManager::getInstance()->getConnection()->getClientPool()->reset();
});
//执行调度器内注册的全部回调
$scheduler->start();
//清理调度器内可能注册的定时器,不要影响到swoole server 的event loop
\Swoole\Timer::clearAll();
}
public static function mainServerCreate(EventRegister $register)
{
$register->add($register::onWorkerStart,function (){
//链接预热
DbManager::getInstance()->getConnection()->getClientPool()->keepMin();
});
/**
* **************** websocket控制器 **********************
*/
// 创建一个 Dispatcher 配置
$conf = new \EasySwoole\Socket\Config();
// 设置 Dispatcher 为 WebSocket 模式
$conf->setType(\EasySwoole\Socket\Config::WEB_SOCKET);
// 设置解析器对象
$conf->setParser(new WebSocketParser());
// 创建 Dispatcher 对象 并注入 config 对象
$dispatch = new Dispatcher($conf);
// 给server 注册相关事件 在 WebSocket 模式下 on message 事件必须注册 并且交给 Dispatcher 对象处理
$register->set(EventRegister::onMessage, function (\swoole_websocket_server $server, \swoole_websocket_frame $frame) use ($dispatch) {
$dispatch->dispatch($server, $frame->data, $frame);
});
// 注册服务事件
$register->add(EventRegister::onOpen, [WebSocketEvent::class, 'onOpen']);
$register->add(EventRegister::onClose, [WebSocketEvent::class, 'onClose']);
/**
* **************** redis **********************
*/
$config = new \EasySwoole\Pool\Config();
$redisConfig = new \EasySwoole\Redis\Config\RedisConfig(Config::getInstance()->getConf('REDIS'));
\EasySwoole\Pool\Manager::getInstance()->register(new \App\Pool\RedisPool($config,$redisConfig),'redis');
/**
* **************** Process 设置 **********************
*/
$processConfig = new \EasySwoole\Component\Process\Config();
$processConfig->setProcessName('testProcess');//设置进程名称
\EasySwoole\Component\Process\Manager::getInstance()->addProcess(new TestProcess($processConfig));
}
public static function onRequest(Request $request, Response $response): bool
{
// TODO: Implement onRequest() method.
return true;
}
public static function afterRequest(Request $request, Response $response): void
{
// TODO: Implement afterAction() method.
}
}
三、新建 \App\Process\TestProcess.php 文件
info('log level info');//记录info级别日志并输出到控制台
\EasySwoole\Component\Timer::getInstance()->loop(10 * 1000, function () {
$redis = Manager::getInstance()->get('redis')->getObj();
$server = ServerManager::getInstance()->getSwooleServer();
$res = $redis->exists('user_list');
if(!empty($res)){
$uid = $redis->lPop('user_list');
$fd = $redis->get('uid_'.$uid);
if($fd){
//推送通知到用户
$server->push($fd,'push in http at '. date('H:i:s'));
}
//添加异步操作 修改数据库数据状态
$task = TaskManager::getInstance();
$task->async(function () use($fd){
$user = User::create()->get($fd);
$user->update([
'status' => 2
]);
echo "异步调用task1\n";
});
}
//回收redis
Manager::getInstance()->get('redis')->recycleObj($redis);
});
echo 'Test Process start'."\n";
}
protected function onPipeReadable(Process $process)
{
/*
* 该回调可选
* 当有主进程对子进程发送消息的时候,会触发的回调,触发后,务必使用
* $process->read()来读取消息
*/
}
protected function onShutDown()
{
echo 'Test Process end'."\n";
/*
* 该回调可选
* 当该进程退出的时候,会执行该回调
*/
}
protected function onException(\Throwable $throwable, ...$args)
{
/*
* 该回调可选
* 当该进程出现异常的时候,会执行该回调
*/
}
}
四、配置一下开始与结束的事件 本次使用的是 WebSocket 所以配置App\WebSocket\WebSocketEvent.php
customHandShake($request, $response)) {
$response->end();
return false;
}
/** 此处是 RFC规范中的WebSocket握手验证过程 必须执行 否则无法正确握手 */
if ($this->secWebsocketAccept($request, $response)) {
$response->end();
return true;
}
$response->end();
return false;
}
/**
* 自定义握手事件
*
* @param \swoole_http_request $request
* @param \swoole_http_response $response
* @return bool
*/
protected function customHandShake(\swoole_http_request $request, \swoole_http_response $response): bool
{
/**
* 这里可以通过 http request 获取到相应的数据
* 进行自定义验证后即可
* (注) 浏览器中 JavaScript 并不支持自定义握手请求头 只能选择别的方式 如get参数
*/
$headers = $request->header;
$cookie = $request->cookie;
// if (如果不满足我某些自定义的需求条件,返回false,握手失败) {
// return false;
// }
return true;
}
/**
* RFC规范中的WebSocket握手验证过程
* 以下内容必须强制使用
*
* @param \swoole_http_request $request
* @param \swoole_http_response $response
* @return bool
*/
protected function secWebsocketAccept(\swoole_http_request $request, \swoole_http_response $response): bool
{
// ws rfc 规范中约定的验证过程
if (!isset($request->header['sec-websocket-key'])) {
// 需要 Sec-WebSocket-Key 如果没有拒绝握手
var_dump('shake fai1 3');
return false;
}
if (0 === preg_match('#^[+/0-9A-Za-z]{21}[AQgw]==$#', $request->header['sec-websocket-key'])
|| 16 !== strlen(base64_decode($request->header['sec-websocket-key']))
) {
//不接受握手
var_dump('shake fai1 4');
return false;
}
$key = base64_encode(sha1($request->header['sec-websocket-key'] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));
$headers = array(
'Upgrade' => 'websocket',
'Connection' => 'Upgrade',
'Sec-WebSocket-Accept' => $key,
'Sec-WebSocket-Version' => '13',
'KeepAlive' => 'off',
);
if (isset($request->header['sec-websocket-protocol'])) {
$headers['Sec-WebSocket-Protocol'] = $request->header['sec-websocket-protocol'];
}
// 发送验证后的header
foreach ($headers as $key => $val) {
$response->header($key, $val);
}
// 接受握手 还需要101状态码以切换状态
$response->status(101);
var_dump('shake success at fd :' . $request->fd);
return true;
}
/**
* 关闭事件
*
* @param \swoole_server $server
* @param int $fd
* @param int $reactorId
*/
static function onClose(\swoole_server $server, int $fd, int $reactorId)
{
$info = $server->getClientInfo($fd);
/**
* 判断此fd 是否是一个有效的 websocket 连接
* 参见 https://wiki.swoole.com/wiki/page/490.html
*/
if ($info && $info['websocket_status'] === WEBSOCKET_STATUS_FRAME) {
/**
* 判断连接是否是 server 主动关闭
* 参见 https://wiki.swoole.com/wiki/page/p-event/onClose.html
*/
if ($reactorId < 0) {
echo "server close \n";
}else{
//清除绑定uid 与 fd
$redis = Manager::getInstance()->get('redis')->getObj();
$uid = $redis->get('fd_'.$fd);
$redis->del('fd_'.$fd);
$redis->del('uid_'.$uid);
echo "server close at fd: ".$fd."\n";
}
}
}
/**
* 打开了一个链接
* @param swoole_websocket_server $server
* @param swoole_http_request $request
*/
static function onOpen(\swoole_websocket_server $server, \swoole_http_request $request)
{
Event::getInstance()->hook('test');
$token = $request->get['token'];
$fd = $request->fd;
if(!isset($token)){
$data = [
"type" => "token expire"
];
$server->push($request->fd, json_encode($data));
$server->close($fd);
return;
}
$user_info = User::create()->where('token', $token)->get();
if(empty($user_info)){
$data = [
"type" => "token expire"
];
$server->push($fd, json_encode($data));
$server->close($fd);
return;
}
//绑定uid 与 fd
$redis = Manager::getInstance()->get('redis')->getObj();
if($redis->exists('uid_'.$user_info['id'])){
$old_fd = $redis->getSet('uid_'.$user_info['id'],$fd);
$redis->del('fd_'.$old_fd);
$redis->set('fd_'.$fd,$user_info['id']);
}else{
$redis->set('uid_'.$user_info['id'],$fd);
$redis->set('fd_'.$fd,$user_info['id']);
}
echo "server connection at fd: ".$request->fd."\n";
$server->push($fd, json_encode($user_info));
}
}
五、本次使用了一下 model 目录App\Models\User.php 需要手动配置一下哦!