Browse Source

refactor AvailabilityStatistics to singleton

tags/3.0
icylogic 9 years ago
parent
commit
aef4b86c91
2 changed files with 35 additions and 49 deletions
  1. +32
    -42
      shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs
  2. +3
    -7
      shadowsocks-csharp/Controller/ShadowsocksController.cs

+ 32
- 42
shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs View File

@@ -1,19 +1,13 @@
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Net.Http;
using System.Net.NetworkInformation; using System.Net.NetworkInformation;
using System.Net.Sockets; using System.Net.Sockets;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

using Shadowsocks.Model; using Shadowsocks.Model;
using Shadowsocks.Util; using Shadowsocks.Util;


@@ -24,19 +18,23 @@ namespace Shadowsocks.Controller


using Statistics = Dictionary<string, List<AvailabilityStatistics.RawStatisticsData>>; using Statistics = Dictionary<string, List<AvailabilityStatistics.RawStatisticsData>>;


//TODO: change to singleton
public class AvailabilityStatistics
public sealed class AvailabilityStatistics
{ {
// Static Singleton Initialization
public static AvailabilityStatistics Instance { get; } = new AvailabilityStatistics();
private AvailabilityStatistics() { }

public const string DateTimePattern = "yyyy-MM-dd HH:mm:ss"; public const string DateTimePattern = "yyyy-MM-dd HH:mm:ss";
private const string StatisticsFilesName = "shadowsocks.availability.csv"; private const string StatisticsFilesName = "shadowsocks.availability.csv";
private const string Delimiter = ","; private const string Delimiter = ",";
private const int Timeout = 500;
private readonly TimeSpan DelayBeforeStart = TimeSpan.FromSeconds(1);
private const int TimeoutMilliseconds = 500;
private readonly TimeSpan _delayBeforeStart = TimeSpan.FromSeconds(1);
public Statistics RawStatistics { get; private set; } public Statistics RawStatistics { get; private set; }
public Statistics FilteredStatistics { get; private set; } public Statistics FilteredStatistics { get; private set; }
public static readonly DateTime UnknownDateTime = new DateTime(1970, 1, 1); public static readonly DateTime UnknownDateTime = new DateTime(1970, 1, 1);
private int Repeat => _config.RepeatTimesNum; private int Repeat => _config.RepeatTimesNum;
private readonly TimeSpan RetryInterval = TimeSpan.FromMinutes(2); //retry 2 minutes after failed
private readonly TimeSpan _retryInterval = TimeSpan.FromMinutes(2); //retry 2 minutes after failed
private TimeSpan Interval => TimeSpan.FromMinutes(_config.DataCollectionMinutes); private TimeSpan Interval => TimeSpan.FromMinutes(_config.DataCollectionMinutes);
private Timer _timer; private Timer _timer;
private Timer _speedMonior; private Timer _speedMonior;
@@ -53,10 +51,10 @@ namespace Shadowsocks.Controller
private int? _latency = 0; private int? _latency = 0;
private Server _currentServer; private Server _currentServer;
private Configuration _globalConfig; private Configuration _globalConfig;
private readonly ShadowsocksController _controller;
private ShadowsocksController _controller;
private long _lastInboundCounter = 0; private long _lastInboundCounter = 0;
private long _lastOutboundCounter = 0; private long _lastOutboundCounter = 0;
private readonly TimeSpan MonitorInterval = TimeSpan.FromSeconds(1);
private readonly TimeSpan _monitorInterval = TimeSpan.FromSeconds(1);


//static constructor to initialize every public static fields before refereced //static constructor to initialize every public static fields before refereced
static AvailabilityStatistics() static AvailabilityStatistics()
@@ -64,13 +62,6 @@ namespace Shadowsocks.Controller
AvailabilityStatisticsFile = Utils.GetTempPath(StatisticsFilesName); AvailabilityStatisticsFile = Utils.GetTempPath(StatisticsFilesName);
} }


public AvailabilityStatistics(ShadowsocksController controller)
{
_controller = controller;
_globalConfig = controller.GetCurrentConfiguration();
UpdateConfiguration(_globalConfig, controller.StatisticsConfiguration);
}

