6

EvaporateJS를 사용하여 멀티 파일 업로드를 사용하여 대용량 파일을 Amazon S3에 업로드하는 웹 앱을 만들고 있습니다. 새로운 덩어리가 시작될 때마다 브라우저가 ~ 2 초 동결 될 수있는 문제를 발견했습니다. 업로드가 진행되는 동안 사용자가 내 앱을 계속 사용할 수 있기를 원합니다.이 동결로 인해 나에게 나쁜 경험이됩니다.Chrome의 작업자 차단 UI 스레드

크롬의 타임 라인을 사용하여 원인을 조사한 결과 SparkMD5의 해시가 발견되었습니다. 그래서 전체 업로드 프로세스를 Worker로 옮겼습니다. 문제를 해결할 것으로 생각했습니다.

이제는 Edge와 Firefox에서 문제가 해결되었지만 Chrome에는 여전히 똑같은 문제가 있습니다. 당신이 볼 수 있듯이, 정지시 내 메인 스레드가 그 시간 동안 실행되는 자바 스크립트의 < 8ms의로, 기본적으로 아무것도하지 않고있다 Timeline

:

여기 내 타임 라인의 스크린 샷입니다. 모든 작업 내 워커 스레드에서 발생하고 심지어 ~ 600ms 또는 내 프레임을 걸리는 1386ms 아니라 실행 중입니다.

나는 무엇이 문제를 일으키는 지 잘 모르겠다. 내가 알고 있어야하는 근로자들과 관련된 문제가 있습니까? 는 AWS 공개 키, AWS 버킷 이름과 AWS 지역, AWS 개체 키 및 입력 파일 객체를 제공하기 위해 호출 스레드에 의존

var window = self; // For Worker-unaware scripts 

// Shim to make Evaporate work in a Worker 
var document = { 
    createElement: function() { 
     var href = undefined; 

     var elm = { 
      set href(url) { 
       var obj = new URL(url); 
       elm.protocol = obj.protocol; 
       elm.hostname = obj.hostname; 
       elm.pathname = obj.pathname; 
       elm.port = obj.port; 
       elm.search = obj.search; 
       elm.hash = obj.hash; 
       elm.host = obj.host; 
       href = url; 
      }, 
      get href() { 
       return href; 
      }, 
      protocol: undefined, 
      hostname: undefined, 
      pathname: undefined, 
      port: undefined, 
      search: undefined, 
      hash: undefined, 
      host: undefined 
     }; 

     return elm; 
    } 
}; 

importScripts("/lib/sha256/sha256.min.js"); 
importScripts("/lib/spark-md5/spark-md5.min.js"); 
importScripts("/lib/url-parse/url-parse.js"); 
importScripts("/lib/xmldom/xmldom.js"); 
importScripts("/lib/evaporate/evaporate.js"); 

DOMParser = self.xmldom.DOMParser; 

var defaultConfig = { 
    computeContentMd5: true, 
    cryptoMd5Method: function (data) { return btoa(SparkMD5.ArrayBuffer.hash(data, true)); }, 
    cryptoHexEncodedHash256: sha256, 
    awsSignatureVersion: "4", 
    awsRegion: undefined, 
    aws_url: "https://s3-ap-southeast-2.amazonaws.com", 
    aws_key: undefined, 
    customAuthMethod: function(signParams, signHeaders, stringToSign, timestamp, awsRequest) { 
     return new Promise(function(resolve, reject) { 
      var signingRequestId = currentSigningRequestId++; 

      postMessage(["signingRequest", signingRequestId, signParams.videoId, timestamp, awsRequest.signer.canonicalRequest()]); 
      queuedSigningRequests[signingRequestId] = function(signature) { 
       queuedSigningRequests[signingRequestId] = undefined; 
       if(signature) { 
        resolve(signature); 
       } else { 
        reject(); 
       } 
      } 
     }); 
    }, 
    //logging: false, 
    bucket: undefined, 
    allowS3ExistenceOptimization: false, 
    maxConcurrentParts: 5 
} 

var currentSigningRequestId = 0; 
var queuedSigningRequests = []; 

var e = undefined; 
var filekey = undefined; 
onmessage = function(e) { 
    var messageType = e.data[0]; 
    switch(messageType) { 
     case "init": 
      var globalConfig = {}; 
      for(var k in defaultConfig) { 
       globalConfig[k] = defaultConfig[k]; 
      } 
      for(var k in e.data[1]) { 
       globalConfig[k] = e.data[1][k]; 
      } 

      var uploadConfig = e.data[2]; 

      Evaporate.create(globalConfig).then(function(evaporate) { 
       var e = evaporate; 

       filekey = globalConfig.bucket + "/" + uploadConfig.name; 

       uploadConfig.progress = function(p, stats) { 
        postMessage(["progress", p, stats]); 
       }; 

       uploadConfig.complete = function(xhr, awsObjectKey, stats) { 
        postMessage(["complete", xhr, awsObjectKey, stats]); 
       } 

       uploadConfig.info = function(msg) { 
        postMessage(["info", msg]); 
       } 

       uploadConfig.warn = function(msg) { 
        postMessage(["warn", msg]); 
       } 

       uploadConfig.error = function(msg) { 
        postMessage(["error", msg]); 
       } 

       e.add(uploadConfig); 
      }); 
      break; 

     case "pause": 
      e.pause(filekey); 
      break; 

     case "resume": 
      e.resume(filekey); 
      break; 

     case "cancel": 
      e.cancel(filekey); 
      break; 

     case "signature": 
      var signingRequestId = e.data[1]; 
      var signature = e.data[2]; 
      queuedSigningRequests[signingRequestId](signature); 
      break; 
    } 
} 

