当前位置: 首页 > 后端技术 > PHP

在同期的

时间:2023-03-29 23:06:17 PHP

采访中,有个问题没有很好的回答。题目是:3个并发的http请求,只要其中一个请求有结果,就会返回并中断另外两个。当时考虑的内容有些偏离了题目的初衷。我一直在思考如何中断http请求,大概是在client->recv()之前判断结果是否已经产生,所以答案是使用socket发送http请求,并在libevent中加入socket循环监听,在回调中判断是否已经获取到结果,如果已经获取到则直接返回。后来越说越觉得不对。由于结果已经是recv,所以不能算是中断了http请求。更何况我从来没有用过libevent。后来也讲了两种实现方式,一种是使用curl_multi_init,一种是使用golang来实现并发。当时的golang版本忘了close的用法,结果不太符合题意。这个问题我没有回答,考官也没有为难我。但是我一直在想这件事,直到我面试完走下楼才明白这意味着什么。也许测试是关于并发和进程线程的应用。所以为了总结这篇文章,让我们谈谈PHP中的并发。本文大致总结了PHP编程中的五种并发方式。Golang的最终实现纯属无聊,可以忽略。如果可用,将添加一个libevent版本。curl_multi_init文档说允许异步处理多个cURL句柄。它确实是异步的。这里需要理解的是select方法,在文档中解释为Blocksuntilaactivityonanycurl_multiconnections。您应该能够理解常见的异步模型。select,epoll都很有名。这是一篇非常好的文章。如果您有兴趣,请阅读解释。0);//关闭句柄curl_multi_remove_handle($mh,$ch_1);curl_multi_remove_handle($mh,$ch_2);curl_multi_close($mh);这里我设置的是,select得到结果,就退出循环,并删除curl资源,从而达到取消http请求的目标的。swoole_clientswoole_client提供了异步模式,这个我其实忘记了。这里的sleep方法要求swoole版本大于等于1.7.21。我还没有升级到这个版本,直接退出即可。on("connect",function($cli){$req="GET/HTTP/1.1\r\nHost:www.baidu.com\r\nConnection:keep-alive\r\nCache-Control:no-cache\r\nPragma:no-cache\r\n\r\n";for($i=0;$i<3;$i++){$cli->发送($req);}});$client->on("receive",function($cli,$data){echo"Received:".$data."\n";exit(0);$cli->sleep();//swoole>=1.7.21});$client->on("error",function($cli){echo"Connectfailed\n";});$client->on("close",function($cli){echo"Connectionclose\n";});//发起网络连接$client->connect('183.207.95.145',80,1);process哎,忘记swoole_process了,所以这里没有使用pcntl模块。但是写完后发现,这其实不是中断请求,而是先读到哪一个,忽略后面的返回值。useQueue();$pid=$process->start();$workers[$pid]=$process;}foreach($workersas$pid=>$process){//子进程也会包含这个事件swoole_event_add($process->pipe,function($pipe)use($process,$lock,&$finished){$lock->lock();if(!$finished){$finished=true;$data=$process->read();echo"RECV:".$data.PHP_EOL;}$lock->unlock();});}函数过程(swoole_process$process){$response='httpresponse';$process->write($response);echo$process->pid,"\t",$process->callback.PHP_EOL;}for($i=0;$i<$worker_num;$i++){$ret=swoole_process::wait();$pid=$ret['pid'];echo"WorkerExit,PID=".$pid.PHP_EOL;}pthreads编译pthreads模块安装的时候提示编译php时必须启用ZTS,所以好像是线程安全版的需要使用它。wamp中的php大部分恰好是TS,直接下载了一个dll,重复文档中的说明。创建对应目录,在win下测试。我还没有完全理解它。找了一篇文章说php的pthreads和POSIXpthreads是完全不同的。代码有点烂,需要多看文档体会。url='http://www.baidu.com';}publicfunctionrun(){}}classProcessextendsWorker{private$text="";publicfunction__construct($text,$object){$this->text=$text;$this->object=$object;}publicfunctionrun(){while(is_null($this->object->response)){print"线程{$this->text}正在运行\n";$this->object->response='http响应';睡觉(1);}}$foo=newFoo();$a=newProcess("A",$foo);$a->start();$b=newProcess("B",$foo);$b->开始();回声$foo->响应;yieldyield生成的生成器可以中断函数,用send向生成器发送消息。协程版本稍后会添加。仍在学习。用Go实现Golang比较简单。回家后检查close,处理一下panic就ok了。代码如下:packagemainimport("fmt")funcmain(){varresultchanstring=make(chanstring,1)forindex:=0;指数<3;index++{godoRequest(result)}res,ok:=<-resultifok{fmt.Println("received",res)}}funcdoRequest(resultchanstring){response:="httpresponse"deferfunc(){如果x:=恢复();x!=nil{fmt.Println("Unabletosend:%v",x)}}()result<-responseclose(result)}以上方法,除了curl_multi_*似乎符合问题(不确定,看源代码),其他方法都不会在请求后中断recv()操作。如果得到response后还有后续操作,则有用,否则无意义。想了想,可能是PHP操作的粒度太大了,猜测使用C/C++应该可以解决问题。写的时候没发现问题。有的方法是返回值,有的直接打印出来。不是很好。返回值应该统一用于获取请求结果。能力有限,先做这个吧。