两个对Action方法的并行ajax请求排队,为什么?

本文关键字:请求 ajax 并行 排队 为什么 方法 两个 Action | 更新日期: 2023-09-27 18:16:43

我正在使用ASP开发一个视频网站。净MVC。

我想在我的应用程序中有一个功能是传输视频。但是,由于转码过程可能非常耗时,因此我想向客户端用户显示该过程的进度。

所以,我的模式是使用一个控制器动作来处理整个转码过程,并将其进度写入存储在服务器上的文件。同时,我使用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 retrieving 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的会话锁定机制引起的。净框架。因此,禁用VideoControllerSessionState或将其设置为只读,确实会使控制器在执行视频转码的动作方法时响应检索转码进度的请求。但是因为我在VideoController中使用Session来存储跨多个请求使用的一些变量,所以这种方式不可能是我问题的合适解决方案。有更好的解决方法吗?

两个对Action方法的并行ajax请求排队,为什么?

你误解了async/await的全部意义。它不会改变这样一个事实,即对于每个请求,只返回一个响应。当你在操作中调用await时,还没有任何东西返回给客户端。它所做的唯一一件事(在非常高级的抽象中)是将处理此请求的当前线程释放到线程池中,以便它可以用于处理其他请求。因此,基本上它允许您更有效地使用服务器资源,因为没有线程浪费等待长时间的I/O操作完成。一旦I/O操作完成,操作(调用await)的执行将继续。只有在操作结束时,响应才会被发送到客户端。

至于你的场景,如果它是一个长时间运行的任务,我会使用某种后台处理解决方案,如Hangfire,并使用SignalR从服务器推送更新。下面是一个例子

您也可以自己实现类似的东西(示例)。

:正如@Menahem在他的评论中所说,我可能误解了你的部分问题。

请求排队问题可能是由于SessionStateBehavior配置错误引起的。由于MvcHandler处理程序是由ASP使用的。. NET MVC用IRequiresSessionState接口标记,每个会话一次只能处理一个请求。为了改变这一点,让你的控制器无会话(或者至少确保你没有在这个控制器中写入会话)并标记它[SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)]属性。

文件创建阻塞调用。换句话说,直到第一个线程不会关闭文件,第二个生成报告的线程将无法读取该文件的内容。作为变通方法,您可以创建带有进度百分比的文件。例如movie1- 5% .txt, movie1- 10% .txt, movie1- 15% .txt等,以避免阻塞对文件系统的调用。然后您可以检查,如果movie1有文件movie1- 15% .txt,那么您可以向ajax调用报告,15%的movie被转换了。或者选择另一种非阻塞存储。例如,您可以在第一个线程中向db报告结果,并在另一个线程中从db读取结果。