Rumah > rangka kerja php > ThinkPHP > Kongsi kes praktikal berfikir-swoole [demonstrasi terperinci]

Kongsi kes praktikal berfikir-swoole [demonstrasi terperinci]

藏色散人
Lepaskan: 2021-07-26 09:11:13
ke hadapan
5672 orang telah melayarinya

Dokumen tapak web rasmi

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

Pemasangan

composer require topthink/think-swoole
Salin selepas log masuk

Baris arahan

php think swoole [start|stop|reload|restart]
Salin selepas log masuk

Permulaan perkhidmatan

Selepas anda melengkapkan pelaksanaan di bawah baris arahan php think swoole, Pelayan HTTP akan dimulakan dan anda boleh terus mengakses aplikasi semasa [Tutorial yang disyorkan: 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,
    ],
],
Salin selepas log masuk

Kemas kini hangat

Apabila pelayan swoole sedang berjalan, fail php berjalan dalam memori pemastautin, supaya anda boleh mengelakkan pembacaan berulang cakera, tafsiran berulang dan kompilasi php, untuk mencapai prestasi tertinggi, jadi mengubah suai kod memerlukan memulakan semula perkhidmatan

Pelanjutan think-swoole menyediakan fungsi kemas kini panas secara automatik selepas mengesan kemas kini pada fail yang berkaitan tidak perlu melengkapkan mula semula secara manual, yang sesuai untuk pembangunan dan penyahpepijatan

Pengeluaran Tidak disyorkan untuk memulakan pemantauan fail dalam persekitaran ini, kerana ia akan menyebabkan kehilangan prestasi Dalam keadaan biasa, fail yang anda ubah suai perlu disahkan sebelum pengerahan kemas kini

.envTetapan dalam APP_DEBUG = true akan mendayakan kemas kini panas secara lalai

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

Penerangan Parameter

参数说明
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();
Salin selepas log masuk

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

'websocket'  => [
    'enable'        => true,
],
Salin selepas log masuk

创建三个事件

php think make:listener SwWsConnect
php think make:listener SwWsClose
php think make:listener SwWsMessage
Salin selepas log masuk

然后将这三个事件写到到事件监听中,分别有以下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
        ]
    ],
Salin selepas log masuk

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

'listen'        => [
    'connect'=>\app\listener\SwWsConnect::class,
    'close'=>\app\listener\SwWsClose::class,
    'message'=> \app\listener\SwWsMessage::class
],
Salin selepas log masuk
怎么选择是保存在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";
}
Salin selepas log masuk

关闭事件app\listener\SwWsClose.php

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

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);
}
Salin selepas log masuk

启动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);
Salin selepas log masuk
注意:在多个实时通讯场景下使用 emit

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

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

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

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

聊天室room实现

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

php think make:listener SwRoomJoin
php think make:listener SwRoomLeave
php think make:listener SwRoomMessage
Salin selepas log masuk

事件绑定

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

加入房间逻辑

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}成功");
}
Salin selepas log masuk

离开房间逻辑

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}已离开了~~");
}
Salin selepas log masuk

在房间发布聊天逻辑

    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);
    }
Salin selepas log masuk

事件订阅

php think make:listener SwSubscribe
Salin selepas log masuk

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);
    }
}
Salin selepas log masuk

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

&#39;subscribe&#39;     => [
    \app\listener\SwSubscribe::class
],
Salin selepas log masuk

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
Salin selepas log masuk

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

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

注册事件app\event.php

&#39;swoole.task&#39;=>[
    \app\listener\SwSendEmailTask::class
],
Salin selepas log masuk

在控制器中投递任务

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 "注册成功";
}
Salin selepas log masuk
三种获取\Swoole\Server,任意选其一

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

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

php think make:listener SwSendEmailFinish
Salin selepas log masuk

注册事件app\event.php

&#39;swoole.finish&#39;=>[
    \app\listener\SwSendEmailFinish::class
],
Salin selepas log masuk

在task任务中调用

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

高性能共享内存 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);
}
Salin selepas log masuk

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
                ],

            ],
        ],
    ],
Salin selepas log masuk

操作数据

$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();
Salin selepas log masuk

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);
}
Salin selepas log masuk

实现接口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. "查询数据遍历";
    }
}
Salin selepas log masuk

注册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; => [
        ],
    ],
Salin selepas log masuk

启动服务端

php think swoole start /  php think swoole:rpc
Salin selepas log masuk

客户端

    &#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;
            ]
            // 更多服务端
        ],
    ],
Salin selepas log masuk

运行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;]];
Salin selepas log masuk

在控制器调用

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

定时任务

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

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

php think make:command SwooleTimer
Salin selepas log masuk

加载命令行config/console.php

&#39;commands&#39; => [
    &#39;swooletimer&#39;=>app\command\SwooleTimer::class
    ...........
],
Salin selepas log masuk

书写命令脚本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");
    }
}
Salin selepas log masuk

Atas ialah kandungan terperinci Kongsi kes praktikal berfikir-swoole [demonstrasi terperinci]. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Label berkaitan:
sumber:segmentfault.com
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan