page contents

swoole 第4次课-浮光掠影:理解网络io与并实现阻塞模型

理解网络io与并实现阻塞模型

补充:Tcp粘包处理方案-包头+协议

           客户端也需要跟服务器端的同样参数设置

服务器端:swoole\tcpServer2.php

<?php
echo swoole_get_local_ip()['ens33'].":9503\n";
$server = new Swoole\Server("0.0.0.0", 9503);
//设置参数
$server->set([
'open_length_check' => true,
'package_length_type' => 'n',
'package_body_offset' => 2,
'package_length_offset' => 0,
'package_max_length' => 2 * 1024 * 1024,
]);

$server->on('connect', function ($server, $fd){
echo "connection open: {$fd}\n";
});
$server->on('receive', function ($server, $fd, $reactor_id, $data) {
echo "接收到数据:".$data."\n";
//echo "接收到数据:".$fd."\n";
$server->send($fd, "Swoole: ok\n");
});
$server->on('close', function ($server, $fd) {
echo "connection close: {$fd}\n";
});
$server->start();

客户端:tcpClient2.php

<?php
$client = new Swoole\Client(SWOOLE_SOCK_TCP );
//客户端也需要对应 设置参数 跟服务器端对应起来
$client->set([
'open_length_check' => true,
'package_length_type' => 'n',
'package_body_offset' => 2,
'package_length_offset' => 0,
'package_max_length' => 2 * 1024 * 1024,
]);
if (!$client->connect('127.0.0.1', 9503, -1)) {
exit("connect failed. Error: {$client->errCode}");
}
//注意,同样还需要pack进行打包
for ($i=0; $i < 10; $i++) {
//$client->send("hello_world_{$i}");
echo "start ====> \n";
$context = "123";
var_dump($context);
//利用Pack打包长度
$len = pack("n",strlen($context));
var_dump($len);//注意,二进制数字打印不出来,可看长度来辨别,确实存在
//组包
$send=$len. $context;
var_dump($send);//注意,其内的二进制数据打印不出来,可看长度来辨别,确实存在
$client->send($send);
echo " <==== end \n";
}
echo $client->recv();

打开两个xshell,进入 对应目录,运行服务端及客户端,结果如下:

attachments-2020-11-jRcCHpBM5fc0402cb7106.png

补充服务器端的几个参数配置

buffer_output_size/output_buffer_size

配置发送输出缓存区内存尺寸。【默认值:2M】

$server->set([
    'buffer_output_size' => 32 * 1024 * 1024, //必须为数字
]);Copy to clipboardErrorCopied
  • 提示

    • 单位为字节,默认为 2M,如设置 32 * 1024 * 1024 表示,单次 Server->send 最大允许发送 32M 字节的数据
    • 调用 Server->send,Http\Server->end/write,WebSocket\Server->push 等发送数据指令时,单次最大发送的数据不得超过 buffer_output_size 配置。


worker_num

设置启动的 Worker 进程数。【默认值:CPU 核数】

如 1 个请求耗时 100ms,要提供 1000QPS 的处理能力,那必须配置 100 个进程或更多。
但开的进程越多,占用的内存就会大大增加,而且进程间切换的开销就会越来越大。所以这里适当即可。不要配置过大。

  • 提示

    • 如果业务代码是全异步 IO 的,这里设置为 CPU 核数的 1-4 倍最合理
    • 如果业务代码为同步 IO,需要根据请求响应时间和系统负载来调整,例如:100-500
    • 默认设置为 swoole_cpu_num(),最大不得超过 swoole_cpu_num() * 1000
    • 假设每个进程占用 40M 内存,100 个进程就需要占用 4G 内存,


daemonize

守护进程化【默认值:0】

设置 daemonize => 1 时,程序将转入后台作为守护进程运行。长时间运行的服务器端程序必须启用此项。
如果不启用守护进程,当 ssh 终端退出后,程序将被终止运行。

  • 提示

    • 启用守护进程后,标准输入和输出会被重定向到 log_file

    • 如果未设置 log_file,将重定向到 /dev/null,所有打印屏幕的信息都会被丢弃

    • 启用守护进程后,CWD(当前目录)环境变量的值会发生变更,相对路径的文件读写会出错。PHP 程序中必须使用绝对路径

    • systemd

      • 使用 systemd 或者 supervisord 管理 Swoole 服务时,请勿设置 daemonize = 1。主要原因是 systemd 的机制与 init 不同。init 进程的 PID 为 1,程序使用 daemonize 后,会脱离终端,最终被 init 进程托管,与 init 关系变为父子进程关系。
      • 但 systemd 是启动了一个单独的后台进程,自行 fork 管理其他服务进程,因此不需要 daemonize,反而使用了 daemonize = 1 会使得 Swoole 程序与该管理进程失去父子进程关系。

