搜索
首页php框架ThinkPHP分享一个think-swoole实战案例【详细演示】

官网文档

thinkphp6文档
https://www.kancloud.cn/manual/thinkphp6_0/1037479 
swoole文档
https://wiki.swoole.com/#/
think-swoole文档
https://www.kancloud.cn/manual/thinkphp6_0/1359700

安装

composer require topthink/think-swoole

命令行

php think swoole [start|stop|reload|restart]

服务启动

当你在命令行php think swoole下执行完成之后就会启动一个HTTP Server,可以直接访问当前的应用【推荐教程:thinkphp

'server'     => [
    'host'      => env('SWOOLE_HOST', '0.0.0.0'), // 监听地址
    'port'      => env('SWOOLE_PORT', 9501), // 监听端口
    'mode'      => SWOOLE_PROCESS, // 运行模式 默认为SWOOLE_PROCESS
    'sock_type' => SWOOLE_SOCK_TCP, // sock type 默认为SWOOLE_SOCK_TCP
    'options'   => [
        // 服务启动后,进程ID存放文件
        'pid_file'              => runtime_path() . 'swoole.pid',
        // swoole 的日志文件
        'log_file'              => runtime_path() . 'swoole.log',
        // 守护进程模式设置 true 后台运行
        'daemonize'             => false,
        // 设置启动的reactor线程数
        'reactor_num'           => swoole_cpu_num(),
        // 设置启动的worker进程数
        'worker_num'            => swoole_cpu_num(),
        //配置Task进程的数量
        'task_worker_num'       => swoole_cpu_num(),
        //开启静态文件请求处理,需配合document_root
        'enable_static_handler' => true,
        //静态文件根目录
        'document_root'         => root_path('public'),
        // 设置最大数据包尺寸,单位字节
        'package_max_length'    => 20 * 1024 * 1024,
        //配置发送输出缓冲区内存尺寸
        'buffer_output_size'    => 10 * 1024 * 1024,
        //设置客户端连接最大允许占用的内存数量
        'socket_buffer_size'    => 128 * 1024 * 1024,
    ],
],

热更新

swoole服务器运行过程中php文件是常驻内存运行,这样就可以避免重复的读取磁盘,重复的解释编译php,以便达到最高的性能,所以修改代码需要重启服务

think-swoole扩展提供热更新功能,在检测相关文件有更新会自动重启,不在需要手动完成重启,方便开发调试

生产环境下不建议开始文件监控,性能损耗,正常情况下你所修改的文件需要确认无误才能进行更新部署

.env里面设置APP_DEBUG = true会默认开启热更新

'hot_update' => [
    'enable'  => env('APP_DEBUG', false),
    'name'    => ['*.php'],
    'include' => [app_path()],
    'exclude' => [],
],

参数说明

参数 说明
enable 是否开启热更新
name 监听哪些类型的文件变动
include 监听哪些目录下的文件变动
exclude 排除目录

websocket

先来一个官方的例子

$server = new Swoole\WebSocket\Server("0.0.0.0", 9501);
$server->on('open', function (Swoole\WebSocket\Server $server, $request) {
    echo "server: handshake success with fd{$request->fd}\n";
});
$server->on('message', function (Swoole\WebSocket\Server $server, $frame) {
    echo "receive from {$frame->fd}:{$frame->data}\n";
    $server->push($frame->fd, "this is server");
});
$server->on('close', function ($ser, $fd) {
    echo "client {$fd} closed\n";
});
$server->start();

开启think-swoole的websocket功能 \config\swoole.php

'websocket'  => [
    'enable'        => true,
],

创建三个事件

php think make:listener SwWsConnect
php think make:listener SwWsClose
php think make:listener SwWsMessage

然后将这三个事件写到到事件监听中,分别有以下2中文件可以修改方式,注意二选一

thinkphp6自带的事件绑定app\event.php

    'listen'    => [
        ........
        // 监听链接
        'swoole.websocket.Connect' => [
            \app\listener\SwWsConnect::class
        ],
        //关闭连接
        'swoole.websocket.Close' => [
            \app\listener\SwWsClose::class
        ],
        //发送消息场景
        'swoole.websocket.Message' => [
            \app\listener\SwWsMessage::class
        ]
    ],

think-swoole事件绑定config\swoole.php

