前言
最近在V站上看到一個外賣推廣的小程式,意思大概是類似淘寶聯盟那種,別人走自己的連結後,自己可以抽取大概4%-6%的提成。覺得還蠻有意思的,一開始開源的是靜態頁面寫死的,所以我這邊用.Net Core寫了個簡單的後臺。
左邊是無後臺的,右邊紅色框是後臺配置的。當然功能是很簡單的,主要是記錄釋出到Ubuntu18.4的時候遇到的問題與解決辦法。
安裝寶塔
寶塔Linux皮膚是提升運維效率的伺服器管理軟體,支援一鍵LAMP/LNMP/叢集/監控/網站/FTP/資料庫/JAVA等100多項伺服器管理功能。
這裡節省時間直接使用寶塔皮膚了,這個真的是太方便了,哈哈。安裝也非常簡單。
因為我使用的是Ubuntu,安裝指令碼
wget -O install.sh http://download.bt.cn/install/install-ubuntu_6.0.sh && sudo bash install.sh
其他版本請參考官方文件:https://www.bt.cn/download/linux.html
安裝完成後會顯示登入地址、使用者名稱、密碼資訊。登入後瀏覽器將彈出推薦安裝套件,為方便直接一鍵安裝LNMP。
安裝.NetCore SDK 3.1
微軟官方文件:https://docs.microsoft.com/zh-cn/dotnet/core/install/linux-ubuntu
因為我使用的18.04,所以找到對應文件。
使用 APT 進行安裝可通過幾個命令來完成。 安裝 .NET 之前,請執行以下命令,將 Microsoft 包簽名金鑰新增到受信任金鑰列表,並新增包儲存庫。
開啟終端並執行以下命令:
wget https://packages.microsoft.com/config/ubuntu/18.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb
安裝 SDK
.NET SDK 使你可以通過 .NET 開發應用。 如果安裝 .NET SDK,則無需安裝相應的執行時。 若要安裝 .NET SDK,請執行以下命令:
sudo apt-get update; \
sudo apt-get install -y apt-transport-https && \
sudo apt-get update && \
sudo apt-get install -y dotnet-sdk-3.1
安裝執行時
sudo apt-get update; \
sudo apt-get install -y apt-transport-https && \
sudo apt-get update && \
sudo apt-get install -y aspnetcore-runtime-3.1
作為 ASP.NET Core 執行時的一種替代方法,你可以安裝不包含 ASP.NET Core 支援的 .NET 執行時:將上一命令中的 aspnetcore-runtime-5.0
替換為 dotnet-runtime-5.0
:
sudo apt-get install -y dotnet-runtime-5.0
其實上述就是照搬微軟的官方文件,官方文件還是寫的很清楚的。
釋出.NetCore專案
我一開始目標執行時選擇的Linux-64,但是出現了這樣的錯誤`錯誤 NU1605: 檢測到包降級: XXXXXXXXXXXXX 從 4.3.0 降級到 XXXXXXXXXXXXX。直接從專案引用包以選擇不同版本。
通過檢視微軟官方文件:https://docs.microsoft.com/zh-cn/nuget/reference/errors-and-warnings/nu1605
問題當在 .NET Core 3.0 或更高版本的專案中同時引用時,與 .NET Core 1.0 和1.1 隨附的某些包組合不相容。 問題包通常以或開頭 System.
Microsoft.
,並具有4.0.0 和4.3.1 之間的版本號。 在這種情況下,降級訊息將具有從執行時開始的包。 依賴關係鏈。
解決方案若要解決此問題,請新增以下 PackageReference:
<PackageReference Include="Microsoft.NETCore.Targets" Version="3.0.0" PrivateAssets="all" />
就是新增引用,但實際上你要保證所有專案的包引用版本是一致的。
另一種方法
釋出的時候目標執行時直接選擇可移植吧~
寶塔皮膚釋出.Net Core專案,並啟動專案
在檔案 wwwroot新建專案資料夾,將本地釋出檔案打包拷貝至伺服器解壓。
在伺服器上終端命令進入部署檔案所在目錄,然後使用dotnet命令啟動服務:
dotnet XXXXXX.Admin.dll --urls "http://localhost:5000"
Nginx設定反代訪問
現在我們還不能直接訪問到我們新部署專案,需要使用Nginx設定反向代理,將特定的埠代理到http://localhost:5000
,這一步可以通過寶塔皮膚來完成,步驟如下:
在寶塔皮膚上新建一個網站,設定為靜態網站即可,並繫結好域名。
在剛才新建的網站中設定反向代理,目標URL填寫http://localhost:5000即可,傳送域名localhost
瀏覽器正式可訪問專案,此處可能需要重啟一下。
使用 Supervisor 守護程式
現在還有個問題,就是當我們關閉xShell等SSH工具的時候服務程式也會停止執行,我們可使用 Supervisor 守護程式執行。
- 在寶塔皮膚上安裝Supervisor
- 新增守護程式(使用者建議選擇www,不要使用root)
問題
問題1
錯誤 NU1605: 檢測到包降級: XXXXXXXXXXXXX 從 4.3.0 降級到 XXXXXXXXXXXXX。直接從專案引用包以選擇不同版本
這個問題一開始我按照官方文件修改了,實際還是不可以。所以我選擇了可移植髮布的。而我在寫這篇文章的時候又可以了。
問題2
驗證碼我使用了
System.Drawing
,不過在Linux下的話,這個是無法顯示的。解決辦法
System.Drawing.Common
元件提供對GDI+圖形功能的訪問。它是依賴於GDI+的,那麼在Linux上它如何使用GDI+,因為Linux上是沒有GDI+的。Mono 團隊使用C語言實現了GDI+介面,提供對非Windows系統的GDI+介面訪問能力(個人認為是模擬GDI+,與系統影像介面對接),這個就是libgdiplus
。進而可以推測System.Drawing.Common
這個元件實現時,對於非Windows系統肯定依賴了ligdiplus
這個元件。如果我們當前系統不存在這個元件,那麼自然會報錯,找不到它,安裝它即可解決。Ubuntu一鍵命令
sudo curl https://raw.githubusercontent.com/stulzq/awesome-dotnetcore-image/master/install/ubuntu.sh|sh
問題3
指定埠啟動
修改
Program.cs
增加程式碼
.ConfigureAppConfiguration(builder => { //dotnet test.dll --urls "http://*:5000;https://*:5001" builder.AddCommandLine(args);//設定新增命令列 })
完整程式碼
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) //將預設ServiceProviderFactory指定為AutofacServiceProviderFactory https://autofaccn.readthedocs.io/en/latest/integration/aspnetcore.html#asp-net-core-3-0-and-generic-hosting .UseServiceProviderFactory(new AutofacServiceProviderFactory()) .ConfigureAppConfiguration(builder => { //dotnet test.dll --urls "http://*:5200;https://*:5100" builder.AddCommandLine(args);//設定新增命令列 }) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });
問題4
驗證碼生成程式碼
驗證碼生成程式碼應該是蠻多的,我把我的分享下
using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.IO; namespace XXX.Util { public static class ValidateCodeHelper { /// <summary> /// 驗證碼的最大長度 /// </summary> public static int MaxLength => 10; /// <summary> /// 驗證碼的最小長度 /// </summary> public static int MinLength => 1; /// <summary> /// 生成驗證碼 /// </summary> /// <param name="length">指定驗證碼的長度</param> /// <returns></returns> public static string CreateValidateCode(int length) { int[] randMembers = new int[length]; int[] validateNums = new int[length]; string validateNumberStr = ""; //生成起始序列值 int seekSeek = unchecked((int)DateTime.Now.Ticks); Random seekRand = new Random(seekSeek); int beginSeek = (int)seekRand.Next(0, Int32.MaxValue - length * 10000); int[] seeks = new int[length]; for (int i = 0; i < length; i++) { beginSeek += 10000; seeks[i] = beginSeek; } //生成隨機數字 for (int i = 0; i < length; i++) { Random rand = new Random(seeks[i]); int pownum = 1 * (int)Math.Pow(10, length); randMembers[i] = rand.Next(pownum, Int32.MaxValue); } //抽取隨機數字 for (int i = 0; i < length; i++) { string numStr = randMembers[i].ToString(); int numLength = numStr.Length; Random rand = new Random(); int numPosition = rand.Next(0, numLength - 1); validateNums[i] = Int32.Parse(numStr.Substring(numPosition, 1)); } //生成驗證碼 for (int i = 0; i < length; i++) { validateNumberStr += validateNums[i].ToString(); } return validateNumberStr; } /// <summary> /// 得到驗證碼圖片的長度 /// </summary> /// <param name="validateNumLength">驗證碼的長度</param> /// <returns></returns> public static int GetImageWidth(int validateNumLength) { return (int)(validateNumLength * 12.0); } /// <summary> /// 得到驗證碼的高度 /// </summary> /// <returns></returns> public static double GetImageHeight() { return 22.5; } //C# MVC 升級版 /// <summary> /// 建立驗證碼的圖片 /// </summary> /// <param name="validateCode">驗證碼</param> public static byte[] CreateValidateGraphic(string validateCode) { Bitmap image = new Bitmap((int)Math.Ceiling(validateCode.Length * 12.0), 22); Graphics g = Graphics.FromImage(image); try { //生成隨機生成器 Random random = new Random(); //清空圖片背景色 g.Clear(Color.White); //畫圖片的干擾線 for (int i = 0; i < 25; i++) { int x1 = random.Next(image.Width); int x2 = random.Next(image.Width); int y1 = random.Next(image.Height); int y2 = random.Next(image.Height); g.DrawLine(new Pen(Color.Silver), x1, y1, x2, y2); } Font font = new Font("Arial", 12, (FontStyle.Bold | FontStyle.Italic)); LinearGradientBrush brush = new LinearGradientBrush(new Rectangle(0, 0, image.Width, image.Height), Color.Blue, Color.DarkRed, 1.2f, true); g.DrawString(validateCode, font, brush, 3, 2); //畫圖片的前景干擾點 for (int i = 0; i < 100; i++) { int x = random.Next(image.Width); int y = random.Next(image.Height); image.SetPixel(x, y, Color.FromArgb(random.Next())); } //畫圖片的邊框線 g.DrawRectangle(new Pen(Color.Silver), 0, 0, image.Width - 1, image.Height - 1); //儲存圖片資料 MemoryStream stream = new MemoryStream(); image.Save(stream, ImageFormat.Jpeg); //輸出圖片流 return stream.ToArray(); } finally { g.Dispose(); image.Dispose(); } } } }
總結
其實就是一篇流水賬,記錄了釋出的過程和遇到的問題及解決辦法。之前伺服器一直是使用的WinServer,因為熟悉。勇於嘗試並去解決問題,慢慢進步~
大學裡也學過Linux,受不了。但是真的去使用了,去探索了,嗯,真香~