-
Notifications
You must be signed in to change notification settings - Fork 54
Марченко Максим ДЗ 2 #55
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,7 @@ | ||
| using System; | ||
| using System.Collections.Concurrent; | ||
| using System.Collections.Generic; | ||
| using System.Diagnostics; | ||
| using System.Linq; | ||
| using System.Text; | ||
| using System.Threading.Tasks; | ||
|
|
@@ -9,15 +11,52 @@ namespace ClusterClient.Clients | |
| { | ||
| public class RoundRobinClusterClient : ClusterClientBase | ||
| { | ||
| private ConcurrentDictionary<string, TimeSpan> _stats; | ||
|
|
||
| public RoundRobinClusterClient(string[] replicaAddresses) : base(replicaAddresses) | ||
| { | ||
| ReplicaAddresses = replicaAddresses; | ||
| _stats = new (); | ||
| foreach (var addr in replicaAddresses) | ||
| { | ||
| _stats.TryAdd(addr, TimeSpan.MaxValue); | ||
| } | ||
| } | ||
|
|
||
| public override Task<string> ProcessRequestAsync(string query, TimeSpan timeout) | ||
| public override async Task<string> ProcessRequestAsync(string query, TimeSpan timeout) | ||
| { | ||
| throw new NotImplementedException(); | ||
| } | ||
| var orderedReplicas = ReplicaAddresses | ||
| .OrderBy(addr => _stats.GetValueOrDefault(addr, TimeSpan.MaxValue)) | ||
| .ToArray(); | ||
|
|
||
| for (int i = 0; i < ReplicaAddresses.Length; i++) | ||
| { | ||
| var replicaTimeout = timeout / (ReplicaAddresses.Length - i); | ||
| var address = orderedReplicas[i]; | ||
| var task = CallRequest(address, query); | ||
| var timeoutTask = Task.Delay(replicaTimeout); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. timeout и replicaTimeout могут оказаться отрицательными. Task.Delay может или выбросить исключение, или, с маленьким шансом, превратиться в никогда не заканчивающуюся таску |
||
| var sw = Stopwatch.StartNew(); | ||
| var completedTask = await Task.WhenAny(task, timeoutTask); | ||
|
|
||
| sw.Stop(); | ||
| _stats[address] = sw.Elapsed; | ||
| timeout -= sw.Elapsed; | ||
| if (completedTask == timeoutTask) | ||
| { | ||
| continue; | ||
| } | ||
|
|
||
| try | ||
| { | ||
| return await task; | ||
| } | ||
| catch | ||
| { | ||
| _stats[address] = TimeSpan.MaxValue; | ||
| } | ||
| } | ||
| throw new TimeoutException(); | ||
| } | ||
| protected override ILog Log => LogManager.GetLogger(typeof(RoundRobinClusterClient)); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,7 @@ | ||
| using System; | ||
| using System.Collections.Concurrent; | ||
| using System.Collections.Generic; | ||
| using System.Diagnostics; | ||
| using System.Linq; | ||
| using System.Text; | ||
| using System.Threading.Tasks; | ||
|
|
@@ -9,13 +11,58 @@ namespace ClusterClient.Clients | |
| { | ||
| public class SmartClusterClient : ClusterClientBase | ||
| { | ||
| private ConcurrentDictionary<string, TimeSpan> _stats; | ||
|
|
||
| public SmartClusterClient(string[] replicaAddresses) : base(replicaAddresses) | ||
| { | ||
| ReplicaAddresses = replicaAddresses; | ||
| _stats = new (); | ||
| foreach (var addr in replicaAddresses) | ||
| { | ||
| _stats.TryAdd(addr, TimeSpan.MaxValue); | ||
| } | ||
| } | ||
|
|
||
| public override Task<string> ProcessRequestAsync(string query, TimeSpan timeout) | ||
| public override async Task<string> ProcessRequestAsync(string query, TimeSpan timeout) | ||
| { | ||
| throw new NotImplementedException(); | ||
| var runningTasks = new Dictionary<Task<string>, string>(); | ||
|
|
||
| var orderedReplicas = ReplicaAddresses | ||
| .OrderBy(addr => _stats.GetValueOrDefault(addr, TimeSpan.MaxValue)) | ||
| .ToArray(); | ||
|
|
||
| for (int i = 0; i < ReplicaAddresses.Length; i++) | ||
| { | ||
| var replicaTimeout = timeout / (ReplicaAddresses.Length - i); | ||
| var address = orderedReplicas[i]; | ||
| var task = CallRequest(address, query); | ||
| var timeoutTask = Task.Delay(replicaTimeout).ContinueWith<string>(_ => throw new TimeoutException()); | ||
| runningTasks.Add(task, address); | ||
| runningTasks.Add(timeoutTask, "timeout"); | ||
| var sw = Stopwatch.StartNew(); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Здесь timeoutTask и Stopwatch тоже лучше перед CallRequest делать |
||
| var completedTask = await Task.WhenAny(runningTasks.Keys); | ||
| sw.Stop(); | ||
|
|
||
| runningTasks.Remove(timeoutTask); | ||
|
|
||
| timeout -= sw.Elapsed; | ||
| if (completedTask == timeoutTask) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Если реплики будут тормозить, то соответствующие таски так и останутся в runningTasks, и статистика для них не обновится |
||
| { | ||
| continue; | ||
| } | ||
|
|
||
| try | ||
| { | ||
| _stats[runningTasks[completedTask]] = sw.Elapsed; | ||
| return await completedTask; | ||
| } | ||
| catch | ||
| { | ||
| _stats[runningTasks[completedTask]] = TimeSpan.MaxValue; | ||
| } | ||
| runningTasks.Remove(completedTask); | ||
| } | ||
| throw new TimeoutException(); | ||
| } | ||
|
|
||
| protected override ILog Log => LogManager.GetLogger(typeof(SmartClusterClient)); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
По-хорошему timeoutTask бы создать до всех CallRequest. Иначе выходит, что запросы уже какое-то время как отправлены