PHP异步编程简述

栏目: PHP · 发布时间: 6年前

内容简介:PHP异步编程简述

概述

异步编程,我们从字面上理解,可以理解为代码非同步执行的。异步编程可以归结为 四种模式 :回调、事件监听、发布/订阅、promise模式。我们最熟悉的两种模式是回调和事件监听,举两个最简单的javascript例子,一个ajax,一个点击事件的绑定:

$.getJSON("uri", params, function(result) {
    do_something_with_data(result);
});
$("#id").click(function(){
    do_something_when_user_click_id();
});

以上两个示例有一个共同的特点,就是把函数当做参数传递给另一个函数。被传递的函数可以被称作为闭包,闭包的执行取决于父函数何时调用它。

优势与劣势

异步编程具有以下优势:

  • 解耦,你可以通过事件绑定,将复杂的业务逻辑分拆为多个事件处理逻辑
  • 并发,结合非阻塞的IO,可以在单个进程(或线程)内实现对IO的并发访问;例如请求多个URL,读写多个文件等
  • 效率,在没有事件机制的场景中,我们往往需要使用轮询的方式判断一个事件是否产生

异步编程的劣势:

异步编程的劣势其实很明显——回调嵌套。相信一部分人在写ajax的时候遇到过这样的场景:

$.getJSON("uri", params, function(result_1) {
    $.getJSON("uri", result_1, function(result_2) {
        $.getJSON("uri", result_2, function(result_3) {
            do_something_with_data(result_3);
        });
    });;
});

这样的写法往往是因为数据的依赖问题,第二次ajax请求依赖于第一次请求的返回结果,第三次ajax依赖于第二次。这样就造成深层次的回调嵌套,代码的可读性急剧下降。虽然有一些框架能够通过一些模式解决这样的问题,然并卵,代码的可读性相比同步的写法依然差很多。

异步编程的另一个劣势就是编写和调试的过程更加复杂,有时候你不知道什么时候你的函数才会被调用,以及他们被调用的顺序。而我们更习惯同步串行的编程方式。

然而,我相信一旦你开始使用异步编程,你一定会喜欢上这种方式,因为他能够带给你更多的便利。

PHP异步编程概述

php 语言中,异步的使用并不像javascript中那么多,归其原因主要是php一般是在web环境下工作,接收请求->读取数据->生成页面,这看起来天生就是一个串行的过程;所以,在php中,异步并没有广泛使用。

在javascript中的4中异步编程模式,均可以在php中实现。

array_walk($arr, function($key, $value){
    $value += 1;
});
print_r($arr);

回调的方式,在大多情况下,代码仍然是顺序执行的(array_walk->print_r的顺序)。回调函数的意义在于被传递者可以调用回调函数对数据进行处理,这样的好处在于提供更好的扩展性和解耦。我们可以把这个回调函数理解为一个格式化器,处理相同的数据,当我传递一个json过滤器时,返回的结果可能是一个json压缩过的字符串,当我传递的是一个xml过滤器时,返回的结果可能是一个xml字符串(有点多态的思想)。

事件监听(定时器,时间事件):

$loop = React\EventLoop\Factory::create();
$loop->addPeriodicTimer(5, function () {
    $memory = memory_get_usage() / 1024;
    $formatted = number_format($memory, 3).'K';
    echo "Current memory usage: {$formatted}\n";
});

$loop->run();

事件监听在PHP中用的并不多,但并不是没有,例如pcntl_signal()监听操作系统信号,以及其他IO事件的监听等等。上面的示例是一个事件事件的侦听,每隔5s中,会执行一次回调函数。

在四种异步模式中,事件监听的应用是更有意义的。然我们看一个同步的例子,下面这段代码用于向百度和google(一个不存在的网站)发起请求,同步的编写写法是先去请求百度或者google,等待请求结束后再请求另一个:

$http = new HTTP();
echo $http->get('http://www.baidu.com');
echo $http->get('http://www.google.com');

基于事件的处理方式可以是这样的:

