天天看點

一步步開發自己的部落格 番外篇(7、異步記錄日志 和 文章閱讀量統計)

一步步開發自己的部落格 番外篇(7、異步記錄日志 和 文章閱讀量統計)

離前一篇《一步步開發自己的部落格 .NET版(6、手機端的相容)》都個把月了。

當時寫完第六篇,很多人問“劇終”了?你還有好多實作沒有講解分析呢。我說沒呢,後期還會有第二版、第三版...至于還沒有分析到,後期補上。你看,我今天不就來了麼。

前段時間寫代碼,手都寫的酸痛酸痛的,歇息了幾天,好多了。然後,又搗鼓了一下webapi。這也是個沒有接觸過的知識。跌跌撞撞的整了點東西出來。有興趣寫移動端的同學可以聯系我,大家一起學習。API文檔和測試位址:http://haojima.net/SwaggerUI

前言 

離前一篇《一步步開發自己的部落格  .NET版(6、手機端的相容)》都個把月了。
一步步開發自己的部落格 番外篇(7、異步記錄日志 和 文章閱讀量統計)
前段時間寫代碼,手都寫的酸痛酸痛的,歇息了幾天,好多了。然後,又搗鼓了一下webapi。這也是個沒有接觸過的知識。跌跌撞撞的整了點東西出來。有興趣寫移動端的同學可以聯系我,大家一起學習。API文檔和測試位址:http://haojima.net/SwaggerUI 

其他的就不多說了,進入今天的主題,異步記錄日志和文章閱讀量統計。

一步步開發自己的部落格 番外篇(7、異步記錄日志 和 文章閱讀量統計)

異步記錄日志

我們常用日志記錄,無非就是,資料庫記錄和文本日志記錄。而今天我要說的是,文本日志記錄。

最簡單的文本記錄: File.WriteAllText(path,messg); 使用靜态類File的WriteAllText 如果檔案存在則覆寫,傳入檔案路徑和消息内容。ok,完事。

當然,我們不能每次都覆寫上一次的記錄。 File.AppendAllText(path,messg); 那麼我們可是在原有内容追加。這裡,我們不用關系檔案流是否關閉,使用靜态類File的這兩個方法都會自動幫我們關閉。

如果,我們是使用的winfrom單線程。那麼,基本的日志記錄就這個兩個方法 完全可以搞定。

但是,如果是web程式就不一樣了,天生的多線程。多個線程同時通路一個檔案,肯定是會報錯的。不信你試試。

一步步開發自己的部落格 番外篇(7、異步記錄日志 和 文章閱讀量統計)

那我們怎麼解決這個問題?有人會說,加鎖呗。鎖肯定是要加,不過要看怎麼加了。如果加到寫檔案内容的時候肯定是不合适的。因為寫檔案要打開檔案流,比較耗時。我們可以先把要寫的日志,統一存記憶體,然後單線程從記憶體取資料,寫到文本。當然,寫記憶體也可能會多線程并發,這個時候,我們就可以把鎖加到寫記憶體的地方。這裡大家就不用擔心了,寫記憶體的速度是非常快的,和直接寫檔案那差的可不是一兩個檔次的問題了。

我們剛才說存記憶體,怎麼存?當然是存集合了。有個資料類型 Queue 為什麼要用它。因為它是隊列,有個特點:先進先出。我們取資料的時候就是去的最早存進去的資料了。

使用:存資料 Queue myQ = new Queue(); myQ.Enqueue("The");//入隊    取資料 var t = myQ.Dequeue(); 直接在取值的時候就把值在隊列中移除了。這樣正好免了我手動移除。

那麼,很簡單。我們記日志的時候就先把日志往 Queue 裡存,然後單獨開個程序取值存值寫檔案裡。ok,完事。

剛才說了,我們要加鎖。是的,要加鎖。因為 Queue 并不是線程安全資料。我們在寫資料和讀資料的時候都要加鎖。

static object myLock= new object();
...
lock (myLock)
     logQueue.Enqueue(logmede);//存
...
lock (myLock)
     var m = logQueue.Dequeue();//取      

我之前在網上查資料說不能多線程同時寫入隊列,經測試其實是不能同時讀和寫隊列。是以在Dequeue取的時候也要鎖定同一個對象