案例演示:客户端同上

服务器端:

<?php
echo swoole_get_local_ip()['ens33'].":9503\n";
$server = new Swoole\Server("0.0.0.0", 9503);
//设置参数
$server->set([
'open_length_check' => true,
'package_length_type' => 'n',
'package_body_offset' => 2,
'package_length_offset' => 0,
'package_max_length' => 2 * 1024 * 1024,
'daemonize' => 1,//守护进程
//'buffer_input_size' => 3 * 1024 * 1024, //设置接收输入缓存区内存大小,默认2M
//'worker_num' => 16, //设置启动的 Worker 进程数。【默认值:CPU 核数】,适合1-4倍,最佳2倍
]);

$server->on('connect', function ($server, $fd){
echo "connection open: {$fd}\n";
});
$server->on('receive', function ($server, $fd, $reactor_id, $data) {
echo "接收到数据:".$data."\n";
//echo "接收到数据:".$fd."\n";
$server->send($fd, "Swoole: ok\n");
});
$server->on('close', function ($server, $fd) {
echo "connection close: {$fd}\n";
});
$server->start();

运行结果如下

attachments-2020-11-rZPylmRn5fc04e55e3ebc.png

服务端后台运行,运行结果仅仅是不显示了,通过ps可以看到,仍在进程中


1.理解linux内核与用户调度

attachments-2020-11-8DkMGQDK5fc1805e3b312.pngattachments-2020-11-jFel9fvd5fc18078cf894.png

linux内核是硬件与软件之间的一个中间层。 作用是将应用程序的请求传递给硬件,充当底层驱动程序,对系统中的各种设备和组件进行寻址。内核是一个资源管理程序。负责将可用的共享资源(CPU时间、磁盘空间、网络连接等)分配得到各个系统进程。

web项目及swoole的访问结构

attachments-2020-11-aytUukyd5fc18137a020e.png

用户空间与内核空间

attachments-2020-11-2Q0OEPzx5fc1815d7b143.png

除段:内核空间从接收用户空间指令到开始处理指令,

           内核空间处理指令并返回结果

也就是说,用户空间从传递用户指令,到得到结果,需要等待一定的时间,这个一定的时间,是内核空间接受指令并处理指令这些过程的时间。这个等待的过程,会出现阻塞问题。

2.理解5大网络io模型

   网络io模型可以理解为方式、就是用户空间和内核空间的通信方式。
  21)阻塞模型
attachments-2020-11-VU96Vl9u5fc2e21dbdf93.png
  22)非阻塞模型
attachments-2020-11-vCyC0sTR5fc2e2582d1bb.png23)多路复用模型
attachments-2020-11-xDswar5Z5fc2e2ca12eb3.png24)信号驱动io模型(图不对,待更换)
attachments-2020-11-ZqLpe6Kj5fc2e325850b5.png25)异步io模型
attachments-2020-11-4rMliaud5fc2e36644cd8.png26)五大网络io模型对比
attachments-2020-11-d3zjx7EW5fc2e3a161224.png

3. 构建worker模型
   31) Worker模型目录结构
    attachments-2020-11-tygjyi7R5fc432927db30.png
        32)构建过程:这里沿用六星shineYork官方的worker的去做后面的东西,以下纯为 iostarjhy纯属测试走流程

    321)创建目录D:\phpstudy_pro\WWW\swoole_2006\iostarjhy\, phpstorm命令行或cmd进入对应目录,运行composer.init
    D:\phpstudy_pro\WWW\swoole_2006\iostarjhy>composer init //回车                                         
    Welcome to the Composer config generator  
   This command will guide you through creating your composer.json config.
  Package name (<vendor>/<name>) [administrator/iostarjhy]: jhy/iostarjhy  //这里可手动填写包的第三方名称及包名称
  Description []: //项目描述略 直接回车                                    
  Author [, n to skip]: jhy <1195863294@qq.com> //作者及邮箱
  Minimum Stability []://回车
  Package Type (e.g. library, project, metapackage, composer-plugin) []: library //这里选择library类型并回车
  License []://协议,回车,一路回车,最后一个yes
  Define your dependencies.
  Would you like to define your dependencies (require) interactively [yes]?  //回车
  Search for a package:
  Would you like to define your dev dependencies (require-dev) interactively [yes]?//回车
  Search for a package:
  {
    "name": "jhy/iostarjhy",
    "type": "library",
    "authors": [
        {
            "name": "jhy",
            "email": "1195863294@qq.com"
       }
     ],
     "require": {}
  }

  Do you confirm generation [yes]? yes //回车

 322) 在生成的composer.json文件中,添加psr4,形成的文件如下:
 
