分佈式任務調度系統,純NET打造的重量級大數據實時計算平臺,萬億級調度經驗積累!面向中小企業大數據分析場景。
開源地址:https://github.com/NewLifeX/AntJob
使用教程:https://www.yuque.com/smartstone/blood/antjob
體驗地址:http://ant.newlifex.com
功能特點
AntJob的核心是螞蟻算法:把任意大數據拆分成爲小塊,採用螞蟻搬家策略計算每一塊!
(螞蟻搬家,一個饅頭掉在地上,衆多小螞蟻會把饅頭掰成小塊小塊往家裏般!)
該算法設計於2008年,最開始用於處理基金公司的短信/郵件/傳真羣發(每批兩百萬)和電話話費分析(上百種國際長途計費規則),數據量不算大,但是有一定複雜度,並且要求支持持續處理(實時計算)以及出錯重試。
2016年在中通快遞某產品項目中使用該算法進行大數據實時計算,成功挑戰每日1200萬的訂單。並進一步發展衍生成爲重量級實時計算平臺,集分佈式計算、集羣調度、配置中心、負載均衡、故障轉移、跨機房冗餘、作業監控告警、百億級數據清洗、超大Redis緩存(>2T)於一身,於2019年達到每年萬億級計算量(2019年雙十一日訂單量破億)。
AntJob是開源簡化版,僅提供分佈式計算和集中調度能力,支持百億級調度(需要改造)。
AntJob主要功能點:
作業處理器。每一個最小業務模塊實現一個處理器類,用於處理這一類作業。例如同步數據表時,每張表寫一個處理器類,並在調度中心註冊一個作業,調度中心按照作業時間切片得到任務,然後把任務(主要包含時間區間)分派給各個計算節點上的處理器類執行。又如,每天彙總計算是一個作業,而每月彙總計算又是另一個作業;
任務上下文。作業處理器類實例化以後,將反覆向調度中心申請任務來執行,每個任務的上下文核心數據是時間區間(數據調度)、時間點(定時調度)、消息體(消息調度)。調度中心記錄任務處理結果;
數據切片。支持按照時間區間(如5秒)把大數據切分爲小片,也即是數據調度,處理過最大單表60億行;
定時調度。支持定時執行(秒級)指定業務邏輯,每個執行時間點得到一個任務;
任務重試。每個任務完整記錄處理結果,失敗任務在延遲一段時間後將會自動重新分派(可能由原節點或其它節點執行);
任務重置。支持批量重置已執行完成的任務,讓其再次執行處理;
作業面板。在Web控制檯上可查看每個應用所有作業的運行狀態,或修改參數;
作業重置。調整作業參數,讓其再次處理某段時間的任務數據,例如重算過去一個月的數據;
其它細節功能將穿插在以下各主要功能點中進行講解。
定時調度
以下源碼位於 https://github.com/NewLifeX/AntJob/tree/master/Samples/HisAgent
新建項目
新建.net core 3.1項目,從nuget引用 AntJob。實例化一個調度器Scheduler,配置網絡提供者。
using System;
using AntJob;
using AntJob.Providers;
using NewLife.Log;
namespace HisAgent
{
class Program
{
static void Main(string[] args)
{
XTrace.UseConsole();
var set = AntSetting.Current;
// 實例化調度器
var sc = new Scheduler();
// 使用分佈式調度引擎替換默認的本地文件調度
sc.Provider = new NetworkJobProvider
{
Server = set.Server,
AppID = set.AppID,
Secret = set.Secret,
};
// 添加作業處理器
sc.Handlers.Add(new HelloJob());
// 啓動調度引擎,調度器內部多線程處理
sc.Start();
Console.WriteLine("OK!");
Console.ReadKey();
}
}
}
然後添加第一個定時調度的作業處理器
using System;
using AntJob;
namespace HisAgent
{
internal class HelloJob : Handler
{
public HelloJob()
{
// 今天零點開始,每10秒一次
var job = Job;
job.Start = DateTime.Today;
job.Step = 10;
}
protected override Int32 Execute(JobContext ctx)
{
// 當前任務時間
var time = ctx.Task.Start;
WriteLog("新生命螞蟻調度系統!當前任務時間:{0}", time);
// 成功處理數據量
return 1;
}
}
}
作業處理器必須繼承自Handler,並且重寫Execute實現業務邏輯。
我們這裏的業務邏輯就是輸出一行日誌,其中的ctx.Task就是切分得到的任務上下文,Start是時間點。
構造函數中設定的開始時間和步進Step,僅用於首次註冊作業到調度中心,後面就沒有用處了。
爲了編譯觀察,修改項目輸出目錄,在項目文件上點右鍵選“編輯項目文件”
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<AssemblyVersion>1.0.*</AssemblyVersion>
<Deterministic>false</Deterministic>
<OutputPath>..\..\Bin\HisAgent</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
</PropertyGroup>
編譯執行
代碼能編譯通過,先跑起來看看
可以看到,調度器首先連接 tcp://127.0.0.1:9999,其次 tcp://ant.newlifex.com:9999 ,而上面代碼中並沒有提及這兩個地址。其實這就是調度中心地址,默認本地用於調試,如果鏈接失敗再連接公開版調度中心,位於配置文件中:
/// <summary>螞蟻配置。主要用於網絡型調度系統</summary>
[Config("Ant")]
public class AntSetting : Config<AntSetting>
{
#region 屬性
/// <summary>調試開關。默認false</summary>
[Description("調試開關。默認false")]
public Boolean Debug { get; set; }
/// <summary>調度中心。逗號分隔多地址,主備架構</summary>
[Description("調度中心。逗號分隔多地址,主備架構")]
public String Server { get; set; } = "tcp://127.0.0.1:9999,tcp://ant.newlifex.com:9999";
/// <summary>應用標識。調度中心以此隔離應用,默認當前應用</summary>
[Description("應用標識。調度中心以此隔離應用,默認當前應用")]
public String AppID { get; set; }
/// <summary>應用密鑰。</summary>
[Description("應用密鑰。")]
public String Secret { get; set; }
#endregion
#region 方法
/// <summary>重載</summary>
protected override void OnLoaded()
{
if (AppID.IsNullOrEmpty())
{
var asm = Assembly.GetEntryAssembly();
if (asm != null) AppID = asm.GetName().Name;
}
base.OnLoaded();
}
#endregion
}
其實上面Main函數中已經看到從配置文件裏面讀取Server+AppID+Secret,該配置類讀取的配置文件在這:
AppID默認取本應用名,Secret由調度中心生成並下發。
調度中心默認打開自動註冊AutoRegistry,任意應用登錄時自動註冊,省去人工配置應用賬號的麻煩。
企業內部正式場景使用時,爲安全期間,建議關閉自動註冊。
再來看看前面跑起來的日誌
21:33:08.470 1 N - 啓動任務調度引擎[AntJob.Providers.NetworkJobProvider],作業[1]項,定時5秒
21:33:08.471 1 N - HelloJob 開始工作 False 區間(2020-04-09 00:00:00, 0001-01-01 00:00:00) Offset=15 Step=10 MaxTask=8
21:33:08.587 5 Y Job HelloJob 停止工作
21:33:09.467 7 Y T [180.174.185.180:53926]上線!X3
啓動了調度引擎,帶有一個作業;
作業HelloJob,就是我們通過 sc.Handlers.Add(new HelloJob())
添加進去的作業處理器實例;
HelloJob狀態False,處於停止工作狀態,那是因爲作業註冊後,默認都是停止狀態,需要去web控制檯配置參數後手工開啓;
最後一個xxx上線,這是螞蟻調度的Peers功能,可以探測得到當前應用下所有已連接節點的狀態。當HisAgent部署於多個服務器時,每個進程都可以通過Peers得知其它節點的存在;
作業管理
不用關閉HistAgent客戶端窗口,我們去線上web控制檯看看 http://ant.newlifex.com/
可以看到應用節點在線,點擊應用名進去作業面板
這就是我們的HelloJob作業,對應HisAgent中的HelloJob作業處理器。
它處於停用狀態,下一次執行時間是 00:00:00 ,也就是今天零點,加上10秒步進,也遠小於當前時間,因此,只要啓用該作業,調度中心將會馬上開始切分任務,並分派給客戶去執行。
我們來點擊紅色叉叉,讓它改變爲啓用狀態
幾秒後,客戶端HisAgent歡快地跑起來!它正在以10秒間隔不斷切分並執行任務。
刷新作業面板,可以看到,開始時間已經變爲當前附近的時間,右邊也有了執行次數。
點擊作業名HelloJob,進去查看任務明細
任務切分後,插入作業任務表,此時狀態爲“就緒”,等待分發給客戶端執行。
客戶端執行後,向調度中心報告執行結果,可能“完成”,可能“錯誤”。
錯誤的任務,會在1分鐘後,重新執行,最多連續錯誤10次。
隨系統自動啓動
至今我們仍然使用控制檯來跑調度程序,怎麼樣實現穩定可靠的自動化處理呢?
那就必須解決隨系統自動啓動,以及進程守護(包括Windows和Linux)的問題。
這裏推薦 NewLife.Agent,可以把調度程序包裝成爲一個 Windows服務,或者Linux守護進程,支持看門狗守護。
此處爲語雀文檔,點擊鏈接查看:https://www.yuque.com/smartstone/nx/agent
多節點部署時,推薦 星塵Stardust 中的星塵代理 StarAgent,調度程序無需修改繼續使用控制檯,由StarAgent負責拉起進程並守護,同時Stardust支持遠程多節點部署以及集中監控。
此處爲語雀文檔,點擊鏈接查看:https://www.yuque.com/smartstone/blood/stardust
雙跑,沸騰吧,分佈式計算
再開兩個HisAgent進程,查看應用在線表,可以看到有三個節點在線。
HisAgent控制檯中,可以看到各自都有機會分配了任務,每個任務有且僅有一個節點執行。
刷新作業HelloJob的任務列表,可以看到不同客戶端執行了不同的任務。
調度中心
公開版調度中心 http://ant.newlifex.com 僅用於開發測試,不建議用於生產場景。各企業內部應該自己部署調度中心。
獲取AntJob源碼 https://github.com/NewLifeX/AntJob ,編譯 AntJob.Server,然後跑起來 AntServer.exe
這是一個標準的NewLife.Agent應用,可以選擇2安裝爲Windows服務(需要管理員權限),或者Linux守護進程(需要root權限)。這裏僅爲了測試,選擇5循環調試,直接跑起來核心業務:
可以看到AntServer在tcp/udp/ipv6上監聽了9999接口,下方是它所使用的RPC接口。除了前三個內置接口意外,AntJob的接口也就7個,非常簡單!
ApiServer的具體內容可參考
此處爲語雀文檔,點擊鏈接查看:https://www.yuque.com/smartstone/nx/api_server
再啓動一個HisAgent
可以看到,它自動連接了本機這個調度中心,因爲配置文件Server裏面,127寫在第一位!
AntJob客戶端支持調度中心的故障轉移,配置多個服務端,其中一個斷開後,自動選擇下一個。
配置文件 Config\AntJob.config 很簡單,只有端口和自動註冊開關。
<?xml version="1.0" encoding="utf-8"?>
<Setting>
<!--調試開關。默認true-->
<Debug>true</Debug>
<!--端口-->
<Port>9999</Port>
<!--自動註冊。任意應用登錄時自動註冊,省去人工配置應用賬號的麻煩,默認true-->
<AutoRegistry>true</AutoRegistry>
</Setting>
多年使用經驗來看,還沒遇到過需要關閉自動註冊的情況,畢竟都是在企業內網。
推薦部署兩套調度中心,一套Web控制檯,共用MySql數據庫!
如果服務器足夠多,或者爲了跨機房,部署4套8套也是可以的。
Web控制檯
爲了查看作業任務狀態,以及調整參數,控制作業啓停,需要藉助控制檯。
獲取源碼 https://github.com/NewLifeX/AntJob ,編譯 AntJob.Web,執行 AntWeb.exe。也可以訪問公開版AntJob控制檯 http://ant.newlifex.com/ 。
首先可以看到應用管理,點擊應用名進去應用面板,管理該應用底下的作業。
雙擊應用所在行空白處,可查看修改應用信息
應用在線記錄每個應用實例(應用可以多跑)的實時狀態,應用歷史記錄操作歷史。
任務處理過程中,如果拋出異常,將會上報給調度中心,標記任務爲“錯誤”狀態,同時把錯誤信息記錄到作業錯誤中來。也可以通過應用或作業的快捷方式鏈接進來。
應用消息用於消息調度,消息生產者把消息推送給調度中心時,就是存儲在“應用消息”數據表中,消費的時候取出來,創建消息型任務,並從“應用消息”表中刪除。
數據調度
數據調度時AntJob毫無疑問的首席角色,它的使用佔比超過70%,可見其重要程度。
爲了方便處理大數據,我們需要新建一些輔助項目,數據結構來自某醫院。
新建數據集項目
新建 .netstandard2.0 類庫項目,nuget引用 NewLife.XCode,準備醫院的模型文件:
<?xml version="1.0" encoding="utf-8"?>
<Tables Version="9.16.7398.1902" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" xs:schemaLocation="http://www.newlifex.com http://www.newlifex.com/Model2020.xsd" NameSpace="HisData" ConnName="His" Output="Entity" BaseClass="Entity" IgnoreNameCase="True" xmlns="http://www.newlifex.com/Model2020.xsd">
<Table Name="ZYBH0" Description="病人基本信息" IgnoreNameCase="False">
<Columns>
<Column Name="ID" DataType="Int32" Identity="True" PrimaryKey="True" Description="編號" />
<Column Name="Bhid" ColumnName="BHID" DataType="Int32" Master="True" Description="病人ID" />
<Column Name="XM" DataType="String" Description="姓名" />
<Column Name="Ryrq" ColumnName="RYRQ" DataType="Int32" Description="入院日期" />
<Column Name="Cyrq" ColumnName="CYRQ" DataType="Int32" Description="出院日期" />
<Column Name="Sfzh" ColumnName="SFZH" DataType="String" Description="身份證號" />
<Column Name="FB" DataType="String" Description="費用類別" />
<Column Name="State" ColumnName="STATE" DataType="Int32" Description="狀態" />
<Column Name="Flag" ColumnName="FLAG" DataType="Int32" Description="標記" />
<Column Name="Remark" DataType="String" Length="500" Description="內容" />
<Column Name="CreateUser" DataType="String" Description="創建者" />
<Column Name="CreateUserID" DataType="Int32" Description="創建者" />
<Column Name="CreateTime" DataType="DateTime" Description="創建時間" />
<Column Name="CreateIP" DataType="String" Description="創建地址" />
<Column Name="UpdateUser" DataType="String" Description="更新者" />
<Column Name="UpdateUserID" DataType="Int32" Description="更新者" />
<Column Name="UpdateTime" DataType="DateTime" Description="更新時間" />
<Column Name="UpdateIP" DataType="String" Description="更新地址" />
</Columns>
<Indexes>
<Index Columns="BHID" Unique="True" />
</Indexes>
</Table>
<Table Name="ZYBHYZ0" Description="病人醫囑信息" IgnoreNameCase="False">
<Columns>
<Column Name="ID" DataType="Int32" Identity="True" PrimaryKey="True" Description="編號" />
<Column Name="Bhid" ColumnName="BHID" DataType="Int32" Description="病人ID" />
<Column Name="Mgroupid" ColumnName="MGROUPID" DataType="Int32" Master="True" Description="醫囑組號" />
<Column Name="Kyzrq" ColumnName="KYZRQ" DataType="Int32" Description="開醫囑日期" />
<Column Name="Tyzrq" ColumnName="TYZRQ" DataType="Int32" Description="停醫囑日期" />
<Column Name="Kyzys" ColumnName="KYZYS" DataType="String" Description="開醫囑醫生" />
<Column Name="State" ColumnName="STATE" DataType="Int32" Description="狀態" />
<Column Name="CreateUser" DataType="String" Description="創建者" />
<Column Name="CreateUserID" DataType="Int32" Description="創建者" />
<Column Name="CreateTime" DataType="DateTime" Description="創建時間" />
<Column Name="CreateIP" DataType="String" Description="創建地址" />
<Column Name="UpdateUser" DataType="String" Description="更新者" />
<Column Name="UpdateUserID" DataType="Int32" Description="更新者" />
<Column Name="UpdateTime" DataType="DateTime" Description="更新時間" />
<Column Name="UpdateIP" DataType="String" Description="更新地址" />
</Columns>
<Indexes>
<Index Columns="BHID,MGROUPID" Unique="True" />
<Index Columns="BHID" />
</Indexes>
</Table>
<Table Name="ZYBHYZ1" Description="病人醫囑明細信息" IgnoreNameCase="False">
<Columns>
<Column Name="ID" DataType="Int32" Identity="True" PrimaryKey="True" Description="編號" />
<Column Name="Dgroupid" ColumnName="DGROUPID" DataType="Int32" Master="True" Description="醫囑組號" />
<Column Name="Yzbm" ColumnName="YZBM" DataType="String" Description="醫囑編碼" />
<Column Name="Yzmc" ColumnName="YZMC" DataType="String" Description="醫囑名稱" />
<Column Name="DJ" DataType="Decimal" Description="單價" />
<Column Name="SL" DataType="Double" Description="數量" />
<Column Name="FY" DataType="Decimal" Description="費用" />
<Column Name="State" ColumnName="STATE" DataType="Int32" Description="狀態" />
<Column Name="CreateUser" DataType="String" Description="創建者" />
<Column Name="CreateUserID" DataType="Int32" Description="創建者" />
<Column Name="CreateTime" DataType="DateTime" Description="創建時間" />
<Column Name="CreateIP" DataType="String" Description="創建地址" />
<Column Name="UpdateUser" DataType="String" Description="更新者" />
<Column Name="UpdateUserID" DataType="Int32" Description="更新者" />
<Column Name="UpdateTime" DataType="DateTime" Description="更新時間" />
<Column Name="UpdateIP" DataType="String" Description="更新地址" />
</Columns>
<Indexes>
<Index Columns="DGROUPID,YZBM" Unique="True" />
<Index Columns="DGROUPID" />
</Indexes>
</Table>
<Table Name="ZYYFQLD" Description="病人藥房請領單分月表202001" IgnoreNameCase="False">
<Columns>
<Column Name="ID" DataType="Int32" Identity="True" PrimaryKey="True" Description="編號" />
<Column Name="Qlrq" ColumnName="QLRQ" DataType="Int32" Description="請領日期" />
<Column Name="Qlsj" ColumnName="QLSJ" DataType="Int32" Description="請領時間" />
<Column Name="Ksbm" ColumnName="KSBM" DataType="String" Description="請領科室" />
<Column Name="Yzgroupid" ColumnName="YZGROUPID" DataType="Int32" Description="醫囑ID" />
<Column Name="Bhid" ColumnName="BHID" DataType="Int32" Description="病人ID" />
<Column Name="Yzbm" ColumnName="YZBM" DataType="String" Description="藥品編碼" />
<Column Name="DJ" DataType="Decimal" Description="單價" />
<Column Name="SL" DataType="Double" Description="請領數量" />
<Column Name="Yfbm" ColumnName="YFBM" DataType="String" Description="發藥藥房" />
<Column Name="Fyrq" ColumnName="FYRQ" DataType="Int32" Description="發藥日期" />
<Column Name="State" ColumnName="STATE" DataType="Int32" Description="狀態" />
<Column Name="Remark" DataType="String" Length="500" Description="內容" />
<Column Name="CreateUser" DataType="String" Description="創建者" />
<Column Name="CreateUserID" DataType="Int32" Description="創建者" />
<Column Name="CreateTime" DataType="DateTime" Description="創建時間" />
<Column Name="CreateIP" DataType="String" Description="創建地址" />
<Column Name="UpdateUser" DataType="String" Description="更新者" />
<Column Name="UpdateUserID" DataType="Int32" Description="更新者" />
<Column Name="UpdateTime" DataType="DateTime" Description="更新時間" />
<Column Name="UpdateIP" DataType="String" Description="更新地址" />
</Columns>
<Indexes>
<Index Columns="BHID" />
</Indexes>
</Table>
<Table Name="ZDSF" Description="收費字典" IgnoreNameCase="False">
<Columns>
<Column Name="ID" DataType="Int32" Identity="True" PrimaryKey="True" Description="編號" />
<Column Name="BM" DataType="String" Master="True" Nullable="False" Description="編碼" />
<Column Name="DH" DataType="String" Description="拼音碼" />
<Column Name="MC" DataType="String" Description="名稱" />
<Column Name="DJ" DataType="Decimal" Description="單價" />
<Column Name="DW" DataType="String" Description="單位" />
<Column Name="Mzyflb" ColumnName="MZYFLB" DataType="Int32" Description="門診費用類別" />
<Column Name="Zyfylb" ColumnName="ZYFYLB" DataType="Int32" Description="住院費用類別" />
<Column Name="Zfbl" ColumnName="ZFBL" DataType="Double" Description="自費比例" />
<Column Name="CreateUser" DataType="String" Description="創建者" />
<Column Name="CreateUserID" DataType="Int32" Description="創建者" />
<Column Name="CreateTime" DataType="DateTime" Description="創建時間" />
<Column Name="CreateIP" DataType="String" Description="創建地址" />
<Column Name="UpdateUser" DataType="String" Description="更新者" />
<Column Name="UpdateUserID" DataType="Int32" Description="更新者" />
<Column Name="UpdateTime" DataType="DateTime" Description="更新時間" />
<Column Name="UpdateIP" DataType="String" Description="更新地址" />
</Columns>
<Indexes>
<Index Columns="BM" Unique="True" />
</Indexes>
</Table>
</Tables>
再去找一個 build_netcore.tt 的T4模板,可以這裏下載 http://x.newlifex.com/XCode_BuildModel.zip 。也可以從AntJob.Web項目中拷貝一個。
記得把build_netcore.tt在vs文件屬性的自定義工具設置爲TextTemplatingFileGenerator。
由於netstandard項目輸出目錄中不包括XCode.dll等引用程序集,因此build.tt需要改一下
<#@ template language="C#" hostSpecific="true" debug="true" #>
<#@ assembly name="netstandard" #>
<#@ assembly name="$(ProjectDir)\..\..\DLL\NewLife.Core.dll" #>
<#@ assembly name="$(ProjectDir)\..\..\DLL\XCode.dll" #>
<#@ import namespace="System.Diagnostics" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="XCode.Code" #>
<#@ output extension=".log" #>
<#
// 設置當前工作目錄
PathHelper.BasePath = Host.ResolvePath(".");
// 導入模型文件並生成實體類,模型文件、輸出目錄、命名空間、連接名、中文文件名、表名字段名大小寫
//EntityBuilder.Build(String xmlFile = null, String output = null, String nameSpace = null, String connName = null, Boolean? chineseFileName = true,Boolean? nameIgnoreCase = null);
EntityBuilder.Build();
//var tables = DAL.ImportFrom("Company.Project.xml");
//EntityBuilder.Build(tables);
#>
我們從AntJob.Web中拷貝兩個文件 NewLife.Core.dll 和 XCode.dll 到外面的DLL目錄中,供build.tt調用。如果實際目錄不同,可以修改build.tt文件的指向。
在build.tt文件上右鍵,執行自定義工具,即可生成一批實體類。
編譯通過
新建Web項目
新建 .netcore3.1 的web項目,Nuget引用 NewLife.Cube.Core,並引用項目HisData。
該Web項目主要用於查看和管理那些數據表的數據。
修改Main函數,增加 XTrace.UseConsole,用於攔截所有日誌
public class Program
{
public static void Main(string[] args)
{
XTrace.UseConsole();
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
Startup.cs 中引用魔方,services.AddCube()/app.UseCube()
編譯運行,瀏覽器訪問 http://localhost:5000/Admin
可以看到魔方跑起來了,但是還沒有我們需要的數據頁面。
新建His控制器區域
區域文件內容:
using System;
using System.ComponentModel;
using NewLife.Cube;
namespace HisWeb.Areas.His
{
[DisplayName("醫院管理")]
public class HisArea : AreaBase
{
public HisArea() : base(nameof(HisArea).TrimEnd("Area")) { }
static HisArea() => RegisterArea<HisArea>();
}
}
爲每個實體類新建一個控制器,如下
using HisData;
using NewLife.Cube;
namespace HisWeb.Areas.His.Controllers
{
[HisArea]
public class ZYBH0Controller : EntityController<ZYBH0>
{
static ZYBH0Controller() => MenuOrder = 100;
}
}
編譯項目,執行 HisWeb.exe,刷新瀏覽器頁面,即可看到每張數據表對應了一個頁面。
生成病人數據
在引用項目HisData。
新建一個作業處理器 BuildPatient 用於隨機生成病人
using System;
using System.Collections.Generic;
using AntJob;
using HisData;
using NewLife.Security;
using XCode;
namespace HisAgent
{
internal class BuildPatient : Handler
{
public BuildPatient()
{
var job = Job;
job.Start = DateTime.Today;
job.Step = 15;
}
protected override Int32 Execute(JobContext ctx)
{
// 隨機造幾個病人
var count = Rand.Next(1, 9);
var list = new List<ZYBH0>();
for (var i = 0; i < count; i++)
{
var time = DateTime.Now.AddSeconds(Rand.Next(-30 * 24 * 3600, 0));
var time2 = time.AddSeconds(Rand.Next(3600, 10 * 24 * 3600));
var pi = new ZYBH0
{
Bhid = Rand.Next(999999),
XM = Rand.NextString(8),
Ryrq = time,
Cyrq = time2,
Sfzh = Rand.NextString(18),
FB = Rand.NextString(6),
State = Rand.Next(8),
Flag = Rand.Next(2),
};
list.Add(pi);
}
list.Insert(true);
// 成功處理數據量
return count;
}
}
}
主函數中把該處理器添加到調度器
編譯運行,HisAgent將在控制檯新增一個作業,把它啓用
很快,作業處理器就跑起來了
每次定時任務所添加病人數時隨機的,我們通過Execute返回,記錄着控制檯作業任務表的“成功”字段。
去HisWeb中看看數據
很不幸,啥也沒有……
原來,我們並沒有配置數據庫連接字符串,各個應用就會默認使用SQLite數據庫,位於自己目錄中,HisAgent生成的數據,HisWeb自然就無法訪問了。
修改HisWeb輸出目錄,讓它跟HisAgent並排
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<AssemblyVersion>1.0.*</AssemblyVersion>
<Deterministic>false</Deterministic>
<OutputPath>..\..\Bin\HisWeb</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
</PropertyGroup>
修改配置文件appsettings.json給的鏈接字符串
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"His": {
"connectionString": "Data Source=..\\Hisagent\\Data\\His.db",
"providerName": "SQLite"
}
}
}
重新跑起來後,成功看到病人數據
清洗病人數據
由於HisAgent需要使用數據調度,除了AntJob,我們還需要從nuget引用AntJob.Extensions。
這一次,我們來實時消費病人數據,爲其生成醫囑,高仿數據清洗過程。
新增生成醫囑的作業處理器 BuildWill
using System;
using AntJob;
using HisData;
using NewLife.Security;
using XCode;
namespace HisAgent
{
class BuildWill : DataHandler
{
public BuildWill()
{
var job = Job;
job.Start = DateTime.Today;
job.Step = 30;
}
public override Boolean Start()
{
// 指定要抽取數據的實體類以及時間字段
Factory = ZYBH0.Meta.Factory;
Field = ZYBH0._.CreateTime;
return base.Start();
}
protected override Boolean ProcessItem(JobContext ctx, IEntity entity)
{
var pi = entity as ZYBH0;
// 創建醫囑信息
var will = new ZYBHYZ0
{
Bhid = pi.Bhid,
Mgroupid = Rand.Next(9999),
Kyzrq = pi.Ryrq.AddHours(1),
Tyzrq = pi.Cyrq.AddHours(-3),
Kyzys = Rand.NextString(8),
State = pi.State,
};
will.Insert();
return true;
}
}
}
數據調度的處理器基類是DataHandler,並且需要在Start之前指定實體工廠以及時間字段。調度系統將會從該表抽取數據,根據調度中心分派的時間區間(StartTime+EndTime),對時間字段進行查詢。
處理函數ProcessItem就是業務核心代碼了,也可以重寫Execute,實現批量處理。
從今天零點開始消費處理數據,步進30秒,也就是每次抽取30秒的數據來分析處理。
不要忘了在Main中把該處理器加入調度器。
跑起來,去控制檯啓用作業
可以看到,在(00:30:00, 00:30:30)區間內,得到4個病人,創建了4個醫囑。
運行HisWeb查看數據
消息調度
設計概要
計算型應用(繼承Handler)
系統架構
調度中心主從架構