当前位置: 首页 > 知识库问答 >
问题:

谷歌云存储不太一致,在可恢复上传完成500毫秒后返回404

金旺
2023-03-14

我使用云存储正在经历不一致的行为似乎与留档相反的文件上传器。

当您将对象上传到云存储并收到成功响应时,该对象可立即从Google提供服务的任何位置进行下载和元数据操作。无论创建新对象还是替换现有对象,这都是正确的。因为上载是高度一致的,所以对于写入后读取或更新后读取的操作,您将永远不会收到404未找到响应或过时的数据。https://cloud.google.com/storage/docs/consistency#strongly_consistent_operations

... 但如果我在上传后立即阅读,我会收到404。

流程如下:

  1. my backend NodeJS API启动一个可恢复的上传,创建一个会话URI到bucket中

我得到了它所有的工作,但然后发现,当一个新文件上传(即不已经存在于桶),有一个500ms的延迟之间的上传完成(步骤2)和读取成功(步骤4)。如果我毫不拖延地做这件事,我会得到404分。

文档中说,除非有缓存,否则通常可以立即上传。

重要提示:公开可读的缓存对象可能不会表现出强一致性。有关详细信息,请参阅缓存控制和一致性。

我正在使用XMLHttpRequest将文件上传到GCS,并使用load事件来检测完成的上载。据我所知,这应该意味着已经收到了200个响应,因此文件已经到位。尽管调试加载事件显示它只是100%的另一个“进度”事件。

我试过的

解决方法是在第3步调用我的应用编程接口之前,在加载事件处理程序中的最后一个回调中添加一个setTimeout(完成,500)

我已经对此进行了几十次测试,它是可靠的,在0-400ms出现故障时可重复,而在500ms左右“修复”它。

我尝试过按照建议将缓存控制头添加到原始POST,这将上传会话设置为没有缓存-添加no-store,这似乎是正确的。我可以在PUT的标题中看到这一点(它实际上在响应中放置了比我设置的更多的无缓存选项)。这似乎根本没有影响行为。

如果文件已经在bucked中并被覆盖,则不会发生这种情况。(虽然我猜如果我上传了不同的文件,内容中可能仍然存在竞争条件)。

我似乎无法捕获异常,因此我不知道是哪个对GCS的调用返回404,无论是bucket.file()还是remoteFile.createReadStream(),还是稍后从中读取(在我将可读流传递到的其他库的深处)。

我没有尝试尝试/重试循环,因为我甚至无法捕获错误。如果我不能保证一致的行为,这就是我想做的。

我试过使用gcs可恢复上传软件包和直接使用Storage.File,两者似乎都是一样的。

代码

启动上载的NodeJS API如下所示:

1a) gcs-resumable-upload 版本

const {createURI} = require('gcs-resumable-upload');

        const sessionURI = await createURI({
            bucket: bucketName,
            file: filename,
            origin: origin,
            customRequestOptions: {                     //todo: this doesn't fixe the race
                headers: {
                    'Cache-Control': 'no-store',
                },
            },
        });

1b)Storage.Fileversion

const {Storage, File} = require('@google-cloud/storage');
const storage = new Storage();
        const bucket = storage.bucket(bucketName);
        const file = bucket.file(filename);
        const resp = await file.createResumableUpload({origin: origin})
        const sessionURI = resp[0];
            var reader = new FileReader();
            var xhr = new XMLHttpRequest();

            xhr.upload.addEventListener("load", function(e){
                setTimeout(done, 500);// todo I get 404s in the next step without 500ms delay?
                // done();  // fails
            }, false);

            xhr.open("PUT", sessionUrl);
            xhr.overrideMimeType('text/plain; charset=x-user-defined-binary');
            reader.onload = function(evt) {
                xhr.send(evt.target.result);
            };
            reader.readAsBinaryString(file);
const {Storage} = require('@google-cloud/storage');
const storage = new Storage();

        const bucket = storage.bucket(bucketName);
        let remoteFile, stream;
        remoteFile = bucket.file(filename);
        stream = remoteFile.createReadStream()

然后返回并发送到一个库,该库使用它读取内容。

这就是它出错的地方,尽管它在tick事件中出错为异步,而且我还没有设法从任何地方尝试/捕获它(这有点奇怪)。

错误

