Skip to content

Commit

Permalink
支持连接失败自动重试机制
Browse files Browse the repository at this point in the history
  • Loading branch information
youmingdot committed Apr 27, 2018
1 parent f3386fc commit 6bc2916
Show file tree
Hide file tree
Showing 10 changed files with 270 additions and 90 deletions.
14 changes: 13 additions & 1 deletion src/Homer/Invokers/Invoker.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public function invoke(Invocation $invocation)
try {
return $this->doInvoke($invocation);
} catch (Throwable $e) {
return new Result(null, $e);
return $this->createExceptionResult($e);
}
}

Expand All @@ -67,4 +67,16 @@ public function invoke(Invocation $invocation)
* @return \Lawoole\Homer\Result
*/
abstract protected function doInvoke(Invocation $invocation);

/**
* 创建调用异常结果
*
* @param \Throwable $e
*
* @return \Lawoole\Homer\Result
*/
protected function createExceptionResult(Throwable $e)
{
return new Result(null, $e);
}
}
30 changes: 0 additions & 30 deletions src/Homer/Invokers/InvokingException.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,6 @@ class InvokingException extends HomerException
*/
const BUSINESS = 1;

/**
* 异常类型:网络超时
*/
const TIMEOUT = 2;

/**
* 异常类型:数据序列化失败
*/
const SERIALIZATION = 3;

/**
* 判断异常是否为业务异常
*
Expand All @@ -34,24 +24,4 @@ public function isBusiness()
{
return $this->getCode() == static::BUSINESS;
}

/**
* 判断异常是否为网络超时
*
* @return bool
*/
public function isTimeout()
{
return $this->getCode() == static::TIMEOUT;
}

/**
* 判断异常是否为数据序列化失败
*
* @return bool
*/
public function isSerialization()
{
return $this->getCode() == static::SERIALIZATION;
}
}
70 changes: 63 additions & 7 deletions src/Homer/Invokers/RemoteInvoker.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Lawoole\Homer\Invocation;
use Lawoole\Homer\Result;
use Lawoole\Homer\Transport\Client;
use Throwable;

class RemoteInvoker extends Invoker
{
Expand Down Expand Up @@ -39,6 +40,16 @@ public function __construct(Context $context, Client $client, $interface, array
$this->client = $client;
}

/**
* 判断是否开启调试
*
* @return bool
*/
protected function isDebug()
{
return $this->options['debug'] ?? false;
}

/**
* 执行调用并得到调用结果
*
Expand All @@ -50,17 +61,62 @@ protected function doInvoke(Invocation $invocation)
{
$invocation->setAttachments($this->context->getAttachments());

$result = $this->client->request($invocation);
$startTime = microtime(true);

if (!$result instanceof Result) {
Log::channel('homer')->warning('Server response an error message', [
'result' => (string) $result
]);
try {
$result = $this->client->request($invocation);

throw new InvokingException('The invoke result must instance of Result, '
.class_basename($result).' given.');
if (!$result instanceof Result) {
Log::channel('homer')->warning('Server response an error message', [
'result' => (string) $result
]);

throw new InvokingException('The invoke result must instance of Result, '
.class_basename($result).' given.');
}
} catch (Throwable $e) {
$result = $this->createExceptionResult($e);
}

$this->logInvoking($invocation, $result, $this->getElapsedTime($startTime));

return $result;
}

/**
* 记录调用日志
*
* @param \Lawoole\Homer\Invocation $invocation
* @param \Lawoole\Homer\Result $result
* @param float $time
*/
protected function logInvoking(Invocation $invocation, Result $result, $time = null)
{
if ($this->isDebug()) {
$invoking = "{$invocation->getInterface()}->{$invocation->getMethod()}()";

Log::channel('homer')->debug(sprintf('%s %5.2fms %s',
$result->hasException() ? 'Success' : 'Failure', $time, $invoking
), [
'during' => $time,
'method' => $invoking,
'arguments' => $invocation->getArguments(),
'attachments' => $invocation->getAttachments(),
'result' => $result->getValue(),
'exception' => $result->getException(),
]);
}
}

/**
* 获得逝去时间差
*
* @param int $start
*
* @return float
*/
protected function getElapsedTime($start)
{
return round((microtime(true) - $start) * 1000, 2);
}
}
2 changes: 1 addition & 1 deletion src/Homer/Serialization/ExceptionSerialization.php
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ protected function packPropertyValue($value)
} elseif ($value instanceof Validator) {
$value = new ValidatorSerialization($value);
} elseif ($value instanceof Throwable) {
new self($value);
$value = new self($value);
} elseif ($value instanceof Closure) {
$value = null;
}
Expand Down
32 changes: 27 additions & 5 deletions src/Homer/Transport/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ public function getTimeout()
*/
public function getRetryTimes()
{
return $this->options['retry_times'] ?? 1;
return $this->options['retry_times'] ?? 0;
}

