文档授权验证

请输入授权码后查看文档内容

Skip to content

配置说明

配置文件存放在config/swoole.php

<?php

return [
    'http'       => [
        'enable'     => true,
        'host'       => '0.0.0.0',
        'port'       => 9501,
        'worker_num' => swoole_cpu_num(),
        'options'    => [],
    ],
    'websocket'  => [
        'enable'        => true,
        'route'         => true,
        'handler'       => \think\swoole\websocket\Handler::class,
        'ping_interval' => 25000,
        'ping_timeout'  => 60000,
        'room'          => [
            'type'  => 'table',
            'table' => [
                'room_rows'   => 8192,
                'room_size'   => 2048,
                'client_rows' => 4096,
                'client_size' => 2048,
            ],
            'redis' => [
                'host'          => '127.0.0.1',
                'port'          => 6379,
                'max_active'    => 3,
                'max_wait_time' => 5,
            ],
        ],
        'listen'        => [],
        'subscribe'     => [],
    ],
    'rpc'        => [
        'server' => [
            'enable'     => false,
            'host'       => '0.0.0.0',
            'port'       => 9000,
            'worker_num' => swoole_cpu_num(),
            'services'   => [
            ],
        ],
        'client' => [],
    ],
    //队列
    'queue'      => [
        'enable'  => false,
        'workers' => [],
    ],
    'hot_update' => [
        'enable'  => env('APP_DEBUG', false),
        'name'    => ['*.php'],
        'include' => [
            app_path(),
            root_path('config'),
            root_path('route'),
            root_path('tmcore'),
        ],
        'exclude' => [],
    ],
    //连接池
    'pool'       => [
        'db'    => [
            'enable'        => true,
            'max_active'    => 3,
            'max_wait_time' => 5,
        ],
        'cache' => [
            'enable'        => true,
            'max_active'    => 3,
            'max_wait_time' => 5,
        ],
        //自定义连接池
    ],
    'ipc'        => [
        'type'  => 'unix_socket',
        'redis' => [
            'host'          => '127.0.0.1',
            'port'          => 6379,
            'max_active'    => 3,
            'max_wait_time' => 5,
        ],
    ],
    //锁
    'lock'       => [
        'enable' => false,
        'type'   => 'table',
        'redis'  => [
            'host' => '127.0.0.1',
            'port'          => 6379,
            'max_active'    => 3,
            'max_wait_time' => 5,
        ],
    ],
    'tables'     => [],
    //每个worker里需要预加载以共用的实例
    'concretes'  => [],
    //重置器
    'resetters'  => [],
    //每次请求前需要清空的实例
    'instances'  => [],
    //每次请求前需要重新执行的服务
    'services'   => [],
];

HTTP服务

通过自定义命令启动,监听端口为 9505

php think swoole:client http -d

服务文件存放在 tmcore/services/swoole/HttpService.php

触发事件, 可在app/event中绑定:

// 服务启动成功
event('swoole_client.http.start')

// 进程启动事件
event('swoole_client.http.workerStart')

// HTTP 请求事件
event('swoole_client.http.request')

// 进程停止事件
event('swoole_client.http.workerStop')

TCP服务

通过自定义命令启动,监听端口为 9502

php think swoole:client tcp -d

服务文件存放在 tmcore/services/swoole/TcpService.php

触发事件, 可在app/event中绑定:

// 服务启动成功
event('swoole_client.tcp.start')

// 进程启动事件
event('swoole_client.tcp.workerStart')

// 接受数据事件
event('swoole_client.tcp.receive.request')

// 进程停止事件
event('swoole_client.tcp.workerStop')

// 客户端关闭连接
event('swoole_client.tcp.close')

UDP服务

通过自定义命令启动,监听端口为 9503

php think swoole:client tcp -d

服务文件存放在 tmcore/services/swoole/UdpService.php

触发事件, 可在app/event中绑定:

// 服务启动成功
event('swoole_client.udp.start')

// 进程启动事件
event('swoole_client.udp.workerStart')

// 接受数据事件
event('swoole_client.udp.receive')

// 进程停止事件
event('swoole_client.udp.workerStop')

WebSocket服务

WebSocket 协议是基于 TCP 的一种新的网络协议,使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

WebSocket 使用场景

社交聊天、弹幕、多玩家游戏、协同编辑、股票基金实时报价、体育实况更新、视频会议聊天、基于位置的应用、在线教育等需要高实时性的运用场景。