思路大體就是這樣了。當然,我們還可以擴充很多的東西。如:定時删除指定過期日志、分檔案大小存儲日志、自動增加的日志檔案命名...等等。

寫到這裡,估計又有大把大把的人要來批判我了。又在造輪子。日志架構那麼多,幹嘛還要自己寫。浪費時間.....等。

沒錯,我确實是在造輪子。我不想解釋太多了。累...   大神請略過....

下面給出,我的具體實作代碼。分為四個檔案 LogMode 包含檔案名、日志内容 LogHelper 存隊列、寫檔案 LogConfig 讀取相關配置 LogSave 外部直接調用

一步步開發自己的部落格 番外篇(7、異步記錄日志 和 文章閱讀量統計)
一步步開發自己的部落格 番外篇(7、異步記錄日志 和 文章閱讀量統計)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;

namespace CommonLib.HiLog
{
    /// <summary>
    /// 日志模型
    /// </summary>
    internal class LogModel
    {      
        #region logFileName
        private string _logFileName;

        /// <summary>
        /// 日志檔案名字
        /// </summary>
        public string logFileName
        {
            get { return _logFileName + "_" + DateTime.Now.ToString("yyyyMMdd"); }
            set { _logFileName = value; }
        }
        #endregion

        #region logMessg
        private string _logMessg;

        /// <summary>
        /// 日志内容
        /// </summary>
        public string logMessg
        {
            get
            {
                return "----begin-------" + DateTime.Now.ToString() + "----Queue.Count:" + LogHelper.LogQueue.Count + "-----------------------------------\r\n\r\n"
                    + _logMessg
                    + "\r\n\r\n----end----------" + DateTime.Now.ToString() + "----Queue.Count:" + LogHelper.LogQueue.Count + "-----------------------------------"
                    + "\r\n\r\n\r\n";
            }
            set { _logMessg = value; }
        }
        #endregion
    }
}      

View Code

