php怎么进行异步编程,php异步编程是怎样的?
前言
传统的 php-fpm 一个进程执行一个请求,要达到多少并发,就要生成多少个进程。更糟糕的是每次请求都需要重新编译执行,导致并发一直上不来。因此出现了 Swoole 和 WorkerMan 两个国内流行的常驻内存框架。这两个框架原理都是通过事件循环,让程序一直停留在内存,等待外部请求,达到高并发。
为什么需要异步
先来看一个例子
在工作目录下新建文件 slowServer.php
sleep(5); // 5秒后才能返回请求
echo 'done';
复制代码
开启服务
$ php -S localhost:8081 slowServer.php
复制代码
开另一个终端,安装依赖
$ pecl install event # 安装 event 扩展
$ composer require workerman/workerman
$ composer require react/http-client:^0.5.9
复制代码
新建文件 worker.php
require_once __DIR__ . '/vendor/autoload.php';
use Workerman\Worker;
use Workerman\Connection\AsyncTcpConnection;
use Amp\Artax\Response;
$http_worker = new Worker("http://0.0.0.0:8082");
$http_worker->count = 1; // 只开一个进程
$http_worker->onMessage = function($connection, $host) {
echo 1;
$data = file_get_contents('http://localhost:8081');
$connection->send($data);
};
Worker::runAll();
复制代码
开启服务器
php worker.php start
复制代码
在浏览器开启两个标签,都打开网址 http://localhost:8082 。这时可以看到终端输出“1”,过了一会儿又输出“1”,原因是8081服务器在处理第一个请求的时候阻塞在了等待8081返回之中,等第一个请求结束后,才开始处理第二个请求。也就是说请求是一个一个执行的,要达到多少个并发,就要建立多少个进程,跟 php-fpm 一样。现在修改一下代码
$http_worker->onMessage = function($connection, $host) {
echo 1;
$loop = Worker::getEventLoop();
$client = new \React\HttpClient\Client($loop);
$request = $client->request('GET', 'http://localhost:8081');
$request->on('error', function(Exception $e) use ($connection) {
$connection->send($e);
});
$request->on('response', function ($response) use ($connection) {
$response->on('data', function ($data) use ($connection) {
$connection->send($data);
});
});
$request->end();
};
复制代码
现在打开服务,再在浏览器发起请求,发现第二个“1”在请求后就马上输出了,而这时第一个请求还没结束。这表明进程不再阻塞,并发量取决于 cpu 和 内存,而不是进程数。
为什么需要异步
通过上面的例子已经很明白了,reactphp 框架通过把 http 请求变成异步,让 onMessage 函数变成非阻塞,cpu 可以去处理下一个请求。即从 cpu 循环等待 8081 返回,变成了 epoll 等待。
异步的意义在于把 cpu 从 io 等待中解放出来,可以处理其他计算任务。 如果你想知道怎么用框架实现异步,看到这里就可以了。WorkerMan 配合 ReactPHP 或者自身的 AsyncTcpConnection 已经可以满足很多 io 请求异步化的需求。下面继续讨论这些框架是怎么做到异步的。
哪些地方应该被做成异步
通过上面的例子已经知道一旦执行到不需要 cpu,但是要等待 io 的时候,应该把 io 的过程做成异步。
实现事件循环
上面的例子是通过 reactphp 把 http 请求变成了异步,其实 WorkerMan 框架本身也是异步的,下面来看看 WorkerMan 是怎么使 onMessage 函数可以异步接受请求。先来新建下面这个文件 react.php
$context = stream_context_create();
$socket = stream_socket_server('tcp://0.0.0.0:8081', $errno, $errmsg, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN,$context); // 注册一个 fd(file descriptor)
function react($socket){
$new_socket = stream_socket_accept($socket, 0, $remote_address);
echo 1;
}
$eventBase = new EventBase();
$event = new Event($eventBase, $socket, Event::READ | Event::PERSIST, 'react', $socket); // 注册一个事件,检测 fd 有没有写入内容
$event->add();
$eventBase->loop(); // 开始循环
复制代码
开始执行
$ php react.php
复制代码
在另一个终端执行
telnet 127.0.0.1 8081
复制代码
这时就会看到第一个终端输出'1'。
这里,事件回调是通过检测 fd 是否有写入内容来实现,这个过程不需要 cpu 参与。当 fd 有内容写入时,会调函数 'react',这时开始使用 cpu。如果这时候进程执行另一个异步请求,比如用 reactphp 框架请求一个网页,那么程序会让出 cpu,此时如果有另一个请求进来,就可以回调执行另一个 'react' 函数。由此提高了并发量。
协程
生成器 Generater
function gen_one_to_three() {
for ($i = 1; $i <= 3; $i++) {
//注意变量$i的值在不同的yield之间是保持传递的。
yield $i;
}
}
$generator = gen_one_to_three();
foreach ($generator as $value) {
echo "$value\n";
}
复制代码
生成器就是每次程序执行到 yield 的时候保存状态,然后返回 $i,是否继续执行 gen_one_to_three 里的循环,取决于主程序是否继续调用
什么是协程
上面的程序另一种写法是
$i = 1;
function gen_one_to_three() {
global $i;
if ($i<=3){
return $i++;
}
}
while ($value = gen_one_to_three()) {
echo "$value\n";
}
复制代码
由此可见,协程就是一种对函数的封装,使其变成一种可以被中断的函数,行为更像是子进程或子线程,而不是函数。协程的具体写法这里不细写,因为协程的写法十分复杂,可能需要再做一层封装才能好用。
协程与异步
既然协程可以被中断,那么只要在程序发起请求后发起事件循环,然后用 yield 返回,然后程序继续执行主程序部分,等事件返回后触发函数,执行 Generatot::next() 或 Generator::send() 来继续执行协程部分。封装好后就好像没有异步回调函数一样,和同步函数很像。
现在已经有 ampphp 和 swoole 两个框架封装了协程,有兴趣可以了解一下。
以上内容希望帮助到大家, 很多PHPer在进阶的时候总会遇到一些问题和瓶颈,业务代码写多了没有方向感,不知道该从那里入手去提升,对此我整理了一些资料,包括但不限于:分布式架构、高可扩展、高性能、高并发、服务器性能调优、TP6,laravel,Redis,Swoole、Swoft、Kafka、Mysql优化、shell脚本、Docker、微服务、Nginx等多个知识点高级进阶干货需要的可以免费分享给大家 ,需要戳这里
php怎么进行异步编程,php异步编程是怎样的?相关推荐
- python2异步编程_python异步编程 (转载)
转自:https://zhuanlan.zhihu.com/p/27258289 本文将会讲述Python 3.5之后出现的async/await的使用方法,以及它们的一些使用目的,如果错误,欢迎指正 ...
- JS笔记(20): JS中的同步编程和异步编程
铺垫:关于定时器 定时器:设定一个定时器,并且设定了等到的时间,当到达指定的时间,浏览器会把对应的方法执行 1)常用的定时器 1.setTimeout(function,intarval) 执行一次 ...
- Javascript异步编程之一异步原理
本系列的例子主要针对node.js环境,但浏览器端的原理应该也是类似的. 本人也是Javascript新手,把自己这段时间学习积累的要点总结下来,希望可以对同样在学习Javascript/node.j ...
- 孙鑫MFC笔记之十四--多线程同步与异步套接字编程
线程同步有三种方式: 1. 互斥对象涉及方法: HANDLE hMutex=CreateMutex(NULL,FALSE,NULL); //第二个参数为FALSE,将互斥对象声明为空闲状态 ...
- C#并发编程之异步编程(一)
写在前面 C#5.0中,对异步编程进行了一次革命性的重构,引入了async和await这两个关键字,使得开发人员在不需要深刻了解异步编程的底层原理,就可以写出十分优美而又代码量极少的代码.如果使用得当 ...
- java future_Java并发编程之异步Future机制的原理和实现
Java并发编程之异步Future机制的原理和实现 项目中经常有些任务需要异步(提交到线程池中)去执行,而主线程往往需要知道异步执行产生的结果,这时我们要怎么做呢?用runnable是无法实现的,我们 ...
- Visual C++串口通信编程---多线程异步方式
Visual C++串口通信编程---多线程异步方式 1. 串口通信基础 提到串口让人想起并口,它们是计算机中两个比较重要的通信方式. 串口:也叫COM口,把字节的二进制位按位列队进行传输,每个字节占 ...
- springboot异步和切面_Spring异步编程 你的@Async就真的异步吗?异步历险奇遇记
引言有点长 前端的宝宝会用ajax,用异步编程到快乐的不行~ 我们java也有异步,用起来比他们还快乐~ 我们biaji一个注(gǒupí)解(gāoyào),也是快乐风男... 且看下面的栗子: 注 ...
- Android中的多线程编程与异步处理
Android中的多线程编程与异步处理 引言 在移动应用开发中,用户体验是至关重要的.一个流畅.高效的应用能够吸引用户并提升用户满意度.然而,移动应用面临着处理复杂业务逻辑.响应用户输入.处理网络请求 ...
- C/C++编程:异步编程入门
背景 在产品端开发软件多了,免不了遇到多线程处理的情况,这也符合多核.异构的现代化硬件发展的需求.多线程处理中常见的有两种应用情形: 并行化算法处理.利用OpenMP/TBB等CPU并行库,或者CUD ...
最新文章
- 手写js的insertAfter
- 安装 | VMware Workstation Pro 16 for Unbuntu 20.10 虚拟机安装教程
- 获取linux服务器基本信息脚本
- java spring oauth2.0_java – Spring引导oauth2管理httpbasic认证
- 图形学的几个非常有用的转换
- git命令——git commit
- 收集表的使用与批量图片下载
- 文件上传(FileUpload)
- JAVA对接大汉三通短信http接口
- 【Matlab】【碎碎念】 clc、close、close all、clear、clear all等的含义
- 辰视冯良炳博士作为专家出席演讲的2021视觉系统设计会议圆满闭幕
- 第七章---8253和8255芯片
- 百度步行导航加poi搜索android,Android Studio百度地图路线规划以及POI搜索功能的实现...
- Spring Boot基于KLock实现分布式锁的使用详解(一)
- 网络状态码含义,常用(204,304, 404, 504,502)
- leetcode 1217. 玩筹码
- python爬电影的优点是_我用 Python 爬了点你们喜欢的电影,这些电影真的很不错!...
- 2022-07-13 第七小组 闫馨月 学习笔记
- python安装教程(配置环境变量)
- 从原生到黑科技:闲鱼 Flutter 图片优化经历了什么?