第六節:SignalR完結篇之依賴註入和分散式部署

来源:https://www.cnblogs.com/yaopengfei/archive/2018/07/24/9353630.html
-Advertisement-
Play Games

一. SignalR中DI思想的應用 DI,即依賴註入,它是一種不負責創建其自己的依賴項對象的一種模式,通常用來降低代碼之間的耦合性,廣泛應用於架構設計,是必不可少的一種思想。 下麵結合一個需求來說一說SignalR中依賴註入思想的應用。 需求:比如在前面章節的聊天室案例中,想把發送的每條消息都記錄 ...


一. SignalR中DI思想的應用

  DI,即依賴註入,它是一種不負責創建其自己的依賴項對象的一種模式,通常用來降低代碼之間的耦合性,廣泛應用於架構設計,是必不可少的一種思想。   下麵結合一個需求來說一說SignalR中依賴註入思想的應用。   需求:比如在前面章節的聊天室案例中,想把發送的每條消息都記錄下來 (下麵的代碼中,使用群發這個介面進行測試)。

 分析解決思路:

 1. 新建Repository類和IRepository介面,裡面聲明SaveMsg方法,用來存儲信息 (PS:便於測試,這裡將信息保存到txt文本文檔中)

代碼如下:

 1   public interface IRepository
 2   {
 3         void SaveMsg(string connectionId, string msg);
 4   }
 5   public class Repository : IRepository
 6   {
 7         /// <summary>
 8         /// 模擬資料庫插入操作
 9         /// 這裡以日誌代替
10         /// </summary>
11         /// <param name="connectionId"></param>
12         /// <param name="msg"></param>
13         public void SaveMsg(string connectionId, string msg)
14         {
15             //此處執行插入資料庫操作
16             FileOperateHelp.WriteFile("/Logs/msg.txt", $"用戶【{connectionId}】發來消息:{msg},時間為:{DateTime.Now.ToLongDateString()}");
17         }
18   }

