我有一个特定的要求,即仅允许5个用户同时访问WebAPI(特定于.NET 4.5)(作为许可计划的一部分)。当达到5个并发限制时,webapi应该拒绝来自 ANY 用户的任何进一步尝试。
我创建了一个Singleton类来计算方法的访问次数,并在处理完成后减少计数器的数量,并检查count> 5。
[HttpGet]
[Route("GetOrderStatus/{id}")]
public async Task<string> GetOrderStatus(int id)
{
GlobalState state = GlobalState.Instance;
string result;
if (state.OrderStatus > 5)
{
Log.Debug("INVALID - state.OrderStatus: " + state.OrderStatus.ToString() + ", ID: " + id.ToString());
return await Task.FromResult("INVALID");
}
else
{
state.OrderStatus++;
Log.Debug("state.OrderStatus: " + state.OrderStatus.ToString() + ", ID: " + id.ToString());
result = DoGetOrder(id);
state.OrderStatus--;
}
return await Task.FromResult(result);
}
private string DoGetOrder(int OrderTypeId)
{
Thread.Sleep(5000);
return "OK_" + OrderTypeId.ToString();
}
我的单例课程在下面。
public sealed class GlobalState
{
private static readonly Lazy<GlobalState> lazy = new Lazy<GlobalState>(() => new GlobalState());
public int OrderStatus { get; set; }
public int PlaceOrder { get; set; }
public static GlobalState Instance { get { return lazy.Value; } }
private GlobalState()
{
}
}
单元测试。
using Flurl.Http;
[TestMethod]
public void GetOrderStatus_Returns_Valid()
{
StringBuilder sb = new StringBuilder();
int success = 0;
int failed = 0;
Parallel.For(0, 100, i =>
{
string url = "http://localhost:61803/api/getorderstatus/" + i.ToString();
var responseString = url.GetStringAsync().Result;
sb.AppendLine(responseString + ", ");
if (responseString.Contains("INVALID"))
{
failed++;
}
else
{
success++;
}
});
}
测试结果出错。日志复制如下。
2018-11-26 10:00:29.431 +00:00 [DBG] state.OrderStatus: 2, ID: 0
2018-11-26 10:00:29.432 +00:00 [DBG] state.OrderStatus: 3, ID: 25
2018-11-26 10:00:29.434 +00:00 [DBG] state.OrderStatus: 4, ID: 50
2018-11-26 10:00:30.310 +00:00 [DBG] state.OrderStatus: 5, ID: 75
2018-11-26 10:00:31.311 +00:00 [DBG] state.OrderStatus: 6, ID: 1
2018-11-26 10:00:32.313 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 26
2018-11-26 10:00:33.311 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 27
2018-11-26 10:00:33.312 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 51
2018-11-26 10:00:34.308 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 28
2018-11-26 10:00:34.308 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 52
2018-11-26 10:00:34.310 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 76
2018-11-26 10:00:35.309 +00:00 [DBG] state.OrderStatus: 4, ID: 3
2018-11-26 10:00:35.309 +00:00 [DBG] state.OrderStatus: 5, ID: 34
2018-11-26 10:00:35.309 +00:00 [DBG] state.OrderStatus: 4, ID: 30
2018-11-26 10:00:36.309 +00:00 [DBG] state.OrderStatus: 5, ID: 54
2018-11-26 10:00:36.309 +00:00 [DBG] state.OrderStatus: 5, ID: 79
2018-11-26 10:00:36.309 +00:00 [DBG] state.OrderStatus: 6, ID: 77
2018-11-26 10:00:36.309 +00:00 [DBG] state.OrderStatus: 6, ID: 53
2018-11-26 10:00:36.311 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 2
2018-11-26 10:00:36.321 +00:00 [DBG] state.OrderStatus: 6, ID: 29
2018-11-26 10:00:37.309 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 5
2018-11-26 10:00:37.309 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 56
2018-11-26 10:00:37.311 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 7
2018-11-26 10:00:38.312 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 8
2018-11-26 10:00:39.310 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 57
2018-11-26 10:00:39.310 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 6
2018-11-26 10:00:39.310 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 10
2018-11-26 10:00:39.311 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 81
2018-11-26 10:00:39.312 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 9
2018-11-26 10:00:40.355 +00:00 [DBG] state.OrderStatus: 4, ID: 14
2018-11-26 10:00:40.356 +00:00 [DBG] state.OrderStatus: 5, ID: 4
2018-11-26 10:00:40.357 +00:00 [DBG] state.OrderStatus: 6, ID: 35
2018-11-26 10:00:40.357 +00:00 [DBG] state.OrderStatus: 6, ID: 36
2018-11-26 10:00:40.357 +00:00 [DBG] state.OrderStatus: 6, ID: 58
2018-11-26 10:00:40.359 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 18
2018-11-26 10:00:42.310 +00:00 [DBG] state.OrderStatus: 3, ID: 78
2018-11-26 10:00:42.310 +00:00 [DBG] state.OrderStatus: 3, ID: 84
2018-11-26 10:00:42.310 +00:00 [DBG] state.OrderStatus: 2, ID: 59
2018-11-26 10:00:42.311 +00:00 [DBG] state.OrderStatus: 4, ID: 31
2018-11-26 10:00:42.311 +00:00 [DBG] state.OrderStatus: 4, ID: 55
2018-11-26 10:00:42.313 +00:00 [DBG] state.OrderStatus: 5, ID: 37
2018-11-26 10:00:42.313 +00:00 [DBG] state.OrderStatus: 6, ID: 80
2018-11-26 10:00:43.313 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 11
2018-11-26 10:00:44.310 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 20
2018-11-26 10:00:44.310 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 12
2018-11-26 10:00:45.310 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 60
2018-11-26 10:00:45.310 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 13
2018-11-26 10:00:45.312 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 21
2018-11-26 10:00:45.312 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 19
2018-11-26 10:00:45.318 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 82
2018-11-26 10:00:45.320 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 39
2018-11-26 10:00:46.313 +00:00 [DBG] state.OrderStatus: 3, ID: 46
2018-11-26 10:00:46.313 +00:00 [DBG] state.OrderStatus: 4, ID: 64
2018-11-26 10:00:46.313 +00:00 [DBG] state.OrderStatus: 3, ID: 69
2018-11-26 10:00:46.314 +00:00 [DBG] state.OrderStatus: 5, ID: 22
2018-11-26 10:00:46.315 +00:00 [DBG] state.OrderStatus: 6, ID: 71
2018-11-26 10:00:46.315 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 61
2018-11-26 10:00:46.315 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 24
2018-11-26 10:00:46.315 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 44
2018-11-26 10:00:46.317 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 15
2018-11-26 10:00:46.321 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 85
2018-11-26 10:00:46.324 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 62
2018-11-26 10:00:46.324 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 87
2018-11-26 10:00:46.325 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 45
2018-11-26 10:00:46.327 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 16
2018-11-26 10:00:46.331 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 88
2018-11-26 10:00:46.331 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 65
2018-11-26 10:00:46.333 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 83
2018-11-26 10:00:46.336 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 40
2018-11-26 10:00:46.338 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 86
2018-11-26 10:00:46.338 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 23
2018-11-26 10:00:46.343 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 66
2018-11-26 10:00:46.345 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 63
2018-11-26 10:00:46.345 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 89
2018-11-26 10:00:46.345 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 17
2018-11-26 10:00:46.351 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 41
2018-11-26 10:00:46.352 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 67
2018-11-26 10:00:46.353 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 90
2018-11-26 10:00:46.355 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 42
2018-11-26 10:00:46.359 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 43
2018-11-26 10:00:46.359 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 68
2018-11-26 10:00:46.362 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 91
2018-11-26 10:00:46.365 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 92
2018-11-26 10:00:46.368 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 93
2018-11-26 10:00:46.371 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 94
2018-11-26 10:00:46.374 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 95
2018-11-26 10:00:46.377 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 96
2018-11-26 10:00:46.380 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 97
2018-11-26 10:00:46.383 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 98
2018-11-26 10:00:46.386 +00:00 [DBG] INVALID - state.OrderStatus: 6, ID: 99
2018-11-26 10:00:47.321 +00:00 [DBG] state.OrderStatus: 1, ID: 38
2018-11-26 10:00:47.321 +00:00 [DBG] state.OrderStatus: 1, ID: 32
2018-11-26 10:00:51.323 +00:00 [DBG] state.OrderStatus: -1, ID: 47
2018-11-26 10:00:51.323 +00:00 [DBG] state.OrderStatus: -2, ID: 72
2018-11-26 10:00:51.324 +00:00 [DBG] state.OrderStatus: 0, ID: 70
2018-11-26 10:00:52.326 +00:00 [DBG] state.OrderStatus: 0, ID: 33
2018-11-26 10:00:56.327 +00:00 [DBG] state.OrderStatus: -1, ID: 73
2018-11-26 10:00:56.331 +00:00 [DBG] state.OrderStatus: 0, ID: 48
2018-11-26 10:01:01.331 +00:00 [DBG] state.OrderStatus: -1, ID: 74
2018-11-26 10:01:01.335 +00:00 [DBG] state.OrderStatus: -1, ID: 49
为了成功和无效,state.OrderStatus值将变为“ 6”,并且在日志中也将变为负值,并且始终不一致。
请告知我我做错了。
答案 0 :(得分:1)
这只是一个并发问题。可以通过多个线程将if (state.OrderStatus > 5)
评估为false,然后它们都使用state.OrderStatus++;
递增(它本身具有读写问题)。
解决方案是对此类脚本使用Interlocked.Increment
(请注意,state.OrderStatus
应该是一个字段,以便编译此代码,而不是属性):
int current = Interlocked.Increment(ref state.OrderStatus);
try
{
if (current > 5)
{
Log.Debug("INVALID - state.OrderStatus: " + state.OrderStatus.ToString() + ", ID: " + id.ToString());
return await Task.FromResult("INVALID");
}
else
{
Log.Debug("state.OrderStatus: " + state.OrderStatus.ToString() + ", ID: " + id.ToString());
result = DoGetOrder(id);
}
}
finally
{
Interlocked.Decrement(ref state.OrderStatus);
}