Реализация Mutex в контроллере IIS asyn c? - PullRequest
0 голосов
/ 19 февраля 2020

Я написал веб-сервис, который является Asp. Net MVC приложением, размещенным в IIS. Данные из веб-службы извлекаются не из базы данных, а с другого сервера, доступ к которому осуществляется через TCP. Мы будем называть это сервером данных. Может быть несколько серверов данных, к которым подключается веб-служба.

Для обсуждения пользователь аутентифицируется, указав имя пользователя и пароль, после чего с соответствующим сервером данных создается сеанс с сокетом. Предполагая, что все хорошо - мы поддерживаем Socket в рабочем состоянии и передаем пользователю токен, который идентифицирует сеанс, к которому он принадлежит.

Для каждого сокета мне нужно предотвратить, чтобы traffi c не прерывал друг друга. Я предполагаю, что лучший способ сделать это - запустить сетевой трафик c на Socket в последовательной форме. Я добился этого с помощью блокировки. Мой код для выполнения запроса на этом сервере данных следующий. У меня проблема в том, что время от времени кажется, что я получаю два запроса, которые сталкиваются, и один может зависнуть. Я вижу, как поступает запрос, но похоже, что он застрял в замке. Безопасен ли механизм блокировки для вызовов IIS asyn c? Могу ли я установить инструментарий, чтобы убедиться, что это действительно ошибка? Я долго искал, но не могу найти руководство для этого конкретного сценария.

 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 Ответ

0 голосов
/ 19 февраля 2020

1: хостинг в IIS - это что-то вроде абсурда?

Нет. Понимаете, dotnetcore и современные разработки используют SignalR, основанный на WebSockets, для подобных вещей - и он размещен в IIS, так что это НЕ абсолютно абсурдно.

2: Мы не можем ответить на этот вопрос. Часть IO тривиальна - IIS может справиться с этим. Но без подробностей о сервере данных - понятия не имею. Как правило, вы хотите как можно больше избегать блокировок, но это не означает полностью. MRSV (несколько считывателей, один писатель), копирование на запись et c. может помочь свести к минимуму количество записей, но в конце вам понадобится НЕКОТОРЫЕ блокировки. Отлаживать это НЕ весело, но это то, за что люди, которым платят большие деньги.

Общие приложения избегают всего этого, снимая блокировку с базы данных на бэкэнде - которая тратит МНОГИЕ разы люди, оптимизирующие блокировку данных. Если вы не можете этого сделать (помните, мы вообще не знаем, что делает ваш сервер данных внутри) - добро пожаловать в аппаратное программирование.

Наилучший шанс отладки - попытаться найти случай повторения, а затем - пока он застрял - подключи отладчик. Подобные вещи, как известно, сложно отлаживать, но, опять же, это похоже на самый верхний уровень обычного программирования (исключая определенные аппаратные средства и особые задачи).

...