9、Workerman 基本使用 - HTTP 会话

作者: 温新

图书: 【Workerman 基本使用】

阅读: 143

时间: 2024-04-24 15:22:56

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);
请登录后再评论