'listen'        => [
    'connect'=>\app\listener\SwWsConnect::class,
    'close'=>\app\listener\SwWsClose::class,
    'message'=> \app\listener\SwWsMessage::class
],
怎么选择是保存在config\swoole.php还是app\event.php配置中呢?

首先我们 我们确定一下我们这个项目中存在有几个实时通讯,

如果只是存在一个实时通讯 个人建议 保存在config\swoole.php

如果是存在多个实时通讯,就保存在app\event.php

key值 必须是swoole.websocket.事件名称 例如 swoole.websocket.Message

开始写事件中中方法

连接事件app\listener\SwWsConnect.php

public function handle($event, \think\swoole\websocket $ws)
{
    // 获取当前发送者的fd
    $fd = $ws->getSender();
    echo "server: handshake success with fd{$fd}\n";
}

关闭事件app\listener\SwWsClose.php

public function handle($event, \think\swoole\websocket $ws)
{
    $fd = $ws->getSender();
    echo "client {$fd} closed\n";
}

message事件app\listener\SwWsMessage.php

public function handle($event, \think\swoole\websocket $ws)
{
    $fd = $ws->getSender();
    $data = json_encode($event);
    echo "receive from {$fd}:{$data}\n";
    $ws->emit("this is server", $fd);
}

启动php think swoole进行测试

think-swoole中的websocket方法总结

//给自己发消息
$ws->emit("this is server", $ws->getSender());
//给指定一个fd发消息
$ws->to($to)->emit("messagecallback",$data);
//给指定多个人发消息
$ws->to([1,2,3])->emit("messagecallback",$data);
//发送给所有的(不包含自己)
$ws->broadcast()->emit("messagecallback",$data);
//模拟formfd 给tofd 发送消息
$ws->setSender($formfd)->to($tofd)->emit("messagecallback",$data);
注意:在多个实时通讯场景下使用 emit

第一个参数传入  传入 事件名称callback 例如 messagecallback

如果你发现你think-swoole中有些没有swoole中的方法可以这么干

$sw = app('swoole.server');
$sw = app("think\swoole\Manager")->getServer();
//以上二选一

$es = $sw->isEstablished($fd); //检查连接是否为有效的WebSocket客户端连接
var_dump($es);

聊天室room实现

前端文件参考 html\room.htmlhtml\room-socket-io.html

php think make:listener SwRoomJoin
php think make:listener SwRoomLeave
php think make:listener SwRoomMessage

事件绑定

// 加入房间
'swoole.websocket.RoomJoin' => [
    \app\listener\SwRoomJoin::class
],
// 离开房间
'swoole.websocket.Roomleave' => [
    \app\listener\SwRoomLeave::class
],
// 在房间发消息
'swoole.websocket.RoomMessage' => [
    \app\listener\SwRoomMessage::class
]

加入房间逻辑

public function handle($event, \think\swoole\websocket $ws, \think\swoole\websocket\room $room)
{
    $fd = $ws->getSender();
    //客户端假如定的room
    $roomid = $event['room'];
    //获取指定房间下有哪些客户端
    $roomfds = $room->getClients($roomid);
    // 判断这个房间有没有自己 如果有自己就不需要再次发送通知
    if (in_array($fd, $roomfds)) {
        $ws->to($roomfds)->emit("roomjoincallback", "房间{$roomid}已加入");
        return;
    }
    //加入房间
    $ws->join($roomid);
    $ws->to($roomfds)->emit("roomjoincallback", "{$fd}加入房间{$roomid}成功");
}

离开房间逻辑

public function handle($event, \think\swoole\websocket $ws, \think\swoole\websocket\Room $room)
{
    $roomid = $event['room'];
    $fd = $ws->getSender();
    $roomfds = $room->getClients($roomid);
    if (!in_array($fd, $roomfds)) {
        $ws->emit("roomleavecallback", "{$fd}不在{$roomid}房间内,怎么离开~");
        return;
    }
    //离开房间
    $ws->leave($roomid);
    //获取当前客户端加入了哪些客户端
    $rooms = $room->getRooms($fd);
    $ws->to($roomfds)->emit("roomleavecallback", "{$fd}已离开了~~");
}

