swoole学习五协程初探 swoole多协程
lipiwang 2024-11-01 14:10 8 浏览 0 评论
- 协程的基本概念
借用大佬的总结就是用户端的线程,也就是用户程序创建、切换、销毁的不经过CPU调度的线程。
目前原生支持协程的语言应该只有Go吧,swoole作为PHP的扩展也实现了协程。
有几个特点:
- CPU是不知道协程的存在,所以CPU操作的还是进程
- 协程存在于用户态,所以没有用户态与内核态的切换
- 协程需要用户程序自己实现切换调度
- 协程切换的时机是程序显示切换(yield->resume)或者遇到I/O阻塞的时候
- 协程的上下文需要程序自己去处理
很幸运的是,swoole已经为我们处理好了这一切。
我们学习的版本是Swoole版本:4.5.2
- 来个Demo理解下协程
<?php
use Swoole\Coroutine;
use Swoole\Runtime;
// 这里创建三个协程,分别去执行
// 当sleep产生阻塞时,当前协程就会挂起,执行下一个
// 开启协程支持 sleep函数如果不开启是不会产生
// IO阻塞的情况
Swoole\Runtime::enableCoroutine();
// 创建一个协程
Swoole\Coroutine::create(function () {
// 模拟I/O阻塞了
sleep(2);
echo 1 . ':' . PHP_EOL;
});
Swoole\Coroutine::create(function () {
sleep(1);
echo 2 . ':' . PHP_EOL;
});
Swoole\Coroutine::create(function () {
sleep(1);
echo 3 . ':' . PHP_EOL;
});
执行发现并不是顺序执行的,第一个等待两秒,但是程序直接执行了第二个,这就是协程带来的变化,非阻塞
gaoz@nobodyMBP hyperf-skeleton % php swoole_demo/coroutine/coroutine.php
2:
3:
1:
再来看普通的阻塞执行
<?php
// 这三个会依次执行
// 阻塞等待
// 执行闭包函数
(function () {
// 阻塞2s
sleep(2);
echo "1:" . PHP_EOL;
})();
(function () {
sleep(1);
echo "2:" . PHP_EOL;
})();
(function () {
sleep(1);
echo "3:" . PHP_EOL;
})();
执行发现是顺序执行,也就是阻塞执行的,我们目前php-fpm模式下就是这样执行的
gaoz@nobodyMBP hyperf-skeleton % php swoole_demo/coroutine/not_coroutine.php
1:
2:
3:
我们换成数据库查询在看对比下了
<?php
\Swoole\Runtime::enableCoroutine(true);
// 协程话之后,swoole自动调度协程的执行
$start = microtime_float();
// 创建一个协程 执行这个查询
\Swoole\Coroutine::create(function(){
print_r(selectDB([10000,10001,10002]));
});
echo "finished---3". PHP_EOL;
// 创建第二个协程 执行这个查询
\Swoole\Coroutine::create(function(){
print_r(selectDB([10003,10004,10005]));
});
echo "finished---5". PHP_EOL;
echo PHP_EOL;
echo '---1---';
$end = microtime_float();
echo "use-time: ". ($end-$start);
function selectDB($ids)
{
$mysqlconn = new mysqli('127.0.0.1', 'root', 'root', 'test');
$result = $mysqlconn->query("
select id, name from heros where id in (".implode(',', $ids).")
");
$arr = [];
foreach ($result->fetch_all(MYSQLI_ASSOC) as $k=>$info) {
$arr[] = $info;
}
return $arr;
}
function microtime_float()
{
list($usec, $sec) = explode(" ", microtime());
return ((float)$usec + (float)$sec);
}
异步执行,结果顺序不确定奥
gaoz@nobodyMBP hyperf-skeleton % php swoole_demo/coroutine/coroutine_mysql.php
finished---3
finished---5
---1---use-time: 0.00034785270690918Array
(
[0] => Array
(
[id] => 10003
[name] => 牛魔
)
[1] => Array
(
[id] => 10004
[name] => 吕布
)
[2] => Array
(
[id] => 10005
[name] => 亚瑟
)
)
Array
(
[0] => Array
(
[id] => 10000
[name] => 夏侯惇
)
[1] => Array
(
[id] => 10001
[name] => 钟无艳
)
[2] => Array
(
[id] => 10002
[name] => 张飞
)
)
<?php
//阻塞模式 一个一个的执行
$start = microtime_float();
print_r(selectDB([10000,10001,10002]));
echo "finished---3". PHP_EOL;
print_r(selectDB([10003,10004,10005]));
echo "finished---5". PHP_EOL;
echo PHP_EOL;
echo '---1---';
$end = microtime_float();
echo "use-time: ". ($end-$start);
function selectDB($ids)
{
$mysqlconn = new mysqli('127.0.0.1', 'root', 'root', 'test');
$result = $mysqlconn->query("
select id, name from heros where id in (".implode(',', $ids).")
");
$arr = [];
foreach ($result->fetch_all(MYSQLI_ASSOC) as $k=>$info) {
$arr[] = $info;
}
return $arr;
}
function microtime_float()
{
list($usec, $sec) = explode(" ", microtime());
return ((float)$usec + (float)$sec);
}
阻塞顺序执行,一目了然
gaoz@nobodyMBP hyperf-skeleton % php swoole_demo/coroutine/not_coroutine_mysql.php
Array
(
[0] => Array
(
[id] => 10000
[name] => 夏侯惇
)
[1] => Array
(
[id] => 10001
[name] => 钟无艳
)
[2] => Array
(
[id] => 10002
[name] => 张飞
)
)
finished---3
Array
(
[0] => Array
(
[id] => 10003
[name] => 牛魔
)
[1] => Array
(
[id] => 10004
[name] => 吕布
)
[2] => Array
(
[id] => 10005
[name] => 亚瑟
)
)
finished---5
---1---use-time: 0.034359931945801
由上面的例子可以看到,协程对于应对I/O密集的操作是很有效果的,我们在平时的工作中一般遇到的恰恰是I/O密集型的,如数据库读取、网络连接等等,CPU密集型的php一般也搞不定的,那我们在遇到这样的问题时可以考虑使用swoole来应对。
回到Hyperf, swoole为每个请求都会创建一个协程处理,如HttpServer 的onRequest事件发生之后,swoole会自动创建一个协程处理,[可以通过enable_coroutine: false 关闭],而当我们使用 Swoole\Runtime::enableCoroutine(true); 启动协程hook之后,大部分php原有的产生io的函数都会转换成协程版本。所以一般来说这样就不会再有阻塞的发生了。
- 自建协程
除了swoole在某些时刻的自动创建的协程,我们还可以手动创建,如下面这样
Swoole\Coroutine::create(function () {
// xxxx
});
- 协程容器
调度器是提供协程容器的,所有协程api都必须要在协程容器中执行,swoole默认创建协程容器
// 创建调度器
$scheduler = new Swoole\Coroutine\Scheduler();
// 添加一个协程任务
$scheduler->add(function($num){
echo "start-one". PHP_EOL;
\Swoole\Coroutine::sleep(1);
echo $num. PHP_EOL;
}, 11);
// 添加第二个协程任务
$scheduler->add(function($num){
echo "start-two". PHP_EOL;
echo $num. PHP_EOL;
}, 12);
// 启动调度器开始执行
$scheduler->start();
- 协程间通讯
协程之间是隔离的,一个协程的变量不能被另一个读取,协程间通信需要通过channel完成,channel可以理解为一个消息队列,协程A把数据push
到channel,协程B从channel中读取数据
<?php
use Swoole\Coroutine;
// 创建通道 配置容量1,每次只能有一条消息,消费完才能push,
// 当通道满了之后,生产者协程就要阻塞等待了
$channel = new Swoole\Coroutine\Channel(1);
Swoole\Coroutine::create(function()use($channel){
for ($i=0; $i<3; $i++) {
// 往通道中push数据
$channel->push($i);
}
});
Swoole\Coroutine::create(function()use($channel){
for ($i=0; $i<3; $i++) {
echo "current-chan-status";
print_r($channel->stats());
Coroutine::sleep(1);
// 从通道获取数据
$data = $channel->pop();
echo "get-from-chan: ". $data. PHP_EOL;
}
});
总结:
swoole本身的异步多进程再加上协程对于提高php应对高并发的业务是完全没有问题的。我们需要在实际工作中多多应用。
相关文档:
swoole关于协程的讲解:https://wiki.swoole.com/#/coroutine
hyperf关于协程的讲解:https://hyperf.wiki/2.0/#/zh-cn/coroutine
本文代码:https://github.com/nobody05/hyperf_study
相关推荐
- ubuntu单机安装open-falcon极度详细操作
-
备注:以下操作均由本人实际操作并得到验证,喜欢的同学可尝试操作安装。步骤一1.1环境准备(使用系统:ubuntu18.04)1.1.1安装redisubuntu下安装(参考借鉴:https://...
- Linux搭建promtail、loki、grafana轻量日志监控系统
-
一:简介日志监控告警系统,较为主流的是ELK(Elasticsearch、Logstash和Kibana核心套件构成),虽然优点是功能丰富,允许复杂的操作。但是,这些方案往往规模复杂,资源占用高,...
- 一文搞懂,WAF阻止恶意攻击的8种方法
-
WAF(Web应用程序防火墙)是应用程序和互联网流量之间的第一道防线,它监视和过滤Internet流量以阻止不良流量和恶意请求,WAF是确保Web服务的可用性和完整性的重要安全解决方案。它...
- 14配置appvolume(ios14.6配置文件)
-
使用AppVolumes应用程序功能,您可以管理应用程序的整个生命周期,包括打包、更新和停用应用程序。您还可以自定义应用程序分配,以向最终用户提供应用程序的特定版本14.1安装appvolume...
- 目前流行的缺陷管理工具(缺陷管理方式存在的优缺点)
-
摘自:https://blog.csdn.net/jasonteststudy/article/details/7090127?utm_medium=distribute.pc_relevant.no...
- 开源数字货币交易所开发学习笔记(2)——SpringCloud
-
前言码云(Gitee)上开源数字货币交易所源码CoinExchange的整体架构用了SpringCloud,对于经验丰富的Java程序员来说,可能很简单,但是对于我这种入门级程序员,还是有学习的必要的...
- 开发JAX-RPC Web Services for WebSphere(下)
-
在开发JAX-RPCWebServicesforWebSphere(上)一文中,小编为大家介绍了如何创建一个Web服务项目、如何创建一个服务类和Web服务,以及部署项目等内容。接下来小编将为大...
- CXF学习笔记1(cxf client)
-
webservice是发布服务的简单并实用的一种技术了,个人学习了CXF这个框架,也比较简单,发布了一些笔记,希望对笔友收藏并有些作用哦1.什么是webServicewebService让一个程序可...
- 分布式RPC最全详解(图文全面总结)
-
分布式通信RPC是非常重要的分布式系统组件,大厂经常考察的Dubbo等RPC框架,下面我就全面来详解分布式通信RPC@mikechen本篇已收于mikechen原创超30万字《阿里架构师进阶专题合集》...
- Oracle WebLogic远程命令执行0day漏洞(CVE-2019-2725补丁绕过)预警
-
概述近日,奇安信天眼与安服团队通过数据监控发现,野外出现OracleWebLogic远程命令执行漏洞最新利用代码,此攻击利用绕过了厂商今年4月底所发布的最新安全补丁(CVE-2019-2725)。由...
- Spring IoC Container 原理解析(spring中ioc三种实现原理)
-
IoC、DI基础概念关于IoC和DI大家都不陌生,我们直接上martinfowler的原文,里面已经有DI的例子和spring的使用示例《InversionofControlContainer...
- Arthas线上服务器问题排查(arthas部署)
-
1Arthas(阿尔萨斯)能为你做什么?这个类从哪个jar包加载的?为什么会报各种类相关的Exception?我改的代码为什么没有执行到?难道是我没commit?分支搞错了?遇到问题无法在...
- 工具篇之IDEA功能插件HTTP_CLENT(idea2021插件)
-
工具描述:Java开发人员通用的开发者工具IDEA集成了HTTPClient功能,之后可以无需单独安装使用PostMan用来模拟http请求。创建方式:1)简易模式Tools->HTTPCl...
- RPC、Web Service等几种远程监控通信方式对比
-
几种远程监控通信方式的介绍一.RPCRPC使用C/S方式,采用http协议,发送请求到服务器,等待服务器返回结果。这个请求包括一个参数集和一个文本集,通常形成“classname.meth...
- 《github精选系列》——SpringBoot 全家桶
-
1简单总结1SpringBoot全家桶简介2项目简介3子项目列表4环境5运行6后续计划7问题反馈gitee地址:https://gitee.com/yidao620/springbo...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- maven镜像 (69)
- undefined reference to (60)
- zip格式 (63)
- oracle over (62)
- date_format函数用法 (67)
- 在线代理服务器 (60)
- shell 字符串比较 (74)
- x509证书 (61)
- localhost (65)
- java.awt.headless (66)
- syn_sent (64)
- settings.xml (59)
- 弹出窗口 (56)
- applicationcontextaware (72)
- my.cnf (73)
- httpsession (62)
- pkcs7 (62)
- session cookie (63)
- java 生成uuid (58)
- could not initialize class (58)
- beanpropertyrowmapper (58)
- word空格下划线不显示 (73)
- jar文件 (60)
- jsp内置对象 (58)
- makefile编写规则 (58)