Swoole 高性能高并发 PHP 协程框架 - 文章教程

Swoole 高性能高并发 PHP 协程框架

发布于 2020-04-08 字数 9254 浏览 2298 评论 0

Swoole 使 PHP 开发人员可以编写高性能高并发的 TCP、UDP、Unix Socket、HTTP、 WebSocket 等服务,让 PHP 不再局限于 Web 领域。Swoole4 协程的成熟将 PHP 带入了前所未有的时期, 为性能的提升提供了独一无二的可能性。

Swoole 高性能高并发 PHP 协程框架

Swoole 可以广泛应用于互联网、移动通信、云计算、 网络游戏、物联网(IOT)、车联网、智能家居等领域。使用 PHP + Swoole 可以使企业 IT 研发团队的效率大大提升,更加专注于开发创新产品。

HTTP Server

//高性能HTTP服务器
$http = new Swoole\Http\Server("127.0.0.1", 9501);

$http->on("start", function ($server) {
  echo "Swoole http server is started at http://127.0.0.1:9501\n";
});

$http->on("request", function ($request, $response) {
  $response->header("Content-Type", "text/plain");
  $response->end("Hello World\n");
});

$http->start();

WebSocket Server

$server = new Swoole\Websocket\Server("127.0.0.1", 9502);

$server->on('open', function($server, $req) {
  echo "connection open: {$req->fd}\n";
});

$server->on('message', function($server, $frame) {
  echo "received message: {$frame->data}\n";
  $server->push($frame->fd, json_encode(["hello", "world"]));
});

$server->on('close', function($server, $fd) {
  echo "connection close: {$fd}\n";
});

$server->start();

TCP Server

$server = new Swoole\Server("127.0.0.1", 9503);
$server->on('connect', function ($server, $fd){
  echo "connection open: {$fd}\n";
});
$server->on('receive', function ($server, $fd, $reactor_id, $data) {
  $server->send($fd, "Swoole: {$data}");
  $server->close($fd);
});
$server->on('close', function ($server, $fd) {
  echo "connection close: {$fd}\n";
});
$server->start();

UDP Server

$serv = new Swoole\Server("127.0.0.1", 9502, SWOOLE_PROCESS, SWOOLE_SOCK_UDP);

//监听数据接收事件
$serv->on('Packet', function ($serv, $data, $clientInfo) {
  $serv->sendto($clientInfo['address'], $clientInfo['port'], "Server ".$data);
  var_dump($clientInfo);
});

//启动服务器
$serv->start();

Task

$server = new Swoole\Server("127.0.0.1", 9502);
$server->set(array('task_worker_num' => 4));
$server->on('receive', function($server, $fd, $reactor_id, $data) {
  $task_id = $server->task("Async");
  echo "Dispatch AsyncTask: [id=$task_id]\n";
});
$server->on('task', function ($server, $task_id, $reactor_id, $data) {
  echo "New AsyncTask[id=$task_id]\n";
  $server->finish("$data -> OK");
});
$server->on('finish', function ($server, $task_id, $data) {
  echo "AsyncTask[$task_id] finished: {$data}\n";
});
$server->start();

Coroutine

睡眠 1 万次,读取,写入,检查和删除文件 1 万次,使用 PDO 和 MySQLi 与数据库通信 1 万次,创建 TCP 服务器和多个客户端相互通信 1 万次,
创建 UDP 服务器和多个客户端到相互通信 1 万次…… 一切都在一个进程一秒内完美完成!