错误堆栈为:

 <ref *2> ApiError: No such object: MY-BUCKETNAME/MY-FILENAME
    at new ApiError (node_modules/@google-cloud/common/build/src/util.js:59:15)
    at Util.parseHttpRespMessage (node_modules/@google-cloud/common/build/src/util.js:161:41)
    at Util.handleResp (node_modules/@google-cloud/common/build/src/util.js:135:76)
    at Duplexify.<anonymous> (node_modules/@google-cloud/storage/build/src/file.js:880:31)
    at Duplexify.emit (events.js:314:20)
    at Duplexify.EventEmitter.emit (domain.js:548:15)
    at PassThrough.emit (events.js:314:20)
    at PassThrough.EventEmitter.emit (domain.js:548:15)
    at onResponse (node_modules/retry-request/index.js:208:19)
    at PassThrough.<anonymous> (node_modules/retry-request/index.js:155:11)
    at PassThrough.emit (events.js:326:22)
    at PassThrough.EventEmitter.emit (domain.js:548:15)
    at node_modules/teeny-request/build/src/index.js:184:27
    at processTicksAndRejections (internal/process/task_queues.js:93:5) 

错误消息是一个大结构:

{
  code: 404,
  errors: [],
  response: <ref *1> PassThrough {
    _readableState: ReadableState {
      objectMode: false,
      highWaterMark: 16384,
      buffer: BufferList { head: null, tail: null, length: 0 },
      length: 0,
      pipes: [],
      flowing: false,
      ended: true,
      endEmitted: true,
      reading: false,
      sync: false,
      needReadable: false,
      emittedReadable: false,
      readableListening: false,
      resumeScheduled: false,
      errorEmitted: false,
      emitClose: true,
      autoDestroy: true,
      destroyed: true,
      errored: null,
      closed: true,
      closeEmitted: true,
      defaultEncoding: 'utf8',
      awaitDrainWriters: Set(0) {},
      multiAwaitDrain: true,
      readingMore: false,
      decoder: null,
      encoding: null,
      [Symbol(kPaused)]: true
    },
    _events: [Object: null prototype] {
      prefinish: [Function: prefinish],
      error: [Array],
      close: [Array],
      end: [Function: onend],
      finish: [Function: onfinish]
    },
    _eventsCount: 5,
    _maxListeners: undefined,
    _writableState: WritableState {
      objectMode: false,
      highWaterMark: 16384,
      finalCalled: false,
      needDrain: false,
      ending: true,
      ended: true,
      finished: true,
      destroyed: true,
      decodeStrings: true,
      defaultEncoding: 'utf8',
      length: 0,
      writing: false,
      corked: 0,
      sync: false,
      bufferProcessing: false,
      onwrite: [Function: bound onwrite],
      writecb: null,
      writelen: 0,
      afterWriteTickInfo: null,
      buffered: [],
      bufferedIndex: 0,
      allBuffers: true,
      allNoop: true,
      pendingcb: 0,
      prefinished: true,
      errorEmitted: false,
      emitClose: true,
      autoDestroy: true,
      errored: null,
      closed: true
    },
    allowHalfOpen: true,
    statusCode: 404,
    statusMessage: 'Not Found',
    request: {
      agent: false,
      headers: [Object],
      href: 'https://storage.googleapis.com/storage/v1/b/MY-BUCKETNAME/o/MY-FILENAME?alt=media'
    },
    body: [Circular *1],
    headers: {
      'alt-svc': 'h3-29=":443"; ma=2592000,h3-T051=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43"',
      'cache-control': 'private, max-age=0',
      connection: 'close',
      'content-length': '55',
      'content-type': 'text/html; charset=UTF-8',
      date: 'Tue, 23 Mar 2021 08:08:50 GMT',
      expires: 'Tue, 23 Mar 2021 08:08:50 GMT',
      server: 'UploadServer',
      vary: 'Origin, X-Origin',
      'x-guploader-uploadid': 'ABg5-Uz0P1kWSLFABXOpJ_mbQY5-4wEnMekQduBli1S4aYDWoIgqVKG1M5zlZ_ePd0iJDlzCl_ThYvmFpvcXpgwCcnN993kZog'
    },
    toJSON: [Function: toJSON],
    [Symbol(kCapture)]: false,
    [Symbol(kTransformState)]: {
      afterTransform: [Function: bound afterTransform],
      needTransform: false,
      transforming: false,
      writecb: null,
      writechunk: null,
      writeencoding: 'buffer'
    }
  },
  domainEmitter: PassThrough {
    _readableState: ReadableState {
      objectMode: false,
      highWaterMark: 16384,
      buffer: BufferList { head: null, tail: null, length: 0 },
      length: 0,
      pipes: [],
      flowing: true,
      ended: false,
      endEmitted: false,
      reading: true,
      sync: false,
      needReadable: true,
      emittedReadable: false,
      readableListening: false,
      resumeScheduled: false,
      errorEmitted: true,
      emitClose: true,
      autoDestroy: true,
      destroyed: true,
      errored: [Circular *2],
      closed: true,
      closeEmitted: false,
      defaultEncoding: 'utf8',
      awaitDrainWriters: null,
      multiAwaitDrain: false,
      readingMore: false,
      decoder: null,
      encoding: null,
      [Symbol(kPaused)]: false
    },
    _events: [Object: null prototype] {
      prefinish: [Function: prefinish],
      reading: [Function: makeRequest],
      data: [Function (anonymous)],
      end: [Function (anonymous)]
    },
    _eventsCount: 4,
    _maxListeners: undefined,
    _writableState: WritableState {
      objectMode: false,
      highWaterMark: 16384,
      finalCalled: false,
      needDrain: false,
      ending: false,
      ended: false,
      finished: false,
      destroyed: true,
      decodeStrings: true,
      defaultEncoding: 'utf8',
      length: 0,
      writing: false,
      corked: 0,
      sync: true,
      bufferProcessing: false,
      onwrite: [Function: bound onwrite],
      writecb: null,
      writelen: 0,
      afterWriteTickInfo: null,
      buffered: [],
      bufferedIndex: 0,
      allBuffers: true,
      allNoop: true,
      pendingcb: 0,
      prefinished: false,
      errorEmitted: true,
      emitClose: true,
      autoDestroy: true,
      errored: [Circular *2],
      closed: true
    },
    allowHalfOpen: true,
    _read: [Function: bound ],
    _write: [Function (anonymous)],
    [Symbol(kCapture)]: false,
    [Symbol(kTransformState)]: {
      afterTransform: [Function: bound afterTransform],
      needTransform: true,
      transforming: false,
      writecb: null,
      writechunk: null,
      writeencoding: null
    }
  },
  domainThrown: false
}