public bool Set(StatisticsStrategyConfiguration config) public bool Set(StatisticsStrategyConfiguration config)
{ {
_config = config; _config = config;
@@ -78,10 +69,10 @@ namespace Shadowsocks.Controller
{ {
if (config.StatisticsEnabled) if (config.StatisticsEnabled)
{ {
if (_timer?.Change(DelayBeforeStart, Interval) == null)
if (_timer?.Change(_delayBeforeStart, Interval) == null)
{ {
_state = new State(); _state = new State();
_timer = new Timer(Run, _state, DelayBeforeStart, Interval);
_timer = new Timer(Run, _state, _delayBeforeStart, Interval);
} }
} }
else else
@@ -102,11 +93,11 @@ namespace Shadowsocks.Controller
{ {
var bytes = _controller.inboundCounter - _lastInboundCounter; var bytes = _controller.inboundCounter - _lastInboundCounter;
_lastInboundCounter = _controller.inboundCounter; _lastInboundCounter = _controller.inboundCounter;
var inboundSpeed = GetSpeedInKiBPerSecond(bytes ,MonitorInterval.TotalSeconds);
var inboundSpeed = GetSpeedInKiBPerSecond(bytes ,_monitorInterval.TotalSeconds);


bytes = _controller.outboundCounter - _lastOutboundCounter; bytes = _controller.outboundCounter - _lastOutboundCounter;
_lastOutboundCounter = _controller.outboundCounter; _lastOutboundCounter = _controller.outboundCounter;
var outboundSpeed = GetSpeedInKiBPerSecond(bytes, MonitorInterval.TotalSeconds);
var outboundSpeed = GetSpeedInKiBPerSecond(bytes, _monitorInterval.TotalSeconds);


if (inboundSpeed > _inboundSpeed) if (inboundSpeed > _inboundSpeed)
{ {
@@ -133,7 +124,7 @@ namespace Shadowsocks.Controller
//ICMP echo. we can also set options and special bytes //ICMP echo. we can also set options and special bytes
try try
{ {
var reply = await ping.SendTaskAsync(IP, Timeout);
var reply = await ping.SendTaskAsync(IP, TimeoutMilliseconds);
ret.Add(new List<KeyValuePair<string, string>> ret.Add(new List<KeyValuePair<string, string>>
{ {
new KeyValuePair<string, string>("Timestamp", timestamp), new KeyValuePair<string, string>("Timestamp", timestamp),
@@ -145,7 +136,7 @@ namespace Shadowsocks.Controller
new KeyValuePair<string, string>("OutboundSpeed", GetRecentOutboundSpeed(server)) new KeyValuePair<string, string>("OutboundSpeed", GetRecentOutboundSpeed(server))
//new KeyValuePair<string, string>("data", reply.Buffer.ToString()); // The data of reply //new KeyValuePair<string, string>("data", reply.Buffer.ToString()); // The data of reply
}); });
Thread.Sleep(Timeout + new Random().Next() % Timeout);
Thread.Sleep(TimeoutMilliseconds + new Random().Next() % TimeoutMilliseconds);
//Do ICMPTest in a random frequency //Do ICMPTest in a random frequency
} }
catch (Exception e) catch (Exception e)
@@ -165,14 +156,12 @@ namespace Shadowsocks.Controller


private string GetRecentOutboundSpeed(Server server) private string GetRecentOutboundSpeed(Server server)
{ {
if (server != _currentServer) return Empty;
return _outboundSpeed.ToString();
return server != _currentServer ? Empty : _outboundSpeed.ToString();
} }


private string GetRecentInboundSpeed(Server server) private string GetRecentInboundSpeed(Server server)
{ {
if (server != _currentServer) return Empty;
return _inboundSpeed.ToString();
return server != _currentServer ? Empty : _inboundSpeed.ToString();
} }


private string GetRecentLatency(Server server) private string GetRecentLatency(Server server)
@@ -183,7 +172,7 @@ namespace Shadowsocks.Controller


private void ResetSpeed() private void ResetSpeed()
{ {
_currentServer = _globalConfig.GetCurrentServer();
_currentServer = _controller.GetCurrentServer();
_latency = null; _latency = null;
_inboundSpeed = 0; _inboundSpeed = 0;
_outboundSpeed = 0; _outboundSpeed = 0;
@@ -191,17 +180,17 @@ namespace Shadowsocks.Controller


private void Run(object obj) private void Run(object obj)
{ {
if (_speedMonior?.Change(DelayBeforeStart, MonitorInterval) == null)
if (_speedMonior?.Change(_delayBeforeStart, _monitorInterval) == null)
{ {
_speedMonior = new Timer(UpdateSpeed, null, DelayBeforeStart, MonitorInterval);
_speedMonior = new Timer(UpdateSpeed, null, _delayBeforeStart, _monitorInterval);
} }
LoadRawStatistics(); LoadRawStatistics();
FilterRawStatistics(); FilterRawStatistics();
evaluate();
Evaluate();
ResetSpeed(); ResetSpeed();
} }


private async void evaluate()
private async void Evaluate()
{ {
foreach (var dataLists in await TaskEx.WhenAll(_servers.Select(ICMPTest))) foreach (var dataLists in await TaskEx.WhenAll(_servers.Select(ICMPTest)))
{ {
@@ -237,14 +226,15 @@ namespace Shadowsocks.Controller
} }
} }


internal void UpdateConfiguration(Configuration config, StatisticsStrategyConfiguration statisticsConfig)
internal void UpdateConfiguration(ShadowsocksController controller)
{ {
Set(statisticsConfig);
_servers = config.configs;
_controller = controller;
ResetSpeed(); ResetSpeed();
Set(controller.StatisticsConfiguration);
_servers = _controller.GetCurrentConfiguration().configs;
} }


private async void FilterRawStatistics()
private void FilterRawStatistics()
{ {
if (RawStatistics == null) return; if (RawStatistics == null) return;
if (FilteredStatistics == null) if (FilteredStatistics == null)
@@ -277,7 +267,7 @@ namespace Shadowsocks.Controller
if (!File.Exists(path)) if (!File.Exists(path))
{ {
try { try {
using (FileStream fs = File.Create(path))
using (var fs = File.Create(path))
{ {
//do nothing //do nothing
} }
@@ -286,8 +276,8 @@ namespace Shadowsocks.Controller
Logging.LogUsefulException(e); Logging.LogUsefulException(e);
} }
if (!File.Exists(path)) { if (!File.Exists(path)) {
Console.WriteLine($"statistics file does not exist, try to reload {RetryInterval.TotalMinutes} minutes later");
_timer.Change(RetryInterval, Interval);
Console.WriteLine($"statistics file does not exist, try to reload {_retryInterval.TotalMinutes} minutes later");
_timer.Change(_retryInterval, Interval);
return; return;
} }
} }


+ 3
- 7
shadowsocks-csharp/Controller/ShadowsocksController.cs View File

@@ -30,7 +30,7 @@ namespace Shadowsocks.Controller
private StrategyManager _strategyManager; private StrategyManager _strategyManager;
private PolipoRunner polipoRunner; private PolipoRunner polipoRunner;
private GFWListUpdater gfwListUpdater; private GFWListUpdater gfwListUpdater;
public AvailabilityStatistics availabilityStatistics { get; private set; }
public AvailabilityStatistics availabilityStatistics = AvailabilityStatistics.Instance;
public StatisticsStrategyConfiguration StatisticsConfiguration { get; private set; } public StatisticsStrategyConfiguration StatisticsConfiguration { get; private set; }
public long inboundCounter = 0; public long inboundCounter = 0;
@@ -268,7 +268,7 @@ namespace Shadowsocks.Controller
public void UpdateStatisticsConfiguration(bool enabled) public void UpdateStatisticsConfiguration(bool enabled)
{ {
if (availabilityStatistics == null) return; if (availabilityStatistics == null) return;
availabilityStatistics.UpdateConfiguration(_config, StatisticsConfiguration);
availabilityStatistics.UpdateConfiguration(this);
_config.availabilityStatistics = enabled; _config.availabilityStatistics = enabled;
SaveConfig(_config); SaveConfig(_config);
} }
@@ -341,11 +341,7 @@ namespace Shadowsocks.Controller
gfwListUpdater.Error += pacServer_PACUpdateError; gfwListUpdater.Error += pacServer_PACUpdateError;
} }
if (availabilityStatistics == null)
{
availabilityStatistics = new AvailabilityStatistics(this);
}
availabilityStatistics.UpdateConfiguration(_config, StatisticsConfiguration);
availabilityStatistics.UpdateConfiguration(this);
if (_listener != null) if (_listener != null)
{ {


Loading…
Cancel
Save