vertx web连接超时 阻塞_SWOOLE进阶-06网络IO模型-阻塞模型

前言

接着咱们前面swoole系列第五篇文章,系统在处理一些事情的时候,有的选择进程处理,有的选择线程处理,至于怎么选择是根据平台和编程语言来决定的。接下来我们需要理解什么是网络IO模型?

stream_socket_server初体验

系统的QQ等客户端在和linux系统内核进行通信是通过socket实现的,那如何去创建socket呢?

php有2个函数可以创建socket套接字,分别是stream_socket_server()和socket_create(),这2个函数都是创建一个套接字,区别是stream_socket_server是有点点封装函数,socket_create是原生的。本次只体验stream_socket_server函数。

接下来我们将用原生的stream_socket_server这个函数去创建一个服务(相对于swoole创建服务,swoole就是封装好的),然后去连接和请求这个服务。

0834502888c61033585b369f831d6ac2.png

原生实现代码初体验:

$host = "tcp://0.0.0.0:9501";
//创建socket服务
$server = stream_socket_server($host);
echo $host."n";
//建立与客户端的连接
//开启服务之后,服务就处于一个挂起的状态,等待连接进来然后创建连接
//stream_socket_accept是阻塞的
//长时间没有连接进来,就会报出一个连接超时警告异常,可以在前面加@来抑制异常
$client = @stream_socket_accept($server);
var_dump($client);

24bd638ea3074c646f748dfd9297687c.png

socket连接处理的阻塞状态

$host = "tcp://0.0.0.0:9501";
//创建socket服务
$server = stream_socket_server($host);
echo $host."n";
//建立与客户端的连接
//开启服务之后,服务就处于一个挂起的状态,等待连接进来然后创建连接
//stream_socket_accept是阻塞的
//长时间没有连接进来,就会报出一个连接超时警告异常,可以在前面加@来抑制异常
//监听连接,当一个连接进来之后,就结束了,那通过一个死循环while来实现一直重复监听连接
while(true){
$client = @stream_socket_accept($server);
var_dump($client);
}

开启服务之后,当有一个客户端连接进来之后,服务端是收到连接的,连接之后又继续监听等待连接;而客户端也一直等待服务端的信息返回,若长时间未收到服务端的数据,就会断开重置。

curl: (56) Recv failure: Connection reset by peer

93c326ff889652d5c571b7631bac5ef6.png

当多个连接进来时,后面的连接会把前面未收到服务端返回信息的连接挤掉

7ce51a41973597e01a66c5a45477ec2a.png
$host = "tcp://0.0.0.0:9501";
//创建socket服务
$server = stream_socket_server($host);
echo $host."n";
//建立与客户端的连接
//开启服务之后,服务就处于一个挂起的状态,等待连接进来然后创建连接
//stream_socket_accept是阻塞的
//长时间没有连接进来,就会报出一个连接超时警告异常,可以在前面加@来抑制异常
//监听连接,当一个连接进来之后,就结束了,那通过一个死循环while来实现一直重复监听连接
while(true){
//建立与客户端的连接
$client = @stream_socket_accept($server);
//读取客户端的信息
$data = @fread($client,1024);
//发送信息给客户端
@fwrite($client,'abc');
//关闭连接
@fclose($client);
var_dump($client);
}

这里是服务端读取客户端的信息和发送信息给客户端

1e3bcd63d422dd40b0b6cd577286d951.png

创建一个客户端:

$host = "tcp://127.0.0.1:9501";
//创建一个客户端的连接,与服务端accept进行信息交互
$client = stream_socket_client($host);
//给socket通道发送信息
fwrite($client,"hello world");
//接收读取socket通道发送来的信息
var_dump(fread($client,65535));
//关闭连接
fclose($client);

88b460dd64c9d5ee7427b7345fc9188e.png

在tcp创建连接之后,假设需要做个耗时操作,sleep(3)一下,看看多个客户端同时连接进来会发生什么情况?

服务端:

$host = "tcp://0.0.0.0:9501";
//创建socket服务
$server = stream_socket_server($host);
echo $host."n";
//建立与客户端的连接
//开启服务之后,服务就处于一个挂起的状态,等待连接进来然后创建连接
//stream_socket_accept是阻塞的
//长时间没有连接进来,就会报出一个连接超时警告异常,可以在前面加@来抑制异常
//监听连接,当一个连接进来之后,就结束了,那通过一个死循环while来实现一直重复监听连接
while(true){
//建立与客户端的连接
$client = @stream_socket_accept($server);
sleep(3);
//读取客户端的信息
$data = @fread($client,1024);
//发送信息给客户端
@fwrite($client,'abc');
//关闭连接
@fclose($client);
var_dump($data);
}

客户端:

$host = "tcp://127.0.0.1:9501";
//创建一个客户端的连接,与服务端accept进行信息交互
$client = stream_socket_client($host);
$time = time();
//给socket通道发送信息
fwrite($client,"hello world");
//接收读取socket通道发送来的信息
var_dump(fread($client,65535));
//关闭连接
fclose($client);
 
echo "n".time()-$time;

62cc836ce6096fd5d4ad21f727fe5f2b.png

这个时候发现第2个客户端必须等待第1个客户端完成之后才会连接,这是为什么呢?

这主要是 stream_socket_server是单线程处理连接的,必须等待前面的连接处理之后再处理后面的,这种状态叫做阻塞调用。那如何解决呢?

1e1812e860f344a028d919dea11c8bf1.png

swoole 通过Reactor模式解决这个问题,php-fpm通过多进程模式解决这个问题,这些模式统称为网络IO模型。

