將app接口服務(wù)器改為dotnet core承載

昨天我的一個(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
啟動(dòng)程序

關(guān)于更多 dotnet core 學(xué)習(xí)內(nèi)容

最后的最后,如果想深入學(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)系。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容