在房间发布聊天逻辑

    public function handle($event, \think\swoole\websocket $ws, \think\swoole\websocket\room $room)
    {
        //
        $roomid = $event['room'];
        $text = $event['text'];
        $fd = $ws->getSender();
        $roomfds = $room->getClients($roomid);
        if (!in_array($fd, $roomfds)) {
            $ws->emit("roommessagecallback", "{$fd}不在{$roomid}房间内,无法进入发布聊天~");
            return;
        }
        $ws->to($roomfds)->emit("roommessagecallback",  $text);
    }

事件订阅

php think make:listener SwSubscribe

applistenerSwSubscribe.php

46bbf8c9f7825572c4d90f0300492227ws = app('think\swoole\Websocket');
    // }

    public function __construct(\think\Container $c)
    {
        $this->ws = $c->make(\think\swoole\Websocket::class);
    }
    
    public function onConnect()
    {
        $fd = $this->ws->getSender();
        echo "server: handshake success with fd{$fd}\n";
    }
    public function onClose()
    {
        $fd = $this->ws->getSender();
        echo "client {$fd} closed\n";
    }
    public function onMessage($event)
    {
        $fd = $this->ws->getSender();
        var_dump($event);
        echo "server: handshake success with fd{$fd}\n";
        $this->ws->emit("this is server", $fd);
    }
}

有点类似 将原生的swoole代码改成面向对象代码,生效方法 config\swoole.php中在subscribe 加入\app\listener\SwSubscribe::class

'subscribe'     => [
    \app\listener\SwSubscribe::class
],

app\event.php文件中的 swoole.websocket.Connect 相当于 app\listener\SwSubscribe.php文件中的onConnect函数。如果同时存在的存在的话,就会向客户端发送2次以上的消息

Task任务投递

https://wiki.swoole.com/#/start/start_task

生成事件

php think make:listener SwSendEmailTask

编写发送邮件方法app\listener\SwSendEmailTask.php

public function handle($event)
{
    var_dump($event);
    //
    echo "开发发送邮件".time();
    sleep(3);
    echo "结束发送邮件".time();
}

注册事件app\event.php

'swoole.task'=>[
    \app\listener\SwSendEmailTask::class
],

在控制器中投递任务

public function doRegister()
{
    $server = app('swoole.server');
    $server->task(\app\listener\SwSendEmailTask::class);
    return "注册成功";
}

public function doRegister(\think\swoole\Manager $manager)
{
    $server = $manager->getServer();
    $server->task(\app\listener\SwSendEmailTask::class);
    return "注册成功";
}
public function doRegister(\Swoole\Server $server)
{
    $server->task(\app\listener\SwSendEmailTask::class);
    return "注册成功";
}
三种获取\Swoole\Server,任意选其一

在swoole中还有一个事件叫finish,它的作用就是把异步任务的结果返回,在think-swool是这么处理的

定义一个发送邮件异步任务处理结果的事件

php think make:listener SwSendEmailFinish

注册事件app\event.php

'swoole.finish'=>[
    \app\listener\SwSendEmailFinish::class
],

在task任务中调用

public function handle($event)
{
    var_dump($event);
    //
    echo "开发发送邮件".time();
    sleep(3);
    echo "结束发送邮件".time();
    $event->finish(\app\listener\SwSendEmailFinish::class);
}

高性能共享内存 Table

https://wiki.swoole.com/#/mem...

先定结构在进行操作数据(原生swoole操作)

$table = new Swoole\Table(1024);
//创建表
$table->column("id", Swoole\Table::TYPE_INT);
$table->column("name", Swoole\Table::TYPE_STRING);
$table->column("money", Swoole\Table::TYPE_FLOAT);
$table->create();

//添加数据
$table->set("zq", [
    'id' => 1,
    'name' => "zhiqiang",
    'money' => 100,
]);
//获取一行数据
$table->get("zq");
// 修改数据
// 字段递增
$table->incr("zq","money",2);
//递减
$table->decr("zq","money",2);
// 返回 table 中存在的条目数。
$table->count();
//遍历table中的数据
foreach($table as $item){
    var_dump($item);
}

think-swoole中的操作

