public function getContents()
{
if (!isset($this->stream)) {
throw new \RuntimeException('Stream is detached');
}
$contents = stream_get_contents($this->stream);
if ($contents === false) {
throw new \RuntimeException('Unable to read stream contents');
}
return $contents;
}
public function __toString()
{
try {
$this->seek(0);
return (string) stream_get_contents($this->stream);
} catch (\Exception $e) {
return '';
}
}
組件版本:
workerman/workerman v4.0.36
workerman/http-client v1.0.9
workerman/webman-framework v1.3.13
在webman中創(chuàng)建自定義進程,該進程只啟動一個進程,進程內注冊一個Timer,Timer的回調函數(shù)中使用workerman/http-client異步請求,請求成功回調中$response->getBody()->getContents()始終為空字符串,代碼大致如下
public function onWorkerStart(Worker $worker)
{
$worker->count = 1;
if($this->configListeners){
// 拉取配置項文件
foreach ($this->configListeners as $listener){
list($dataId, $group, $tenant, $configPath) = $listener;
if(!file_exists($configPath)){
$this->_get($dataId, $group, $tenant, $configPath);
}
$this->timers[$dataId] = Timer::add(
$this->longPullingInterval,
function () use($dataId, $group, $tenant, $configPath){
$this->client->config->listenerAsyncUseEventLoop([
'dataId' => $dataId,
'group' => $group,
'contentMD5' => md5(file_get_contents($configPath)),
'tenant' => $tenant
], function (Response $response) use($dataId, $group, $tenant, $configPath){
if($response->getStatusCode() === 200){
if($response->getBody()->getContents() !== ''){
$this->_get($dataId, $group, $tenant, $configPath);
}
}
}, function (\Exception $exception){
Log::channel('error')->error($exception->getMessage(), $exception->getTrace());
}, $this->longPullingInterval * 1000);
});
}
}
}
public function requestAsyncUseEventLoop(string $method, string $uri, array $options = [])
{
try {
# 同步阻塞獲取token
if($token = $this->issueToken()){
$options[RequestOptions::QUERY]['accessToken'] = $token;
}
$queryString = http_build_query($options[RequestOptions::QUERY] ?? []);
$headers = array_merge($options[RequestOptions::HEADERS] ?? [], [
'Connection' => 'keep-alive'
]);
$this->httpClientAsync()->request(
sprintf('http://%s:%d%s?%s', $this->host, $this->port, $uri, $queryString),
[
'method' => $method,
'version' => '1.1',
'headers' => $headers,
'data' => $options['data'] ?? [],
'success' => $options['success'] ?? function (Response $response) {},
'error' => $options['error'] ?? function (\Exception $exception) {}
]
);
} catch (RequestException $exception) {
if ($exception->hasResponse()) {
if (200 != $exception->getResponse()->getStatusCode()) {
return $this->setError(false, $exception->getResponse()->getBody()->getContents());
}
}
return $this->setError(false, 'server notice:' . $exception->getMessage());
}
}
實際上我打印了Request中onMessage的$response_data中是由body值,且不為空字符串的,如下圖:
^ array:3 [
"start-line" => "HTTP/1.1 200 "
"headers" => array:7 [
"Pragma" => array:1 [
0 => "no-cache"
]
"Expires" => array:1 [
0 => "Thu, 01 Jan 1970 00:00:00 GMT"
]
"Cache-Control" => array:1 [
0 => "no-cache,no-store"
]
"Content-Type" => array:1 [
0 => "application/json;charset=UTF-8"
]
"Content-Length" => array:1 [
0 => "44"
]
"Date" => array:1 [
0 => "Thu, 12 May 2022 07:50:38 GMT"
]
"Connection" => array:1 [
0 => "close"
]
]
"body" => "config.yaml%02DEFAULT_GROUP%02fj_oh-test%01\n"
]
稍微調試了一下,感覺是Request中367行的handleData方法中的write沒有把數(shù)據寫入stream,因為我打印了傳入的data是有值的,但是write后立馬對其getContents,依然是空字符串:
public function handleData($connection, $data)
{
try {
$body = $this->_response->getBody();
$count = $body->write($data);
# 輸出打印
dump($body->getContents());
if ($this->_expectedLength) {
$recv_length = $body->getSize();
if ($this->_expectedLength <= $recv_length) {
$this->emitSuccess();
}
}
} catch (\Exception $e) {
$this->emitError($e);
}
}
我在Stream的write方法中直接對流進行操作:
public function write($string)
{
if (!isset($this->stream)) {
throw new \RuntimeException('Stream is detached');
}
if (!$this->writable) {
throw new \RuntimeException('Cannot write to a non-writable stream');
}
// We can't know the size after writing anything
$this->size = null;
$result = fwrite($this->stream, $string);
# 打印相關內容
dump(__METHOD__, $string, $result, stream_get_contents($this->stream));
if ($result === false) {
throw new \RuntimeException('Unable to write to stream');
}
return $result;
}
很奇怪,fwrite返回了int,表示數(shù)據寫入了流,但我立馬stream_get_content該流,卻依舊是空字符串
這里直接使用stream_get_contents($this->stream)無法獲取到數(shù)據,但是stream_get_contents($this->stream,$result,0)就可以
這個地方是因為打開的流沒有將偏移歸零,始終是在流末尾,所以按照默認參數(shù)獲取的數(shù)據是空,這個時候用fseek進行歸零就好了。
在workerman/http-client下用(string)$this->_response->getBody()代替$this->_response->getBody()->getContent()是解決辦法,這個是一個bug,在__toString中,使用了fseek,但在getContent()中并沒有,所以獲取不到數(shù)據。