來自:農碼一生
連結:https://www.cnblogs.com/zhaopei/p/netcore2.html
上篇講了《ASP.NET Core在Linux上的環境部署》。http://www.cnblogs.com/zhaopei/p/netcore.html今天我們將做幾個小玩意實戰一下。用到的技術和工具有mysql、websocket、AngleSharp(爬蟲html解析)、nginx多站點部署。
NO1 留言板(mysql的使用)
演示:http://haojima.net
這個功能很簡單。就是對資料庫的寫入和展示。如果在Windows下,相信大家分分鐘都可以搞定。而初次接觸.net core + mysql可能需要註意些細節。
首先開啟vs2017新建一個asp.net core專案(選Web應用程式),然後nuget 匯入Microsoft.EntityFrameworkCore.Tools 1.1.1和MySql.Data.EntityFrameworkCore 8.0.8-dmr。
然後新建一個DbContext類。
public class DataContext : DbContext
{
//【註意】連線字串一定要加 sslmode=none
string str = @"Data Source=;Database=;User ID=;Password=;pooling=true;CharSet=utf8;port=3306;sslmode=none";
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) =>
optionsBuilder.UseMySQL(str);
//下麵就可以新增要加入資料庫的物體了
//public DbSet Messages { get; set; }
}
到此為止,我們已經可以利用EF Core直接連線mysql進行增刪改查操作了。註意:需要匯入名稱空間
using Microsoft.EntityFrameworkCore;
using MySQL.Data.EntityFrameworkCore.Extensions;
當然。你會說,連線字串不能硬編碼到程式碼裡面。我們也可以放配置檔案。
appsettings.json
{
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Warning"
}
},
"ConnectionStrings": { "SqlServerConnection": "Data Source=;Database=;User ID=;Password=;pooling=true;CharSet=utf8;port=3306;sslmode=none" }
}
然後把上面的硬編碼註釋掉。在Startup.cs檔案的ConfigureServices方法新增
var connection = Configuration.GetConnectionString("SqlServerConnection");
services.AddDbContext(options => options.UseMySQL(connection));
註意:專案名稱和路徑最好不要有中文,不然會出現些亂七八糟的問題。
完整程式碼:
https://github.com/zhaopeiym/BlogDemoCode/tree/master/MessageBoard
NO2 聊天室(WebSocket的使用)
演示:http://socket.haojima.net
WebSocket是Html5新增的一個很酷的技術。下麵我們簡單講解下這個很酷的技術
var Socket = new WebSocket(url);//建立 WebSocket 物件
建立了一個WebSocket物件後會觸發開啟連線事件:
Socket.onopen = function(){ }
除了onopen事件,還有其他三個事件:
Socket.onmessage //客戶端接收服務端資料時觸發
Socket.onerror //通訊發生錯誤時觸發
Socket.onclose //連線關閉時觸發
另外還有兩個方法:
Socket.send() //使用連線傳送資料
Socket.close() //關閉連線
最後還有四個連線狀態屬性:
Socket.readyState
0 - 表示連線尚未建立。
1 - 表示連線已建立,可以進行通訊。
2 - 表示連線正在進行關閉。
3 - 表示連線已經關閉或者連線不能開啟。
整個WebSocket常用功能知識點就四個事件、兩個方法、四種狀態。簡單吧,下麵我們看看asp.net core後臺的配合:
後臺新增一個SocketHandler類,並新增一個靜態方法Map:
///
/// 請求
///
///
public static void Map(IApplicationBuilder app)
{
app.UseWebSockets(); //【註意】需要 nuget 匯入 Microsoft.AspNetCore.WebSockets.Server
app.Use(Acceptor);
}
然後新增對應的Acceptor方法:
///
/// 接收請求
///
///
///
///
static async Task Acceptor(HttpContext httpContext, Func n)
{
需要在Startup.cs類裡面的Configure方法裡面加入
app.Map("/ws", SocketHandler.Map); //傳入我們剛才新建的靜態方法Map
現在為止,基本的類和配置已經完成。
我們主要操作,是在Acceptor方法裡面接收和傳送訊息。
//建立連線
var socket = await httpContext.WebSockets.AcceptWebSocketAsync();
//等待接收資料
await socket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
//傳送訊息
await socket.SendAsync(arraySegment, WebSocketMessageType.Text, true, CancellationToken.None);
後臺關鍵程式碼也就這三句,建立連線、等待接收、傳送訊息。
不過這裡有一點需要理解。建立連線後,可以接收任意多次客戶端訊息。所以ReceiveAsync等待接收這裡需要死迴圈接收訊息,直到連線斷開。
(不用擔心真的死迴圈,沒有訊息傳送的時候,程式碼會阻塞在那裡等待訊息)
完整實現:https://github.com/zhaopeiym/ChatRoom
NO3 找工作(AngleSharp的使用)
演示:http://job.haojima.net
對於爬蟲抓包,我相信大家初次接觸都非常的熱衷於此。我也不例外。
那麼在asp.net core下麵是否也有這樣的外掛呢?答案是肯定的。
《.NET Core HtmlAgilityPack HTML解析利器》(感謝博主對.net core的貢獻)。不過xpath用起來超級噁心。
之前在.NET下麵有一款Jumony:
http://www.cnblogs.com/Ivony/p/3447536.html(部落格園大牛寫的)。
支援CSS選擇和linq查詢。簡直不要太爽。
可是不支援.net core。(本人試了下遷移.net core,發現很多類在.net core沒有實現)
最後還是到了一款支援.net core的解析元件。並可以媲美Jumony,同樣支援css選擇和linq查詢。那就是AngleSharp。
新建專案,nuget 安裝 AngleSharp。然後以下簡單使用:
using (HttpClient http = new HttpClient())
{
var htmlString = await http.GetStringAsync(url);
HtmlParser htmlParser = new HtmlParser();
var jobInfos = htmlParser.Parse(htmlString)
.QuerySelectorAll(".newlist_list_content table")
.Where(t => t.QuerySelectorAll(".zwmc a").FirstOrDefault() != null)
.Select(t => new JobInfo()
{
PositionName = t.QuerySelectorAll(".zwmc a").FirstOrDefault().TextContent,
CorporateName = t.QuerySelectorAll(".gsmc a").FirstOrDefault().TextContent,
Salary = t.QuerySelectorAll(".zwyx").FirstOrDefault().TextContent,
WorkingPlace = t.QuerySelectorAll(".gzdd").FirstOrDefault().TextContent,
.ToList();
return jobInfos;
}
看到沒有,就像jq一樣解析html。如果你說不爽我都不信。
完整實現:https://github.com/zhaopeiym/JobWanted
部署多個站點
以上,這些專案都比較簡單。關鍵技術點和難點都進行的分析。我相信大家都可以動起手練習起來了。
不過有個問題,前面我們只說了部署一個應用程式。如果是多個該怎麼部署呢?
首先我們把多個程式釋出包放到伺服器上。
然後修改nginx的配置檔案/etc/nginx/conf.d/default.conf
server {
listen 80;
server_name www.haojima.net; #對應的域名
root /home/projects/messagBoard; #程式路徑
location / {
proxy_pass http://localhost:5000; #內網埠
proxy_http_version 1.1;
proxy_set_essay-header Upgrade $http_upgrade;
proxy_set_essay-header Connection keep-alive;
proxy_set_essay-header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_essay-header X-real-ip $remote_addr;
proxy_set_essay-header Upgrade $http_upgrade;
}
}
有幾個程式就新增幾個server,不過需要修改你解析到的域名、程式路徑和內網對應的埠(看配置裡的註釋) 。
然後修改supervisor的配置檔案/etc/supervisor/conf.d/supervisord.conf
[program:MessageBoard]
command=dotnet MessageBoard.dll ; 執行程式的命令
directory= /home/projects/messagBoard/ ; 命令執行的目錄
autorestart=true ; 程式意外退出是否自動重啟
stderr_logfile=/var/log/WebApplication1.err.log ; 錯誤日誌檔案
stdout_logfile=/var/log/WebApplication1.out.log ; 輸出日誌檔案
environment=ASPNETCORE_ENVIRONMENT=Production ; 行程環境變數
user=root ; 行程執行的使用者身份
stopsignal=INT
有幾個程式就往下複製幾份program。需要修改program名稱,只要名稱不重覆就可以。然後修改 執行程式的命令 對應的dll和命令執行的目錄(看配置檔案的註釋)。
如此就可以部署多個程式了。
開始我還以為是在域名解析的時候,解析IP + 埠。原來是多個域名解析到同一個IP,然後nginx在內部做域名和內網埠分發。
一些其它的細節
部署阿裡雲
我們在linux的防火牆開放了埠,發現在外面還是訪問不了(可以telnet IP 埠 來測試)。有可能是阿裡雲攔截了。https://help.aliyun.com/document_detail/25471.html 在安全組新增某埠哪些IP可以訪問。
mysql的客戶端
對於mysql,我們安裝好之後總不能每次命令操作吧。在Windows下麵有個客戶端Navicat可以方便管理mysql。
下載連結:http://pan.baidu.com/s/1nvMEQQt 密碼:5eao
獲取ip
用了nginx後發現取不到瀏覽器IP了。那是因為我們程式都是瀏覽器訪問nginx,然後nginx轉發內網程式埠。所以取到的IP都是內網本機IP。如果需要取瀏覽器IP需要在nginx配置
server {
listen 80;
server_name www.haojima.net;
root /home/projects/messagBoard;
location / {
proxy_pass http://localhost:5000;
proxy_http_version 1.1;
proxy_set_essay-header Upgrade $http_upgrade;
proxy_set_essay-header Connection keep-alive;
proxy_set_essay-header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_essay-header X-real-ip $remote_addr; # 新新增
}
}
然後程式碼裡面取IP:
var ip = HttpContext.Request.Headers["X-real-ip"].FirstOrDefault();
WebSocket在nginx的配置
上面我們寫的WebSocket直接執行發現沒有任何問題,可是部署在nginx去跑不起來了。那是因為需要nginx支援WebSocket,需要配置。
http://nginx.org/en/docs/http/websocket.html
server {
listen 80;
server_name job.haojima.net;
root /home/projects/jobWanted;
location / {
proxy_pass http://localhost:5002;
proxy_http_version 1.1;
proxy_set_essay-header Upgrade $http_upgrade;
proxy_set_essay-header Connection keep-alive;
proxy_set_essay-header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_essay-header X-real-ip $remote_addr;
proxy_set_essay-header Upgrade $http_upgrade; # 新增
#proxy_set_essay-header Connection "upgrade"; # 新增
proxy_set_essay-header Connection $http_connection; #ws和post同時使用 https://github.com/aspnet/KestrelHttpServer/issues/1263
}
}
WebSocket心跳
經過上面的配置,我們的WebSocket在nginx上跑起來了。萬分歡喜的我們,發現一分鐘不發訊息就自動掉線了。鬱悶至極到頭大。細心的同學透過上面的連結資料其實已經有說明:
By default, the connection will be closed if the proxied server does not transmit any data within 60 seconds. This timeout can be increased with the proxy_read_timeout directive. Alternatively, the proxied server can be configured to periodically send WebSocket ping frames to reset the timeout and check if the connection is still alive.
預設情況下,如果代理的伺服器在60秒內沒有傳輸任何資料,則連線將被關閉。可以使用proxy_read_timeout指令增加此超時 。或者,代理伺服器可以配置為定期傳送WebSocket ping幀以重置超時並檢查連線是否仍然存在。
nginx給出了兩種解決方案。第一種,修改proxy_read_timeout (預設60秒)。第二種,瀏覽器客戶端定時傳送心跳包(時間要短於proxy_read_timeout)。
我使用的是第二種方式。
第一種雖然簡單粗暴,但是時間再長也是一個值,還是會有超時的可能。再者,誰能保證瀏覽器端不會new 很多個WebSocket出來搗蛋。
第二種方式,瀏覽器定時傳送一條訊息,內容和後臺約定下。如傳送“心跳”,然後後臺接收訊息是,判斷如果是“心跳”則不做任何處理。
中文編碼
在做“找工作”爬前程無憂的資料時,發現他們使用的GBK編碼。而在.net core中預設不支援這種格式,導致取到的資料都是亂碼。
我們需要nuget安裝System.Text.Encoding.CodePages。然後在Startup.cs的Configure裡面註冊:
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);//註冊編碼提供程式
使用:
var htmlBytes = await http.GetByteArrayAsync(url);
var htmlString = Encoding.GetEncoding("GBK").GetString(htmlBytes);
ASP.NET Core 埠分配
asp.net core 預設埠都是5000。那麼我們執行第二個程式的時候就會提示5000埠被佔用。這個時候,我們就需要為每個程式分配不同的埠了。
在根目錄新建一個json檔案hosting.json
{
"server.urls": "http://*:5002"
}
在Program.cs檔案修改
public static void Main(string[] args)
{
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("hosting.json", optional: true)
.Build();
var host = new WebHostBuilder()
.UseKestrel()
.UseConfiguration(config)
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup()
.UseApplicationInsights()
.Build();
host.Run();
}
爬拉勾資料
在爬拉勾網的時候沒有搞定,不知道是不是因為https的原因。
using (HttpClient http = new HttpClient())
{
var url = "https://www.lagou.com/zhaopin/Java/?labelWords=label";
var htmlString = await http.GetStringAsync(url);
}
在.net core中報錯:An unhandled exception occurred while processing the request.
在.net 4.5 中抓到的資料是“頁面載入中…”。和瀏覽器訪問的結果不一樣。
原因未知。如果有大佬解惑,感激不盡!
參考
http://www.runoob.com/html/html5-websocket.html
http://www.cnblogs.com/liguobao/p/6130121.html
http://www.cnblogs.com/linezero/p/5806814.html
演示
http://haojima.net
http://socket.haojima.net
http://job.haojima.net
原始碼
https://github.com/zhaopeiym/JobWanted
https://github.com/zhaopeiym/ChatRoom
https://github.com/zhaopeiym/BlogDemoCode
朋友會在“發現-看一看”看到你“在看”的內容