在 WebSocket 之前,传统方式我们想要做聊天程序,可能会使用 JavaScript 定时器,每隔一秒钟发送一次 HTTP 请求到服务器,查询有没有新消息。

有了 WebSocket ,客户端通过浏览器以 HTTP 方式向服务端发送 WebSocket 连接请求,然后服务器发出回应,这个过程通常称为“握手” 。浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道,将协议升级为 WebSocket,有新消息的话,服务端主动将消息推送给客户端。

think-swoole

扩展内自带websocket服务,支持聊天室、心跳包等自带服务

关联配置:

'websocket'  => [
    'enable'        => true,
    'route'         => true,
    'handler'       => \think\swoole\websocket\Handler::class,
    'ping_interval' => 25000,
    'ping_timeout'  => 60000,
    'room'          => [
        'type'  => 'table',
        'table' => [
            'room_rows'   => 8192,
            'room_size'   => 2048,
            'client_rows' => 4096,
            'client_size' => 2048,
        ],
        'redis' => [
            'host'          => '127.0.0.1',
            'port'          => 6379,
            'max_active'    => 3,
            'max_wait_time' => 5,
        ],
    ],
    'listen'        => [],
    'subscribe'     => [],
],

关联事件,在app/event.php中绑定:

'swoole.websocket.Open' => [\tmcore\listens\SwooleWebSocketOpen::class], // websocket 连接事件
'swoole.websocket.Message' => [\tmcore\listens\SwooleWebSocketMessage::class], // websocket 消息事件
'swoole.websocket.Close' => [\tmcore\listens\SwooleWebSocketClose::class], // websocket 关闭事件

控制器Controller中调用

<?php

namespace app\controller\admin\kefu;

use app\controller\admin\AdminController;

class Manage extends AdminController  {

    public function websocket(){

        return \think\swoole\helper\websocket();
    }
}

前端调用

<!DOCTYPE HTML>
<html>
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>

消息:<input type="text" id="message">
接收者:<input type="text" id="to">
<button onclick="send()">发送</button>

<script>

    var ws = new WebSocket("http:127.0.0.1/admin/kefu/ws");
    ws.onopen = function(){
        console.log('连接成功');
    }

    //数据返回的解析
    function mycallback(data){
        var start = data.indexOf('[') // 第一次出现的位置
        var start1 = data.indexOf('{')

        if(start < 0){
            start = start1;
        }
        if(start >= 0 && start1 >= 0){
            start = Math.min(start,start1);
        }

        if(start >= 0){
            console.log(data);
            var json = data.substr(start); //截取
            var json = JSON.parse(json);
            console.log(json);
        }
    }

    ws.onmessage = function(data){
        // console.log(data.data);
        mycallback(data.data);
    }

    ws.onclose = function(){
        console.log('连接断开');
    }

    function send()
    {
        var message = document.getElementById('message').value;
        var to = document.getElementById('to').value;
        console.log("准备给" + to + "发送数据:" + message);
        ws.send(JSON.stringify(['test',{
            to:to,
            message:message
        }])); //发送的数据必须是 ['test',数据] 这种格式
    }
</script>
</body>
</html>

自定义

通过自定义命令启动,监听端口为 9504

php think swoole:client ws -d

服务文件存放在 tmcore/services/swoole/WebSocketService.php

触发事件, 可在app/event中绑定:

// 服务启动成功
event('swoole_client.ws.start')

// 客户端握手成功
event('swoole_client.ws.open')

// 收到客户端消息
event('swoole_client.ws.message')

// 客户端断开连接
event('swoole_client.ws.close')

协程MYSQL

服务文件存放在 tmcore/services/swoole/MysqlService.php

使用示例:

$mysqlService = new MysqlService();

// 测试连接
if (!$mysqlService->isConnected()) {
    if (!$mysqlService->connect()) {
        return [
            'status' => 500,
            'data' => [
                'error' => '数据库连接失败',
                'message' => $mysqlService->getError(),
            ]
        ];
    }
}

// 获取 MySQL 版本
$version = $mysqlService->queryValue("SELECT VERSION() as version");

// 获取当前数据库
$database = $mysqlService->queryValue("SELECT DATABASE() as db");

// 获取表列表(限制前 10 个)
$tables = $mysqlService->query("SHOW TABLES LIMIT 10");

// 获取连接统计信息
$stats = $mysqlService->getStats();

