配置说明
配置文件存放在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控制
优势:
- 性能强悍,单线程每秒可读写200万次;
- 应用代码无需加锁,Table内置行锁自旋锁,所有操作均是多线程 / 多进程安全。用户层完全不需要考虑数据同步问题;
- 支持多进程,Table可以用于多进程之间共享数据;
- 使用行锁,而不是全局锁,仅当 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实现,数据结构使用最小堆,可支持添加大量定时器。
- 在同步 IO 进程中使用setitimer和信号实现,如Manager和TaskWorker进程
- 在异步 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目录结构如下:

定义接口抽象方法
<?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");
}
}