分享一個文件相關操作的工具類FileOperateHelp:

  1  public class FileOperateHelp
  2     {
  3         #region 01.寫文件(.txt-覆蓋)
  4         /// <summary>
  5         /// 寫文件(覆蓋源文件內容)
  6         /// 文件不存在的話自動創建
  7         /// </summary>
  8         /// <param name="FileName">文件路徑(web里相對路徑,控制台在根目錄下寫)</param>
  9         /// <param name="Content">文件內容</param>
 10         public static string Write_Txt(string FileName, string Content)
 11         {
 12             try
 13             {
 14                 Encoding code = Encoding.GetEncoding("gb2312");
 15                 string htmlfilename = FileOperateHelp.PathConvert(FileName);
 16                 //string htmlfilename = HttpContext.Current.Server.MapPath(FileName + ".txt"); //保存文件的路徑  
 17                 string str = Content;
 18                 StreamWriter sw = null;
 19                 {
 20                     try
 21                     {
 22                         sw = new StreamWriter(htmlfilename, false, code);
 23                         sw.Write(str);
 24                         sw.Flush();
 25                     }
 26                     catch { }
 27                 }
 28                 sw.Close();
 29                 sw.Dispose();
 30                 return "ok";
 31             }
 32             catch (Exception ex)
 33             {
 34 
 35                 return ex.Message;
 36             }
 37 
 38         }
 39         #endregion
 40 
 41         #region 02.讀文件(.txt)
 42         /// <summary>
 43         /// 讀文件
 44         /// </summary>
 45         /// <param name="filename">文件路徑(web里相對路徑,控制台在根目錄下寫)</param>
 46         /// <returns></returns>
 47         public static string Read_Txt(string filename)
 48         {
 49 
 50             try
 51             {
 52                 Encoding code = Encoding.GetEncoding("gb2312");
 53                 string temp = FileOperateHelp.PathConvert(filename);
 54                 //  string temp = HttpContext.Current.Server.MapPath(filename + ".txt");
 55                 string str = "";
 56                 if (File.Exists(temp))
 57                 {
 58                     StreamReader sr = null;
 59                     try
 60                     {
 61                         sr = new StreamReader(temp, code);
 62                         str = sr.ReadToEnd(); // 讀取文件  
 63                     }
 64                     catch { }
 65                     sr.Close();
 66                     sr.Dispose();
 67                 }
 68                 else
 69                 {
 70                     str = "";
 71                 }
 72                 return str;
 73             }
 74             catch (Exception ex)
 75             {
 76 
 77                 return ex.Message;
 78             }
 79         }
 80         #endregion
 81 
 82         #region 03.寫文件(.txt-添加)
 83         /// <summary>  
 84         /// 寫文件  
 85         /// </summary>  
 86         /// <param name="FileName">文件路徑(web里相對路徑,控制台在根目錄下寫)</param>  
 87         /// <param name="Strings">文件內容</param>  
 88         public static string WriteFile(string FileName, string Strings)
 89         {
 90             try
 91             {
 92                 string Path = FileOperateHelp.PathConvert(FileName);
 93 
 94                 if (!System.IO.File.Exists(Path))
 95                 {
 96                     System.IO.FileStream f = System.IO.File.Create(Path);
 97                     f.Close();
 98                     f.Dispose();
 99                 }
100                 System.IO.StreamWriter f2 = new System.IO.StreamWriter(Path, true, System.Text.Encoding.UTF8);
101                 f2.WriteLine(Strings);
102                 f2.Close();
103                 f2.Dispose();
104                 return "ok";
105             }
106             catch (Exception ex)
107             {
108 
109                 return ex.Message;
110             }
111         }
112         #endregion
113 
114         #region 04.讀文件(.txt)
115         /// <summary>  
116         /// 讀文件  
117         /// </summary>  
118         /// <param name="FileName">文件路徑(web里相對路徑,控制台在根目錄下寫)</param>  
119         /// <returns></returns>  
120         public static string ReadFile(string FileName)
121         {
122             try
123             {
124                 string Path = FileOperateHelp.PathConvert(FileName);
125                 string s = "";
126                 if (!System.IO.File.Exists(Path))
127                     s = "不存在相應的目錄";
128                 else
129                 {
130                     StreamReader f2 = new StreamReader(Path, System.Text.Encoding.GetEncoding("gb2312"));
131                     s = f2.ReadToEnd();
132                     f2.Close();
133                     f2.Dispose();
134                 }
135                 return s;
136             }
137             catch (Exception ex)
138             {
139                 return ex.Message;
140             }
141         }
142         #endregion
143 
144         #region 05.刪除文件
145         /// <summary>  
146         /// 刪除文件  
147         /// </summary>  
148         /// <param name="Path">文件路徑(web里相對路徑,控制台在根目錄下寫)</param>  
149         public static string FileDel(string Path)
150         {
151             try
152             {
153                 string temp = FileOperateHelp.PathConvert(Path);
154                 File.Delete(temp);
155                 return "ok";
156             }
157             catch (Exception ex)
158             {
159                 return ex.Message;
160             }
161         }
162         #endregion
163 
164         #region 06.移動文件
165         /// <summary>  
166         /// 移動文件  
167         /// </summary>  
168         /// <param name="OrignFile">原始路徑(web里相對路徑,控制台在根目錄下寫)</param>  
169         /// <param name="NewFile">新路徑,需要寫上路徑下的文件名,不能單寫路徑(web里相對路徑,控制台在根目錄下寫)</param>  
170         public static string FileMove(string OrignFile, string NewFile)
171         {
172             try
173             {
174                 OrignFile = FileOperateHelp.PathConvert(OrignFile);
175                 NewFile = FileOperateHelp.PathConvert(NewFile);
176                 File.Move(OrignFile, NewFile);
177                 return "ok";
178             }
179             catch (Exception ex)
180             {
181                 return ex.Message;
182             }
183         }
184         #endregion
185 
186         #region 07.複製文件
187         /// <summary>  
188         /// 複製文件  
189         /// </summary>  
190         /// <param name="OrignFile">原始文件(web里相對路徑,控制台在根目錄下寫)</param>  
191         /// <param name="NewFile">新文件路徑(web里相對路徑,控制台在根目錄下寫)</param>  
192         public static string FileCopy(string OrignFile, string NewFile)
193         {
194             try
195             {
196                 OrignFile = FileOperateHelp.PathConvert(OrignFile);
197                 NewFile = FileOperateHelp.PathConvert(NewFile);
198                 File.Copy(OrignFile, NewFile, true);
199                 return "ok";
200             }
201             catch (Exception ex)
202             {
203                 return ex.Message;
204             }
205         }
206         #endregion
207 
208         #region 08.創建文件夾
209         /// <summary>  
210         /// 創建文件夾  
211         /// </summary>  
212         /// <param name="Path">相對路徑(web里相對路徑,控制台在根目錄下寫)</param>  
213         public static string FolderCreate(string Path)
214         {
215             try
216             {
217                 Path = FileOperateHelp.PathConvert(Path);
218                 // 判斷目標目錄是否存在如果不存在則新建之  
219                 if (!Directory.Exists(Path))
220                 {
221                     Directory.CreateDirectory(Path);
222                 }
223                 return "ok";
224             }
225             catch (Exception ex)
226             {
227                 return ex.Message;
228             }
229         }
230         #endregion
231 
232         #region 09.遞歸刪除文件夾目錄及文件
233         /// <summary>  
234         /// 遞歸刪除文件夾目錄及文件  
235         /// </summary>  
236         /// <param name="dir">相對路徑(web里相對路徑,控制台在根目錄下寫) 截止到哪刪除到哪,eg:/a/ 連a也刪除</param>    
237         /// <returns></returns>  
238         public static string DeleteFolder(string dir)
239         {
240 
241             try
242             {
243                 string adir = FileOperateHelp.PathConvert(dir);
244                 if (Directory.Exists(adir)) //如果存在這個文件夾刪除之   
245                 {
246                     foreach (string d in Directory.GetFileSystemEntries(adir))
247                     {
248                         if (File.Exists(d))
249                             File.Delete(d); //直接刪除其中的文件                          
250                         else
251                             DeleteFolder(d); //遞歸刪除子文件夾   
252                     }
253                     Directory.Delete(adir, true); //刪除已空文件夾                   
254                 }
255                 return "ok";
256             }
257             catch (Exception ex)
258             {
259                 return ex.Message;
260             }
261         }
262 
263         #endregion
264 
265         #region 10.將相對路徑轉換成絕對路徑
266         /// <summary>
267         /// 10.將相對路徑轉換成絕對路徑
268         /// </summary>
269         /// <param name="strPath">相對路徑</param>
270         public static string PathConvert(string strPath)
271         {
272             //web程式使用
273             if (HttpContext.Current != null)
274             {
275                 return HttpContext.Current.Server.MapPath(strPath);
276             }
277             else //非web程式引用             
278             {
279                 strPath = strPath.Replace("/", "\\");
280                 if (strPath.StartsWith("\\"))
281                 {
282                     strPath = strPath.TrimStart('\\');
283                 }
284                 return System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, strPath);
285             }
286         }
287         #endregion
288 
289     }
View Code