网络IO模型就好比 socket连接处理的框架或架构,比如:原生PHP=》laravel,thinkphp,yii。

5大IO模型

IO管理连接模型

内核:可以理解为 帮助我们去处理这份代码,如何去读,写,关闭,输出。

用户空间:可以理解为去告诉内核处理一些事情,代码就在用户空间里写。

a828b937a1830d8ed0f284022c5d7c47.png

5大io模型:

  • 阻塞I/O(blocking IO):用户空间一直等待内核处理完之前未处理完的事情,且在等待时间内不会去做其他的事情。效率是最低的。
  • 非阻塞I/O(noblocking IO):用户空间先检查内核是否有未处理完的事情,若有,就去做其他的事情,且每隔一段时间检查一次内核是否处理完,这种就是轮询的方式。
  • I/O多路复用 (IO multiplexing ):开辟多个内核,用户空间根据哪个内核空闲就将信息发送给该内核去处理。
  • 信号驱动I/O (signal driven IO):用户空间设置一个信息号就会去做其他的事情(对用户空间来说是非阻塞的),内核处理完未处理的事情之后会处理这个事情,处理完这个事情之后会返回该信号,表示处理完这个事情。触发回调函数。
  • 异步I/O (asynchronous IO):预定模式 ,用户空间一般用事件event=》事先今天发送一个信息给内核,并告诉内核在指定的时间节点去处理这件事情,(对于用户空间来说是非阻塞的),触发回调函数。通常事件发送完之后还需要事件清理(原因是内核处理完一件事情后,在下一个时间节点有可能在做其他的事情了)。

为什么要理解5大IO模型?对我们来说有什么意义?

通过理解网络编程的一些技术知识,为了更好的理解学习swoole。

简单IO架构(代码层面)

应用composer命名空间

io

src

SignalDriven

worker.php

test

signal-driver

server.php

client.php

先创建io目录,打开cmd,cd到io目录下,输入composer init,回车,创建属于自己的命名空间;

52de46a0921e55e48c65e2922249f1d6.png

配置一下Package name ,Auth,Package Type,License

6f80263f3dbca7552ed14cdd7f2ba046.png

就会在io目录下生成一个composer.json文件

1e766559e4a13e11b048ae8eec4dac36.png

然后创建一下目录 ,src(存放自己原生写的封装的类),test目录(测试用的目录包)。然后再composer.json文件里配置一下命名空间,添加配置如下:

"autoload":{
"psr-4":{
"zjlio":"./src/"
}
},

3a6e76ce76fe77b84bb725bb671b0b53.png

测试一下:

在src目录下创建Index.php,编写测试代码如下:

055e321a1e82d7a93a7ba0a0eeb99f6e.png

然后composer update一下

1a3085e7274177e539415ae94367e14a.png

这时候会生成vendor目录及文件

b576b16751ef18394920b7d46e260bba.png

然后在io目录下创建一个test.php。(可以参照workerman的架构)

e6bf0d4b5fabe4a17ecd89b990ee0a5e.png

然后再cmd 上运行一下 php test.php ,这样就可以了。

b9fada1d0c864869d6c54caa69cf2700.png

阻塞IO模型

Worker.php
<?php
namespace zjlioBlocking;
 
/**
* 用户连接服务
*/
class Worker
{
/**
* 自定义服务的事件注册函数
* 闭包函数
* @var null
*/
public $onReceive = null;
public $onConnect = null;
public $onClose = null;
//连接
public $socket = null;
 
public function __construct($socket_address)
{
$this->socket = stream_socket_server($socket_address);
echo $socket_address."n";
}
 
/**
* 处理事情
*/
public function accept()
{
//接收连接,处理事情
while(true)
{
$client = @stream_socket_accept($this->socket);
//判断是不是闭包
if(is_callable($this->onConnect)){
//执行闭包函数
($this->onConnect)($this,$client);
}
$data = fread($client,65535);
if(is_callable($this->onReceive)){
//执行闭包函数
($this->onReceive)($this,$client,$data);
}
//处理完关闭连接
fclose($client);
}
}
public function send($conn,$data)
{
fwrite($conn,$data);
}
//启动服务
public function start()
{
$this->accept();
}
}
server.php
<?php
require __DIR__."/../../vendor/autoload.php";
use zjlioBlockingWorker;
 
$host = "tcp://0.0.0.0:9501";
$server = new Worker($host);
 
//连接服务
$server->onConnect = function ($server,$client){
echo "有一个连接进来n";
var_dump($client);
};
//接收和处理信息
$server->onReceive = function ($server,$client,$data){
echo "给连接发送信息n";
$server->send($client,"hello world client n");
};
$server->start();
 
测试
php server.php
 
curl http://127.0.0.1:9501

d830c8e4ce53a471b54738564fb4aee6.png

以上内容希望帮助到大家,很多PHPer在进阶的时候总会遇到一些问题和瓶颈,业务代码写多了没有方向感,不知道该从那里入手去提升,对此我整理了一些资料,包括但不限于:分布式架构、高可扩展、高性能、高并发、服务器性能调优、TP6,laravel,YII2,Redis,Swoole、Swoft、Kafka、Mysql优化、shell脚本、Docker、微服务、Nginx等多个知识点高级进阶干货需要的可以免费分享给大家,需要

PHP进阶架构师>>>视频、面试文档免费获取​shimo.im
57a440a614917e0c862be967d9d29b2e.png

或 者关注咱们下面的知乎专栏

PHP7进阶架构师​zhuanlan.zhihu.com
3fcb728197542f398b1632a92cfb6357.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值