【问题标题】:Setting thumbnail via API causes 503 backendError通过 API 设置缩略图会导致 503 backendError
【发布时间】:2018-02-26 16:26:45
【问题描述】:

我们有一个基于移动设备的视频编辑器,可以将人们的视频和缩略图上传到 AWS S3 存储桶。然后,这会启动一个 AWS SWF 进程来编码、上传视频、上传缩略图并等待所述视频的处理,然后再告诉用户他们的视频已完成。每个用户都使用自己的访问令牌,以便将视频上传到自己的 YouTube 频道。

这在过去 12 个月左右一直很好,从 YouTube 端上传缩略图时偶尔会抛出 backendError,我们有一个重试策略,将等待 1、3、8、15 等秒。到第二次或第三次重试时,YouTube 服务器已自行解决,一切都恢复正常。

截至 2017 年 9 月 17 日 17:29(澳大利亚东部标准时间),我们的所有视频都没有成功上传缩略图。系统抛出 Google_Service_Exception,状态码为 503,响应正文为,

{“错误”:{“错误”:[{ “域”:“全球”, “原因”:“后端错误”, “消息”:“后端错误”}],“代码”:503,“消息”:“后端错误”} }

此时,没有更新任何服务器包,也没有更新我们自己的代码或使用的作曲家包。

我尝试撤销我的访问令牌,在另一个帐户上生成了我自己的一个新项目和新的 API 密钥/秘密,但在我们的测试服务器上仍然得到相同的错误处理缩略图(应该排除速率限制,我们还很远低于任何限制)。

视频上传仍然可以正常工作。从我们的系统编辑视频标题仍然可以正常工作(意味着访问令牌是有效的,并且如果频道允许,则需要设置缩略图的范围)。手动将缩略图直接上传到 YouTube 仍然可以正常工作(排除频道本身有问题)。

我什至重新下载了sample code 并创建了一个新测试,但它仍然失败。它在所有情况下都失败的具体行是

$status = $media->nextChunk($chunk);

我不知道是什么导致了这个突然的问题,如果我们没有改变,所有的图像都是 1920x1080 jpg 远低于 2mb,完全不同的项目和访问令牌,全新的示例代码。而且我假设如果每个使用 YouTube 数据 API 的人在过去 24 小时内都遇到此错误,将会有更多关于它的帖子。所以不确定我们还能尝试什么。

有什么建议吗?

编辑: 嗯……这很奇怪。我设法通过命令行从我们的服务器使用 cURL 上传。

curl -X POST -F "image=@thumbnail_test.jpg" "https://www.googleapis.com/upload/youtube/v3/thumbnails/set?videoId=dCdQ2tJ5wIs&key={REMOVED}&access_token={REMOVED}"

而且效果很好。这样就排除了我们的服务器问题(IP 被阻止或其他问题),我们的访问令牌问题或我们的 client_id 密钥问题。它还以某种方式排除了 YouTube 后端的不良行为(假设 cURL 和 php 通过相同的机制连接)。

剩下的就是,我们将缩略图上传到 YouTube(并且只有缩略图)现在通过 PHP 失败,没有服务器或服务器代码更改,但在命令行中通过 cURL 使用相同的访问令牌和密钥可以正常工作。

编辑 2: 我已经上传了一个示例here on github(包括一个示例图像和 composer.json 文件)。它在 1 中有 2 个示例。显示脚本因 youtube php sdk 失败,但随后使用相同凭据成功处理 curl 请求。脚本不包括获取访问或刷新令牌。

主要的test.php脚本如下。

<?php 

define('GOOGLE_CLIENT_ID', 'REPLACE_ME');
define('GOOGLE_CLIENT_SECRET', 'REPLACE_ME');

require_once 'vendor/autoload.php';

$token = [
    'access_token' => 'REPLACE_ME',
    'refresh_token' => 'REPLACE_ME',
    'token_type' => 'Bearer',
    'expires_in' => '3600',
    'created' => REPLACE_ME
];

$youtube_id = 'REPLACE_ME';
$thumbnail_path = realpath('thumbnail.jpg');


/*
// Doing the exact same request in cURL works fine.
$data = [
    'filedata' => new CURLFile($thumbnail_path, 'image/jpeg', basename($thumbnail_path)),
];      
// Execute remote upload
$curl = curl_init();

curl_setopt($curl, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer '.$token['access_token'],
]);

curl_setopt($curl, CURLOPT_URL, 'https://www.googleapis.com/upload/youtube/v3/thumbnails/set?videoId='.$youtube_id.'&key='.GOOGLE_CLIENT_SECRET);
curl_setopt($curl, CURLOPT_TIMEOUT, 30);
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);

curl_setopt($curl, CURLOPT_VERBOSE, 1);
$response = curl_exec($curl);
curl_close($curl);
echo $response;
*/



// Based on php sample from https://developers.google.com/youtube/v3/docs/thumbnails/set
$client = new Google_Client();   
$client->setClientId(GOOGLE_CLIENT_ID);
$client->setClientSecret(GOOGLE_CLIENT_SECRET);
$client->setScopes([
    'https://www.googleapis.com/auth/yt-analytics.readonly', 
    'https://www.googleapis.com/auth/youtube', 
    'https://www.googleapis.com/auth/youtube.upload',
    'https://www.googleapis.com/auth/userinfo.email',
    'https://www.googleapis.com/auth/userinfo.profile',
]);

$client->setAccessType('offline');  

// If you want to use a refresh token instead to get an access token from it.
//$token = $client->fetchAccessTokenWithRefreshToken($refresh_token);
//var_dump($token);
$client->setAccessToken($token);

$service = new Google_Service_YouTube($client);