Swoole\Runtime::enableCoroutine();
//此行代码后,文件操作,sleep,Mysqli,PDO,streams等都变成异步IO,见文档'一键协程化'章节
$s = microtime(true);
//Co/run()见文档'协程容器'章节
Co\run(function() {
// i just want to sleep...
for ($c = 100; $c--;) {
  go(function () {
    for ($n = 100; $n--;) {
      usleep(1000);
    }
  });
}

// 10k file read and write
for ($c = 100; $c--;) {
  go(function () use ($c) {
    $tmp_filename = "/tmp/test-{$c}.php";
    for ($n = 100; $n--;) {
      $self = file_get_contents(__FILE__);
      file_put_contents($tmp_filename, $self);
      assert(file_get_contents($tmp_filename) === $self);
    }
    unlink($tmp_filename);
  });
}

// 10k pdo and mysqli read
for ($c = 50; $c--;) {
  go(function () {
    $pdo = new PDO('mysql:host=127.0.0.1;dbname=test;charset=utf8', 'root', 'root');
    $statement = $pdo->prepare('SELECT * FROM `user`');
    for ($n = 100; $n--;) {
      $statement->execute();
      assert(count($statement->fetchAll()) > 0);
    }
  });
}
for ($c = 50; $c--;) {
  go(function () {
    $mysqli = new Mysqli('127.0.0.1', 'root', 'root', 'test');
    $statement = $mysqli->prepare('SELECT `id` FROM `user`');
    for ($n = 100; $n--;) {
      $statement->bind_result($id);
      $statement->execute();
      $statement->fetch();
      assert($id > 0);
    }
  });
}

// php_stream tcp server & client with 12.8k requests in single process
function tcp_pack(string $data): string{
  return pack('n', strlen($data)) . $data;
}

function tcp_length(string $head): int{
  return unpack('n', $head)[1];
}

go(function () {
  $ctx = stream_context_create(['socket' => ['so_reuseaddr' => true, 'backlog' => 128]]);
  $socket = stream_socket_server(
    'tcp://0.0.0.0:9502',
    $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctx
  );
  if (!$socket) {
    echo "$errstr ($errno)\n";
  } else {
    $i = 0;
    while ($conn = stream_socket_accept($socket, 1)) {
      stream_set_timeout($conn, 5);
      for ($n = 100; $n--;) {
        $data = fread($conn, tcp_length(fread($conn, 2)));
        assert($data === "Hello Swoole Server #{$n}!");
        fwrite($conn, tcp_pack("Hello Swoole Client #{$n}!"));
      }
      if (++$i === 128) {
        fclose($socket);
        break;
      }
    }
  }
});
for ($c = 128; $c--;) {
  go(function () {
    $fp = stream_socket_client("tcp://127.0.0.1:9502", $errno, $errstr, 1);
    if (!$fp) {
      echo "$errstr ($errno)\n";
    } else {
      stream_set_timeout($fp, 5);
      for ($n = 100; $n--;) {
        fwrite($fp, tcp_pack("Hello Swoole Server #{$n}!"));
        $data = fread($fp, tcp_length(fread($fp, 2)));
        assert($data === "Hello Swoole Client #{$n}!");
      }
      fclose($fp);
    }
  });
}

// udp server & client with 12.8k requests in single process
go(function () {
  $socket = new Swoole\Coroutine\Socket(AF_INET, SOCK_DGRAM, 0);
  $socket->bind('127.0.0.1', 9503);
  $client_map = [];
  for ($c = 128; $c--;) {
    for ($n = 0; $n < 100; $n++) { $recv = $socket->recvfrom($peer);
      $client_uid = "{$peer['address']}:{$peer['port']}";
      $id = $client_map[$client_uid] = ($client_map[$client_uid] ?? -1) + 1;
      assert($recv === "Client: Hello #{$id}!");
      $socket->sendto($peer['address'], $peer['port'], "Server: Hello #{$id}!");
    }
  }
  $socket->close();
});
for ($c = 128; $c--;) {
  go(function () {
    $fp = stream_socket_client("udp://127.0.0.1:9503", $errno, $errstr, 1);
    if (!$fp) {
      echo "$errstr ($errno)\n";
    } else {
      for ($n = 0; $n < 100; $n++) {
        fwrite($fp, "Client: Hello #{$n}!");
        $recv = fread($fp, 1024);
        list($address, $port) = explode(':', (stream_socket_get_name($fp, true)));
        assert($address === '127.0.0.1' && (int)$port === 9503);
        assert($recv === "Server: Hello #{$n}!");
      }
      fclose($fp);
    }
  });
}
});
echo 'use ' . (microtime(true) - $s) . ' s';

Channel

Co\run(function(){
  //使用Channel进行协程间通讯
  $chan = new Swoole\Coroutine\Channel(1);
  Swoole\Coroutine::create(function () use ($chan) {
    for($i = 0; $i < 100000; $i++) { co::sleep(1.0); $chan->push(['rand' => rand(1000, 9999), 'index' => $i]);
      echo "$i\n";
    }
  });
  Swoole\Coroutine::create(function () use ($chan) {
    while(1) {
      $data = $chan->pop();
      var_dump($data);
    }
  });
});

Swoole 特性

Swoole 使用 C/C++ 语言编写,提供了 PHP 语言的异步多线程服务器、异步 TCP/UDP 网络客户端、异步 MySQL、异步 Redis、数据库连接池、AsyncTask、消息队列、毫秒定时器、异步文件读写、异步DNS查询。 Swoole内置了Http/WebSocket服务器端/客户端、Http2.0服务器端。

除了异步 IO 的支持之外,Swoole 为 PHP 多进程的模式设计了多个并发数据结构和IPC通信机制,可以大大 简化多进程并发编程的工作。其中包括了并发原子计数器、并发 HashTable、Channel、Lock、进程间通信IPC 等丰富的功能特性。

Swoole4.0 支持了类似 Go 语言的协程,可以使用完全同步的代码实现异步程序。PHP 代码无需额外增加任何 关键词,底层自动进行协程调度,实现异步IO。

开源、高性能、高生产力

如果你对这篇文章有疑问,欢迎到本站 社区 发帖提问或使用手Q扫描下方二维码加群参与讨论,获取更多帮助。

扫码加入群聊

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

目前还没有任何评论,快来抢沙发吧!

关于作者

JSmiles

生命进入颠沛而奔忙的本质状态,并将以不断告别和相遇的陈旧方式继续下去。

2891 文章
评论
84935 人气
更多

推荐作者

勿忘心安

文章 0 评论

ekko

文章 0 评论

江挽川

文章 0 评论

献世佛

文章 0 评论

Meets

文章 0 评论