共有1个答案

暴绪
2023-03-14

您是否使用云CDN或任何第三方CDN?

询问原因有二:

存储也与第三方CDN兼容

来源

为了在向用户交付内容时获得最佳性能,我们建议将云存储与云CDN结合使用。

来源

我建议首先看看您是否已经有任何CDN,这些CDN可能会影响对象缓存,从而导致您提到的延迟。

如果不是这样,我建议使用云CDN作为文档状态,与云存储相结合,它可以提供最佳性能。除了可能带来的优化性能外,云CDN还具有一些您可能会感兴趣的缓存设置。

最后,您提到了在HTTP请求中使用no store标志,但请注意以下几点:

注意:Cache-Control 也是您可以在对象的 HTTP 请求中指定的标头;但是,Cloud Storage 会忽略此标头并根据存储的元数据值设置响应 Cache-Control 标头。

来源

 类似资料:
  • google cloud bucket中的结果将具有正确的video/mp4 mimetype(这是由浏览器决定的),但仍然无法在浏览器上查看。 编辑: 我也尝试过使用chrome扩展名“Postman”在收到一个可恢复的上传链接后上传一个文件,但它的属性在上传到GCS时也会丢失,所以它似乎与将文件上传到GCS时涉及的JS端无关。

  • 我应该做什么才能成功上传图片?任何帮助都将不胜感激。 非常感谢。

  • 我们一直在使用服务,在(AWS)中,我们意外删除了一个目录,因此我们认为它应该在中,但是经过查看,尽管处于打开状态,但它并不存在。

  • 我试图在vue js中构建镜像上传器,我希望它可以恢复上传,我从浏览器直接将请求发送到云存储服务器(例如,不使用php作为发送上传请求的中间件) 但是文档说我必须有访问令牌才能获取会话url,我稍后将使用它来上传图像,如何使用JavaScript获取此访问令牌,谷歌云没有javascript库,但在我不能用于浏览器使用的节点js中 邮递https://www.googleapis.com/uplo

  • 追踪下面。 相关的Python片段: 最终触发(从ssl库): 溢出错误:字符串长度超过2147483647字节 我想我缺少一些特殊的配置选项? 这可能与这名1.5岁的年轻人有关,显然他还没有解决问题:https://github.com/googledatalab/datalab/issues/784. 谢谢你的帮助! 完整跟踪: [File”/usr/src/app/gcloud/downlo

  • 我想按照官方文档中提供的示例将一个文件上传到Google云存储 然而,我得到了一个错误: 线程“main”com.google.cloud.storage.StorageException中的异常:获取服务帐户的访问令牌时出错:400个错误请求{“错误”:“无效的授予”、“错误描述”:“无效的JWT:令牌必须是短期令牌(60分钟)并且在合理的时间范围内。请检查JWT声明中的iat和exp值。”位于