热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

了解PHPyield的高级用法

刚开始接触PHP的yield的时候,感觉yield是什么黑科技,百度一下yield是协程,生成器,很多文章都在讲Iterator,Generater,这就是PHP迭代器的一个补充。

开篇

刚开始接触PHPyield 的时候,感觉,yield 是什么黑科技,百度一下:yield——协程,生成器。很多文章都在讲 IteratorGenerater, 蛤~,这东西是 PHP 迭代器的一个补充。再翻几页,就是Go 协程。我出于好奇点开看了下Go 协程, 里面都是 并发线程管道通讯这类字眼,wc,nb, 这tm才是黑科技啊,再回来看PHP,分分钟想转 Go

相关学习推荐:PHP编程从入门到精通

yield 语法加入 PHP

yield语法是在版本5.5加入PHP的,配合迭代器使用,功能上就是 流程控制 代码,和gotoreturn 类似。

以下就是官方提供的 yield 小例子,通过执行结果,我们可分析当代码执行到 yield $i 时,他会进行 return $i, 待 echo "$value\n" 后, goto for ($i = 1; $i <= 3; $i++) {, 对!PHP 的 yield 就是一个能出能进的语法。在z代码中七进七出,把 $i 平平安安得送了出来。

我们遇到了什么问题

写代码就是解决问题。我们来看看他们遇到了什么问题:php官方呢,需要言简意赅地把yield介绍给大家。一部分网友呢,需要在有限的资源内完成大文件操作。而我们的鸟哥。面对的一群对当下yield的教程停留于初级而不满意的phper,就以一个任务调度器作为例子,给大家讲了一种yield高级用法。

php.net:生成器语法,
PHP如何读取大文件,
风雪之隅:在PHP中使用协程实现多任务调度.

提出问题,再用yield来解答,看到以上答案,我觉得呢,这PHP协程不过如此(Go协程相比 )。

有句话——一个好问题比答案更重要,目前广大网友还没有给yield提出更好,更困难的问题。

yield这个进进出出的语法,很多举例都是再让yield做迭代器啊,或者利用低内存读取超大文本的Excelcsv什么的,再高级就是用它实现一个简单的任务调度器,并且这个调度器,一看代码都差不多。

我来出道题

正如一个好的问题,比答案更有价值

  1. 用PHP实现一个 Socket Server,他能接收请求,并返回Server的时间。

好,这是第一个问题,铺垫。 官方答案

  1. 在原来的代码上,我们加个需求,该Socket Server 处理请求时,依赖其他 Socket Server,还需要有 Client 功能。也就是他能接收请求,向其它Server发起请求。

这是第二个问题,也是铺垫。

  1. 原来的Socket Server同一时间只能服务一个客户,希望能实现一个 非阻塞I/O Socket Server, 这个 Server 内有 Socket Client 功能,支持并发处理收到的请求,和主动发起的请求。要求不用多线程,多进程。

这个问题,还是铺垫,这几个问题很干,大家可以想一想,2,3题的答案,都放在一个脚本里了:nio_server.php

以上这段代码,我列举了一个具体的业务,就是用户请求购物车加购动作, 而购物车服务呢,又需要和 产品服务,库存服务,优惠服务 交互,来验证加购动作可行性。有同步,异步方式请求,并做对比。

后续还有很多代码,我都放gitee链接了。使用方法,见readme.md

  1. 最后一个问题:在PHP中,用同步写代码,程序呢异步执行?需要怎么调整代码。

提示:这个和 PHPyield 语法有关。

再提示:yield 语法特征是什么,进进出出!

看着我们的代码,同步, 异步,进进出出 你想到了什么?

看到代码,同步处理模式下,这三个函数checkInventory checkProduct checkPromo 时,发起请求,并依次等待返回的结果,这三个函数执行后,再响应客户请求。

异步处理模式下,这三个函数发起请求完毕后,代码就跳出循环了,然后是在select()下的一个代码分支中接收请求, 并收集结果。每次收到结果后判断是否完成,完成则响应客户端。

那么能不能这样:在异步处理的流程中,当 Server收到 自己发起的 client 有数据响应后,代码跳到 nio_server.php 的 247行呢,这样我们的收到请求校验相关的代码就能放到这里,编码能就是同步,容易理解。不然,client 的响应处理放在 280 行以后,不通过抓包,真的很难理解,执行了第 247 行代码后,紧接着是从 280 行开始的。

诶~这里是不是有 进进出出 那种感觉了~ 代码从 247 行出去,开始监听发出 Client 响应,收到返回数据,带着数据再回到 247 行,继续进行逻辑校验,综合结果后,再响应给客户端。

用yield来解决问题

基于 yield 实现的,同步编码,"异步"I/OSocket Server 就实现了。代码。

这里 “异步” 打了引号,大佬别扣这个字眼了。 该是非阻塞I/O

不等大家的答案了,先上我的结果代码吧,代码呢都放在这个目录下了。

gitee https://gitee.com/xupaul/PHP-generator-yield-Demo/tree/master/yield-socket

运行测试代码

clone 代码到本地后,需要拉起4个 command 命令程序:

拉起3个第三方服务

## 启动一个处理耗时2s的库存服务$ php ./other_server.php 8081 inventory 2## 启动一个处理耗时4s的产品服务$ php ./other_server.php 8082 product 4## 监听8083端口,处理一个请求 耗时6s的 promo 服务$ php ./other_server.php 8083 promo 6

启动购物车服务

## 启动一个非阻塞购物车服务$ php ./async_cart_server.php 

## 或者启动一个一般购物车服务$ php ./cart_server.php

发起用户请求

$ php ./user_client.php

运行结果呢如下,通过执行的时间日志,可得这三个请求是并发发起的,不是阻塞通讯。

在看我们的代码,三个函数,发起socket请求,没有设置callback,而是通过yield from 接收了三个socket的返回结果。

也就是达到了,同步编码,异步执行的效果。

运行结果

非阻塞模式

client 端日志:

cart 服务,依次阻塞方式请求第三方服务,顺序执行完毕后,共耗时12s,当然如果第一个,获第二个服务报错的话,会提前结束这个检查。会节约一点时间。

工作原理

这里就是用到了 yield 的工作特点——进进出出,在发起非阻塞socket请求后,不是阻塞方式等待socket响应,而是使用yield跳出当前执行生成器,等待有socket响应后,在调用生成器的send方法回到发起socket请求的函数内,在 yield from Async::all() 接收数据响应数据搜集完毕后,返回。

和Golang比一比

考虑到网速原因,我这就放上一个国内教程链接:Go 并发 教程

php的协程是真协程,而Go是披着协程外衣的轻量化线程(“协程”里,都玩上“锁”了,这就是线程)。

我个人偏爱,协程的,觉得线程的调度有一定随机性,因此需要锁机制来保证程序的正确,带来了额外开销。协程的调度(换入换出)交给了用户,保证了一段代码执行连续性(当然进程级上,还是会有换入换出的,除非是跨进程的资源访问,或者跨机器的资源访问,这时,就要用到分布式锁了,这里不展开讨论),同步编码,异步执行,只需要考虑那个哪个方法会有IO交互会协程跳出即可。

和NodeJS比划一下

Javascript 和 PHP 两个脚本语言有很多相似的地方,弱类型,动态对象,单线程,在Web领域生态丰富。不同的是,Javascript在浏览器端一开始就是异步的(如果js发起网络请求只能同步进行,那么你的网页渲染线程会卡住),例如AjaxsetTimeoutsetInterval,这些都是异步+回调的方式工作。

基于V8引擎而诞生的NodeJS,天生就是异步的,在提供高性能网络服务有很大的优势,不过它的IO编码范式么。。。刚开始是 回调——毁掉地狱,后来有了Promise——屏幕竖起来看,以及Generator——遇事不绝yield一下吧,到现在的Async/Await——语法糖?真香!

可以说JS的委员非常勤快,在异步编程范式的标准制定也做的很好(以前我尝试写NodeJS时,几个回调就直接把我劝退了),2009年诞生的NodeJS有点后来居上的意思。目前PHP只是赶上了协程,期待PHP的Async/Await语法糖的实现吧。

PHP yield 使用注意事项

一旦使用上 yield 后,就必须注意调用函数是,会得到函数结果,还是 生成器对象。PHP 不会自动帮你区别,需要你手动代码判断结果类型—— if ($re instanceof \Generator) {}, 如果你得到的是 生成器,但不希望去手动调用 current() 去执行它,那么在生成器前 使用 yield from 交给上游(框架)来解决。

爆改 Workerman

博客写到这,就开始手痒痒了,看到Workerman框架,我在基础上二开,使其能——同步编码,异步执行

代码已放到:PaulXu-cn/CoWorkerman.git

目前还是dev阶段,大家喜欢可以先 体验一波。

$ composer require paulxu-cn/co-workerman

一个简单的单线程 TCP Server

count = 1;$worker->OnConnect= function (CoTcpConnection  $connection) {
    try {
        $cOnName= "{$connection->getRemoteIp()}:{$connection->getRemotePort()}";
        echo PHP_EOL . "New Connection, {$conName} \n";

        $re = yield from $connection->readAsync(1024);
        CoWorker::safeEcho(&#39;get request msg :&#39; . $re . PHP_EOL );

        yield from CoTimer::sleepAsync(1000 * 2);

        $connection->send(json_encode(array(&#39;productId&#39; => 12, &#39;re&#39; =>true)));

        CoWorker::safeEcho(&#39;Response to :&#39; . $conName . PHP_EOL . PHP_EOL);
    } catch (ConnectionCloseException $e) {
        CoWorker::safeEcho(&#39;Connection closed, &#39; . $e->getMessage() . PHP_EOL);
    }};CoWorker::runAll();

这里设置fork 一个worker线程,处理逻辑中带有一个sleep() 2s的操作,依然不影响他同时响应多个请求。

启动测试程序

## 启动CoWorker服务$ php ./examples/example2/coWorkermanServer.php start## 启动请求线程$ php ./examples/example2/userClientFork.php

运行结果

以上就是了解PHP yield的高级用法的详细内容,更多请关注 第一PHP社区 其它相关文章!


推荐阅读
  • SpringBoot整合SpringSecurity+JWT实现单点登录
    SpringBoot整合SpringSecurity+JWT实现单点登录,Go语言社区,Golang程序员人脉社 ... [详细]
  • 在Docker中,将主机目录挂载到容器中作为volume使用时,常常会遇到文件权限问题。这是因为容器内外的UID不同所导致的。本文介绍了解决这个问题的方法,包括使用gosu和suexec工具以及在Dockerfile中配置volume的权限。通过这些方法,可以避免在使用Docker时出现无写权限的情况。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • 使用在线工具jsonschema2pojo根据json生成java对象
    本文介绍了使用在线工具jsonschema2pojo根据json生成java对象的方法。通过该工具,用户只需将json字符串复制到输入框中,即可自动将其转换成java对象。该工具还能解析列表式的json数据,并将嵌套在内层的对象也解析出来。本文以请求github的api为例,展示了使用该工具的步骤和效果。 ... [详细]
  • 推荐系统遇上深度学习(十七)详解推荐系统中的常用评测指标
    原创:石晓文小小挖掘机2018-06-18笔者是一个痴迷于挖掘数据中的价值的学习人,希望在平日的工作学习中,挖掘数据的价值, ... [详细]
  • 解决Cydia数据库错误:could not open file /var/lib/dpkg/status 的方法
    本文介绍了解决iOS系统中Cydia数据库错误的方法。通过使用苹果电脑上的Impactor工具和NewTerm软件,以及ifunbox工具和终端命令,可以解决该问题。具体步骤包括下载所需工具、连接手机到电脑、安装NewTerm、下载ifunbox并注册Dropbox账号、下载并解压lib.zip文件、将lib文件夹拖入Books文件夹中,并将lib文件夹拷贝到/var/目录下。以上方法适用于已经越狱且出现Cydia数据库错误的iPhone手机。 ... [详细]
  • Google Play推出全新的应用内评价API,帮助开发者获取更多优质用户反馈。用户每天在Google Play上发表数百万条评论,这有助于开发者了解用户喜好和改进需求。开发者可以选择在适当的时间请求用户撰写评论,以获得全面而有用的反馈。全新应用内评价功能让用户无需返回应用详情页面即可发表评论,提升用户体验。 ... [详细]
  • FeatureRequestIsyourfeaturerequestrelatedtoaproblem?Please ... [详细]
  • 树莓派语音控制的配置方法和步骤
    本文介绍了在树莓派上实现语音控制的配置方法和步骤。首先感谢博主Eoman的帮助,文章参考了他的内容。树莓派的配置需要通过sudo raspi-config进行,然后使用Eoman的控制方法,即安装wiringPi库并编写控制引脚的脚本。具体的安装步骤和脚本编写方法在文章中详细介绍。 ... [详细]
  • 本文介绍了如何使用jQuery和AJAX来实现动态更新两个div的方法。通过调用PHP文件并返回JSON字符串,可以将不同的文本分别插入到两个div中,从而实现页面的动态更新。 ... [详细]
  • Sleuth+zipkin链路追踪SpringCloud微服务的解决方案
    在庞大的微服务群中,随着业务扩展,微服务个数增多,系统调用链路复杂化。Sleuth+zipkin是解决SpringCloud微服务定位和追踪的方案。通过TraceId将不同服务调用的日志串联起来,实现请求链路跟踪。通过Feign调用和Request传递TraceId,将整个调用链路的服务日志归组合并,提供定位和追踪的功能。 ... [详细]
  • 本文总结了初学者在使用dubbo设计架构过程中遇到的问题,并提供了相应的解决方法。问题包括传输字节流限制、分布式事务、序列化、多点部署、zk端口冲突、服务失败请求3次机制以及启动时检查。通过解决这些问题,初学者能够更好地理解和应用dubbo设计架构。 ... [详细]
  • 本文讨论了在使用Git进行版本控制时,如何提供类似CVS中自动增加版本号的功能。作者介绍了Git中的其他版本表示方式,如git describe命令,并提供了使用这些表示方式来确定文件更新情况的示例。此外,文章还介绍了启用$Id:$功能的方法,并讨论了一些开发者在使用Git时的需求和使用场景。 ... [详细]
  • 云原生应用最佳开发实践之十二原则(12factor)
    目录简介一、基准代码二、依赖三、配置四、后端配置五、构建、发布、运行六、进程七、端口绑定八、并发九、易处理十、开发与线上环境等价十一、日志十二、进程管理当 ... [详细]
  • Vue基础一、什么是Vue1.1概念Vue(读音vjuː,类似于view)是一套用于构建用户界面的渐进式JavaScript框架,与其它大型框架不 ... [详细]
author-avatar
围脖上的博博_771
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有