系列文章
- 基于.NetCore開發部落格項目 StarBlog - (1) 為什麼需要自己寫一個部落格?
- 基于.NetCore開發部落格項目 StarBlog - (2) 環境準備和建立項目
- 基于.NetCore開發部落格項目 StarBlog - (3) 模型設計
- 基于.NetCore開發部落格項目 StarBlog - (4) markdown部落格批量導入
- 基于.NetCore開發部落格項目 StarBlog - (5) 開始搭建Web項目
- 基于.NetCore開發部落格項目 StarBlog - (6) 頁面開發之部落格文章清單
- 基于.NetCore開發部落格項目 StarBlog - (7) 頁面開發之文章詳情頁面
- 基于.NetCore開發部落格項目 StarBlog - (8) 分類層級結構展示
- 基于.NetCore開發部落格項目 StarBlog - (9) 圖檔批量導入
- 基于.NetCore開發部落格項目 StarBlog - (10) 圖檔瀑布流
- 基于.NetCore開發部落格項目 StarBlog - (11) 實作通路統計
- 基于.NetCore開發部落格項目 StarBlog - (12) Razor頁面動态編譯
- 基于.NetCore開發部落格項目 StarBlog - (13) 加入友情連結功能
- 基于.NetCore開發部落格項目 StarBlog - (14) 實作主題切換功能
- 基于.NetCore開發部落格項目 StarBlog - (15) 生成随機尺寸圖檔
- 基于.NetCore開發部落格項目 StarBlog - (16) 一些新功能 (監控/統計/配置/初始化)
前言
最近好幾天都忙着寫代碼,沒更新文章,近期給部落格增加了一些功能,本文一次性介紹~
新增的功能如下:
- 系統監控:sentry、exceptionless、CLRStats
- 通路統計
- 配置管理
- 初始化
同時所有項目代碼已經上傳GitHub,歡迎各位大佬Star/Fork!
- 網站位址:http://blog.deali.cn
- 部落格後端+前台項目位址:https://github.com/Deali-Axy/StarBlog
- 管理背景前端項目位址:https://github.com/Deali-Axy/StarBlog-Admin
系統監控
PS:其實前面兩篇關于日志收集工具介紹的文章也是為了給本文做鋪墊
系統監控這塊包括日志收集、性能監測和系統狀态監測。
日志收集和性能監測我交給了ExceptionLess和Sentry,這倆開源的工具可以很好的完成這些工作,詳情可以看我之前寫的這倆篇文章:
- Sentry的安裝、配置、使用
- ExceptionLess的安裝、配置、使用
然後狀态監測我是基于GitHub上的一個開源項目來魔改的:CLRStats
這個元件可以實時檢視CPU、GC、線程的狀态,不過原版的實作是作為一個中間件嵌入AspNetCore項目,并且通路的位址隻能用Basic認證,不适用。
于是我把代碼clone下來之後魔改了一下,變成一個可以調用的服務,并且重新寫了API接口,終于友善起來了~
這個接口拿到的資料是這樣的,後續在管理背景裡面做成可視化圖表也比較友善。
{
"server": {
"machineName": "machineName",
"systemDateTime": "7/26/2022 11:30:22 PM"
},
"application": {
"cpu": {
"usagePercent": 0
},
"gc": {
"gen0CollectCount": 24,
"gen1CollectCount": 23,
"gen2CollectCount": 22,
"heapMemory": 38328872,
"heapMemoryFormat": "36 M",
"isServerGC": true
},
"thread": {
"availableCompletionPortThreads": 1000,
"availableWorkerThreads": 32766,
"usedCompletionPortThreads": 0,
"usedWorkerThreads": 1,
"usedThreadCount": 29,
"maxCompletionPortThreads": 1000,
"maxWorkerThreads": 32767
}
}
}
具體代碼就不複制粘貼了,我把它放在
StarBlog.Contrib
項目中,作為一個獨立的元件友善調用。
通路統計
雖然前面這篇文章有介紹通路統計的實作:基于.NetCore開發部落格項目 StarBlog - (11) 實作通路統計
不過隻是單純講了通過中間件實作通路記錄,這些資料存在資料庫之後并沒有被利用起來
現在就實作了一些簡單的統計,目前主要實作了總覽資料(總通路、今日、昨日通路)、趨勢資料、指定日期統計,這幾個功能。
邏輯代碼在
StarBlog.Web/Services/VisitRecordService.cs
中
總覽資料代碼在這
PS:我發現FreeSQL的ISelect對象在鍊式操作時候的行為很奇怪,我知道是懶加載,但是已經執行了
.Count()
似乎還沒執行,下一行調用的代碼甚至會把前面的篩選條件加上,無奈我之内在方法内又寫了一個嵌套的方法……
也就是這個
,這點讓我這種用習慣DjangoORM的人覺得很不适應 = =..
GetQuerySet
這個接口計算總通路量、今日通路量、昨日通路量,友善做對比(受知乎的創作者中心啟發)
public object Overview() {
ISelect<VisitRecord> GetQuerySet() => _repo.Where(a => !a.RequestPath.StartsWith("/Api"));
return new {
TotalVisit = GetQuerySet().Count(),
TodayVisit = GetQuerySet().Where(a => a.Time.Date == DateTime.Today).Count(),
YesterdayVisit = GetQuerySet().Where(a => a.Time.Date == DateTime.Today.AddDays(-2).Date).Count(),
};
}
趨勢資料
也就是統計最近n天的通路量
PS:C#的日期處理還是比較舒服的
public object Trend(int days = 7) {
return _repo.Where(a => !a.RequestPath.StartsWith("/Api"))
.Where(a => a.Time.Date > DateTime.Today.AddDays(-days).Date)
.GroupBy(a => a.Time.Date)
.ToList(a => new {
time = a.Key,
date = $"{a.Key.Month}-{a.Key.Day}",
count = a.Count()
});
}
按日期統計
這個簡單粗暴不用多說
public object Stats(DateTime date) {
var data = _repo.Where(a => a.Time.Date == date.Date && !a.RequestPath.StartsWith("/Api"));
return new { Count = data.Count() };
}
注意這裡面所有的統計我都過濾了以
/Api
開頭的位址,因為我隻需要統計部落格前台的通路量就行了。
配置管理
部落格還是有很多需要配置的東西,比如說Host
之前我是寫在
appsettings.json
檔案裡的,按理說也可以,修改這檔案之後也能hot reload,不過問題是沒法實作在管理背景中修改并儲存
是以我打算實作一個配置管理的功能
一開始是把目光瞄準了KV資料庫,甚至要求找一個嵌入式的、C#實作的開源項目,疊了這麼多buff,果然沒找到合适的
(不過前幾天好像看到有個大佬發了篇文章介紹用C#手寫一個KV資料庫的,大贊!)
于是還是用部落格本身的資料來實作好了,也不難
老規矩,繼續寫Service:
StarBlog.Web/Services/ConfigService.cs
這裡隻把關鍵代碼放出來,完整代碼可以看GitHub
public class ConfigService {
private readonly IConfiguration _conf;
private readonly IBaseRepository<ConfigItem> _repo;
public ConfigItem? GetByKey(string key) {
var item = _repo.Where(a => a.Key == key).First();
if (item == null) {
// 嘗試讀取初始化配置
var section = _conf.GetSection($"StarBlog:Initial:{key}");
if (!section.Exists()) return null;
item = new ConfigItem { Key = key, Value = section.Value, Description = "Initial" };
item = AddOrUpdate(item);
}
return item;
}
public ConfigItem AddOrUpdate(ConfigItem item) {
return _repo.InsertOrUpdate(item);
}
public int? Update(string key, string value, string? description = default) {
var item = GetByKey(key);
if (item == null) return null;
item.Value = value;
if (description != null) item.Description = description;
return _repo.Update(item);
}
public string this[string key] {
get {
var item = GetByKey(key);
return item == null ? "" : item.Value;
}
set {
var item = GetByKey(key) ?? new ConfigItem { Key = key };
item.Value = value;
AddOrUpdate(item);
}
}
}
這個
ConfigService
實作了索引器,可以比較友善的實作配置的讀取和儲存
比如這樣
var conf = xxx; // 注入 ConfigService
// 讀取配置
Console.WriteLine(conf["host"]);
// 修改配置
conf["host"] = "http://dealiaxy.com";
同時我也寫了幾個接口,可以通過HTTP的方式管理配置,代碼就不放了~
初始化
在我的設計中,這個功能是依賴于配置管理的
是以把配置管理做完之後,我的初始化頁面也做出來了
看起來是這樣的
也就是首次運作本項目的時候,會進入這個頁面,目前的初始化配置就隻有建立管理、設定Host兩個,後續應該會慢慢增加其他的
背景是通過
is_init
這個字段來判斷是否有初始化的
直接上Controller代碼
[HttpGet]
public IActionResult Init([FromServices] ConfigService conf) {
if (conf["is_init"] == "true") {
_messages.Error("已經完成初始化!");
return RedirectToAction(nameof(Index));
}
return View(new InitViewModel {
Host = conf["host"]
});
}
[HttpPost]
public IActionResult Init([FromServices] ConfigService conf, [FromServices] IBaseRepository<User> userRepo, InitViewModel vm) {
if (!ModelState.IsValid) return View();
// 儲存配置
conf["host"] = vm.Host;
conf["is_init"] = "true";
// 建立使用者
// todo 這裡暫時存儲明文密碼,後期要換成MD5加密存儲
userRepo.Insert(new User {
Id = Guid.NewGuid().ToString(),
Name = vm.Username,
Password = vm.Password
});
_messages.Success("初始化完成!");
return RedirectToAction(nameof(Index));
}
同時還要實作一個View頁面,這個就比較簡單,代碼不放了
微信公衆号:「程式設計實驗室」
專注于網際網路熱門新技術探索與團隊靈活開發實踐,包括架構設計、機器學習與資料分析算法、移動端開發、Linux、Web前後端開發等,歡迎一起探讨技術,分享學習實踐經驗。