简体   繁体   English

IIS 异步控制器操作中的互斥体实现?

[英]Mutex implementation within IIS async controller action?

I have written a web service that's an Asp.Net MVC application hosted in IIS.我编写了一个 Web 服务,它是一个托管在 IIS 中的 Asp.Net MVC 应用程序。 The data from the web service isn't retrieved from a database but from another server that is accessed via TCP.来自 Web 服务的数据不是从数据库中检索的,而是从通过 TCP 访问的另一台服务器检索的。 We'll call this the Data Server.我们将其称为数据服务器。 There can be multiple Data Servers that the web service connects to. Web 服务可以连接到多个数据服务器。

For the sake of discussion, a user authenticates by specifying a username and password then a "Session" with a Socket is created with the appropriate Data Server.为便于讨论,用户通过指定用户名和密码进行身份验证,然后使用适当的数据服务器创建带有套接字的“会话”。 Assuming everything is good - we keep the Socket alive and pass the user a token that identifies the Session they belong to.假设一切正常 - 我们保持 Socket 处于活动状态并向用户传递标识他们所属会话的令牌。

For each Socket I need to prevent traffic from interrupting each other.对于每个套接字,我需要防止流量相互中断。 I assume that the best way to do that is to run the network traffic on a Socket in a serialized manner.我认为最好的方法是以序列化的方式在 Socket 上运行网络流量。 I have achieved this by using a lock.我通过使用锁实现了这一点。 My code to execute a query on this Data Server follows.我在此数据服务器上执行查询的代码如下。 The problem I'm having is that once in a while it appears that I'm getting two queries that collide and one may hang.我遇到的问题是,有时我会遇到两个冲突的查询,一个可能会挂起。 I see a query come in but it looks like it gets stuck at the lock.我看到一个查询进来,但看起来它卡在了锁上。 Is the lock mechanism safe for IIS async calls? IIS 异步调用的锁定机制是否安全? Is there instrumentation I can put in to make sure this is actually the bug?是否有我可以插入的工具来确保这实际上是错误? I've been looking for a while but I can't find guidance for this particular scenario.我一直在寻找一段时间,但找不到针对此特定场景的指导。

 private async Task<string> query(string request, AbstractPermission context = null, bool bUpdateDateTime = true)
        {
            try
            {
                string reply = "";
                isErrorMsg = false;

                //this lock prevents the keep alive thread from coming in here while we're on the socket
                lock (socketLock)
                {
                    sendDone.Reset();
                    receiveDone.Reset();

                    // Send test data to the remote device.  
                    Send(Socket, request);
                    sendDone.WaitOne();

                    // Receive the response from the remote device.
                    Receive(Socket);
                    receiveDone.WaitOne();

                    reply = QueryResponse;

                }  //done reading - let's unlock

                if (bUpdateDateTime)
                {
                    this.LastUsed = DateTime.Now;
                }

                return QueryResponse;
            }
            catch (Exception e)
            {
                throw e;
            }
        }


  private void Send(Socket client, String data)
        {
            byte[] byteData = Encoding.ASCII.GetBytes(data);

            client.BeginSend(byteData, 0, byteData.Length, 0,
                new AsyncCallback(SendCallback), client);
        }

        private void SendCallback(IAsyncResult ar)
        {
            try
            {
                // Retrieve the socket from the state object.  
                Socket client = (Socket)ar.AsyncState;

                // Complete sending the data to the remote device.  
                int bytesSent = client.EndSend(ar);

                // Signal that all bytes have been sent.  
                sendDone.Set();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }


        private void Receive(Socket client)
        {
            try
            {
                // Create the state object.  
                StateObject state = new StateObject();
                state.workSocket = client;
                state.PostInitialRead = false;

                // Begin receiving the data from the remote device.  
                client.BeginReceive(state.buffer, 0, StateObject.BufferSize, SocketFlags.None,
                    new AsyncCallback(ReceiveCallback), state);
            }
            catch (Exception e)
            {
                LogError(e);
            }
        }

        private void ReceiveCallback(IAsyncResult ar)
        {
            try
            {
                // Retrieve the state object and the client socket   
                // from the asynchronous state object.  
                StateObject state = (StateObject)ar.AsyncState;
                Socket client = state.workSocket;
                bool PostInitialRead = state.PostInitialRead;

                // Read data from the remote device.  
                int bytesRead = client.EndReceive(ar);

                //
                //
                var thisBatch = Encoding.ASCII.GetString(state.buffer, 0, bytesRead);
                var endIdx = thisBatch.IndexOf('\x04');

                if (!PostInitialRead)
                {
                    if (bytesRead == 0)
                        throw new ApplicationException("Timeout waiting for response");

                    if (endIdx != -1)
                    {
                        thisBatch = thisBatch.Substring(0, endIdx);
                    }
                    if (state.buffer[0] != 0)
                    {
                        thisBatch = thisBatch.Substring(1, state.buffer[0]);
                        isErrorMsg = true;
                    }
                    else if (state.buffer[1] != 0)
                    {
                        thisBatch = thisBatch.Substring(2);
                        isErrorMsg = true;
                    }
                    else
                    {
                        thisBatch = thisBatch.Substring(2);
                    }
                    state.sb.Append(thisBatch);
                    state.PostInitialRead = true;
                }
                else
                {
                    state.ms.Write(state.buffer, 0, endIdx!=-1?endIdx:bytesRead);
                }

                if (endIdx != -1)
                {
                    // Got everything
                    state.sb.Append(Encoding.ASCII.GetString(state.ms.ToArray()));
                    QueryResponse = state.sb.ToString();
                    receiveDone.Set();
                    return;
                }
                else
                {
                    // Get the rest of the data.  
                    client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                        new AsyncCallback(ReceiveCallback), state);
                }

            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }



1: Is hosting something like this within IIS completely absurd? 1:在 IIS 中托管这样的东西是完全荒谬的吗?

No. See, dotnetcore and modern development uses SignalR based on WebSockets for stuff like that - and it is hosted in IIS, so it is NOT completely absurd.不。看,dotnetcore 和现代开发使用基于 WebSockets 的 SignalR 来处理类似的事情 - 它托管在 IIS 中,所以它并不完全荒谬。

2: We can not really answer that. 2:我们不能真正回答那个。 The IO part is trivial - IIS can handle this. IO 部分是微不足道的 - IIS 可以处理这个。 But without details on the data server - no idea.但是没有关于数据服务器的详细信息 - 不知道。 YOu generally want to avoid locks as much as possible, but that does not mean totally.您通常希望尽可能避免锁定,但这并不意味着完全。 MRSV (multiple Reader, Single Writer), copy on write etc. can help minimizing writes, but at the end you will need SOME locking. MRSV (multiple Reader, Single Writer),写时复制等可以帮助最小化写,但最后你需要一些锁定。 It is NOT fun debugging this, but that is what people doing that get paid big bucks for.调试这个并不有趣,但这就是人们所做的得到大笔报酬的事情。

General applicatiosn avoid all that by offloading the locking to the database at the backend - which spends a LOT of time by many people optimizing locking on data.一般应用程序通过将锁定卸载到后端的数据库来避免所有这些 - 许多人花费大量时间优化数据锁定。 If you can not do that (remember, we do not know at all what your data server does internally) - welcome to the hard programming.如果您不能这样做(请记住,我们根本不知道您的数据服务器在内部做什么)- 欢迎使用硬编程。

Your best chance for debugging is trying to find a repro case and then - while it is stuck - attach a debugger.调试的最佳机会是尝试找到一个重现案例,然后 - 当它被卡住时 - 附加一个调试器。 Stuff like that is NOTORIOUSLY hard to debug - but again, this is like the topmost level of regular programming (leaving out certain hardware and very special tasks).像这样的东西很难调试 - 但同样,这就像常规编程的最高级别(忽略某些硬件和非常特殊的任务)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM