# How to Allow Concurrent Requests PerSession
## 研究起因
發送 request A 後端進行等待(await等方法),此時相同使用者發送request B 後端應要直接response,但卻發現需要等A進行回應才會回應B。
## 如何發現跟Session有關
在原有專案撰寫簡單sample code,測試可以正常response不需等await結束,但相同程式碼更新至測試站從測試入口跳轉該頁面卻發現需要等await。

程式相同卻有不同結果所以首先先懷疑了
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` `併發處理`