return [
    'status' => 200,
    'data' => [
        'message' => 'MySQL 协程客户端测试成功',
        'mysql_version' => $version,
        'current_database' => $database,
        'table_count' => $tables !== false ? count($tables) : 0,
        'tables' => $tables !== false ? array_slice($tables, 0, 5) : [],
        'connection_stats' => $stats,
    ]
];

协程REDIS

服务文件存放在 tmcore/services/swoole/RedisService.php

使用示例:

$redisService = new RedisService();
            
// 测试连接
if (!$redisService->isConnected()) {
    if (!$redisService->connect()) {
        return [
            'status' => 500,
            'data' => [
                'error' => 'Redis 连接失败',
                'message' => '请检查 Redis 配置和连接',
            ]
        ];
    }
}

// 测试基本操作
$testKey = 'test:http:' . time();
$testValue = 'Hello Redis from Swoole!';

// 设置值
$setResult = $redisService->set($testKey, $testValue, 60);

// 获取值
$getValue = $redisService->get($testKey);

// 测试哈希
$hashKey = 'test:hash:' . time();
$redisService->hSet($hashKey, 'name', 'Swoole Redis');
$redisService->hSet($hashKey, 'version', '1.0.0');
$hashData = $redisService->hGetAll($hashKey);

// 测试列表
$listKey = 'test:list:' . time();
$redisService->lPush($listKey, 'item1');
$redisService->lPush($listKey, 'item2');
$listLength = $redisService->lLen($listKey);

// 测试集合
$setKey = 'test:set:' . time();
$redisService->sAdd($setKey, ['member1', 'member2', 'member3']);
$setSize = $redisService->sCard($setKey);

// 获取连接统计信息
$stats = $redisService->getStats();

// 清理测试数据
$redisService->delete([$testKey, $hashKey, $listKey, $setKey]);

return [
    'status' => 200,
    'data' => [
        'message' => 'Redis 协程客户端测试成功',
        'connection_stats' => $stats,
        'test_results' => [
            'set_get' => [
                'set' => $setResult,
                'get' => $getValue,
            ],
            'hash' => $hashData,
            'list_length' => $listLength,
            'set_size' => $setSize,
        ],
    ]
];

高性能内存操作table

由于PHP语言不支持多线程,因此Swoole使用多进程模式,在多进程模式下存在进程内存隔离,在工作进程内修改global全局变量和超全局变量时,在其他进程是无效的。

推荐使用共享内存来保存数据,Swoole\Table一个基于共享内存和锁实现的超高性能,并发数据结构。用于解决多进程 / 多线程数据共享和同步加锁问题。Table的内存容量不受PHP的memory_limit控制

优势:

  1. 性能强悍,单线程每秒可读写200万次;
  2. 应用代码无需加锁,Table内置行锁自旋锁,所有操作均是多线程 / 多进程安全。用户层完全不需要考虑数据同步问题;
  3. 支持多进程,Table可以用于多进程之间共享数据;
  4. 使用行锁,而不是全局锁,仅当 2 个进程在同一CPU时间,并发读取同一条数据才会进行发生抢锁。

服务文件存放在 tmcore/services/swoole/TableService.php

使用示例:

// 创建缓存表
$cacheTable = TableService::createCacheTable(1024);

// 测试缓存操作
$testKey = 'test:http:' . time();
$testValue = 'Hello Swoole Table!';
$cacheTable->setCache($testKey, $testValue, 60);
$getValue = $cacheTable->getCache($testKey);

// 创建计数器表
$counterTable = TableService::createCounterTable(1024);

// 测试计数器操作
$counterKey = 'http:requests';
$counterTable->incrCounter($counterKey);
$counterTable->incrCounter($counterKey, 5);
$counterValue = $counterTable->getCounter($counterKey);

// 创建用户表
$userTable = TableService::createUserTable(1024);

// 测试用户数据操作
$userId = 'user_' . time();
$userTable->set($userId, [
    'id' => 1,
    'name' => 'Test User',
    'email' => 'test@example.com',
    'score' => 100,
    'balance' => 99.99,
    'status' => 1,
    'update_time' => time(),
]);
$user = $userTable->get($userId);

// 测试原子操作
$userTable->incr($userId, 'score', 10);
$userTable->incr($userId, 'balance', 0.5);
$updatedUser = $userTable->get($userId);