先对table表结构进行初始化config\swoole.php

    'tables'     => [
        'user'=>[
            'size'=>1024,
            'columns'=>[
                [
                    'name'=>'id',
                    'type'=>\Swoole\Table::TYPE_INT
                ],
                [
                    'name'=>'name',
                    'type'=>\Swoole\Table::TYPE_STRING,
                    'size'=>32
                ],
                [
                    'name'=>'money',
                    'type'=>\Swoole\Table::TYPE_FLOAT
                ],

            ],
        ],
    ],

操作数据

$table =  app('swoole.table.user');
$table->set("zq", [
    'id' => 1,
    'name' => "zhiqiang",
    'money' => 100
]);
//获取一行数据
$table->get("zq");
// 修改数据
// 字段递增
$table->incr("zq", "money", 2);
//递减
$table->decr("zq", "money", 2);
// 返回 table 中存在的条目数。
$table->count();
//遍历table中的数据
foreach ($table as $item) {
var_dump($item);
}
// 检查 table 中是否存在某一个 key。
$table->exist('zq');
//获取实际占用内存尺寸,单位字节
$table->momorySize();

RPC

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

详细介绍:https://developer.51cto.com/a...

  • 解决分布式系统中,服务之间的调用问题。
  • 远程调用时,要能够像本地调用一样方便,让调用者感知不到远程调用的逻辑。
  • 节点角色说明:
  • Server: 暴露服务的服务提供方
  • Client: 调用远程服务的服务消费方
  • Registry: 服务注册与发现的注册中心

think-swoole实现RPC功能

服务器端

接口定义app/rpc/interfaces/UserInterface.php

3726315c3b3d0564a100f1ab9a1450d6 [
        'server' => [
            //开启rpc服务
            'enable'   => true,
            //rpc端口
            'port'     => 9000,
            'services' => [
                //注册服务
                \app\rpc\services\UserService::class
            ],
        ],
        // 如果填写也是可以调用其他服务端
        'client' => [
        ],
    ],

启动服务端

php think swoole start /  php think swoole:rpc

客户端

    'rpc'        => [
        'server' => [
        ],
        'client' => [
            'tp6'=>[
                //服务端的ip地址
                'host'=>'127.0.0.1',
                //服务端对应的端口
                'port'=>'9000'
            ]
            // 更多服务端
        ],
    ],

运行php think rpc:interface生成RPC接口文件app\rpc.php

3bc2c1b4a3deab348d628488d9a683ad ['rpc\contract\tp6\UserInterface']];

在控制器调用

    public function index(\rpc\contract\tp6\UserInterface $user)
    {
        //
        $user->find(1);
//        $user->create();
    }

定时任务

在think-swoole 2.0版本的时候还是支持自定义定时任务配置,详细参考https://github.com/top-think/think-swoole/tree/2.0

在3.0就不支持了,在这里介绍一个通用的命令行启动定时任务

php think make:command SwooleTimer

加载命令行config/console.php

'commands' => [
    'swooletimer'=>app\command\SwooleTimer::class
    ...........
],

书写命令脚本app/command/SwooleTimer.php

2c7875dc6c653367e938e61ba5593bfdsetName('app\command\swooletimer')
            ->addArgument('action', Argument::OPTIONAL, "start | stop", 'start')
            ->setDescription('Swoole Timer for ThinkPHP');
    }


    public function handle()
    {
        $action = $this->input->getArgument('action');
        if (in_array($action, ['start','stopall'])) {
            $this->app->invokeMethod([$this, $action], [], true);
        } else {
            $this->output->writeln("bbbcc7d749ec7f3e0fb2ba5af34b74a7Invalid argument action:{$action}, Expected start4c8d80f29f204b22c4490983d39ebac7");
        }
    }

    /**
     * 启动定时任务 主要任务计划在这里书写
     */
    protected function start()
    {
        // https://wiki.swoole.com/#/timer
        $timer_id=swoole_timer_tick(2000,function (){
            echo "2s循环执行需要做的事情".time()."\n";
        });
        $this->output->writeln("Swoole Timer_id:{$timer_id} ");
    }

    /**
     * 清除所有的定时任务
     */
    protected  function stop(){
        swoole_timer_clear_all();
        $this->output->writeln("Swoole Timer  clear all ok");
    }
}

以上是分享一个think-swoole实战案例【详细演示】的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文转载于:segmentfault。如有侵权,请联系admin@php.cn删除
ThinkPHP内置测试框架的关键功能是什么?ThinkPHP内置测试框架的关键功能是什么?Mar 18, 2025 pm 05:01 PM

