目錄
- 目錄
- 引言
- 思路
- 中間件實作
- 代碼執行個體
- MessageQueueCS
- SingleThreadCS
- CommonCS
- 結束語
引言
高并必的日志處理在網上可以找到很多的解決方案,在做這個Demo之前我也是個菜鳥;當然,做完這個Demo還是一個菜鳥。廢話不多說,先看下先輩們的案例:
- 系統架構~高并發日志系統設計[http://www.cnblogs.com/lori/p/3921310.html]
- 自己DIY的[http://blog.csdn.net/hawksoft/article/details/7213652]
這篇文章主要講的是思路,代碼還有待優化,使用的是生産者與消費者模式、另外還使用了單例模式;這隻是一種做法而已,程式設計路上的想法與實作想法。
思路
解決問題思路非常重要,有了思路我們才會有目标、方向;
從問題的來源說起吧,因為DMS系統需要記錄日志,然而在現有的方式是記錄到資料庫,這個方法有好也有壞,壞處在于:
- 并必量高的時候增加了資料庫伺服器的壓力為一些日常日志、業務日志或者重要的系統日志來增加資料庫伺服器的壓力還是有些不值得的;
-
在增加資料庫伺服器的壓力同時還會增加垃圾資料,這些日志别看量很少,單條資料量不大,但對于龐大的并必量時将是不堪;
基于這些原因老大要我對日志子產品進行優化;使用系統現有的日志子產品方式寫檔案無疑是最好的,對線上系統的維護來說是比較好的選擇,能快速的反應事實,但還沒上線,在本地浏覽器裡面随便的Enter,浏覽器大哥就跟我說IO沖突,想想也是有可能的,對于WEB伺服器來說是多線程,IO沖突也是需要注意的事情,這一點小夥伴們要記住了。好了這是為什麼有這遍文章的原由(這是一段屁話-_-)。
有了這個原由後就要開始想怎麼解決IO沖突了,這時想到了生産者與消費者模式;這個模式可以說是為了這種問題而生;WEB伺服器做生産者,日志子產品做消費者,對于系統現狀來說這也是最省的方式,因為日志子產品也有了;那我們要做的事就是做這個生産者與消費者的中間件了;
對中間件的設計思路:
- 首先要有一條單獨的線程向檔案寫日志;
- 需要有一個倉庫來存儲生産者生産的産物;
-
倉庫和線程之間的互動,線程最好能自我化的管理;
UML圖如下:
說明/注釋:
- 為了保證整個過程中隻會有一條線程來對日志檔案進行操作,這裡就會使用單例模式,單例模式能很好的保護和管理這一問題,在任何情況下都能保證不會有另外一條線程來與目前線程産生IO沖突;
- 同樣也是使用單例模式來管理消息隊列(也是就是上面提到的生産者的産物倉庫),對于消息隊列也是一樣,所有的生産者的的産物都放一同一消息隊列中,儲存消息隊列的唯一性;
中間件實作
對于中間件我做了三個類,分别為SingleThread、MessageQueue、Common
三者類關系圖:
這裡使用Common組合SingleThread和MessageQueue;SingleThread提供對線程的支援,MessageQueue提供對消息隊列的支援;而Common剛是對他們的一個封裝,開放給生産者一個接口(AddMessage),Web伺服器不需要知道其它的去作,隻需要告訴Common我需要寫日志了,我把我需要寫的日志資訊給你,你去幫我寫到檔案;WEB伺服器做完這些并将它的日志資訊交到消息隊列後就可以去做他自己的事情(這樣做的目錄是将WEB伺服器與日志進行解耦,WEB伺服器不用去管日志是怎麼寫入到檔案的,有沒有寫入,這樣也友善後期的維護,不知道有沒有提升WEB伺服器的性能,我認為是有提升的,從理論的角度來講WEB伺服器不用去做寫日志的事情);好了,說到這裡大家應該從宏觀的角度能對這個中間件有一些了解了,那些看來看來具體的代碼實作。
代碼執行個體
MessageQueue.CS
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Log.MiddleWare.Message
{
/// <summary>
/// 文本消息隊列處理類
/// </summary>
public class MessageQueue
{
private Queue<string> _queue = null;
private static MessageQueue _messageQueue = null;
private MessageQueue()
{
}
#region 屬性
/// <summary>
/// 擷取消息隊列 在實際應用中請使用線程安全的隊列或者其它方式
/// </summary>
private Queue<string> GetQueue
{
get
{
if (_queue == null)
{
_queue = new Queue<string>();
}
return _queue;
}
}
/// <summary>
/// 擷取消息隊列長度
/// </summary>
public long Count
{
get { return GetQueue.Count; }
}
#endregion
#region 方法
/// <summary>
/// 添加消息
/// </summary>
/// <param name="message">需要添加的消息文本</param>
public void AddMessage(string message)
{
GetQueue.Enqueue(message);
}
/// <summary>
/// 擷取消息
/// </summary>
/// <returns></returns>
public string GetMessage()
{
return GetQueue.Dequeue();
}
/// <summary>
/// 擷取類對象
/// </summary>
/// <returns></returns>
public static MessageQueue GetMessageQueue()
{
return _messageQueue ?? new MessageQueue();
}
#endregion
}
}
SingleThread.CS
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
namespace Log.MiddleWare.Thread
{
/// <summary>
/// 單線程自我管理基類
/// </summary>
public class SingleThread
{
/// <summary>
/// 自我管理線程
/// </summary>
private object _thread = new Object();
/// <summary>
/// 線程初始化狀态
/// </summary>
public bool flog = false;
private static SingleThread singleThread = null;
#region 屬性
/// <summary>
/// 線程委托方法
/// </summary>
public ThreadStart StartFuncation { get; set; }
/// <summary>
/// 擷取線程
/// </summary>
public System.Threading.Thread GetThread
{
get
{
lock (_thread)
{
if (_thread.GetType()==typeof(System.Threading.Thread)&&(_thread as System.Threading.Thread).ThreadState == ThreadState.Stopped)
{
(_thread as System.Threading.Thread).DisableComObjectEagerCleanup();
flog = false;
}
if (!flog)
{
if (StartFuncation == null)
{
throw new Exception("請先設定線程委托執行方法");
}
_thread = new System.Threading.Thread(StartFuncation);
(_thread as System.Threading.Thread).Name = "singlethread";
flog = true;
}
}
return _thread as System.Threading.Thread;
}
}
/// <summary>
/// 擷取線程目前狀态
/// </summary>
public ThreadState ThreadState
{
get { return GetThread.ThreadState; }
}
#endregion
private SingleThread()
{
}
public static SingleThread GetSingleThread()
{
if (singleThread == null)
{
singleThread = new SingleThread();
}
return singleThread;
}
/// <summary>
/// 開始執行線程
/// </summary>
public void StartThread()
{
GetThread.Start();
}
/// <summary>
/// 暫停線程執行
/// </summary>
public void AbortThread()
{
GetThread.Suspend();
}
}
}
Common.CS
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading;
namespace Log.MiddleWare
{
public class Common
{
private Thread.SingleThread singleThread = Thread.SingleThread.GetSingleThread();
private Message.MessageQueue messageQueue = Message.MessageQueue.GetMessageQueue();
private static readonly Common _Common = new Common();
private Common()
{
singleThread.StartFuncation = new ThreadStart(WriteLog);
LogHelper.LogHelper.SetConfig();
}
#region 方法
/// <summary>
/// 将消息從隊列中寫入到檔案
/// </summary>
private void WriteLog()
{
while (true)
{
string message = messageQueue.GetMessage();
LogHelper.LogHelper.WriteLog(message);
//這裡為了增加線程處理時間添加一些事務處理,正式使用請删除
#region 增加事務處理以延長線程處理時間
DataTable dt=new DataTable();
dt.Columns.Add("a", typeof(string));
dt.Columns.Add("b", typeof(string));
dt.Columns.Add("d", typeof(string));
dt.Columns.Add("e", typeof(string));
dt.Columns.Add("f", typeof(string));
dt.Columns.Add("g", typeof(string));
dt.Columns.Add("h", typeof(string));
dt.Columns.Add("i", typeof(string));
dt.Columns.Add("j", typeof(string));
dt.Columns.Add("k", typeof(string));
dt.Columns.Add("l", typeof(string));
dt.Columns.Add("m", typeof(string));
for (int i = ; i < ; i++)
{
DataRow dr = dt.NewRow();
dr[] = "1111";
dr[] = "1111111111111111111111";
dr[] = "1111111111111111111111";
dr[] = "1111111111111111111111";
dr[] = "1111111111111111111111";
dr[] = "1111111111111111111111";
dr[] = "1111111111111111111111";
dr[] = "1111111111111111111111";
dr[] = "1111111111111111111111";
dr[] = "1111111111111111111111";
dr[] = "1111111111111111111111";
dr[] = "1111111111111111111111";
dt.Rows.Add(dr);
}
var listdr = (from DataRow dr in dt.AsEnumerable()
select new
{
a = Convert.ToDecimal(dr[]),
b = dr[],
c = dr[]
}).Sum(s => s.a);
#endregion
if (messageQueue.Count == )
{
singleThread.AbortThread();
}
}
}
/// <summary>
/// 将日志寫入檔案
/// </summary>
/// <param name="message"></param>
public void AddLogMessage(string message)
{
messageQueue.AddMessage(message+"\t" + messageQueue.Count + "\t" + singleThread.GetThread.ManagedThreadId + "\t" + singleThread.GetThread.Name);
if (singleThread.ThreadState==ThreadState.Unstarted)
{
singleThread.StartThread();
}
if (singleThread.ThreadState==ThreadState.Suspended)
{
singleThread.GetThread.Resume();
}
}
/// <summary>
/// 擷取Common對象
/// </summary>
/// <returns></returns>
public static Common GetCommon()
{
return _Common;
}
#endregion
}
}
結束語
這個中間件隻是一個例子,單例線程處理類中使用了委托來動态的定義需要執行的方法,以便對這個中間件的功能擴充;另外這隻是一個例子,寫這個中間件的目錄并不是隻為了處理高并發的日志,這個中間如果再進行優化應該是可以用在其它相同案例的地方。
日志子產品說明:日志子產品使用的是開源的Log4Net插件