---
# System prepended metadata

title: How to Allow Concurrent Requests PerSession
tags: [併發處理, Session]

---

# How to Allow Concurrent Requests PerSession

## 研究起因
發送 request A 後端進行等待(await等方法)，此時相同使用者發送request B 後端應要直接response，但卻發現需要等A進行回應才會回應B。

## 如何發現跟Session有關
在原有專案撰寫簡單sample code，測試可以正常response不需等await結束，但相同程式碼更新至測試站從測試入口跳轉該頁面卻發現需要等await。

![](https://i.imgur.com/5L967uU.png)

程式相同卻有不同結果所以首先先懷疑了
1. **機器狀態**: 是否因為機器較為忙碌導致response 速度被影響，架了一個乾淨的站台並在==只有一個client的只發送兩個request的情況依舊為必發==。
2. **瀏覽器問題**: 是否瀏覽器偷偷調整了[maximum TCP 數量](https://stackoverflow.com/questions/29206067/understanding-chrome-network-log-stalled-state/29564247)，導致發送的請求超過上限需等待，架了一個乾淨的站台並發送數個不須等待的request以chrome為例可以發送到6個。
3. **IIS 設置**: 檢查了下發現測試站及RD機設置皆相同，但最底層的OS不同 一個是 window server 2012、一個是2016..，查找了兩者差異後決定先暫時放棄往這邊探索
4. **操作步驟是否影響**: 反覆測試了下操作步驟後忽然發現若RD機的網頁從測試入口登入一樣會有不支援併發request的狀況，比較了下兩者差異程式碼一步步刪減後發現是Session!! 當TestLogin ==有對 session進行寫入的狀況就會導致無法進行併發request==

## 參考解法
### 調整設定檔
1. 設定 webconfig:
    ```
    <appSettings>
        <add key="aspnet:AllowConcurrentRequestsPerSession" value="true" />
        <add key="aspnet:RequestQueueLimitPerSession" value="2147483647"/>
    </appSettings>
    ```
2. 發送 request 的頁面設定參數
    ```
    <%@ Page EnableSessionState="False"%>
    ```
但以上方法測試後發現皆沒有效果...

### 清除session(需將cookie內的ASP.NET_SessionId 也清除)
從前面的測試發現只要曾經寫入到Session內就會造成request無法進行併發的狀態，原以為清空Session即是最暴力但一定可行的方式，程式碼如下:
```C#
//清除 剛剛新增的session
Session.Remove("剛剛新增的Session");
```
上述操作無效，於是那在更暴力直接Abandon一樣不行，程式碼如下:
```C#
//清除 session
Session.Abandon();
```
最後測試結果發現==需將cookie內的ASP.NET_SessionId 進行清空==才可以不被之前執行寫入Session的動作影響，程式碼如下:
```C#
//清除 session
Session.Abandon();
//清除 cookie內的sessionId
Response.Cookies.Add(new HttpCookie("ASP.NET_SessionId", ""));
```
不過此行為會造成跳轉頁面的時候產生新的SessionId，所以正常應該是無法這樣解除非網頁驗證登入狀態都不靠SessionId了。

另外若是想用JS直接改cookie內的ASP.NET_SessionId也是不行的，因為是預設HttpOnly所以Cookie只限被伺服端存取，無法在用戶端讀取。

### Session的讀寫改使用WebAPI進行
[詳細架設方式請參考此連結](https://hackmd.io/zK04Jjg7QSCTXGWelg5cxA)

此作法是想藉由WEB頁面本身都不寫入Session的方式，統一將Session值寫在WEBAPI內當需要相對應的資料就呼叫API，但此作法需考量

## 結論
目前測是唯一有效的方式就是，專案不進行Session的寫入處理，或是在乾脆一點有await的處理都不再WEB端做，不過這應該沒什麼機會QQ

## 補充
### sample code
#### 前端: 兩顆按鈕一個發送會進行await的request，一個不等待。
```htmlmixed
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="TestAwait.aspx.cs" Inherits="BB_v3.bbView.TestAwait"%>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title></title>
</head>
<body>
    <h1>簡易測試</h1>
    <h2>請開F12 看console 有 response 會直接印</h2>
    <div id="resultsEasy"></div>
    <div>
    <button type="button" style="width:30%;height:10%;font-size:20px;" onclick="AjaxAwait()">發送ajax請求至await</button>
    <button type="button" style="width:30%;height:10%;font-size:20px;" onclick="AjaxNotAwait()">發送ajax請求至notAwait</button>
    </div>
    <script src="/js/jquery-2.1.4.min.js" type="text/javascript"></script>
    <script type="text/javascript">
        function AjaxAwait() {
            $.ajax({
                type: 'POST',
                url: '/ajax/TestAwait.aspx',
                data: { method: 'NeedAwait'},
                dataType: 'json',
                error: function (e) {
                    console.log(e.responseText);
                },
                success: function (data) {
                    console.log(data)
                }
            });
        }

        function AjaxNotAwait() {
            $.ajax({
                type: 'POST',
                url: '/ajax/TestAwait.aspx',
                data: { method: 'NotAwait' },
                dataType: 'json',
                error: function (e) {
                    console.log(e.responseText);
                },
                success: function (data) {
                    console.log(data)
                }
            });
        }
    </script>
</body>
</html>
```

#### 後端
```C#
using System;
using System.Threading.Tasks;

public partial class TestAwait : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        string method = Request.Form["method"];

        switch (method)
        {
            case "NeedAwait":
                NeedAwait();
                break;
            case "NotAwait":
                NotAwait();
                break;
            default:
                break;
        }
    }

    public async void NeedAwait()
    {
        //進行延遲
        await Task.Delay(5 * 1000);
        Response.Write("NeedAwait 運作完成");
    }

    public void NotAwait()
    {
        Response.Write("NotAwait 運作完成");
    }
}
```

###### tags: `Session` `併發處理`