目錄
官網文件
安裝
命令列
服務啟動
websocket
聊天室room实现
事件订阅
Task任务投递
高性能共享内存 Table
RPC
服务器端
接口定义app/rpc/interfaces/UserInterface.php
实现接口app/rpc/services/UserService.php
注册rpc服务config/swoole.php
客户端
定时任务
首頁 php框架 ThinkPHP 分享一個think-swoole實戰案例【詳細示範】

分享一個think-swoole實戰案例【詳細示範】

Jul 23, 2021 pm 03:08 PM
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,
    ],
],
登入後複製
熱更新生產環境下不建議開始檔案監控,效能損耗,正常情況下你所修改的檔案需要確認無誤才能進行更新部署#.envAPP_DEBUG = true
'hot_update' => [
    'enable'  => env('APP_DEBUG', false),
    'name'    => ['*.php'],
    'include' => [app_path()],
    'exclude' => [],
],
登入後複製
參數說明#參數##說明enablename
swoole伺服器運行過程中php檔案是常駐記憶體運行,這樣就可以避免重複的讀取磁碟,重複的解釋編譯php,以便達到最高的效能,所以修改程式碼需要重啟服務think-swoole擴充功能提供熱更新功能,在偵測相關檔案有更新會自動重啟,不在需要手動完成重啟,方便開發調試
裡面設定會預設開啟熱更新
是否開啟熱更新
監聽哪些類型的檔案變動########## ##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

<?php
declare (strict_types = 1);

namespace app\listener;

class SwSubscribe
{
    protected $ws = null;

    // public function __construct()
    // {
    //     $this->ws = app(&#39;think\swoole\Websocket&#39;);
    // }

    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

&#39;subscribe&#39;     => [
    \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

&#39;swoole.task&#39;=>[
    \app\listener\SwSendEmailTask::class
],
登入後複製

在控制器中投递任务

public function doRegister()
{
    $server = app(&#39;swoole.server&#39;);
    $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

&#39;swoole.finish&#39;=>[
    \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", [
    &#39;id&#39; => 1,
    &#39;name&#39; => "zhiqiang",
    &#39;money&#39; => 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

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

            ],
        ],
    ],
登入後複製

操作数据

$table =  app(&#39;swoole.table.user&#39;);
$table->set("zq", [
    &#39;id&#39; => 1,
    &#39;name&#39; => "zhiqiang",
    &#39;money&#39; => 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(&#39;zq&#39;);
//获取实际占用内存尺寸,单位字节
$table->momorySize();
登入後複製

RPC

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

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

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

think-swoole实现RPC功能

服务器端

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

<?php
namespace app\rpc\interfaces;
interface UserInterface
{
    public function create();
    public function find(int $id);
}
登入後複製

实现接口app/rpc/services/UserService.php

<?php
namespace app\rpc\services;
use app\rpc\interfaces\UserInterface;
class UserService implements UserInterface
{
    public function create()
    {
        // TODO: Implement create() method.
        return "service create success";
    }
    public function find(int $id)
    {
        // TODO: Implement find() method.
        return $id. "查询数据遍历";
    }
}
登入後複製

注册rpc服务config/swoole.php

    &#39;rpc&#39;        => [
        &#39;server&#39; => [
            //开启rpc服务
            &#39;enable&#39;   => true,
            //rpc端口
            &#39;port&#39;     => 9000,
            &#39;services&#39; => [
                //注册服务
                \app\rpc\services\UserService::class
            ],
        ],
        // 如果填写也是可以调用其他服务端
        &#39;client&#39; => [
        ],
    ],
登入後複製

启动服务端

php think swoole start /  php think swoole:rpc
登入後複製

客户端

    &#39;rpc&#39;        => [
        &#39;server&#39; => [
        ],
        &#39;client&#39; => [
            &#39;tp6&#39;=>[
                //服务端的ip地址
                &#39;host&#39;=>&#39;127.0.0.1&#39;,
                //服务端对应的端口
                &#39;port&#39;=>&#39;9000&#39;
            ]
            // 更多服务端
        ],
    ],
登入後複製

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

<?php
/**
 * This file is auto-generated.
 */
declare(strict_types=1);
namespace rpc\contract\tp6;
interface UserInterface
{
    public function create();
    public function find(int $id);
}
return [&#39;tp6&#39; => [&#39;rpc\contract\tp6\UserInterface&#39;]];
登入後複製

在控制器调用

    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

&#39;commands&#39; => [
    &#39;swooletimer&#39;=>app\command\SwooleTimer::class
    ...........
],
登入後複製

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

<?php
declare (strict_types = 1);

namespace app\command;

use think\console\Command;
use think\console\input\Argument;


class SwooleTimer extends Command
{
    protected function configure()
    {
        // 指令配置
        $this->setName(&#39;app\command\swooletimer&#39;)
            ->addArgument(&#39;action&#39;, Argument::OPTIONAL, "start | stop", &#39;start&#39;)
            ->setDescription(&#39;Swoole Timer for ThinkPHP&#39;);
    }


    public function handle()
    {
        $action = $this->input->getArgument(&#39;action&#39;);
        if (in_array($action, [&#39;start&#39;,&#39;stopall&#39;])) {
            $this->app->invokeMethod([$this, $action], [], true);
        } else {
            $this->output->writeln("<error>Invalid argument action:{$action}, Expected start</error>");
        }
    }

    /**
     * 启动定时任务 主要任务计划在这里书写
     */
    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中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

swoole協程如何在laravel使用 swoole協程如何在laravel使用 Apr 09, 2024 pm 06:48 PM

Laravel 中使用 Swoole 協程可以並發處理大量請求,優點包括:同時處理:允許同時處理多個請求。高效能:基於 Linux epoll 事件機制,高效處理請求。低資源消耗:所需伺服器資源更少。易於整合:與 Laravel 框架無縫集成,使用簡單。

如何使用Swoole實現高效能的HTTP反向代理伺服器 如何使用Swoole實現高效能的HTTP反向代理伺服器 Nov 07, 2023 am 08:18 AM

如何使用Swoole實現高效能的HTTP反向代理伺服器Swoole是一款基於PHP語言的高效能、非同步、並發的網路通訊框架。它提供了一系列的網路功能,可以用來實作HTTP伺服器、WebSocket伺服器等。在本文中,我們將介紹如何使用Swoole來實作一個高效能的HTTP反向代理伺服器,並提供具體的程式碼範例。環境配置首先,我們需要在伺服器上安裝Swoole擴展

swoole和workerman哪個好 swoole和workerman哪個好 Apr 09, 2024 pm 07:00 PM

Swoole 和 Workerman 都是高效能 PHP 伺服器框架。 Swoole 以其非同步處理、出色的效能和可擴展性而聞名,適用於需要處理大量並發請求和高吞吐量的專案。 Workerman 提供了非同步和同步模式的靈活性,具有直覺的 API,更適合易用性和處理較低並發量的專案。

swoole框架怎麼重啟服務 swoole框架怎麼重啟服務 Apr 09, 2024 pm 06:15 PM

若要重新啟動 Swoole 服務,請依照下列步驟操作:檢查服務狀態並取得 PID。使用 "kill -15 PID" 停止服務。使用啟動服務的相同命令重新啟動服務。

swoole_process 怎麼讓使用者切換 swoole_process 怎麼讓使用者切換 Apr 09, 2024 pm 06:21 PM

Swoole Process 中可讓使用者切換,具體操作步驟為:建立進程;設定進程使用者;啟動進程。

swoole和java哪個表現好 swoole和java哪個表現好 Apr 09, 2024 pm 07:03 PM

效能比較:吞吐量:Swoole 以協程機制,吞吐量更高。延遲:Swoole 的協程上下文切換開銷更低,延遲更小。記憶體消耗:Swoole 的協程佔用記憶體較少。易用性:Swoole 提供更易於使用的並發程式設計 API。

Swoole實戰:如何使用協程進行並發任務處理 Swoole實戰:如何使用協程進行並發任務處理 Nov 07, 2023 pm 02:55 PM

Swoole實戰:如何使用協程進行並發任務處理引言在日常的開發中,我們常常會遇到需要同時處理多個任務的情況。傳統的處理方式是使用多執行緒或多進程來實現並發處理,但這種方式在效能和資源消耗上存在一定的問題。而PHP作為一門腳本語言,通常無法直接使用多執行緒或多進程的方式來處理任務。然而,借助於Swoole協程庫,我們可以使用協程來實現高效能的並發任務處理。本文將介

swoole協程是怎樣調度的 swoole協程是怎樣調度的 Apr 09, 2024 pm 07:06 PM

Swoole協程是一種輕量級並發函式庫,可讓開發者編寫並發程式。 Swoole協程調度機制基於協程模式和事件循環,使用協程堆疊管理協程執行,在協程讓出控制權後掛起它們。事件循環處理IO和定時器事件,協程讓出控制權時被掛起並返回事件循環。當事件發生時,Swoole從事件循環切換到掛起的協程,透過儲存和載入協程狀態完成切換。協程調度使用優先權機制,支援掛起、休眠和復原作業以靈活控制協程執行。

See all articles