/**
Expand All @@ -140,6 +140,10 @@ public function connect()
} catch (TransportException $e) {
$this->disconnect();

Log::channel('homer')->warning($e->getMessage(), [
'exception' => $e
]);

throw $e;
} catch (Throwable $e) {
$this->disconnect();
Expand Down Expand Up @@ -188,8 +192,26 @@ public function request($message)
{
$this->reconnectIfLostConnection();

$retryTimes = $this->getRetryTimes();

try {
return $this->doRequest($message);
$body = $this->serializer->serialize($message);

do {
try {
$data = $this->doRequest($body);

break;
} catch (TransportException $e) {
if ($e->isConnection() && $retryTimes-- > 0) {
continue;
}

throw $e;
}
} while ($retryTimes > 0);

return $this->serializer->unserialize($data);
} catch (HomerException $e) {
$this->disconnect();

Expand Down Expand Up @@ -244,9 +266,9 @@ abstract protected function doDisconnect();
/**
* 发送消息请求
*
* @param mixed $message
* @param string $data
*
* @return mixed
* @return string
*/
abstract protected function doRequest($message);
abstract protected function doRequest($data);
}
58 changes: 42 additions & 16 deletions src/Homer/Transport/Http/HttpClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
namespace Lawoole\Homer\Transport\Http;

use GuzzleHttp\Client as GuzzleHttpClient;
use Illuminate\Support\Str;
use Lawoole\Homer\Transport\Client;
use Lawoole\Homer\Transport\TransportException;
use Throwable;

class HttpClient extends Client
{
Expand Down Expand Up @@ -40,9 +42,8 @@ public function isConnected()
protected function doConnect()
{
$this->client = new GuzzleHttpClient([
'base_uri' => "http://{$this->getRemoteAddress()}/",
'timeout' => $this->getTimeout(),
'connect_timeout' => $this->getTimeout(),
'base_uri' => "http://{$this->getRemoteAddress()}/",
'timeout' => $this->getTimeout() / 1000.0,
]);
}

Expand All @@ -57,26 +58,51 @@ protected function doDisconnect()
/**
* 发送消息请求
*
* @param mixed $message
* @param string $data
*
* @return mixed
* @return string
*/
protected function doRequest($message)
protected function doRequest($data)
{
$body = $this->serializer->serialize($message);
try {
$response = $this->client->request('POST', '', [
'expect' => false,
'body' => $data
]);

$response = $this->client->request('POST', '', [
'expect' => false,
'body' => $body
]);
if ($response->getStatusCode() != 200) {
throw new TransportException($response->getBody()->getContents() ?: 'Http request failed, status: '
.$response->getStatusCode(), TransportException::REMOTE);
}
} catch (TransportException $e) {
throw $e;
} catch (Throwable $e) {
if ($this->causedByConnectionProblem($e)) {
$this->disconnect();

throw new TransportException($e->getMessage(), TransportException::CONNECTION, $e);
}

if ($response->getStatusCode() != 200) {
throw new TransportException($response->getBody()->getContents() ?: 'Http request failed, status: '
.$response->getStatusCode());
throw new TransportException($e->getMessage(), 0, $e);
}

$body = $response->getBody()->getContents();
return $response->getBody()->getContents();
}

return $this->serializer->unserialize($body);
/**
* 判断异常是否由连接问题引发
*
* @param \Throwable $e
*
* @return mixed
*/
protected function causedByConnectionProblem(Throwable $e)
{
$message = $e->getMessage();

return Str::contains($message, [
'error 6: Couldn\'t resolve host',
'error 7: couldn\'t connect to host',
]);
}
}
5 changes: 5 additions & 0 deletions src/Homer/Transport/Http/HttpServerSocketHandler.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?php
namespace Lawoole\Homer\Transport\Http;

use Illuminate\Support\Facades\Log;
use Lawoole\Contracts\Foundation\Application;
use Lawoole\Homer\Dispatcher;
use Lawoole\Homer\Transport\SerializeServerSocketMessages;
Expand Down Expand Up @@ -70,6 +71,10 @@ public function onRequest($server, $serverSocket, $request, $response)

$this->respond($response, 200, $body);
} catch (Throwable $e) {
Log::channel('homer')->warning('Handle invoking failed, cause: '.$e->getMessage(), [
'exception' => $e
]);

$this->respond($response, 500, $e->getMessage());
}
}
Expand Down
30 changes: 30 additions & 0 deletions src/Homer/Transport/TransportException.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ class TransportException extends HomerException
*/
const TIMEOUT = 2;

/**
* 异常类型:数据序列化失败
*/
const SERIALIZATION = 3;

/**
* 异常类型:远端处理异常
*/
const REMOTE = 4;

/**
* 判断异常是否为连接异常
*
Expand All @@ -39,4 +49,24 @@ public function isTimeout()
{
return $this->getCode() == static::TIMEOUT;
}

/**
* 判断异常是否为数据序列化失败
*
* @return bool
*/
public function isSerialization()
{
return $this->getCode() == static::SERIALIZATION;
}

/**
* 判断异常是否为远端处理异常
*
* @return bool
*/
public function isRemote()
{
return $this->getCode() == static::REMOTE;
}
}
Loading

0 comments on commit 6bc2916

Please sign in to comment.