2016-11-06 18 views
6

我正在開發一個使用ASP.NET MVC的視頻網站。對Action方法的兩個並行ajax請求排隊,爲什麼?

我想在我的應用程序中使用的一項功能是轉換視頻。但是,由於轉碼過程可能非常耗時,我想向客戶端用戶展示該過程的進度。

因此,我的模式是使用一個控制器動作來處理整個轉碼過程並將其進度寫入存儲在服務器上的文件中。同時我使用Ajax調用另一個控制器動作來讀取指定文件,檢索進度信息並在轉碼過程中每2秒將其發送回客戶端進行顯示。

爲了實現我的計劃,我寫了下面的代碼:

服務器端:

public class VideoController : Controller 
{ 
     //Other action methods 
     .... 
    //Action method for transcoding a video given by its id 
    [HttpPost] 
    public async Task<ActionResult> Transcode(int vid=0) 
    { 
     VideoModel VideoModel = new VideoModel(); 
     Video video = VideoModel.GetVideo(vid); 
     string src = Server.MapPath("~/videos/")+video.Path; 
     string trg = Server.MapPath("~/videos/") + +video.Id+".mp4"; 
     //The file that stores the progress information 
     string logPath = Server.MapPath("~/videos/") + "transcode.txt"; 
     string pathHeader=Server.MapPath("../"); 
     if (await VideoModel.ConvertVideo(src.Trim(), trg.Trim(), logPath)) 
     { 
      return Json(new { result = "" }); 
     } 
     else 
     { 
      return Json(new { result = "Transcoding failed, please try again." }); 
     } 
    } 
    //Action method for retrieving the progress value from the specified log file 
    public ActionResult GetProgress() 
    { 
     string logPath = Server.MapPath("~/videos/") + "transcode.txt"; 
     //Retrive the progress from the specified log file. 
     ... 
     return Json(new { progress = progress }); 
    } 
} 

客戶端:

var progressTimer = null; 
var TranscodeProgress=null; 
//The function that requests server for handling the transcoding process 
function Transcode(vid) { 
    //Calls the Transcode action in VideoController 
    var htmlobj = $.ajax({ 
     url: "/Video/Transcode", 
     type: "POST", 
     //dataType: 'JSON', 
     data: { 'vid': vid }, 
     success: function(data) 
     { 
     if(data.result!="") 
      alert(data.result); 
     } 
     else 
     { 
      //finalization works 
      .... 
     } 
    } 
    }); 
    //Wait for 1 seconds to start retrieving transcoding progress 
    progressTimer=setTimeout(function() 
    { 
     //Display progress bar 
     ... 
     //Set up the procedure of retireving progress every 2 seconds 
     TranscodeProgress = setInterval(Transcoding, 2000); 
    }, 1000); 
} 

//The function that requests the server for retrieving the progress information every 2 seconds. 
function Transcoding() 
{ 
    //Calls the GetProgress action in VideoController 
    $.ajax({ 
     url: "/Video/GetProgress", 
    type: "POST", 
    //dataType: 'JSON', 
    success: function (data) 
    { 
     if (data.progress == undefined || data.progress == null) 
      return; 
     progressPerc = parseFloat(data.progress); 
     //Update progress bar 
     ... 
    } 
    }); 
} 

現在的客戶端代碼和Transcode行動方法一切正常。問題是,GetProgress方法將永遠不會被調用,直到Transcode操作完成其整個過程。那麼我的代碼有什麼問題?我如何修改它以使這兩個動作自發地工作以實現我的目標?

更新:

根據Alex的答案,我發現我的問題是由Asp.Net框架的會話鎖定機制造成的。因此,禁用VideoControllerSessionState或將其設置爲只讀會使控制器響應轉碼視頻操作方法執行時檢索轉碼進度的請求。但是因爲我在我的VideoController中使用Session來存儲一些變量以供跨多個請求使用,所以這種方式不適合我的問題。有沒有更好的方法來解決它?

+0

伊萬,你應該接受@AlexArt。的答案,因爲它解決了你的問題 – Menahem

+0

@Menahem當然,我做到了。但我仍然需要爲我的問題找到最佳解決方案。 – Ivan

回答

7

你誤解了關於異步/等待的觀點。它不會改變這樣的事實:對於每個單個請求,都會返回一個響應。當你在你的行動中呼叫等待時,沒有任何東西返回給客戶端。它所做的唯一事情(在一個非常高層次的抽象中)是將處理這個請求的當前線程釋放到線程池,以便它可以用於處理其他請求。所以基本上它可以讓你更有效地使用你的服務器資源,因爲沒有線程被浪費等待長I/O操作完成。一旦I/O操作完成,繼續執行該操作(即要求等待)。只有在行動結束時,響應纔會發送給客戶。

至於你的情況,如果它是一個長期運行的任務,我會使用某種後臺處理解決方案,如Hangfire,並使用SignalR從服務器推送更新。Here is an example

你也可以自己實現類似的東西(example)。

UPDATE: 正如@Menahem在他的評論中指出的,我可能會誤解你的部分問題。

請求排隊問題可能是由於SessionStateBehavior的錯誤配置引起的。由於ASP.NET MVC使用的MvcHandler處理程序標有IRequiresSessionState接口,因此每個會話只能處理一個請求。爲了改變這種情況,讓你的控制器保持無時間(或至少確保你沒有在這個控制器中寫入會話)並用 [SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)]屬性標記它。

+0

你可能對OP有什麼不對,但要注意的是,他在客戶端上使用Ajax來實現2個請求的並行處理。所以這不是爲什麼他的請求正在排隊的答案。 – Menahem

+0

@Menahem,我明白你的觀點。將相應地更新答案 –

+0

我通過在'VideoController'上面放置'[SessionState(SessionStateBehavior.Disabled)]'來測試它,當'Transcode'動作正在進行時''GetProgress''確實會被調用。所以我得到了導致我的問題的原因。但是,當我使用'TempData'在我的'VideoController'內部使用'Session'來存儲一些我需要跨請求使用的變量時,將整個控制器設置爲只讀到'Session'可能不適合我的情況。 – Ivan

1

文件創建正在阻止呼叫。換句話說,直到第一個線程不會關閉文件,第二個報告將不能讀取該文件的內容。作爲解決方法,您可以創建具有進度百分比的文件。例如movie1-5-percent.txt,movie1-10-percent.txt,movie1-15-percent.txt等,以避免阻止對文件系統的調用。然後你可以檢查,如果對於movie1有文件movie1-15-percent.txt,那麼你可以報告給ajax調用,那15%的電影已經被轉換。或者選擇另一個非阻塞存儲。例如,您可以在第一個線程中將結果報告給db,並在另一個線程中從db讀取結果。

相關問題