昨天我的一個(gè) app 的接口服務(wù)器掛掉了,國(guó)外的小雞意外的翻車,連同程序和數(shù)據(jù)一起,猝不及防。我的服務(wù)端程序是 asp.net mvc ,小雞是 256 M 的內(nèi)存跑不了 windows 系統(tǒng),裝的 mono 。服務(wù)器用的 jexus,但是還有一個(gè) apache+php+mysql 的全家桶占用了 80 端口,所以這個(gè)接口是通過 apache 反向代理的。
這樣一來本來環(huán)境就很復(fù)雜了,我 ubuntu 16.04 裝 mono 下載了差不多700 mb 的數(shù)據(jù),安裝后體積更大,簡(jiǎn)直太不環(huán)保了,只有不到 10G 的硬盤。
于是狠下心將服務(wù)器端程序重寫,其它快餐語言我不會(huì),據(jù)說 nodejs 和 python 會(huì)很快,部署也方便。但我還是用我的大 C#,好在現(xiàn)在有 dotnet core 了,也給大家安利一發(fā),它是一個(gè)模塊化的開發(fā)棧,也是未來的所有.NET平臺(tái)的基礎(chǔ),橫跨
Windows、Linux、OSX 三大主流系統(tǒng)。
dotnet core https://dotnet.github.io/
因?yàn)槲业慕涌诒容^簡(jiǎn)單,主要是輸出 json 以及幾個(gè)靜態(tài)頁面。所以不需要?jiǎng)?chuàng)建 web 項(xiàng)目,我并不想讓他寄宿在服務(wù)器軟件上運(yùn)行,自己實(shí)現(xiàn) Http 監(jiān)聽處理請(qǐng)求即可,不過這些 dotnet core 已經(jīng)為你準(zhǔn)備好了一個(gè) Server.Kestrel,不需要自己造輪子。
關(guān)于 Server.Kestrel 可以參考這篇文章 ,更多的還是官方更詳細(xì),傳送門 ,以及源碼和示例:https://github.com/aspnet/KestrelHttpServer
在包管理控制臺(tái)執(zhí)行安裝:
PM> Install-Package Microsoft.AspNetCore.Server.Kestrel -Pre
另外,如果需要靜態(tài)文件支持,還需要下面的庫:
PM> Install-Package Microsoft.AspNetCore.StaticFiles -Pre
使用很簡(jiǎn)單,在 Main 方法里實(shí)例化一個(gè) WebHostBuilder 并調(diào)用 run 方法就可以,其他的都是配置。
var host = new WebHostBuilder()
.UseKestrel()
.UseUrls("http://*:5001")
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Program>()
.Build();
host.Run();
處理請(qǐng)求簡(jiǎn)直不要太簡(jiǎn)單:
app.Run(async (context) =>
{
byte[] data = Encoding.UTF8.GetBytes("hello world");
await context.Response.Body.WriteAsync(data, 0, data.Length).ConfigureAwait(false);
});
但是顯然不夠強(qiáng)大,無法處理 url 路由,接下來寫一個(gè)抽象類處理 http 請(qǐng)求。
abstract class HandlerBase
{
public abstract void Process(HttpContext context);
}
這里可以用一個(gè) Dictionary<string,Handler> 保存路由:
_routes = new Dictionary<string, HandlerBase>();
_routes.Add("/home/hello", new Hello());
_routes.Add("/test/demo", new Demo());
Hello 這個(gè)類需要繼承 HandlerBase 抽象類,重寫 Process 方法:
class Hello : HandlerBase
{
public async override void Process(HttpContext context)
{
byte[] data = Encoding.UTF8.GetBytes("hello world");
await context.Response.Body.WriteAsync(data, 0, data.Length).ConfigureAwait(false);
}
}
這樣就避免了為了處理路由寫一堆 if else,擴(kuò)展性也比較好,根據(jù) url 路徑找到對(duì)應(yīng)的 HandlerBase 的實(shí)現(xiàn),并調(diào)用 Process 處理請(qǐng)求。
app.Run(async (context) =>
{
HandlerBase handler = null;
_routes.TryGetValue(context.Request.Path.ToString().ToLower(), out handler);
if (handler != null) handler.Process(context);
else
{
byte[] data = Encoding.UTF8.GetBytes("HTTP 404");
await context.Response.Body.WriteAsync(data, 0, data.Length).ConfigureAwait(false);
}
});

然后就是靜態(tài)文件的處理問題,建議放一個(gè)文件夾存放靜態(tài)文件,比如創(chuàng)建 dotnet core web 程序時(shí),會(huì)有一個(gè) www 的文件夾。
Kestrel 處理靜態(tài)內(nèi)容也很簡(jiǎn)單:
app.UseStaticFiles(new StaticFileOptions()
{
FileProvider = _fileProvider,
RequestPath = ""
});
FileProvider 是必須是實(shí)現(xiàn)了 IFileProvider 的類。
IFileProvider _fileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "www"));
由于 RequestPath 是空字符串,這樣一來只要訪問 /abc.txt 就會(huì)直接映射到 www 目錄下的 abc.txt 文件并原始返回。
發(fā)布項(xiàng)目后會(huì)產(chǎn)生一個(gè) PublishOutput 文件夾,將里面的內(nèi)容復(fù)制到主機(jī) /home/test 目錄中。要運(yùn)行這個(gè)項(xiàng)目還需要在服務(wù)器安裝 dotnet core ,這并不需要再原代碼重新編譯了,怎么安裝可以參考官網(wǎng)。
執(zhí)行下面命令運(yùn)行你的項(xiàng)目,如果你的項(xiàng)目叫 demo ??:
dotnet demo.dll

關(guān)于更多 dotnet core 學(xué)習(xí)內(nèi)容
- 官方 https://docs.microsoft.com/zh-cn/dotnet/
- Microsoft on Github https://github.com/Microsoft/dotnet
- 張善友-博客園 http://www.cnblogs.com/shanyou/
- 蔣金楠-博客園 http://www.cnblogs.com/artech/
最后的最后,如果想深入學(xué)習(xí),不要只是創(chuàng)個(gè)虛擬機(jī)配個(gè)環(huán)境執(zhí)行個(gè) Hello World。
本文在 簡(jiǎn)書 以及 我的公眾號(hào) 天兵公園 和 博客 同步發(fā)布,轉(zhuǎn)載前請(qǐng)務(wù)必聯(lián)系。