2. 採用構造函數註入的方式,在MySpecHub1這個Hub類中進行配置。

代碼如下圖:

3. 配置註入代碼,在Startup類中的Configuration方法中,進行依賴註入代碼的配置。

 代碼如下:

  每當需要創建MySpecHub1實例,SignalR 將調用此匿名函數。
  GlobalHost.DependencyResolver.Register(typeof(MySpecHub1), () => new MySpecHub1(new Repository()));

 4. 在群發介面中進行SaveMsg方法的調用進行測試。

5. 測試結果:

 

 

二. 基於SQLServer或Redis進行部署

  我們都知道,當用戶量併發量非常大的時候,單台伺服器已經無法承載所需的業務,這個時候我們會配置負載均衡,項目會部署在多台伺服器上,通常利用Nginx進行反向代理。   另外在使用SignalR的過程中,你會發現,當連接數比較大的時候,會比較卡頓,所以分散式部署或許是一種不錯的解決方案,但我們會面臨一個問題,如何打通不同地址間的SignalR的通訊呢?   這個時候可以引入“中間件”的概念,比如可以用“SQLServer”或“Redis”為底板,來實現不同地址間SignalR的通訊。(此方案可能非最佳方案,不喜勿噴) PS:   1.  引入“中間件”後,SignalR之間的通訊勢必會減慢,正如魚和熊掌不可兼得哦。   2.  以Redis為底板性能肯定要比SQLServer要高的多。   

 下麵以SQLServer為例簡單的配置一下。

1. 通過Nuget下載程式集:Microsoft.AspNet.SignalR.SqlServer

2. 在SQLServer中新建一個資料庫,比如 SignalRDB,不需要創建任何表,因為程式運行時,會自動生成所需表

 

 