一步步開發自己的部落格 番外篇(7、異步記錄日志 和 文章閱讀量統計)
一步步開發自己的部落格 番外篇(7、異步記錄日志 和 文章閱讀量統計)
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace CommonLib.HiLog
{
    /// <summary>
    /// 日志操作輔助類
    /// [email protected]
    /// 建立20150104 修改20151003
    /// </summary>
    internal class LogHelper
    {
        /// <summary>
        /// 消息隊列
        /// </summary>
        private static Queue<LogModel> logQueue = new Queue<LogModel>();
        /// <summary>
        /// 消息隊列 對外隻讀
        /// </summary>
        public static Queue<LogModel> LogQueue
        {
            get { return LogHelper.logQueue; }
        }

        /// <summary>
        /// 标志鎖
        /// </summary>
        static string myLock = "true";

        /// <summary>
        /// 寫入日志檔案(異步單線程 記錄日志)
        /// </summary>
        /// <param name="logmede"></param>
        public static void logWrite(LogModel logmede)
        {
            // 這裡需要鎖上 不然會出現:源數組長度不足。請檢查 srcIndex 和長度以及數組的下限。異常   
            //網上有資料說 http://blog.csdn.net/greatbody/article/details/26135057  不能多線程同時寫入隊列
            //其實  不僅僅 不能同時寫入隊列 也不能同時讀和寫如隊列  是以  在Dequeue 取的時候也要鎖定一個對象
            lock (myLock)
                logQueue.Enqueue(logmede);
            logStartWrite();
        }

        /// <summary>
        /// 檔案編碼格式
        /// </summary>
        public static Encoding encoding = Encoding.Default;

        /// <summary>
        /// 是否開始自動記錄日志
        /// </summary>
        private static bool isStart = false;

        /// <summary>
        /// 用來 辨別 最好一次 檢測是否 需要 清理 日志檔案 時間
        /// </summary>
        private static DateTime time = DateTime.MinValue;
        /// <summary>
        /// 每個日志檔案夾 對應的檔案下标
        /// </summary>
        private static Dictionary<string, int> logFileNum = new Dictionary<string, int>();
        /// <summary>
        /// 開始把隊列消息寫入檔案
        /// </summary>
        private static void logStartWrite()
        {
            if (isStart)
                return;
            isStart = true;
            Task.Run(() =>
            {
                while (true)
                {
                    if (LogHelper.logQueue.Count >= 1)
                    {
                        LogModel m = null;
                        lock (myLock)
                            m = LogHelper.logQueue.Dequeue();
                        if (m == null)
                            continue;

                        if (string.IsNullOrEmpty(LogConfig.logFilePath))
                            throw new Exception("請先初始化日志儲存路徑LogModel._logFilePath");

                        TestingInvalid();

                        if (!Directory.Exists(LogConfig.logFilePath + m.logFileName + @"\"))
                            Directory.CreateDirectory(LogConfig.logFilePath + m.logFileName + @"\");

                        // int i = m.logFileNum;
                        if (!logFileNum.Keys.Contains(m.logFileName))
                            logFileNum.Add(m.logFileName, 0);
                        //部分 日志 檔案路徑
                        string SectionfileFullName = LogConfig.logFilePath + m.logFileName + @"\" + m.logFileName + "_" + logFileNum[m.logFileName].ToString("000") + ".txt";
                        //最新的寫了内容的 部分 日志檔案路徑
                        string TopSectionfileFullName = SectionfileFullName;
                        // 需要實時更新的 最新日志檔案 路徑
                        string LogfileFullNqme = LogConfig.logFilePath + m.logFileName + @"\" + m.logFileName + ".txt";

                        FileInfo file = new FileInfo(SectionfileFullName);
                        while (file.Exists && file.Length >= LogConfig.SectionlogFileSize)
                        {
                            TopSectionfileFullName = SectionfileFullName;
                            logFileNum[m.logFileName]++;
                            SectionfileFullName = LogConfig.logFilePath + m.logFileName + @"\" + m.logFileName + "_" + logFileNum[m.logFileName].ToString("000") + ".txt";
                            file = new FileInfo(SectionfileFullName);
                        }

                        try
                        {
                            if (!file.Exists)//如果不存在 這個檔案 就說明需要 建立新的部分日志檔案了
                            {
                                //因為SectionfileFullName路徑的檔案不存在    是以建立
                                File.WriteAllText(SectionfileFullName, m.logMessg, encoding);

                                FileInfo Logfile = new FileInfo(LogfileFullNqme);
                                if (Logfile.Exists && Logfile.Length >= LogConfig.FileSize)
                                    //先清空  然後加上 上一個部分檔案的内容
                                    File.WriteAllText(LogfileFullNqme, File.ReadAllText(TopSectionfileFullName, encoding), encoding);//如果存在則覆寫                           
                            }
                            else
                                File.AppendAllText(SectionfileFullName, m.logMessg, encoding);//累加

                            //追加這次内容 到動态更新的日志檔案
                            File.AppendAllText(LogfileFullNqme, m.logMessg, encoding);
                        }
                        catch (Exception ex)
                        {
                            throw ex;
                        }

                    }
                    else
                    {
                        isStart = false;//标記下次可執行
                        break;//跳出循環
                    }
                }
            });
        }

        /// <summary>
        /// 檢測 并删除 之前之外的 日志檔案
        /// </summary>
        public static void TestingInvalid()
        {
            #region 檢測 并删除 之前之外的 日志檔案
            if (time.AddMinutes(LogConfig.TestingInterval) <= DateTime.Now)// 時間内 檢測一次
            {
                try
                {
                    time = DateTime.Now;
                    List<string> keyNames = new List<string>();
                    foreach (var logFileName in logFileNum.Keys)
                    {
                        CreatePath(LogConfig.logFilePath + logFileName + @"\");
                        DirectoryInfo dir = new DirectoryInfo(LogConfig.logFilePath + logFileName + @"\");
                        if (dir.CreationTime.AddMinutes(LogConfig.DelInterval) <= DateTime.Now)//删除 設定時間 之前的日志
                            foreach (var fileInfo in dir.GetFiles())
                            {
                                if (fileInfo.LastWriteTime.AddMinutes(LogConfig.DelInterval) <= DateTime.Now)//最後修改時間算起
                                    File.Delete(fileInfo.FullName);

                            }
                        if (dir.GetFiles().Length == 0)
                            keyNames.Add(logFileName);//臨時存儲沒有日志檔案的檔案夾
                    }
                    foreach (var key in keyNames)//删除沒有日志檔案的檔案夾
                    {
                        logFileNum.Remove(key);
                        Directory.Delete(LogConfig.logFilePath + key + @"\", false);
                    }
                }
                catch (Exception ex)
                {
                    LogSave.ErrLogSave("手動捕獲[檢測并删除日志出錯!]", ex, "記錄日志出錯");
                }                
            }
            #endregion
        }

        #region  建立路徑
        /// <summary>
        /// 建立路徑
        /// </summary>
        /// <param name="paht"></param>
        /// <returns></returns>
        public static bool CreatePath(string paht)
        {
            if (!Directory.Exists(paht))
            {
                Directory.CreateDirectory(paht);
                return true;
            }
            return false;
        }
        #endregion
    }
}      
一步步開發自己的部落格 番外篇(7、異步記錄日志 和 文章閱讀量統計)
一步步開發自己的部落格 番外篇(7、異步記錄日志 和 文章閱讀量統計)
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;

namespace CommonLib.HiLog
{
    /// <summary>
    /// 日志相關配置    
    /// </summary>
    public static class LogConfig
    {
        #region 輔助方法
        /// <summary>
        /// GetAppSettings
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static string GetAppSettings(string key)
        {
            if (ConfigurationManager.AppSettings.AllKeys.Contains(key))
                return ConfigurationManager.AppSettings[key].ToString();
            return string.Empty;
        }

        /// <summary>
        /// 計算字元串 轉 計算結果
        /// </summary>
        /// <param name="v"></param>
        /// <returns></returns>
        public static string toCompute(this string v)
        {
            return new DataTable().Compute(v, "").ToString();
        }
        #endregion

        #region 靜态屬性和字段

        #region logFilePath 路徑
        /// <summary>
        /// 日志要存的路徑 預設路徑:網站根目錄 + Log 檔案夾
        /// 在程式第一次啟動是設定
        /// </summary>       
        private static string _logFilePath;

        /// <summary>
        /// 日志要存的路徑 預設路徑:網站根目錄 + Log 檔案夾
        /// 在程式第一次啟動是設定
        /// </summary>   
        public static string logFilePath
        {
            get
            {
                if (string.IsNullOrEmpty(_logFilePath))
                {
                    try
                    {
                        _logFilePath = HttpContext.Current.Server.MapPath("~/");
                    }
                    catch (Exception)
                    {
                        try
                        {
                            _logFilePath = System.Windows.Forms.Application.StartupPath + @"\";
                        }
                        catch (Exception)
                        {
                            throw new Exception("請先初始化要儲存的路徑:LogModel._logFilePath");
                        }
                    }
                }
                return _logFilePath;
            }

            set
            {
                _logFilePath = value;
            }
        }

        #endregion

        #region 檢測間隔時間(分鐘)
        private static int _TestingInterval;
        /// <summary>
        /// 檢測間隔時間(分鐘) 預設:一天
        /// 配置:appSettings->Log_TestingInterval 機關:秒
        /// </summary>
        public static int TestingInterval
        {
            get
            {
                if (_TestingInterval <= 0)
                {
                    var Log_TestingInterval = GetAppSettings("Log_TestingInterval");
                    if (string.IsNullOrEmpty(Log_TestingInterval))
                        _TestingInterval = 1 * 60 * 24;
                    else
                        _TestingInterval = Convert.ToInt32(Log_TestingInterval.toCompute());
                }
                return _TestingInterval;
            }
        }
        #endregion

        #region 删除 N分鐘(最後修改時間)之前的的日志
        private static int _DelInterval;
        /// <summary>
        /// 删除 N分鐘(最後修改時間)之前的的日志 預設:15天
        /// 配置:appSettings->Log_DelInterval 機關:秒
        /// </summary>
        public static int DelInterval
        {
            get
            {
                if (_DelInterval <= 0)
                {
                    var Log_DelInterval = GetAppSettings("Log_DelInterval");
                    if (string.IsNullOrEmpty(Log_DelInterval))
                        _DelInterval = 1 * 60 * 24 * 15;
                    else
                        _DelInterval = Convert.ToInt32(Log_DelInterval.toCompute());
                }
                return _DelInterval;
            }
        }
        #endregion

        #region 部分日志檔案大小(Byte)
        private static int _SectionlogFileSize;
        /// <summary>
        /// 部分日志檔案大小(Byte) 預設:1024Byte * 1024 * 1 = 1MB
        /// 配置:appSettings->Log_SectionlogFileSize 機關:Byte
        /// </summary>
        public static int SectionlogFileSize
        {
            get
            {
                if (_SectionlogFileSize <= 0)
                {
                    var Log_SectionlogFileSize = GetAppSettings("Log_SectionlogFileSize");
                    if (string.IsNullOrEmpty(Log_SectionlogFileSize))
                        _SectionlogFileSize = 1024 * 1024 * 1;
                    else
                        _SectionlogFileSize = Convert.ToInt32(Log_SectionlogFileSize.toCompute());
                }
                return _SectionlogFileSize;
            }
        }
        #endregion

        #region 變動檔案大小(Byte)
        private static int _FileSize;
        /// <summary>
        /// 變動檔案大小(Byte) 預設:1024 * 1024 * 4 = 4M
        /// 配置:appSettings->Log_FileSize 機關:Byte
        /// </summary>
        public static int FileSize
        {
            get
            {
                if (_FileSize <= 0)
                {
                    var Log_FileSize = GetAppSettings("Log_FileSize");
                    if (string.IsNullOrEmpty(Log_FileSize))
                        _FileSize = 1024 * 1024 * 4;
                    else
                        _FileSize = Convert.ToInt32(Log_FileSize.toCompute());
                }
                return _FileSize;
            }
        }
        #endregion

        #endregion
    }
}      
一步步開發自己的部落格 番外篇(7、異步記錄日志 和 文章閱讀量統計)
一步步開發自己的部落格 番外篇(7、異步記錄日志 和 文章閱讀量統計)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;

namespace CommonLib.HiLog
{
    /// <summary>
    /// 異步單線程
    /// </summary>
    public class LogSave
    {
        /// <summary>
        /// 獲得Exception 的詳細資訊
        /// </summary>
        /// <param name="ex"></param>
        /// <returns></returns>
        public static string GetExceptionInfo(Exception ex)
        {
            StringBuilder str = new StringBuilder();
            str.Append("錯誤資訊:" + ex.Message);
            str.Append("\r\n錯誤源:" + ex.Source);
            str.Append("\r\n異常方法:" + ex.TargetSite);
            str.Append("\r\n堆棧資訊:" + ex.StackTrace);
            return str.ToString();
        }

        /// <summary>
        /// 系統 自動 捕捉異常
        /// 儲存異常詳細資訊 
        /// 包括: 浏覽器  浏覽器版本 作業系統 頁面  Exception
        /// </summary>
        /// <param name="ex"></param>
        /// <param name="fileName">檔案名 預設:SysErr</param>
        public static void SysErrLogSave(Exception ex, string fileName = null)
        {
            StringBuilder str = new StringBuilder();
            string ip = "";
            if (HttpContext.Current.Request.ServerVariables.Get("HTTP_X_FORWARDED_FOR") != null)
                ip = HttpContext.Current.Request.ServerVariables.Get("HTTP_X_FORWARDED_FOR").ToString().Trim();
            else
                ip = HttpContext.Current.Request.ServerVariables.Get("Remote_Addr").ToString().Trim();
            str.Append("Ip:" + ip);
            str.Append("\r\n浏覽器:" + HttpContext.Current.Request.Browser.Browser.ToString());
            str.Append("\r\n浏覽器版本:" + HttpContext.Current.Request.Browser.MajorVersion.ToString());
            str.Append("\r\n作業系統:" + HttpContext.Current.Request.Browser.Platform.ToString());
            str.Append("\r\n頁面:" + HttpContext.Current.Request.Url.ToString());
            str.Append("\r\n" + GetExceptionInfo(ex));
            LogHelper.logWrite(new LogModel()
            {
                logFileName = "SysErr" + fileName ?? string.Empty,
                logMessg = str.ToString()
            });
        }

        /// <summary>
        /// 異常日志記錄
        /// </summary>
        /// <param name="strmes"></param>
        /// <param name="ex"></param>
        public static void ErrLogSave(string strmes, Exception ex, string fileName = null)
        {
            StringBuilder str = new StringBuilder();
            str.Append(strmes);
            if (ex != null)
                str.Append("\r\n" + GetExceptionInfo(ex));
            LogHelper.logWrite(new LogModel()
            {
                logFileName = fileName ?? "Err",
                logMessg = str.ToString()
            });
        }

        /// <summary>
        /// 警告日志記錄
        /// </summary>
        /// <param name="str"></param>
        public static void WarnLogSave(string str, string fileName = null)
        {
            if (str != null && !string.IsNullOrEmpty(str.Trim()))
                LogHelper.logWrite(new LogModel()
                {
                    logFileName = fileName ?? "Warn",
                    logMessg = str
                });
        }

        /// <summary>
        /// 追蹤日志記錄
        /// </summary>
        /// <param name="str"></param>
        public static void TrackLogSave(string str, string fileName = null)
        {
            if (str != null && !string.IsNullOrEmpty(str.Trim()))
                LogHelper.logWrite(new LogModel()
                {
                    logFileName = fileName ?? "Track",
                    logMessg = str
                });
        }

        /// <summary>
        /// 追蹤日志記錄
        /// </summary>
        /// <param name="str"></param>
        public static void TrackLogSave(string str)
        {
            if (!string.IsNullOrEmpty(str.Trim()))
                LogHelper.logWrite(new LogModel()
                {
                    logFileName = "SqlTrack",
                    logMessg = str
                });
        }
    }
}      

寫好之後,下次我在别的項目裡面就直接引用。

一步步開發自己的部落格 番外篇(7、異步記錄日志 和 文章閱讀量統計)

如果你使用的是EF,那麼我再告訴你一個小秘密。 DbContext 中的 Database.Log 可以直接記錄所有EF執行的sql語句和參數。

使用如: dbContext.Database.Log = LogSave.TrackLogSave;  而LogSave.TrackLogSave我們在上面已經封裝過。

效果圖1  效果圖2 

文章閱讀量統計

我在一開始就琢磨着怎麼統計閱讀量。之前也在http://www.cnblogs.com/zhaopei/p/4744846.html的最後提出了這個疑問。

一步步開發自己的部落格 番外篇(7、異步記錄日志 和 文章閱讀量統計)

遺憾的是,并沒有誰告訴我更好的解決方案。

好吧,靠人不如靠己。還是自己瞎折騰吧。

但是,實作方式還是使用的我自己的提出的“如果實在是找不到好的解決方案,我打算用 IP+系統版本+浏覽器版本号+.... 作為“聯合主鍵”,如果“主鍵”24小時内重複兩次以上,則不統計,如果cookie存在也不統計。”

1、我們在每次浏覽器通路的時候都種下cookie,并設定過期時間為24小時。下次,浏覽器通路的時候。我們檢測如果存在我們種下的cookie。則直接忽略。

2、如果沒有帶上我們的cookie。我們就先組合“聯合主鍵”。然後檢測24小時内的記錄有沒有這個“聯合主鍵”。如果有,則忽略,否則在原有閱讀量的基礎上加一,然後存入“聯合主鍵”。

這裡的"聯合主鍵"有個小技巧。大家肯定都發現了,這個主鍵有點長。存資料庫有點浪費空間(我資料庫本來就隻有50M),然後查詢檢索應該也會慢些吧(并不清楚)。我們想想,其實我們要的不是這麼長一串東東。其實,我們隻要得到這串東西代表的唯一性就可以了。那麼我們可以用到md5,咱不管你是1G、2G還是高清或是無碼。統統給你傳回一定長度字元串(我取的是16位小寫)。

随着資料的增加,這個統計閱讀量的表資料,肯定是所有表中最大的。然而,我們統計閱讀量是在,點選通路文章的時候,然後在統計閱讀量這個環節卡太久,給人的感覺就是這個頁面通路太慢,體驗不好。

然而,我們每次統計都需要檢測資料庫裡面是否存在,且資料量還不小。那我們隻有再開個程序來做統計。

具體實作代碼:

#region 判斷是否閱讀過 如果沒有 這在BlogReadInfo 插入一條辨別資訊
private bool IsRead(Blogs.ModelDB.Blogs blogobj, string md5)
{
    if (blogobj.BlogReadInfo.Where(t => t.MD5 == md5 && t.LastTime.AddHours(24) > DateTime.Now).Count() > 0)
        return true;
    else
    {
        //BLL.
        blogobj.BlogReadInfo.Add(new Blogs.ModelDB.BlogReadInfo()
        {
            MD5 = md5,
            IsDel = false,
            BlogsId = blogobj.Id,
            CreateTime = DateTime.Now,
            UpTime = DateTime.Now,
            LastTime = DateTime.Now
        });
        return false;
    }
}
#endregion      
#region 統計閱讀量 異步調用方法
delegate void SaveReadDelegate(ModelDB.Blogs blogobj, string md5);
private void SaveReadNum(ModelDB.Blogs blogobj, string md5)
{
    LogSave.TrackLogSave(GetUserDistinguish(Request, false), "ReadBlogLog");
    var isup = true;
    BLL.BlogsBLL blogbll = new BLL.BlogsBLL();
    var blogtemp = blogbll.GetList(t => t.Id == blogobj.Id, isAsNoTracking: false).FirstOrDefault();
    if (blogtemp.BlogReadNum == null)
        blogtemp.BlogReadNum = 1;
    else if (!IsRead(blogtemp, md5))
        blogtemp.BlogReadNum++;
    else
        isup = false;
    if (isup)
        BLL.BlogCommentSetBLL.StaticSave();
}      
#region 擷取用戶端辨別(僞)
/// <summary>
///  擷取用戶端辨別 用來判斷是否已經閱讀過此文章
/// </summary>
/// <param name="requestt"></param>
/// <param name="IsMD5">是否已經md5加密</param>
/// <returns></returns>
private string GetUserDistinguish(HttpRequestBase requestt, bool IsMD5 = true)
{
    //request
    StringBuilder str = new StringBuilder();
    string ip = "";
    if (requestt.ServerVariables.AllKeys.Contains("HTTP_X_FORWARDED_FOR") && requestt.ServerVariables.Get("HTTP_X_FORWARDED_FOR") != null)
        ip = requestt.ServerVariables.Get("HTTP_X_FORWARDED_FOR").ToString().Trim();
    else
        ip = requestt.ServerVariables.Get("Remote_Addr").ToString().Trim();
    str.Append("Ip:" + ip);
    str.Append("\r\n浏覽器:" + requestt.Browser.Browser.ToString());
    str.Append("\r\n浏覽器版本:" + requestt.Browser.MajorVersion.ToString());
    str.Append("\r\n作業系統:" + requestt.Browser.Platform.ToString());
    str.Append("\r\n頁面:" + requestt.Url.ToString());
    //str.Append("用戶端IP:" + requestt.UserHostAddress);
    str.Append("\r\n使用者資訊:" + User);
    str.Append("\r\n浏覽器辨別:" + requestt.Browser.Id);
    str.Append("\r\n浏覽器版本号:" + requestt.Browser.Version);
    str.Append("\r\n浏覽器是不是測試版本:" + requestt.Browser.Beta);
    //str.Append("<br/>浏覽器的分辨率(像素):" + Request["width"].ToString() + "*" + Request["height"].ToString());//1280/1024                        
    str.Append("\r\n是不是win16系統:" + requestt.Browser.Win16);
    str.Append("\r\n是不是win32系統:" + requestt.Browser.Win32);
    if (IsMD5)
        return str.ToString().GetMd5_16();
    else
        return str.ToString();
}
#endregion      

(當然,這個方式統計也不一定準。請求頭資訊改改就被僞造了。)

然後我們通過委托從線程池抓去線程異步調用

//........................異步調用....................
new SaveReadDelegate(SaveReadNum).BeginInvoke(blogobj, GetUserDistinguish(Request), null, null);      

ok,統計完事。 

如果您對本篇文章感興趣,那就麻煩您點個贊,您的鼓勵将是我的動力。      

當然您還可以加入QQ群:469075305讨論。

如果您有更好的處理方式,希望不要吝啬賜教。

一步步開發自己的部落格 .NET版系列:http://www.cnblogs.com/zhaopei/tag/Hi-Blogs/

本文連結:http://www.cnblogs.com/zhaopei/p/4887573.html 

開源位址:http://git.oschina.net/zhaopeiym/Hi-Blogs

  • 學習本是一個不斷抄襲、模仿、練習、創新的過程。
  • 雖然,園中已有本人無法超越的同主題博文,為什麼還是要寫。
  • 對于自己,博文隻是總結。在總結的過程發現問題,解決問題。
  • 對于他人,在此過程如果還能附帶幫助他人,那就再好不過了。
  • 由于部落客能力有限,文中可能存在描述不正确,歡迎指正、補充!
  • 感謝您的閱讀。如果文章對您有用,那麼請輕輕點個贊,以資鼓勵。
  • 工控物聯Q群:995475200

繼續閱讀