Swoole使用示例

出品 | 漫云科技

整理 | 赵建平

⛳️ 异步任务

1.服务端处理任务
<?php
namespace app\admin\command;

use Swoole\Server;
use app\common\service\SwooleAsync;
use think\console\Command;
use think\console\Input;
use think\console\Output;
use think\Env;

class TestSwoole extends Command
{
  protected function configure()
  {
    $this->setName('test_swoole')->setDescription('test');
  }
  /**
   * @param Input $input
   */
  protected function execute(Input $input, Output $output)
  {
    $serve_ip = Env::get('app.SWOOLE_IP', '127.0.0.1');
    $serve_port = Env::get('app.SWOOLE_PORT', 9502);
    $serv = new Server($serve_ip, $serve_port);
    //设置异步任务的工作进程数量。
    $serv->set([
       'dispatch_mode' => 3, // 数据包分发策略,1=轮循模式/2=固定模式/3=抢占模式/4=IP分配
       'daemonize' => 1, // 以守护进程的方式开启
       'worker_num' => 8, //Worker 进程数,设置为 CPU 核数的 1-4 倍
       'task_worker_num' => 20, // Task 进程的数量
       'max_wait_time' => 60, // 进程收到停止服务通知后最大等待时间
       'backlog'       => 256,   // 设置Listen队列长度
       'max_request'   => 80,    // 每个进程最大接受请求数
     ]);
    //此回调函数在worker进程中执行。
    $serv->on('Receive', function($serv, $fd, $reactor_id, $data) {
      $data = json_decode($data, true);
      //投递异步任务
      $task_id = $serv->task($data);
    });

    //处理异步任务(此回调函数在task进程中执行)。
    $serv->on('Task', function ($serv, $task_id, $reactor_id, $data) {
      // 因为swoole改动代码每次都需要重启,建议逻辑任务处理放在外部调用方便测试,改动后仍然需要重启才可以生效
      // 例如:SwooleAsync::actionTask($data);
      // 返回任务执行的结果
      // $serv->finish("{$data} -> OK");
    });

    //处理异步任务的结果(此回调函数在worker进程中执行)。
    $serv->on('Finish', function ($serv, $task_id, $data) {
      echo "AsyncTask[{$task_id}] Finish: {$data}".PHP_EOL;
    });
    //启动服务器
    $serv->start();
  }
}
2.客户端client,用于投送任务,重启小技巧.
<?php
use Swoole\Client;
try {
  $client = new Client(SWOOLE_SOCK_TCP);
  // 第三个参数$timeout 超时时间单位:秒,默认值:0.5
  if (!$client->connect('127.0.0.1', 9502, 0.5)) {
    echo '任务错误: 无法连接TCP服务,请联系管理员!';
    exit();
  }
  if (!$client->send(json_encode($arr))) {
    echo '任务错误:任务投送失败,请联系管理员!';
    exit();
  }
  //关闭连接
  $client->close();
}catch (\Exception $e){
	/*投送任务异常,大概率是服务挂了,此时执行启动命令
  	还可在适当位置杀死所有swoole进程,然后再启动
  	shell_exec('sudo fuser -k -n tcp 9502')
  */
  if(strstr($e->getMessage(),"Swoole\Client::connect(): connect to server[127.0.0.1:9502] failed. Error: Connection refused")){
      shell_exec('cd /www/wwwroot/根目录 && php think test_swoole');
  }
  return false;
}

⛳️ 高并发场景协程

1.客户端
<?php
try {
    $ip = '127.0.0.1';
    $port = 9501;
    $handle = stream_socket_client("udp://{$ip}:{$port}", $errno, $errstr);
    if (!$handle) {
        echo '服务器异常' . $errstr;
    }
    fwrite($handle, $sendMsg . "\n");
    $result = fread($handle, 1024);
    fclose($handle);
  	$ret = json_decode($result, true); // 获取结果
    if (empty($ret)) {
        echo '服务异常';
    }
    // 根据返回结果$ret进行业务处理
  	
} catch (Exception $e) {
  	echo '服务器异常' . $e->getMessage();
}
2.服务端
<?php
namespace app\admin\command;

use Swoole\Coroutine;
use Swoole\Coroutine\Channel;
use think\console\Command;
use think\console\Input;
use think\console\Output;
use think\Env;
class Swoole extends Command
{
  protected function configure()
  {
    $this->setName('swoole')->setDescription('swoole');
  }
  /**
   * @param Input $input
   */
  protected function execute(Input $input, Output $output)
  {
    $serve_ip = Env::get('app.SWOOLE_IP', '127.0.0.1');
    $serve_port = Env::get('app.SWOOLE_PORT', 9502);
    // 构造函数的第三个参数,可以填 2 个常量值 -- SWOOLE_BASE 或 SWOOLE_PROCESS
    // SWOOLE_PROCESS 多进程模式(默认),可以应对大量慢速连接,与客户端交互
    // SWOOLE_BASE 基本模式,客户端连接之间不需要交互,可以使用 BASE 模式
    // 构造函数的第四个参数,端口协议类型如:SWOOLE_SOCK_TCP/SWOOLE_SOCK_UDP
    $server = new \Swoole\Server($serve_ip, $serve_port, SWOOLE_PROCESS, SWOOLE_SOCK_UDP);
    $Chain = new channel(2000);
    //监听数据接收事件
    $server->on('Packet', function ($server, $data, $clientInfo) use ($Chain) {
      $data = json_decode($data, true);
      Coroutine::create(function () use ($Chain, $data) {
          // 向通道中写入数据
          $Chain->push($data);
      });
      Coroutine::create(function () use ($Chain, $data, &$ret) {
          // 从通道中读取数据
          $data = $Chain->pop();
          try {
              // 在这里进行逻辑任务处理
              $ret = [1, '处理成功'];
          } catch (\Exception $e) {
              $ret = [0, '数据插入异常'];
          }
      });
      $server->sendto($clientInfo['address'], $clientInfo['port'], json_encode($ret, 256));
    });
    //启动服务器
    $server->start();
  }
}

1.异步任务适用于复杂、大量数据处理,性能远远优于queue队列

2.协程适用于秒杀抢购等高并发场景

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zzoood

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值