$http = new HTTP();
$http->get('www.baidu.com');
$http->get('www.huyanping.cn');
$http->on('response', function($response){
    echo $response  . PHP_EOL;
});
$http->run();

异步的写法允许我们同时处理多个事务,谁先完成,就先去处理谁。一个简单的异步http客户端见: async-http-php

PHP有很多扩展和包提供了这方面的支持:

ext-libevent libevent扩展,基于libevent库,支持异步IO和时间事件

ext-event event扩展,支持异步IO和时间事件

ext-libev libev扩展,基于libev库,支持异步IO和时间事件

ext-eio eio扩展,基于eio库,支持磁盘异步操作

ext-swoole swoole扩展,支持异步IO和时间,方便编写异步socket服务器,推荐使用

package-react react包,提供了全面的异步编程方式,包括IO、时间事件、磁盘IO等等

package-workerman workerman包,类似swoole,php编写

发布/订阅:

$lookup = new nsqphp\Lookup\Nsqlookupd;
$nsq = new nsqphp\nsqphp($lookup);
$nsq->subscribe('mytopic', 'somechannel', function($msg) {
    echo $msg->getId() . "\n";
})->run();

promise:

function getJsonResult()
{
    return queryApi()
    ->then(
        // Transform API results to an object
        function ($jsonResultString) {
            return json_decode($jsonResultString);
        },
        // Transform API errors to an exception
        function ($jsonErrorString) {
            $object = json_decode($jsonErrorString);
            throw new  ApiErrorException($object->errorMessage);
        }
    );
}

// Here we provide no rejection handler. If the promise returned has been
// rejected, the ApiErrorException will be thrown
getJsonResult()
->done(
    // Consume transformed object
    function ($jsonResultObject) {
        // Do something with $jsonResultObject
    }
);

promise模式的意义在于解耦,就在刚刚我们提到的异步回调嵌套的问题,可以通过promise解决。其原理是在每一次传递回调函数的过程中,你都会拿到一个promie对象,而这个对象有一个then方法,then方法仍然可以返回一个promise对象,通过传递promise对象可以实现把多层嵌套分离出来。具体的代码需要去研究一下源码才可以,有点难懂,PHP的promise推荐阅读: promise

异步的实现原理

异步的实现大多情况下少不了循环监听事件,例如我们上面看到$loop->run(),这里其实是一个死循环,监听到事件则调用相应的处理函数。如果你对pcntl熟悉,你一定知道declare(tick=1),其实它也是一种循环,含义是每执行tick行代码,则检查一次是否有尚未处理的信号。虽然会有一个阻塞的死循环(大多数情况下,declare属于特殊情况),但我们可以对多个事件进行监听处理,同时可以在某一个事件处理的过程中停止循环,这样就可以实现并发异步的IO访问,甚至更多。

一段伪代码如下:

$async = new Async();
$async->on('request', function($requset){
    do_something_with($request);
});

// 这里其实就是$loop->run()的核心代码
while(true){
    $async->hasRequest() ? $async->callRequestCallback() : null;
    sleep(1);
}

整片文章其实并不够详细,充其量算是一篇介绍性的文章,算是我在异步编程方面的一次总结。异步编程的学习并不像学习一门语言或者 设计模式 那样简单,它要求我们改变传统的编程方式。而异步IO对于学习者要求也略高,首先你必须熟悉同步的IO操作,甚至你需要了解一些协议解析的内容。

希望上面的内容对于初学者有一些帮助。文中若有错误的地方,还望指正。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

复杂网络理论及其应用

复杂网络理论及其应用

汪小帆、李翔、陈关荣 / 清华大学出版社 / 2006 / 45.00元

国内首部复杂网络专著 【图书目录】 第1章 引论 1.1 引言 1.2 复杂网络研究简史 1.3 基本概念 1.4 本书内容简介 参考文献 第2章 网络拓扑基本模型及其性质 2.1 引言 2.2 规则网络 2.3 随机图 2.4 小世界网络模型 2.5 无标度网络模型 ......一起来看看 《复杂网络理论及其应用》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

SHA 加密
SHA 加密

SHA 加密工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具