gRPC 给定 PHP 的错误元数据

Posted

技术标签:

【中文标题】gRPC 给定 PHP 的错误元数据【英文标题】:gRPC Bad metadata given PHP 【发布时间】:2019-03-01 08:42:28 【问题描述】:

目前我正准备将一个简单的代码从 Python 重写为 php。 此代码用于使用 ProtoBuf 和 gRPC 向 Yandex Cloud(语音工具包)发出请求。

在 Python (v3) 上一切正常。

然后,我希望在 PHP 中也一样(MacOS 上为 7.1.23)。 我使用protoc 编译 Protobuf 和 grpc php 插件来获取服务客户端。

我遇到的第一个复杂情况是客户端的方法接口是SttServiceClient->StreamingRecognize(array metadata[], array options=[]),这相当令人困惑,因为我认为它应该接受StreamingRecognitionRequest

第二个是我得到一个异常 InvalidArgumentException: Bad metadata value given:

PHP Fatal error:  Uncaught InvalidArgumentException: Bad metadata value given in /Users/cyberpug/Documents/repos/php/yaskit/vendor/grpc/grpc/src/lib/BidiStreamingCall.php:37
Stack trace:
#0 /Users/cyberpug/Documents/repos/php/yaskit/vendor/grpc/grpc/src/lib/BidiStreamingCall.php(37): Grpc\Call->startBatch(Array)
#1 /Users/cyberpug/Documents/repos/php/yaskit/vendor/grpc/grpc/src/lib/BaseStub.php(384): Grpc\BidiStreamingCall->start(Array)
#2 /Users/cyberpug/Documents/repos/php/yaskit/vendor/grpc/grpc/src/lib/BaseStub.php(595): Grpc\BaseStub->Grpc\closure('/yandex.cloud.a...', Array, Array, Array)
#3 /Users/cyberpug/Documents/repos/php/yaskit/STT/proto/Yandex/Cloud/Ai/Stt/V2/SttServiceClient.php(26): Grpc\BaseStub->_bidiRequest('/yandex.cloud.a...', Array, Array, Array)
#4 /Users/cyberpug/Documents/repos/php/yaskit/test.php(83): Yandex\Cloud\Ai\Stt\V2\SttServiceClient->StreamingRecognize(Array, Array)
#5 main
  thrown in /Users/cyberpug/Documents/repos/php/yaskit/vendor/grpc/grpc/src/lib/BidiStreamingCall.php on line 37

好吧,我在这里无能为力。我不明白为什么会这样(不是预期的客户端界面)以及为什么会发生这样的错误? 今天看了这么多关于 Protobuf 和 gRPC 的东西都没有用!

这是 Python (v3) 上的代码:

class Yandex:

def __init__(self, **kwargs):
    self._host = kwargs["url"]
    self._credentials = grpc.ssl_channel_credentials()
    self._iamToken = kwargs["iamToken"]

    spec = stt_service_pb2.RecognitionSpec(
        language_code=kwargs["lang"],
        profanity_filter=kwargs["profanityFilter"],
        model=kwargs["model"],
        partial_results=kwargs["partialResults"],
        audio_encoding=kwargs["audioEncoding"],
        sample_rate_hertz=kwargs["sampleRateHertz"]
    )

    self._config = stt_service_pb2.RecognitionConfig(
        specification=spec,
        folder_id=kwargs["folderId"]
    )

def _createStub(self):
    return stt_service_pb2_grpc.SttServiceStub(
        channel=grpc.secure_channel(
            self._host,
            self._credentials
        )
    )

def _createHandshake(self):
    return stt_service_pb2.StreamingRecognitionRequest(
        config=self._config
    )

def _createRequest(self, data):
    return stt_service_pb2.StreamingRecognitionRequest(
        audio_content=data
    )

def _wrap(self, method):
    yield self._createHandshake()
    for chunk in method():
        yield self._createRequest(chunk)

def stream(self, method):
    stub = self._createStub()
    return stub.StreamingRecognize(
        self._wrap(method),
        metadata=(('authorization', 'Bearer %s' %
                   self._iamToken),)
    )

这是我尝试过的 PHP 代码:

#!/usr/bin/env php
<?php

namespace yaskit;

require "Configuration.php";

use Yaskit\Settings;
use Yaskit\TokenManager;
use \Yandex\Cloud\Ai\Stt\V2\RecognitionConfig;
use \Yandex\Cloud\Ai\Stt\V2\RecognitionSpec;
use \Yandex\Cloud\Ai\Stt\V2\RecognitionSpec\AudioEncoding;
use \Yandex\Cloud\Ai\Stt\V2\StreamingRecognitionRequest;
use \Yandex\Cloud\Ai\Stt\V2\SttServiceClient;

$auth_set = new Settings();
$app_set = new Settings();

$auth_set->load("settings.auth.json");
$app_set->load("settings.speech-to-text.json");

$mgr = new TokenManager("private-test.pem");
$mgr->useLogger($app->createLogger(TokenManager::class));

$mgr->validate($auth_set);

$url = "stt.api.cloud.yandex.net:443";
$iam = $auth_set["iam-token"];

$raw_req = array(
    "config" => array(
        "specification" => array(
            "sample_rate_hertz" => $app_set["sample-rate-hertz"],
            "language_code" => $app_set["lang"],
            "profanity_filter" => $app_set["profanity-filter"],
            "model" => $app_set["model"],
            "partial_results" => $app_set["partial-results"],
            "audio_encoding" => AudioEncoding::LINEAR16_PCM,
        ),
        "folder_id" => $auth_set["folder-id"],
    ),
);

$spec = new RecognitionSpec(array(
    "sample_rate_hertz" => $app_set["sample-rate-hertz"],
    "language_code" => $app_set["lang"],
    "profanity_filter" => $app_set["profanity-filter"],
    "model" => $app_set["model"],
    "partial_results" => $app_set["partial-results"],
    "audio_encoding" => AudioEncoding::LINEAR16_PCM,
));

$config = new RecognitionConfig(array(
    "specification" => $spec,
    "folder_id" => $auth_set["folder-id"],
));

$req = new StreamingRecognitionRequest(array(
    "config" => $config,
));

$service = new SttServiceClient($url, [
    "credentials" => \Grpc\ChannelCredentials::createSsl(),
]);

$res = $service->StreamingRecognize(["authorization" => "Bearer $iam"], $raw_req);

我不知道我应该在这里发布哪个编译文件(因为有很多),所以我只发布 Service Client 和 raw protobuf 本身。如果需要,我会发布更多内容。

SttServiceClient:

<?php
// GENERATED CODE -- DO NOT EDIT!

namespace Yandex\Cloud\Ai\Stt\V2;

/**
 */
class SttServiceClient extends \Grpc\BaseStub 

    /**
     * @param string $hostname hostname
     * @param array $opts channel options
     * @param \Grpc\Channel $channel (optional) re-use channel object
     */
    public function __construct($hostname, $opts, $channel = null) 
        parent::__construct($hostname, $opts, $channel);
    

    /**
     * @param array $metadata metadata
     * @param array $options call options
     */
    public function StreamingRecognize($metadata = [], $options = []) 
        return $this->_bidiRequest('/yandex.cloud.ai.stt.v2.SttService/StreamingRecognize',
        ['\Yandex\Cloud\Ai\Stt\V2\StreamingRecognitionResponse','decode'],
        $metadata, $options);
    


原始 protobuf:

syntax = "proto3";

package yandex.cloud.ai.stt.v2;

option go_package = "github.com/yandex-cloud/go-genproto/yandex/cloud/ai/stt/v2;stt";

service SttService 
  rpc StreamingRecognize (stream StreamingRecognitionRequest) returns (stream StreamingRecognitionResponse) 
  


message StreamingRecognitionRequest 
  oneof streaming_request 
    RecognitionConfig config = 1;
    bytes audio_content = 2;
  


message RecognitionConfig 
  RecognitionSpec specification = 1;
  string folder_id = 2;


message RecognitionSpec 
  enum AudioEncoding 
    AUDIO_ENCODING_UNSPECIFIED = 0;

    // 16-bit signed little-endian (Linear PCM)
    LINEAR16_PCM = 1;

    OGG_OPUS = 2;
  

  AudioEncoding audio_encoding = 1;

  // 8000, 16000, 48000 only for pcm
  int64 sample_rate_hertz = 2;

  // code in BCP-47
  string language_code = 3;

  bool profanity_filter = 4;
  string model = 5;

  // If set true, tentative hypotheses may be returned as they become available (final=false flag)
  // If false or omitted, only final=true result(s) are returned.
  bool partial_results = 7;
  bool single_utterance = 8;


message StreamingRecognitionResponse 
  repeated SpeechRecognitionChunk chunks = 1;
  bool end_of_single_utterance = 2;


message SpeechRecognitionChunk 
  repeated SpeechRecognitionAlternative alternatives = 1;
  bool final = 2;


message SpeechRecognitionAlternative 
  string text = 1;
  float confidence = 2;

【问题讨论】:

【参考方案1】:

似乎元数据是数组(键=>数组())。例子: https://github.com/grpc/grpc/blob/master/src/php/tests/interop/interop_client.php#L416-L419

$res = $service->StreamingRecognize(["authorization" => ["Bearer $iam"]], $raw_req);

【讨论】:

上面的链接不再指向正确的地方。这是一些元数据声明的永久链接:github.com/grpc/grpc/blob/…

以上是关于gRPC 给定 PHP 的错误元数据的主要内容,如果未能解决你的问题,请参考以下文章

在 golang 中将元数据传递到上下文时出错

403 禁止错误和 html 元标记

grpc swift客户端拦截器提议

Symfony 3.4没有元数据类来处理错误

php 对 WEBP 图像元数据的支持

如何在 Oracle 中动态分析给定模式名和表名的元数据?