try
{
    // Specify the size of each chunk of data, in bytes. Set a higher value for
    // reliable connection as fewer chunks lead to faster uploads. Set a lower
    // value for better recovery on less reliable connections.
    $chunkSizeBytes = 1 * 1024 * 1024;

    // Create a MediaFileUpload object for resumable uploads.
    // Parameters to MediaFileUpload are:
    // client, request, mimeType, data, resumable, chunksize.
    $client->setDefer(true);
    $request = $service->thumbnails->set($youtube_id);
    $client->setDefer(false);
    $mimeType = 'image/jpeg';

    $media = new Google_Http_MediaFileUpload(
        $client,
        $request,
        $mimeType,
        null,
        true,
        $chunkSizeBytes
    );

    $filesize = filesize($thumbnail_path);
    echo "Filesize: $filesize\n";
    $media->setFileSize($filesize);

    // Read the media file and upload it chunk by chunk.
    $status = false;
    $handle = fopen($thumbnail_path, "rb");
    while (!$status && !feof($handle))
    {
        $chunk = fread($handle, $chunkSizeBytes);
        $status = $media->nextChunk($chunk); // The line where the Google_Service_Exception exception is thrown. 
    }

    fclose($handle);

    echo "Thumbnail uploaded success\n";                              
}
catch (Google_Service_Exception $err)
{
    echo $err->getMessage();
}
catch (Exception $err)
{
    echo $err->getMessage();
}

【问题讨论】:

  • 寻求调试帮助的问题(“为什么这段代码不起作用?”)必须包括所需的行为、特定的问题或错误以及在问题本身中重现它所需的最短代码。没有明确问题陈述的问题对其他读者没有用处。请参阅:如何创建最小、完整和可验证的示例。
  • 您能否为我链接到最小、完整和可验证的示例,以便我可以为其他读者更新问题的最佳实践?
  • stackoverflow.com/help/mcve 你不应该包括你的客户 ID 和我自己的秘密。

标签: php youtube-api thumbnails youtube-data-api


【解决方案1】:
{  
   "error":{  
      "errors":[  
         {  
            "domain":"global",
            "reason":"backendError",
            "message":"Backend Error"
         }
      ],
      "code":503,
      "message":"Backend Error"
   }
}

是 Google 方面的服务器错误。除了等待几分钟再试一次之外,您实际上无能为力。这通常是由于禁食引起的。

建议的操作:使用exponential backoff,在重试非幂等请求之前进行检查。

实现指数退避

指数退避是网络应用程序的标准错误处理策略,其中客户端会在越来越长的时间内定期重试失败的请求。如果大量请求或繁重的网络流量导致服务器返回错误,指数退避可能是处理这些错误的好策略。相反,它不是处理与速率限制、网络容量或响应时间无关的错误的相关策略,例如无效的授权凭据或找不到文件错误。

如果使用得当,指数退避可以提高带宽使用效率,减少获得成功响应所需的请求数量,并最大限度地提高并发环境中的请求吞吐量。

创建请求不是幂等的。简单的重试是不够的,可能会导致重复的实体。重试前检查实体是否存在。

实现简单指数退避的流程如下。

  1. 向 API 发出请求
  2. 接收具有可重试错误代码的错误响应
  3. 等待 1s + random_number_milliseconds 秒
  4. 重试请求
  5. 接收具有可重试错误代码的错误响应
  6. 等待 2s + random_number_milliseconds 秒
  7. 重试请求
  8. 接收具有可重试错误代码的错误响应
  9. 等待 4s + random_number_milliseconds 秒
  10. 重试请求
  11. 接收具有可重试错误代码的错误响应
  12. 等待 8 秒 + random_number_milliseconds 秒
  13. 重试请求
  14. 接收具有可重试错误代码的错误响应
  15. 等待 16 秒 + random_number_milliseconds 秒
  16. 重试请求
  17. 如果仍然出现错误,请停止并记录错误。

【讨论】:

  • 我觉得你没有真正阅读我的问题。我们实施了指数退避。系统已故障 24 小时。具有相同访问令牌、相同密钥、相同 youtube 视频、相同缩略图的 CLI curl 请求 100% 的时间都在工作,但使用 php sdk 的时间 100% 失败。两者都来自同一台机器。
  • 我已经阅读了您的问题,对于这个问题没有其他解决方案。这是后端错误或防洪问题。如果您已正确实施指数退避,那么您无能为力。如果它使用 curl 而不是 PHP,那么您是否认为您的代码中有一个循环在某个地方向 API 发送垃圾邮件,导致 PHP 始终返回防洪错误?由于您没有包含足够的代码来重现问题,因此我们可以做很多事情。
  • 指数退避没有让缩略图上传,我们已经记录了错误,这就是为什么我们在失败超过 24 小时后来到 SO ;) CLI 工作正常,通过 php 失败。环境大约一周没有改变。
  • 你上传了多少?有一个限制。 stackoverflow.com/a/43087391/1841839你有没有试过等到明天你的配额重置。
  • 我们远远低于配额,CLI 重复工作,php 失败。两者都来自同一台机器,具有完全相同的凭据和完全相同的视频的缩略图。如果我们达到了上传限制,它将返回 400,就像那个答案所说的那样。这也意味着 CLI 也会失败。
【解决方案2】:

不知从何而来,4 天后它突然自行修复。是 YT 内部的一个问题,但由于某种原因不能在他们的所有 SDK 中复制(同样,服务器没有被触及,远程代码突然工作,本地使用的测试代码突然工作)。

【讨论】:

    猜你喜欢
    • 2013-07-15
    • 1970-01-01
    • 2016-01-24
    • 1970-01-01
    • 2011-12-01
    • 2020-07-03
    • 2013-05-02
    • 1970-01-01
    • 2015-09-18
    相关资源
    最近更新 更多