前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何实现异步执行

如何实现异步执行

作者头像
PM吃瓜
发布2019-08-12 14:24:51
1K0
发布2019-08-12 14:24:51
举报
文章被收录于专栏:PM吃瓜(公众号)

浏览器和服务器之间只一种面向无连接的HTTP协议进行通讯的,面向无连接的程序的特点是客户端请求服务端,服务端根据请求输出相应的程序,不能保持持久连接。

这样就出现了一个问题,一个客户端的相应服务端可能执行1秒也有可能执行1分钟,这样浏览器就会一直处于等待状态,如果程序执行缓慢,用户可能就没耐心关掉了浏览器。

而有的时候我们不需要关心程序执行的结果,没有必要这样浪费时间和耐心等待,那我们就要想出办法让程序不收等待在后台静默执行。

比如现在有一个场景,给1000个用户发送一封推荐邮件,用户输入或者导入邮件账号了提交服务器执行发送。

<?php $count=count($emailarr); for($i=0;$i<$count;$i++) {   sendmail(.....);//发送邮件 } ?>

这段代码用户体验极差,也无法实际运用,首先发送这么多邮件会产生服务器运行超时,其实漫长的用户等待时间会让用户对系统产品怀疑和失去信心。但是用户不需要等待到1000封邮件都发送完毕了才提交发送成功,我们完全可以提交后台后直接给用户提示发送成功,然后让后台程序静默依次发送。

这个时候我们就需要“异步执行”技术来执行代码,异步执行的特点是后台静默执行,用户无需等待代码的执行结果,使用异步执行的好处:

1.摆脱了应用程序对单个任务的依赖性

2.提高了程序的执行效率

3.提高了程序的扩展性

4.在一定场景提高了用户体验

5.因为PHP不支持多线程,使用异步调用的请求多个HTTP的方式达到了程序并行执行效果,但是注意的是请求的HTTP过多的话,会大大加大了系统的开销

因此对于耗时的操作适合异步执行,服务器接收到请求后,处理完客户端需要的数据就返回,再异步在服务器执行耗时的操作。

解决方法

fsockopen支持socket编程,可以使用fsockopen实现邮件发送等socket程序等等,使用fcockopen需要自己手动拼接出header部分 可以参考: http://cn.php.net/fsockopen/ 使用示例如下:

  1. $fp = fsockopen("www.34ways.com", 80, $errno, $errstr, 30);
  2. if (!$fp) {
  3. echo "$errstr ($errno)<br />\n";
  4. } else {
  5. $out = "GET /index.php / HTTP/1.1\r\n";
  6. $out .= "Host: www.34ways.com\r\n";
  7. $out .= "Connection: Close\r\n\r\n";
  8. fwrite($fp, $out);
  9. /*忽略执行结果
  10. while (!feof($fp)) {
  11. echo fgets($fp, 128);
  12. }*/
  13. fclose($fp);
  14. }

所以总结来说,fscokopen()函数应该可以满足您的要求。可以尝试一下。

fscokopen的问题和popen一样,并发非常多时会产生很多子进程,当达到apache的连接限制数时,就会挂掉,我问题已经说了这种情况。

PHP本身没有多线程的东西,但可以曲线的办法来造就出同样的效果,比如多进程的方式来达到异步调用,只限于命令模式。还有一种更简单的方式,可用于 Web 程序中,那就是用fsockopen()、fwrite() 来请求一个 URL 而无需等待返回,如果你在那个被请求的页面中做些事情就相当于异步了。

关键代码如下:

代码语言:javascript
复制
  1. $fp=fsockopen('localhost',80,$errno,$errstr,5);
  2. if(!$fp){
  3. echo "$errstr ($errno)<br />\n";
  4. }
  5. fputs($fp,"GET another_page.php?flag=1\r\n");
  6. fclose($fp);

上面的代码向页面 another_page.php 发送完请求就不管了,用不着等待请求页面的响应数据,利用这一点就可以在被请求的页面 another_page.php 中异步的做些事情了。

比如,一个很切实的应用,某个 Blog 在每 Post 了一篇新日志后需要给所有它的订阅者发个邮件通知。如果按照通常的方式就是:

日志写完 -> 点提交按钮 -> 日志插入到数据库 -> 发送邮件通知 -> 告知撰写者发布成功

那么作者在点提交按钮到看到成功提示之间可能会等待很常时间,基本是在等邮件发送的过程,比如连接邮件服务异常、或器缓慢或是订阅者太多。而实际上是不管邮件发送成功与否,保证日志保存成功基本可接受的,所以等待邮件发送的过程是很不经济的,这个过程可异步来执行,并且邮件发送的结果不太关心或以日志形式记录备查。

改进后的流程就是:

日志写完 -> 点提交按钮 -> 日志插入到数据库 ---> 告知撰写者发布成功 └ 发送邮件通知 -> [记下日志]

用个实际的程序来测试一下,有两个 php,分别是 write.php 和 sendmail.php,在 sendmail.php 用 sleep(seconds) 来模拟程序执行使用时间。

write.php,执行耗时 1 秒

  1. <?php
  2. /** * 耗时异步操作 * @param $url 模板 模块名称/控制器/方法?参数名=参数值 * 严格注意url的格式和fsockopen,fwrite,fclose的格式 * */
  3. function asyn_sendmail() {
  4. $fp=fsockopen('localhost',80,$errno,$errstr,5);
  5. if(!$fp){
  6. echo "$errstr ($errno)<br />\n";
  7. }
  8. sleep(1);
  9. $out = "GET /sendmail.php?param=1 HTTP/1.1\r\n"; $out .= "Host:localhost\r\n"; $out .= "Connection: Close\r\n\r\n"; fwrite($fp, $out);
  10. fclose($fp);
  11. }
  12. echo time().'<br>';
  13. echo 'call asyn_sendmail<br>';
  14. asyn_sendmail();
  15. echo time().'<br>';
  16. ?>

sendmail.php,执行耗时 10 秒

代码语言:javascript
复制
  1. <?php
  2. //sendmail();
  3. //sleep 10 seconds
  4. sleep(10);
  5. fopen('C:\'.time(),'w');
  6. ?>

通过页面访问 write.php,页面输出:

1272472697 call asyn_sendmail 1272472698

并且在 C:\ 生成文件:

1272472708

从上面的结果可知 sendmail.php 花费至少 10 秒,但不会阻塞到 write.php 的继续往下执行,表明这一过程是异步的。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-03-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Tech爬虫 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档