{
"name": "jhy/iostarjhy",
"type": "library",
"authors": [
{
"name": "jhy",
"email": "1195863294@qq.com"
}
],
"autoload": {
"psr-4": {
"IoStarJhy\\": "src"
}
},
"require": {}
}
在src下建立index.php文件,如下
<?php
namespace IoStarJhy;

class Index
{

public function index()
{
return "this is index index";
}
}
建立src/test目录,建立test.php文件
<?php
require __DIR__."/../vendor";
phpstorm命令行或cmd进入对应目录,运行composer. update
D:\phpstudy_pro\WWW\swoole_2006\iostarjhy>composer update  //回车等待,出现如下信息
Loading composer repositories with package information
Updating dependencies
Nothing to modify in lock file
Writing lock file
Installing dependencies from lock file (including require-dev)
Nothing to install, update or remove
Generating autoload files

最终,形成composer.lock及vendor目录包文件

323)改写test文件并测试
swoole_2006\iostarjhy\test\test.php
<?php
require __DIR__."/../vendor/autoload.php";

use IoStarJhy\Index;

echo (new Index())->index();
测试:phpstorm命令行或cmd进入对应目录
D:\phpstudy_pro\WWW\swoole_2006\iostarjhy\test>php test.php
this is index index

说明环境项目初始成功
4. 实现阻塞io模式-函数介绍及测试应用
attachments-2020-12-WJO6CG1m5fc59d7beee25.png

41)客户端利用php原生函数连接服务端
服务端iostarjhy\test\swoole\tcpserver.php  跟之前一样,无什么变化,代码如下 :
<?php
echo swoole_get_local_ip()['ens33'].":9503\n";
$server = new Swoole\Server("0.0.0.0", 9503);

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

$server->on('receive', function ($server, $fd, $reactor_id, $data) {
echo "接收到信息".$data." \n";

});

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

$server->start();
客户端iostarjhy\test\swoole\clinet.php
<?php
//利用php原生函数stream_socket_client与服务端进行连接,这里并没有利用到swoole(new Swoole\Server等形式)
$fp = stream_socket_client("tcp://192.168.204.168:9503");
if (!$fp) {
echo "$errstr ($errno)<br />\n";
} else {
fwrite($fp, "GET / HTTP/1.0\r\nHost: www.example.com\r\nAccept: */*\r\n\r\n");
while (!feof($fp)) {
echo fgets($fp, 1024);
}
fclose($fp);
}

运行结果

attachments-2020-12-NqGkxuuY5fc6d88e85028.jpg
客户端与服务端的连接本质:由上可见,客户端并没有应用swoole,通过php的原生函数stream_socket_client()连接到了服务端,其实,客户端与服务端的连接本质,都是通过socket进行连接的, socket就是一个w受服务端及客户端监控的可写可读的资源文件,具体连接流程如下所示。
attachments-2020-12-ZuX9OrL45fc6d8556d32b.jpg  attachments-2020-12-Bqrxfpll5fc6d86837388.png42)服务端利用原生函数连接客户端
客户端与之前的一个,利用swoole连接:iostarjhy\test\swoole\clinet2.php
<?php
//连接服务端
$client = new Swoole\Client(SWOOLE_SOCK_TCP );

if (!$client->connect('127.0.0.1', 8000, -1)) {
exit("connect failed. Error: {$client->errCode}\n");
}
$client->send("hello world\n");
/*$client->send("hello world\n");
$client->send("hello world\n");
$client->send("hello world\n");*/

echo $client->recv();

$client->close();
服务端:iostarjhy\test\swoole\tcpserver2.php
<?php
//建立协议服务
$socket = stream_socket_server("tcp://0.0.0.0:8000", $errno, $errstr);
//创建服务和客户和客户连接的socket stream_socket_accept
if (!$socket) {
echo "$errstr ($errno)<br />\n";
} else {
while ($conn = stream_socket_accept($socket)) {
fwrite($conn, 'The local time is ' . date('n/j/Y g:i a') . "\n");
fclose($conn);
}
fclose($socket);
}
运行结果
attachments-2020-12-KaUJOVyK5fc6ddf914d36.png

上面服务端,同样没有应用swoole,而是利用原生的函数stream_socket_server

43) swoole与 workman的实现


attachments-2020-12-jVn10WX25fc82779e91f5.png

其它, worker目录的创建结构
worker目录建立
建立src/Blocking目录
建立test/Blocking目录



  • 发表于 2020-11-27 07:59
  • 阅读 ( 673 )

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
吉洪叶
吉洪叶

21 篇文章

作家榜 »

  1. 轩辕小不懂 2403 文章
  2. 小柒 1478 文章
  3. Pack 1135 文章
  4. Nen 576 文章
  5. 王昭君 209 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章