// 获取统计信息
$cacheStats = $cacheTable->getStats();
$counterStats = $counterTable->getStats();
$userStats = $userTable->getStats();

// 获取所有表
$allTables = TableService::getAllTables();

return [
    'status' => 200,
    'data' => [
        'message' => 'Swoole Table 测试成功',
        'test_results' => [
            'cache' => [
                'set' => $testValue,
                'get' => $getValue,
                'stats' => $cacheStats,
            ],
            'counter' => [
                'value' => $counterValue,
                'stats' => $counterStats,
            ],
            'user' => [
                'original' => $user,
                'after_increment' => $updatedUser,
                'stats' => $userStats,
            ],
        ],
        'all_tables' => $allTables,
    ]
];

毫秒级定时器

毫秒精度的定时器。底层基于epoll_wait和setitimer实现,数据结构使用最小堆,可支持添加大量定时器。

  1. 在同步 IO 进程中使用setitimer和信号实现,如Manager和TaskWorker进程
  2. 在异步 IO 进程中使用epoll_wait/kevent/poll/select超时时间实现

服务文件存放在 tmcore/services/swoole/TimerService.php

<?php

namespace tmcore\services\swoole;

use Swoole\Timer;
use think\facade\Log;

/**
 * 定时器服务
 */
class TimerService
{
    /**
     * 循环定时器
     *
     * @param int $limit 时间间隔(毫秒)
     * @param callable $fn 回调函数
     */
    public function tick($limit, $fn)
    {t
        Timer::tick($limit, function () use ($fn) {
            try {
                $fn();
            } catch (\Throwable $e) {
                Log::error('定时器报错[' . class_basename($this) . ']: ' . $e->getMessage());
            }
        });
    }
}

在app/event.php中绑定 \tmcore\listens\CreateTimer 触发定时器

事件中使用示例:

<?php

namespace tmcore\listens;

use tmcore\services\swoole\TimerService;
use tmcore\interfaces\ListenerInterface;
use tmcore\services\SysClearService;

/**
 * 清除系统日志事件
 */
class ClearSysLog extends TimerService implements ListenerInterface
{
    public function handle($event): void
    {
        $this->tick(1000 * 60 * 60, function () {
            (new SysClearService)->deleteLog(30);
        });
    }
}

RPC远程调用

RPC(Remote Procedure Call):远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的思想。

解决问题: 解决分布式系统中,服务之间的调用问题。 远程调用时,要能够像本地调用一样方便,让调用者感知不到远程调用的逻辑

Think-Swoole中已经实现了的基于TCP的PRC,这样我们使用传统型框架也可以做简单的分布式架构应用了。

服务端

修改 config/swoole.php

'rpc'        => [
    'server' => [
        'enable'   => true,
        'port'     => 9000,
        'services' => [
            \app\rpc\service\UserService::class
        ],
    ],
    'client' => [
    ],
],

新增rpc目录结构如下:

alt image

定义接口抽象方法

<?php

namespace app\rpc\interfaces;

interface UserInterface
{
    public function add($name);
}

实现接口方法

<?php

namespace app\rpc\service;

use app\rpc\interfaces\UserInterface;

class UserService implements UserInterface
{
    public function add($name)
    {
        return "您要新增的用户名是:{$name}";
    }
}

服务端的操作就已经完成

客户端

修改 config/swoole.php

'rpc'        => [
    'server' => [
        'enable'   => false,
        'port'     => 9000,
        'services' => [	
        ],
    ],
    'client' => [
        'userservice'=>[
            //RPC服务端的ip地址
            'host' => '127.0.0.1',
             //RPC服务端的端口
            'port' => 9000
        ]
    ],
],

生成RPC服务接口

php think rpc:interface

会在app目录下生成一个rpc.php的文件,它就是RPC服务接口文件

文件内容大致:

<?php

/**
 * This file is auto-generated.
 */

declare(strict_types=1);

namespace rpc\contract\userservice;

interface UserInterface
{
	public function add($name);
}
return ['userservice' => ['rpc\contract\userservice\UserInterface']];

注意:return中就是每个服务接口的命名空间;在控制器中使用该命名空间实例对象就可以调用

controller调用

<?php

namespace app\controller\admin;

use app\controller\admin\AdminController;
use rpc\contract\userservice\UserInterface;

class User extends AdminController
{
    public function add(UserInterface $user)
    {
       echo $user->add("admin");
    }
}

Released under the Apache-2.0 License.