網絡程序應該注冊成為系統服務,以保證其自啟動以及穩定可靠運行!
這一場,講講怎么建立一個生產級別的網絡服務。
老規矩,先上源碼:https://github.com/NewLifeX/NewLife.Net
系統服務功能,由網絡庫的兄弟框架,X組件的Agent來支撐,以前也叫XAgent,網上搜索 NewLife XAgent 可以找到不少文章。
XAgent在X組件里面很年輕,才10年,設計於2008年,上海陸家嘴。
〇、最終效果
先來看看最終效果,大家也可以telnet net.newlifex.com 1234 來看效果

左邊窗口就是這次要講的網絡服務程序,工作在調試模式。
右邊窗口是上一次的EchoTest客戶端,連接左邊網絡服務。
一、建立控制台項目
建立一個控制台項目,通過nuget引用NewLife.Core
新建一個服務類 MyService,繼承自泛型基類 AgentServiceBase<MyService>
Program.Main里面增加一行引導程序:
class Program { static void Main(String[] args) { // 引導進入我的服務控制類 MyService.ServiceMain(); } }
下面就開始慢慢完善我們的服務類MyService
public MyService() { ServiceName = "EchoAgent"; DisplayName = "回聲服務"; Description = "這是NewLife.Net的一個回聲服務示例!"; // 准備兩個工作線程,分別負責輸出日志和向客戶端發送時間 ThreadCount = 2; Intervals = new[] { 1, 5 }; }
指定一些基本參數,看效果圖可以猜到用途

服務名、顯示名、描述,就這么多!
ThreadCount = 2指定兩個工作線程,Intervals指定它們的輪詢周期分別是1秒和5秒
系統服務的標准動作就是啟動和停止
MyNetServer _Server; /// <summary>開始服務</summary> /// <param name="reason"></param> protected override void StartWork(String reason) { // 實例化服務端,指定端口,同時在Tcp/Udp/IPv4/IPv6上監聽 var svr = new MyNetServer { Port = 1234, Log = XTrace.Log }; svr.Start(); _Server = svr; base.StartWork(reason); } /// <summary>停止服務</summary> /// <param name="reason"></param> protected override void StopWork(String reason) { _Server.TryDispose(); _Server = null; base.StopWork(reason); }
我們重載啟動函數,初始化網絡服務,並重啟停止函數來銷毀網絡服務。
這里的MyNetServer從上一個例程拷貝過來。
網絡服務做一個成員資源,避免被GC回收。
XAgent默認帶來多線程任務調度,其核心是 Work(Int32 index)
/// <summary>調度器讓每個任務線程定時執行Work,index標識任務</summary> /// <param name="index"></param> /// <returns></returns> public override Boolean Work(Int32 index) { switch (index) { case 0: ShowStat(_Server); break; case 1: SendTime(_Server); break; } return false; } private String _last; /// <summary>顯示服務端狀態</summary> /// <param name="ns"></param> private void ShowStat(NetServer ns) { var msg = ns.GetStat(); if (msg == _last) return; _last = msg; WriteLog(msg); } /// <summary>向所有客戶端發送時間</summary> /// <param name="ns"></param> private void SendTime(NetServer ns) { var str = DateTime.Now.ToFullString() + Environment.NewLine; var buf = str.GetBytes(); ns.SendAllAsync(buf); }
XAgent內部設計有一個任務調度器,它會實際創建2個線程(ThreadCount指定),每個線程定時執行Work(Int32 index)函數,index參數用於標識哪一個任務線程。
我們這只需要一個很簡單的switch,0號線程負責輸出服務端狀態,每秒一次,1號線程負責給連接到服務端的所有會話發送服務器當前時間。
多說幾句XAgent:
1,任務線程具有較高線程優先級,比一般線程有更多機會得到CPU時間
2,調度器有個最高優先級的管理線程,負責監管所有任務線程,如果任務線程崩潰或者超時,它會干掉並新建
3,管理線程還負責監控線程數、句柄數、內存占用等
二、開發調試
既然是控制台項目,先跑起來看看:

紅色字體顯示重要信息,黃色字體顯示菜單,常用功能是235。
我們選擇5,循環調試,其實就是在控制台里面模擬服務工作流程,讓網絡服務跑起來。
底下日志可以看到,它監聽了4個套接字。
2是安裝服務,也就是把當前應用安裝成為Windows服務,這里特別注意,一般需要管理員權限,才能安裝成功,除非關閉系統UAC。
3是啟動服務,只有在安裝了服務之后,才能看到。
所以,XAgent程序,既是開發調試控制台程序,也是安裝卸載、啟動停止服務的操作台,更是Windows服務程序本身!
細心的同學可以發現,安裝好的Windows服務實質上就是 EchoAgent.exe -s,帶有-s參數。
三、安裝服務
最后,我們把它安裝到一台公網服務器上,tcp://net.newlifex.com:1234,telnet上去看看效果


從日志文件可以看到,它的應用類型 ApplicationType 是 Service,也就是Windows Service。
下面的日志,在A0線程(也就是0號任務線程)輸出服務端狀態。
在線1/1,當前在線/最大在線
發送 2/20/0,共發送2次,最大速度每秒20字節,當前速度每秒0字節
既然有A0線程,同樣也會有A1,還會有An(ThreadCount>n),可用於區分不同任務線程輸出的日志。
至此,我們的Windows網絡服務程序開發完成,並安裝到公網服務器上,持續對外提供Echo服務!