本文讨论了ThinkPHP的内置测试框架,突出了其关键功能(例如单元和集成测试),以及它如何通过早期的错误检测和改进的代码质量来增强应用程序可靠性。

如何使用ThinkPHP来构建实时股票市场数据源?如何使用ThinkPHP来构建实时股票市场数据源?Mar 18, 2025 pm 04:57 PM

文章讨论了使用ThinkPHP进行实时股票市场数据提要,重点是设置,数据准确性,优化和安全措施。

在无服务器体系结构中使用ThinkPHP的关键注意事项是什么?在无服务器体系结构中使用ThinkPHP的关键注意事项是什么?Mar 18, 2025 pm 04:54 PM

本文讨论了在无服务器体系结构中使用ThinkPHP的关键注意事项,专注于性能优化,无状态设计和安全性。它突出了诸如成本效率和可扩展性之类的收益,但也应对挑战

如何在ThinkPHP微服务中实现服务发现和负载平衡?如何在ThinkPHP微服务中实现服务发现和负载平衡?Mar 18, 2025 pm 04:51 PM

本文讨论了在ThinkPHP微服务中实施服务发现和负载平衡,重点是设置,最佳实践,集成方法和推荐工具。[159个字符]

ThinkPHP依赖性注入容器的高级功能是什么?ThinkPHP依赖性注入容器的高级功能是什么?Mar 18, 2025 pm 04:50 PM

ThinkPHP的IOC容器提供了高级功能,例如懒惰加载,上下文绑定和方法注入PHP App中有效依赖性管理的方法。Character计数:159

如何使用ThinkPHP来构建实时协作工具?如何使用ThinkPHP来构建实时协作工具?Mar 18, 2025 pm 04:49 PM

本文讨论了使用ThinkPHP来构建实时协作工具,重点关注设置,Websocket集成和安全性最佳实践。

使用ThinkPHP来构建SaaS应用程序的主要好处是什么?使用ThinkPHP来构建SaaS应用程序的主要好处是什么?Mar 18, 2025 pm 04:46 PM

ThinkPHP具有轻巧的设计,MVC架构和可扩展性。它通过各种功能提高可扩展性,加快开发并提高安全性。

如何使用ThinkPHP和RabbitMQ构建分布式任务队列系统?如何使用ThinkPHP和RabbitMQ构建分布式任务队列系统?Mar 18, 2025 pm 04:45 PM

本文概述了使用ThinkPhp和RabbitMQ构建分布式任务队列系统,重点是安装,配置,任务管理和可扩展性。关键问题包括确保高可用性,避免常见的陷阱,例如不当

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
1 个月前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
1 个月前By尊渡假赌尊渡假赌尊渡假赌
威尔R.E.P.O.有交叉游戏吗?
1 个月前By尊渡假赌尊渡假赌尊渡假赌

热工具

MinGW - 适用于 Windows 的极简 GNU

MinGW - 适用于 Windows 的极简 GNU

这个项目正在迁移到osdn.net/projects/mingw的过程中,你可以继续在那里关注我们。MinGW:GNU编译器集合(GCC)的本地Windows移植版本,可自由分发的导入库和用于构建本地Windows应用程序的头文件;包括对MSVC运行时的扩展,以支持C99功能。MinGW的所有软件都可以在64位Windows平台上运行。

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一个PHP/MySQL的Web应用程序,非常容易受到攻击。它的主要目标是成为安全专业人员在合法环境中测试自己的技能和工具的辅助工具,帮助Web开发人员更好地理解保护Web应用程序的过程,并帮助教师/学生在课堂环境中教授/学习Web应用程序安全。DVWA的目标是通过简单直接的界面练习一些最常见的Web漏洞,难度各不相同。请注意,该软件中

SecLists

SecLists

SecLists是最终安全测试人员的伙伴。它是一个包含各种类型列表的集合,这些列表在安全评估过程中经常使用,都在一个地方。SecLists通过方便地提供安全测试人员可能需要的所有列表,帮助提高安全测试的效率和生产力。列表类型包括用户名、密码、URL、模糊测试有效载荷、敏感数据模式、Web shell等等。测试人员只需将此存储库拉到新的测试机上,他就可以访问到所需的每种类型的列表。

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器