참고 : 여기에

내 노동자에 대한 코드입니다 이는 모두 'init'메시지에서 제공됩니다. 서명 된 것이 필요하면 부모 스레드에 'signingRequest'메시지를 보냅니다.이 메시지는 API의 서명 끝점에서 가져온 '서명'메시지에 서명을 제공해야합니다.

+0

전혀 도움이 되나요? https://github.com/TTLabs/EvaporateJS/issues/257 – user650881

+0

아닙니다.EvaporateJS의 오버 헤드에 대해 알고 있었고 성능 문제가 발생하여 Worker 스레드를 사용하기 시작했습니다. 내 질문은 왜 모든 작업이 작업자에서 일어나는 경우에도 UI 스레드가 여전히 고정되어 있습니다. –

+0

호기심에서 벗어나서이 문제를 해결 했습니까? – tony19

답변

3

아주 좋은 예제를 제공하거나 작업자 코드로 수행중인 작업을 분석 할 수는 없지만이 문제는 주 스레드에서 청크 읽기 또는 예상치 못한 문제 메인 스레드의 청크에서 수행중인 처리. 어쩌면 postMessage을 호출하는 주 스레드 코드를 Worker에 게시 하시겠습니까?

지금 디버깅 중이라면 FileReader 작업을 작업자로 옮기려고합니다. 청크가로드되는 동안 Worker 차단에 신경 쓰지 않는다면 FileReaderSync을 사용할 수도 있습니다.

후 의견 갱신

파일 내용 + 메타 데이터 + 키를 해싱 필요한 presigned URL을 생성합니까? 해시 파일 내용은 청크의 크기로 O (n)을 사용하며 해시가 Blob에서 읽는 첫 번째 작업 인 경우 해싱이 시작될 때까지 파일 콘텐츠의로드가 지연 될 수 있습니다. 주 스레드에 서명을 유지하도록 강요받지 않으면 (핵심 자료로 작업자를 신뢰하지 않습니까?) 이는 작업자에게 가져 오는 또 다른 좋은 것입니다. 작업자에 서명을 이동하는 것은 너무 많은 경우

, 당신은 읽을 수 뭔가가 Blob을 강제로 않은 노동자가 및/또는 주에 ArrayBuffer (또는 Uint8Array 또는 당신이) 다시 파일 내용의를 통과 할 수 서명을위한 스레드; 이렇게하면 주 스레드에서 청크 읽기가 발생하지 않습니다.

+0

나는 지금 코드에 액세스 할 수 없으며 (내 현상금이 끝날 때까지 다시 액세스 할 수 없으므로) 슬프게도 주 스레드 코드를 게시 할 수 없습니다. 어떻게 작동하는지에 대한 개요를 제공하기 위해 최선을 다할 것입니다. 먼저 FileReader 작업이 실제로 Worker 내에서 발생하고 있음을 분명히 밝힙니다. EvaporateJS가 파일 읽기를 처리하기 때문에 내가 게시 한 코드에서 호출을 볼 수 없지만 주 스레드에서는 FileReader 함수를 사용하지 않거나 EvaporateJS를로드했습니다. –

+0

다음은 주 스레드 작업입니다. (1) 사용자가 input [type = file] 요소를 사용하여 파일을 선택합니다. 메인 스레드는 파일 이름을 잡고 API를 요청하여 파일 업로드 위치를 찾습니다. 그런 다음 주 스레드는 postMessage를 사용하여 'init'메시지를 작업자에게 보냅니다. (2) 작업자가 업로드 프로세스를 시작합니다. AWS에 미리 지정된 URL이 필요할 때마다 'signingRequest'라는 postMessage를 주 스레드로 보냅니다. (3) 주 스레드가 비동기 적으로 서명 요청으로 내 API를 호출합니다. 일단 서명이 있으면 '서명'이라는 postMessage를 작업자에게 보냅니다. –

+0

(4) 진행자가 업데이트되면 작업자가 주 스레드에 '진행률'메시지를 보냅니다. (5) 메인 스레드는 Angular 다이제스트를 호출하여 새로운 UI로 UI를 업데이트합니다. (6) 작업자가 업로드가 완료되면 '완료'메시지를 보냅니다. –

관련 문제