問(wèn)題背景:
做了個(gè)api接口,使用到第三方的api,為避免進(jìn)程阻塞,使用了異步請(qǐng)求第三方api,但是第三方的請(qǐng)求結(jié)果無(wú)法返回給客戶端,這個(gè)要怎么解決?
代碼如下:
use Workerman\Http\Client;
use support\Request;
class TestController
{
public function test(Request $r)
{
$options = [
'max_conn_per_addr' => 128, // 每個(gè)地址最多維持多少并發(fā)連接
'keepalive_timeout' => 15, // 連接多長(zhǎng)時(shí)間不通訊就關(guān)閉
'connect_timeout' => 30, // 連接超時(shí)時(shí)間
'timeout' => 30, // 等待響應(yīng)的超時(shí)時(shí)間
];
$http = new Client($options);
$url = $r->input('url');
$http->get($url, function($response){
$body = $response->getBody();
return response($body); // <------ 想要把這個(gè)結(jié)果發(fā)給客戶端,請(qǐng)問(wèn)如何實(shí)現(xiàn)呢?
}, function($exception){
echo $exception;
});
return 'sync ok'; // <----客戶端只能收到這個(gè)結(jié)果
}
}
只搜索到這個(gè)文檔
http://www.wtbis.cn/doc/workerman/components/workerman-http-client.html#Optinons%20%E9%80%89%E9%A1%B9
實(shí)現(xiàn)了一個(gè)不太好的方案如下,但是該方案會(huì)有一個(gè)問(wèn)題,不會(huì)關(guān)閉連接。請(qǐng)問(wèn)官方有沒(méi)有計(jì)劃支持類似的場(chǎng)景
新建文件
support\HoldConnetion.php, 代碼如下:
<?php
namespace support;
use Workerman\Connection\TcpConnection;
class HoldConnetion
{
public static function encode($response, TcpConnection $connection)
{
return '';
}
}
修改TestController的代碼如下:
use support\HoldConnetion;
use support\Request;
use Webman\App;
use Workerman\Http\Client;
class TestController
{
public function test(Request $r)
{
$options = [
'max_conn_per_addr' => 128, // 每個(gè)地址最多維持多少并發(fā)連接
'keepalive_timeout' => 15, // 連接多長(zhǎng)時(shí)間不通訊就關(guān)閉
'connect_timeout' => 30, // 連接超時(shí)時(shí)間
'timeout' => 30, // 等待響應(yīng)的超時(shí)時(shí)間
];
$http = new Client($options);
$conn = App::connection();
$url = $r->input('url');
$protocol = $conn->protocol; // 保留舊的protocol
$conn->protocol = new HoldConnetion();
$http->get($url, function($response) use ($conn, $protocol) {
$body = $response->getBody();
$conn->protocol = $protocol; // 恢復(fù)舊的protocol
$conn->send(response($body)); // <--- 異步發(fā)送給客戶端
}, function($exception){
echo $exception;
});
return '不會(huì)發(fā)送到客戶端'; // <-- 修改protocol后,這部分內(nèi)容不會(huì)再發(fā)送到客戶端
}
}
異步請(qǐng)請(qǐng)求建議用自定義進(jìn)程去做。
process/api.php
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.wtbis.cn/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace process;
use Workerman\Connection\TcpConnection;
use Workerman\Http\Client;
use Workerman\Protocols\Http\Request;
class Api
{
public function onMessage(TcpConnection $connection, Request $request)
{
if ($request->path() == '/test/test') {
$options = [
'max_conn_per_addr' => 128, // 每個(gè)地址最多維持多少并發(fā)連接
'keepalive_timeout' => 15, // 連接多長(zhǎng)時(shí)間不通訊就關(guān)閉
'connect_timeout' => 30, // 連接超時(shí)時(shí)間
'timeout' => 30, // 等待響應(yīng)的超時(shí)時(shí)間
];
$http = new Client($options);
$http->get('http://www.baidu.com', function($response) use ($connection) {
$body = (string)$response->getBody();
echo $body;
$connection->send(response($body));
}, function($exception){
echo $exception;
});
return;
}
$connection->send(response('404 not found'), 404);
}
}
config/process.php
return [
// ....
'api' => [
'handler' => \process\Api::class,
'listen' => 'http://0.0.0.0:8686',
'count' => 4,
]
];
訪問(wèn) http://127.0.0.1:8686/test/test
nginx做個(gè)代理,將需要異步的接口比如/test/test
轉(zhuǎn)發(fā)到端口8686即可
感謝回復(fù),使用自定義進(jìn)程去做會(huì)存在以下問(wèn)題:
1.不可 復(fù)用路由 和 中間件 等配置(?)
2.由于業(yè)務(wù)原因,幾乎所有接口都會(huì)調(diào)用第三方 (第三方可能是公司內(nèi)部其它服務(wù)) 的API, 改動(dòng)起來(lái)較大
從webman代碼看,從框架層面支持該功能似乎改動(dòng)不大,比如在 webman\App:send 方法里加個(gè)判斷response類型
即可。
大佬是否考慮從框架層面增加支持,畢竟可以大大增加抗并發(fā)能力,場(chǎng)景也比較通用。
這個(gè)暫時(shí)不支持。后面workerman v5出來(lái),可以做到框架和業(yè)務(wù)代碼不用改動(dòng),支持異步調(diào)用url,以同步的方式返回結(jié)果。v5發(fā)布時(shí)間還沒(méi)確定,你可以先用你現(xiàn)在的方案開(kāi)發(fā)
現(xiàn)在可以用php8.1 workerman V5 協(xié)程了,賊屌
v5.0.0-beta.5 這個(gè)版本沒(méi)啥問(wèn)題,我測(cè)試用了,協(xié)程很好用,windows 單進(jìn)程 請(qǐng)求第三方不會(huì)阻塞,無(wú)敵
很簡(jiǎn)單 http://www.wtbis.cn/doc/workerman/components/workerman-http-client.html 協(xié)程用法,webman中寫(xiě)法也是一樣的
$http = new Client($options);
$url = 'http://***';
$response = $http->request($url, [
'method' => 'POST',
'version' => '1.1',
'headers' => $ypHeader,
'data' => json_encode($data),
]);
$r = $response->getBody()->getContents();
我壓測(cè),這個(gè)真不阻塞,能同時(shí)接受1000請(qǐng)求
這個(gè)不太符合我的場(chǎng)景, 我的場(chǎng)景是 A->我->C , 我需要異步請(qǐng)求C,然后把結(jié)果再響應(yīng)給A。
你這個(gè)只能做到A->我, 我異步->C, 我異步->D,請(qǐng)求C、D時(shí)是沒(méi)有阻塞的,但是結(jié)果無(wú)法響應(yīng)給A了。
搜索了一番發(fā)現(xiàn)在同步里調(diào)用異步實(shí)現(xiàn)我這種場(chǎng)景是做不到的,只能反過(guò)來(lái),即異步里調(diào)用同步,英語(yǔ)好的可以看下
https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/
我不太明白你的意思,但是我看這個(gè)協(xié)程是可以符合你的需求的啊。 你的需求是不是搭建web服務(wù)端提供api給A方調(diào)用,然后你的服務(wù)端程序調(diào)用C方的api,最終將C方的返回結(jié)果返回給A方? 如果是這樣的話,在你的程序里使用異步客戶端的方式調(diào)用C接口。這樣你的服務(wù)端程序進(jìn)程不會(huì)阻塞啊,A方可以進(jìn)來(lái)更多的請(qǐng)求。可以試一下
下面是我提問(wèn)的,問(wèn)完我就去測(cè)試了,redis 協(xié)程沒(méi)成功,qps基本沒(méi)差別,估計(jì)是redis足夠快的原因,請(qǐng)求外網(wǎng)第三方協(xié)程方式,很好用,不阻塞,普通方式請(qǐng)求第三方開(kāi)一個(gè)進(jìn)程,qps基本幾個(gè),協(xié)程方式一個(gè)進(jìn)程居然能跑好幾百
我還沒(méi)實(shí)驗(yàn),不過(guò)異步協(xié)程webman中不是只有一個(gè)HTTP客戶端:workerman/http-client嗎,怎么還有異步redis?