3. 在Startup中配置映射資料庫,代碼如下:

 1   public class Startup
 2     {
 3         public void Configuration(IAppBuilder app)
 4         {   
 5             app.UseCors(CorsOptions.AllowAll).MapSignalR();
 6             //四. 性能優化 
 7            // 1. SQLServer版本(跨伺服器通信代碼配置)
 8             string sqlConnectionString = "data source=localhost;initial catalog=SignalRDB;persist security info=True;user id=sa;password=123456;";
 9             GlobalHost.DependencyResolver.UseSqlServer(sqlConnectionString);
10    
11         }
12     }

 以上3步,已經實現了不同地址間SignalR間的通訊,配置非常簡單,內部複雜實現微軟已經給實現好了,那麼下麵我們簡單的部署一下,分別部署在1001 和 1002 埠下,進行通訊。

 

PS:補充Redis的配置

1. 通過Nuget下載程式集:Microsoft.AspNet.SignalR.Redis

2. 代碼配置:GlobalHost.DependencyResolver.UseRedis("127.0.0.1", 6379, "123456", "mykey");

 

截止到此處Signalr系列入門已經全部更新完成,再深入的需要小伙伴們自行研究了,原計劃的項目案例由於剝離代碼實在是太耗時間了,暫時擱置,後面有時間在補充,下一步會給該系列做一個目錄就徹底告一段落。

 

 

 

 

 

!

  • 作       者 : Yaopengfei(姚鵬飛)
  • 博客地址 : http://www.cnblogs.com/yaopengfei/
  • 聲     明1 : 本人才疏學淺,用郭德綱的話說“我是一個小學生”,如有錯誤,歡迎討論,請勿謾罵^_^。
  • 聲     明2 : 原創博客請在轉載時保留原文鏈接或在文章開頭加上本人博客地址,否則保留追究法律責任的權利。
 
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 需求分析 要做安全方面的內容,依靠人臉識別通過和庫中的臉比對後判定相似率來驗證用戶身份。 快速從圖片中識別出人的信息,用於尋人功能等。 其實從安全的角度出發有很多可以囊括的。比如智能家居中的刷臉開門,支付軟體的刷臉支付等都是例子。 人臉特征提取的步驟 真正的人臉識別需要很多的知識,大體上粗略的可以分 ...
  • 互聯網這股東風不久前刮到了甘涼國,國王老甘獨具慧眼,想趕緊趁著東風未停大力發展移動互聯網,因為他篤信布斯雷的理論:“站在風口上,豬都能飛起來”。無奈地方偏僻落後,國內無可用之才啊。老甘一籌莫展的低頭凝思應聲被打斷,“啟奏陛下,有四個從東土大唐來的和尚前來更換通關文牒”,聽到“東土大唐”四個字,老甘心 ...
  • 因為IDEA和Goland來自同一家非常有名的捷克公司:JetBrains 很好用,智能化程度高 最新版下載地址:http://www.jetbrains.com/go/?fromMenu 相關使用手冊:https://www.jetbrains.com/help/go/discover-golan ...
  • 作者:freewind 比原項目倉庫: Github地址:https://github.com/Bytom/bytom Gitee地址:https://gitee.com/BytomBlockchain/bytom 在前一篇文章中,我們試圖理解比原是如何交易的,但是由於內容太多,我們把它分成了幾個小 ...
  • 作者:freewind 比原項目倉庫: Github地址:https://github.com/Bytom/bytom Gitee地址:https://gitee.com/BytomBlockchain/bytom 在比原的dashboard中,我們可以為一個帳戶創建地址(address),這樣就可 ...
  • 作者:freewind 比原項目倉庫: Github地址:https://github.com/Bytom/bytom Gitee地址:https://gitee.com/BytomBlockchain/bytom 在前面一篇文章,我們粗略的研究了一下比原的dashboard是如何做出來的,但是對里 ...
  • #include using namespace std; template class Person{ public: T age; public: explicit Person(T newAge); void show(); }; //類模版函數在外部定義時,必須加上template標識 te... ...
  • 1 sockaddr_in、sockaddr_in6和sockadd_un等都可以強制轉換為sockaddr. 2 IP地址是電分十進位形式的字元串,但是在網路中使用的是unsigned int類型的數才能使用。 3 函數listen, 第二個參數backlog,表示內核監聽隊列的最大長度,如果超過 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...