[英]Architecture of real-time, iteration application
抱歉抽象的問題,但是我正在尋找一些有關應用程序類型的示例/建議/文章,這些示例/建議/文章會在周期中執行一些等效操作,並且周期的每次迭代都應在一定的時間段(例如10秒)內顯示其結果。 。
我的應用程序在外部WCF服務和本地數據庫之間進行數據同步。 在每次迭代中,應用程序都會檢索對WCF服務的數據傳遞請求的更改,並將更改放入數據庫,反之亦然。 此應用程序最困難的要求之一是,迭代應每十秒鍾觸發一次。
因此,這里出現了問題。 我如何保證迭代完成不超過10秒?
我猜這種類型的應用程序稱為實時應用程序(以實時操作系統的形式)。
我們使用的DAL組件隨機作用於連接超時行為。 因此,數據庫操作可能需要10秒以上的時間。
這是一次迭代的估計代碼:
Stopwatch s1 = new Stopwatch();
s1.Start();
Parallel.ForEach(Global.config.databases, new ParallelOptions { MaxDegreeOfParallelism = -1 }, (l) =>
{
Console.WriteLine("Started for {0}", l.key.name);
DB db = new DB(l.connectionString);
DateTime lastIterationTS = GetPreviousIterationTS(l.id);
ExternalService serv = new ExternalService(l.id);
List<ChangedData> ChangedDataDb = db.GetChangedData(DateTime.Now.AddSeconds((lastIterationTS == DateTime.MinValue) ? -300 : -1 * (DateTime.Now - lastIterationTS).Seconds));
List<Data> ChangedDataService = serv.GetModifiedData();
Action syncDBChanges = new Action(() =>
{
// Изменения в БД
foreach (ChangedData d in ChangedDataDb)
{
try
{
// ...
// analyzing & syncing
}
catch (Exception e)
{
logger.InfoEx("Exception_SyncDatabase", e.ToString());
}
}
}
);
Action syncService = new Action(() =>
{
foreach (Data d in ChangedDataService)
{
try
{
// ...
// analyzing & syncing
}
catch (Exception e)
{
logger.InfoEx("Exception_SyncService", e.ToString());
}
}
});
List<WaitHandle> handles = new List<WaitHandle>();
IAsyncResult ar1 = syncDBChanges.BeginInvoke(syncDBChanges.EndInvoke, null);
IAsyncResult ar2 = syncService.BeginInvoke(syncService.EndInvoke, null);
handles.Add(ar1.AsyncWaitHandle);
handles.Add(ar2.AsyncWaitHandle);
WaitHandle.WaitAll(handles.ToArray(), (int)((Global.config.syncModifiedInterval - 1) * 1000));
SetCurrentIterationTS(l.id);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
logger.InfoEx("Exception_Iteration", e.ToString());
continue;
}
}
logger.InfoEx("end_Iteration", IterationContextParams);
}
);
s1.Stop();
Console.WriteLine("Main iteration done for {0}...", s1.Elapsed);
您可以考慮幾個選擇...
如果迭代超過10秒,則終止該迭代,並希望下一次迭代可以完成該過程。 這種方法的問題在於,很可能所有迭代都不會完成,因此同步過程將永遠不會發生。 我建議以下選項...
如果迭代花費的時間超過10秒,請等待其完成並跳過下一個迭代。 這樣,您可以確保過程至少完成一次。 以下是簡化的代碼示例,以供參考...
class Updater { Timer timer = new Timer(); public object StateLock = new object(); public string State; public Updater() { timer.Elapsed += timer_Elapsed; timer.Interval = 10000; timer.AutoReset = true; timer.Start(); } void timer_Elapsed(object sender, ElapsedEventArgs e) { if (State != "Running") { Process(); } } private void Process() { try { lock (StateLock) { State = "Running"; } // Process lock (StateLock) { State = ""; } } catch { throw; } } }
...
class Program
{
static void Main(string[] args)
{
Updater updater = new Updater();
Console.ReadLine();
}
}
Quartz.net是.NET平台的出色調度程序,我認為它可以滿足您的需求。
我通常將更新周期與實際計時器分開
計時器執行兩件事:
1)如果更新未運行則啟動它。
2)如果服務已經在運行,請設置一個標志以使其繼續運行。
更新周期:
1)設置運行標志
2)做更新
3)將運行標志設置為false
4)如果將繼續運行設置為1)。
您可能想閱讀有關.Net中可用的各種Timer對象的信息: http : //msdn.microsoft.com/zh-cn/magazine/cc164015.aspx
我個人喜歡System.Threading.Timer
因為您可以輕松使用lambda,並且如果您創建單獨的回調,它可以傳遞狀態對象。
我還建議您使用System.Threading.Tasks
庫,因為它允許您在工作完成之前經過計時器的情況下,優雅地處理取消操作。 MSDN示例: http : //msdn.microsoft.com/en-us/library/dd537607.aspx
這是一個在10分鍾的計時器中同時使用這些組件的示例:注意:要對sql數據庫執行此操作,您需要將Asynchronous Processing=true;
設置Asynchronous Processing=true;
和MultipleActiveResultSets=True;
CancellationTokenSource cancelSource = new CancellationTokenSource();
System.Threading.Timer timer = new System.Threading.Timer(callback =>
{
//start sync
Task syncTask = Task.Factory.StartNew(syncAction =>
{
using (SqlConnection conn =
new SqlConnection(
ConfigurationManager.ConnectionStrings["db"].ConnectionString))
{
conn.Open();
using (SqlCommand syncCommand = new SqlCommand
{
CommandText = "SELECT getdate() \n WAITFOR DELAY '00:11'; ",
CommandTimeout = 600,
Transaction = conn.BeginTransaction(),
Connection = conn
})
{
try
{
IAsyncResult t = syncCommand.BeginExecuteNonQuery();
SpinWait.SpinUntil(() =>
(t.IsCompleted || cancelSource.Token.IsCancellationRequested));
if (cancelSource.Token.IsCancellationRequested && !t.IsCompleted)
syncCommand.Transaction.Rollback();
}
catch (TimeoutException timeoutException)
{
syncCommand.Transaction.Rollback();
//log a failed sync attepmt here
Console.WriteLine(timeoutException.ToString());
}
finally
{
syncCommand.Connection.Close();
}
}
}
}, null, cancelSource.Token);
//set up a timer for processing in the interim, save some time for rollback
System.Threading.Timer spinTimer = new System.Threading.Timer(c => {
cancelSource.Cancel();
}, null, TimeSpan.FromMinutes(9), TimeSpan.FromSeconds(5));
//spin here until the spintimer elapses;
//this is optional, but would be useful for debugging.
SpinWait.SpinUntil(()=>(syncTask.IsCompleted || cancelSource.Token.IsCancellationRequested));
if (syncTask.IsCompleted || cancelSource.Token.IsCancellationRequested)
spinTimer.Dispose();
}, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(10));
也許嘗試一下。 請確保您沒有在DoWork()方法中創建和使用任何新線程。
class DatabaseUpdater
{
private readonly Timer _timer;
private List<Thread> _threads;
private readonly List<DatabaseConfig> _dbConfigs;
public DatabaseUpdater(int seconds, List<DatabaseConfig> dbConfigs)
{
_timer = new Timer(seconds * 1000);
_timer.Elapsed += TimerElapsed;
_dbConfigs = dbConfigs;
}
public void Start()
{
StartThreads();
_timer.Start();
}
public void Stop()
{
_timer.Stop();
StopThreads();
}
void TimerElapsed(object sender, ElapsedEventArgs e)
{
StopThreads();
StartThreads();
}
private void StartThreads()
{
var newThreads = new List<Thread>();
foreach (var config in _dbConfigs)
{
var thread = new Thread(DoWork);
thread.Start(config);
newThreads.Add(thread);
}
_threads = newThreads;
}
private void StopThreads()
{
if (_threads == null) return;
var oldThreads = _threads;
foreach (var thread in oldThreads)
{
thread.Abort();
}
}
static void DoWork(object objConfig)
{
var dbConfig = objConfig as DatabaseConfig;
if (null == dbConfig) return;
var n = GetRandomNumber();
try
{
ConsoleWriteLine("Sync started for : {0} - {1} sec work.", dbConfig.Id, n);
// update/sync db
Thread.Sleep(1000 * n);
ConsoleWriteLine("Sync finished for : {0} - {1} sec work.", dbConfig.Id, n);
}
catch (Exception ex)
{
// cancel/rollback db transaction
ConsoleWriteLine("Sync cancelled for : {0} - {1} sec work.",
dbConfig.Id, n);
}
}
static readonly Random Random = new Random();
[MethodImpl(MethodImplOptions.Synchronized)]
static int GetRandomNumber()
{
return Random.Next(5, 20);
}
[MethodImpl(MethodImplOptions.Synchronized)]
static void ConsoleWriteLine(string format, params object[] arg)
{
Console.WriteLine(format, arg);
}
}
static void Main(string[] args)
{
var configs = new List<DatabaseConfig>();
for (var i = 1; i <= 3; i++)
{
configs.Add(new DatabaseConfig() { Id = i });
}
var databaseUpdater = new DatabaseUpdater(10, configs);
databaseUpdater.Start();
Console.ReadKey();
databaseUpdater.Stop();
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.