9、Workerman 基本使用 - HTTP 会话
hi,我是温新,一名 PHPer
Session 会话
设置 session
1、存储单个值
<?php
/**
* session.php
*
* 会话
*/
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
use Workerman\Protocols\Http\Request;
require_once __DIR__ . '/vendor/autoload.php';
$worker = new Worker('http://0.0.0.0:8888');
$worker->onMessage = function (TcpConnection $connection, Request $request) {
// 设置 session
$request->session()->set('name', '王美丽丽');
$connection->send('succ');
};
Worker::runAll();
2、存储多个值
...
$request->session()->put(['username'=>'王大丽', 'age'=>19]);
...
获取 session
1、获取单个 session
...
$worker->onMessage = function (TcpConnection $connection, Request $request) {
$session = $request->session()->get('name');
$connection->send($session);
};
...
2、获取所有 sesssion
...
$sessions = $request->session()->all();
$connection->send(var_export($sessions, true));
...
删除 session
1、删除单个 session
...
$request->session()->forget('username');
$request->session()->delete('age');
...
2、获取并删除单个 session
...
$request->session()->pull('username');
...
3、删除所有 session
...
$request->session()->flush();
...
判断 session 是否存在
...
$has1 = $request->session()->has('name');
$has2 = $request->session()->exists('age');
...
- has:当对应的 session 不存在或者对应的 session 值为 null 时返回 false,否则返回 true
- exists:当对应的 session 项值为 null 时,也返回 true
Session 管理
session 默认使用的是文件存储引擎。另外还支持 redis 存储引擎。
使用 redis 作为存储引擎
<?php
/**
* session.php
*
* 使用 Redis 作为存储引擎
*/
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
use Workerman\Protocols\Http\Request;
use Workerman\Protocols\Http\Session;
use Workerman\Protocols\Http\Session\RedisSessionHandler;
require_once __DIR__ . '/vendor/autoload.php';
$worker = new Worker('http://0.0.0.0:8888');
// redis配置
$config = [
'host' => '127.0.0.1', // 必选参数
'port' => 6379, // 必选参数
'timeout' => 2, // 可选参数
'auth' => '', // 可选参数
'database' => 1, // 可选参数
'prefix' => 'workerman_session_' // 可选参数
];
// 使用 Workerman\Protocols\Http\Session::handlerClass 方法来更改 session 底层驱动类
Session::handlerClass(RedisSessionHandler::class, $config);
$worker->onMessage = function (TcpConnection $connection, Request $request) {
$session = $request->session();
$session->set('username', '王丽丽');
$connection->send($session->get('username'));
};
Worker::runAll();
浏览器访问 http://127.0.0.1:8888/
,然后打开 redis 查看写入的信息。
127.0.0.1:6379[1]> keys *
1) "workerman_session_17d739312378d941414279d8a25f1229"
设置 session 的存储位置
使用默认的存储引擎时,session 是存储在磁盘中的,因此可以使用 session_save_path()
修改默认的存储默认。
<?php
/**
* session.php
*
* 使用文件存储,修改位置
*/
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
use Workerman\Protocols\Http\Request;
use Workerman\Protocols\Http\Session\FileSessionHandler;
require_once __DIR__ . '/vendor/autoload.php';
$worker = new Worker('http://0.0.0.0:8888');
// 设置文件存储引擎的默认位置
FileSessionHandler::sessionSavePath('/tmp/workerman/session');
$worker->onMessage = function (TcpConnection $connection, Request $request) {
$session = $request->session();
$session->set('username', '王丽丽');
$connection->send($session->get('username'));
};
Worker::runAll();
浏览器访问之后,查看一下 session。
$ ll /tmp/workerman/session/
总用量 4
-rw-r--r-- 1 codeing codeing 37 2月 29 22:32 session_17d739312378d941414279d8a25f1229
自定义存储驱动
Workerman session 中,除了文件 session 存储引擎
和 redis session 存储引擎
外,可以通过 SessionHandlerInterface
接口实现自定义存储引擎,如 mongodb、mysql 存储引擎等等。
实现存储引擎的步骤
- 1、实现 SessionHandlerInterface 接口
- 2、使用
Workerman\Protocols\Http\Session::handlerClass($class_name, $config)
方法替换底层SessionHandler接口
实现 SessionHandlerInterface 接口
自定义 session 存储驱动须实现 SessionHandlerInterface 接口。这个接口包含以下方法
SessionHandlerInterface {
/* Methods */
abstract public read ( string $session_id ) : string
abstract public write ( string $session_id , string $session_data ) : bool
abstract public destroy ( string $session_id ) : bool
abstract public gc ( int $maxlifetime ) : int
abstract public close ( void ) : bool
abstract public open ( string $save_path , string $session_name ) : bool
}
替换底层驱动
Workerman\Protocols\Http\Session::handlerClass($class_name, $config);
- $class_name 为实现 SessionHandlerInterface 接口的 SessionHandler 类的名字。如果有命名空间则需要带上完整的命名空间
- $config 为 SessionHandler 类的构造函数的参数
实现自定义存储驱动
1、创建自定义存储驱动文件
根目录下:Session/MySessionHandler.php
<?php
namespace Session;
class MySessionHandler implements \SessionHandlerInterface
{
protected static $store = [];
public function __construct($config) {
var_dump($config);
}
/**
* 打开会话存储。在此示例中,始终返回 true 表示打开成功。
*
* @param $save_path
* @param $name
* @return true
*/
public function open($save_path, $name)
{
return true;
}
/**
* 读取指定会话 ID 对应的会话数据
*
* @param $session_id
* @return mixed|string
*/
public function read($session_id)
{
return isset(static::$store[$session_id]) ? static::$store[$session_id]['content'] : '';
}
/**
* 写入会话数据到存储中
*
* @param $session_id
* @param $session_data
* @return void
*/
public function write($session_id, $session_data)
{
static::$store[$session_id] = ['content' => $session_data, 'timestamp' => time()];
}
/**
* 关闭会话存储。在此示例中,始终返回 true 表示关闭成功。
*
* @return bool 是否成功关闭会话存储
*/
public function close(): bool
{
return true;
}
/**
* 销毁指定会话ID的会话数据
*
* @param $session_id
* @return true
*/
public function destroy($session_id)
{
unset(static::$store[$session_id]);
return true;
}
/**
* 回收站功能,清除过期会话数据
*
* @param int $maxlifetime 最大生命周期,单位秒
* @return void
*/
public function gc($maxlifetime): void
{
$time_now = time();
foreach (static::$store as $session_id => $info) {
if ($time_now - $info['timestamp'] > $maxlifetime) {
unset(static::$store[$session_id]);
}
}
}
}
2、使用自定义驱动
<?php
/**
* session.php
*
* 使用文件存储,使用自定义驱动
*/
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
use Workerman\Protocols\Http\Request;
use Workerman\Protocols\Http\Session;
use Session\MySessionHandler;
require_once __DIR__ . '/vendor/autoload.php';
$worker = new Worker('http://0.0.0.0:8888');
$config = ['host' => 'localhost'];
Session::handlerClass(MySessionHandler::class, $config);
$worker->onMessage = function (TcpConnection $connection, Request $request) {
$session = $request->session();
$session->set('username', '王丽丽');
$connection->send($session->get('username'));
};
Worker::runAll();
3、测试
浏览器中访问地址,然后观察服务端显示:
$ php session.php start
...
array(1) {
["host"]=>
string(9) "localhost"
}
这样,自定义驱动就成功啦。
SSE
SSE(Server-sent Events)是一种服务端推送技术。本质是客户端发送携带 Accept: text/event-stream
头的 HTTP 请求后,不关闭连接,服务端可以在这个连接上不断的给客户端推送数据。
与 Websocket 与的区别如下:
SSE | Websocket |
---|---|
只能服务端向客户端推 | 双向通讯 |
默认支持断线重连 | 需要自己实现 |
只能传输 utf8 文本,二进制数据需要编码成 utf8 后传送 | 默认支持传送 utf8 和二进制数据 |
自带消息类型 | 需要自己实现 |
<?php
/**
* session.php
*
* 使用文件存储,SSE
*/
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
use Workerman\Protocols\Http\Request;
use Workerman\Protocols\Http\ServerSentEvents;
use Workerman\Protocols\Http\Response;
use Workerman\Timer;
use Session\MySessionHandler;
use Workerman\Protocols\Http\Session;
require_once __DIR__ . '/vendor/autoload.php';
$worker = new Worker('http://0.0.0.0:8888');
$config = ['host' => 'localhost'];
Session::handlerClass(MySessionHandler::class, $config);
$worker->onMessage = function (TcpConnection $connection, Request $request) {
// 如果 Accept 头是 text/event-stream 则说明是 SSE 请求
if ($request->header('accept') === 'text/event-stream') {
// 首先发送一个 Content-Type: text/event-stream 头的响应
$connection->send(new Response(200, ['Content-Type' => 'text/event-stream'], "\r\n"));
// 定时向客户端推送数据
$timer_id = Timer::add(2, function () use ($connection, &$timer_id){
// 连接关闭的时候要将定时器删除,避免定时器不断累积导致内存泄漏
if ($connection->getStatus() !== TcpConnection::STATUS_ESTABLISHED) {
Timer::del($timer_id);
return;
}
// 发送 message 事件,事件携带的数据为 hello,消息id可以不传
$connection->send(new ServerSentEvents(['event' => 'message', 'data' => 'hello', 'id'=>1]));
});
return;
}
$connection->send('ok');
};
Worker::runAll();
注意:这个案例使用的是自定义存储驱动。
测试:
1、浏览器访问:127.0.0.1:8888
;
2、打开 F12,输入如下代码:
let source = new EventSource('http://127.0.0.1:8888');
source.addEventListener('message', function (event) {
let data = event.data;
console.log(data); // 输出 hello
}, false);