Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[QUESTION] grpc服务端,异常处理该怎么做? #192

Closed
minxinqing opened this issue Jul 12, 2019 · 11 comments · Fixed by #235
Closed

[QUESTION] grpc服务端,异常处理该怎么做? #192

minxinqing opened this issue Jul 12, 2019 · 11 comments · Fixed by #235
Labels
question Further information is requested

Comments

@minxinqing
Copy link
Contributor

我现在像http服务端一样配置了grpc的异常处理器,但是客户端不能在异常情况下正确获得响应。
非异常时,controller返回的对象客户端能正常获取。

exceptions.php

return [
    'handler' => [
        'http' => [
            App\Exception\Handler\BusinessExceptionHandler::class,
            App\Exception\Handler\AppExceptionHandler::class,
        ],
        'grpc' => [
            App\Exception\GrpcHandler\BusinessExceptionHandler::class,
            App\Exception\GrpcHandler\AppExceptionHandler::class,
        ]
    ],
];

BusinessExceptionHandler.php

<?php
namespace App\Exception\GrpcHandler;

use App\Exception\BusinessException;
use App\Exception\OrderException;
use Hyperf\HttpMessage\Stream\SwooleStream;
use Psr\Http\Message\ResponseInterface;
use Throwable;
use App\Helper\Log;

class BusinessExceptionHandler extends GrpcExceptionHandler
{

    public function handle(Throwable $throwable, ResponseInterface $response)
    {
        // 判断传入的异常是否是该处理器希望处理的异常
        if ($throwable instanceof BusinessException) {
            Log::warning($throwable->getMessage());

            $this->stopPropagation();
            return $this->handleResponse(null, 200, (string)$throwable->getCode(), $throwable->getMessage());
        }

        return $response;
    }

    public function isValid(Throwable $throwable): bool
    {
        return true;
    }

}

GrpcExceptionHandler.php

<?php

declare(strict_types=1);

namespace App\Exception\GrpcHandler;

use Google\Protobuf\Internal\Message;
use Hyperf\ExceptionHandler\ExceptionHandler;
use Hyperf\Grpc\Parser;
use Hyperf\HttpMessage\Stream\SwooleStream;
use Hyperf\Utils\Context;
use Psr\Http\Message\ResponseInterface;


abstract class GrpcExceptionHandler extends ExceptionHandler
{

    /**
     * Handle GRPC Response.
     * @param int $httpStatus
     */
    protected function handleResponse(?Message $message, $httpStatus = 200, string $grpcStatus = '0', string $grpcMessage = ''): ResponseInterface
    {
        $response = $this->response()->withStatus($httpStatus)
            ->withBody(new SwooleStream(Parser::serializeMessage($message)))
            ->withAddedHeader('Server', 'Hyperf')
            ->withAddedHeader('Content-Type', 'application/grpc')
            ->withAddedHeader('trailer', 'grpc-status, grpc-message');

        $response->getSwooleResponse()->trailer('grpc-status', $grpcStatus);
        $response->getSwooleResponse()->trailer('grpc-message', $grpcMessage);
        return $response;
    }

    /**
     * Get response instance from context.
     */
    protected function response(): ResponseInterface
    {
        return Context::get(ResponseInterface::class);
    }
}

GrpcHandler\BusinessExceptionHandler中的日志能正常输出,所以异常肯定是进来了,handleResponse()是从Hyperf\GrpcServer\CoreMiddleware复制过来的。

@minxinqing minxinqing added the question Further information is requested label Jul 12, 2019
@huangzhhui
Copy link
Member

请提供一个具体的 demo 压缩包

@minxinqing
Copy link
Contributor Author

minxinqing commented Jul 12, 2019

https://github.com/minxinqing/test-hyperf master分支

分别执行
composer test -- --filter=testQueryOne
composer test -- --filter=testQueryTwo

testQueryOne能正常获取结果

[
    [0] => rr1
    [1] => 0
]

testQueryTwo希望获取结果

[
    [0] => "订单不存在"
    [1] => 200333
]

现结果:

[
    [0] => No response
    [1] => -1
]

App\Controller\Grpc\OrderController中处理逻辑如下:

 public function query(OrderQueryRequest $queryRequest) {

        $shopOrderId = $queryRequest->getShopOrderId();

        if ($shopOrderId == 2) {
            throw new BusinessException(200333, "订单不存在");
        }
        $oi = new OrderInfo();
        $oi->setShopOrderId('rr'.$shopOrderId);
        return $oi;
}

@huangzhhui
Copy link
Member

你的 proto 文件在哪?

@minxinqing
Copy link
Contributor Author

另一个项目中

syntax = "proto3";

package proto.payment;

service Order{
    // 申请支付单
    rpc CreateOrder(OrderCreateRequest) returns (OrderInfo) {}

    // 关闭支付单
    rpc CloseOrder(OrderCloseRequest) returns (OrderInfo) {}

    // 查询支付单
    rpc QueryOrder(OrderQueryRequest) returns (OrderInfo) {}
}

message OrderCreateRequest{
    string shop_order_id = 5; // 商城订单ID
    AggPlatform agg_platform = 6; // 支付平台
    string body = 7; // 商品描述
    int32 total_fee = 8; // 支付金额
    string spbill_create_ip = 9; // 客户端IP
    string open_id = 10; // 用户OPENID
}

enum AggPlatform {
    PLATFORM_UNKNOWN = 0;
    PLATFORM_CMB = 1;
    PLATFORM_WX = 2;
}

message OrderCloseRequest{
    string shop_order_id = 5; // 商城订单ID
}

message OrderQueryRequest{
    string shop_order_id = 5; // 商城订单ID
}

message OrderInfo{
    string shop_order_id = 5; // 商城订单ID
    AggPlatform agg_platform = 6; // 支付平台
    OrderState state = 7; // 订单状态
    PayData pay_data = 8; // PayData
}

enum OrderState {
    ORDER_STATE_UNKNOWN = 0;
    //未支付、支付进行中
    ORDER_STATE_NOTPAY = 1;
    //已关闭
    ORDER_STATE_CLOSED = 2;
    //支付成功
    ORDER_STATE_SUCCESS = 3;
    //支付失败
    ORDER_STATE_PAYERROR = 4;
    //支付撤销
    ORDER_STATE_CANCEL = 5;
    //转入退款
    ORDER_STATE_REFUND = 6;
}

message PayData{
    string timeStamp = 1;
    string package = 2;
    string paySign = 3;
    string appId = 4;
    string signType = 5;
    string nonceStr = 6;
}

@minxinqing
Copy link
Contributor Author

@huangzhhui
能复现吗

@huangzhhui
Copy link
Member

@minxinqing 能复现,我需要点时间重新梳理一下 GRPC 的底层流程,太久没碰这块了

@limingxinleo
Copy link
Member

image
直接跑的你的项目,是没有问题的。。
composer 源换成 阿里源

@minxinqing
Copy link
Contributor Author

minxinqing commented Jul 18, 2019

好的,等你们发版

@minxinqing
Copy link
Contributor Author

@limingxinleo 更新了1.0.6,依然不可以

@limingxinleo
Copy link
Member

我直接 clone 了你的项目,测试是没问题的。。。

@minxinqing
Copy link
Contributor Author

swoole 4.3.5 下不可以,升级到 4.4.1之后正常了

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants