一. SignalR中DI思想的应用

  DI,即依赖注入,它是一种不负责创建其自己的依赖项对象的一种模式,通常用来降低代码之间的耦合性,广泛应用于架构设计,是必不可少的一种思想。
  下面结合一个需求来说一说SignalR中依赖注入思想的应用。
  需求:比如在前面章节的聊天室案例中,想把发送的每条消息都记录下来 (下面的代码中,使用群发这个接口进行测试)。

 分析解决思路:

 1. 新建Repository类和IRepository接口,里面声明SaveMsg方法,用来存储信息 (PS:便于测试,这里将信息保存到txt文本文档中)

代码如下:

   public interface IRepository
{
void SaveMsg(string connectionId, string msg);
}
public class Repository : IRepository
{
/// <summary>
/// 模拟数据库插入操作
/// 这里以日志代替
/// </summary>
/// <param name="connectionId"></param>
/// <param name="msg"></param>
public void SaveMsg(string connectionId, string msg)
{
//此处执行插入数据库操作
FileOperateHelp.WriteFile("/Logs/msg.txt", $"用户【{connectionId}】发来消息:{msg},时间为:{DateTime.Now.ToLongDateString()}");
}
}

分享一个文件相关操作的工具类FileOperateHelp:

  public class FileOperateHelp
{
#region 01.写文件(.txt-覆盖)
/// <summary>
/// 写文件(覆盖源文件内容)
/// 文件不存在的话自动创建
/// </summary>
/// <param name="FileName">文件路径(web里相对路径,控制台在根目录下写)</param>
/// <param name="Content">文件内容</param>
public static string Write_Txt(string FileName, string Content)
{
try
{
Encoding code = Encoding.GetEncoding("gb2312");
string htmlfilename = FileOperateHelp.PathConvert(FileName);
//string htmlfilename = HttpContext.Current.Server.MapPath(FileName + ".txt"); //保存文件的路径
string str = Content;
StreamWriter sw = null;
{
try
{
sw = new StreamWriter(htmlfilename, false, code);
sw.Write(str);
sw.Flush();
}
catch { }
}
sw.Close();
sw.Dispose();
return "ok";
}
catch (Exception ex)
{ return ex.Message;
} }
#endregion #region 02.读文件(.txt)
/// <summary>
/// 读文件
/// </summary>
/// <param name="filename">文件路径(web里相对路径,控制台在根目录下写)</param>
/// <returns></returns>
public static string Read_Txt(string filename)
{ try
{
Encoding code = Encoding.GetEncoding("gb2312");
string temp = FileOperateHelp.PathConvert(filename);
// string temp = HttpContext.Current.Server.MapPath(filename + ".txt");
string str = "";
if (File.Exists(temp))
{
StreamReader sr = null;
try
{
sr = new StreamReader(temp, code);
str = sr.ReadToEnd(); // 读取文件
}
catch { }
sr.Close();
sr.Dispose();
}
else
{
str = "";
}
return str;
}
catch (Exception ex)
{ return ex.Message;
}
}
#endregion #region 03.写文件(.txt-添加)
/// <summary>
/// 写文件
/// </summary>
/// <param name="FileName">文件路径(web里相对路径,控制台在根目录下写)</param>
/// <param name="Strings">文件内容</param>
public static string WriteFile(string FileName, string Strings)
{
try
{
string Path = FileOperateHelp.PathConvert(FileName); if (!System.IO.File.Exists(Path))
{
System.IO.FileStream f = System.IO.File.Create(Path);
f.Close();
f.Dispose();
}
System.IO.StreamWriter f2 = new System.IO.StreamWriter(Path, true, System.Text.Encoding.UTF8);
f2.WriteLine(Strings);
f2.Close();
f2.Dispose();
return "ok";
}
catch (Exception ex)
{ return ex.Message;
}
}
#endregion #region 04.读文件(.txt)
/// <summary>
/// 读文件
/// </summary>
/// <param name="FileName">文件路径(web里相对路径,控制台在根目录下写)</param>
/// <returns></returns>
public static string ReadFile(string FileName)
{
try
{
string Path = FileOperateHelp.PathConvert(FileName);
string s = "";
if (!System.IO.File.Exists(Path))
s = "不存在相应的目录";
else
{
StreamReader f2 = new StreamReader(Path, System.Text.Encoding.GetEncoding("gb2312"));
s = f2.ReadToEnd();
f2.Close();
f2.Dispose();
}
return s;
}
catch (Exception ex)
{
return ex.Message;
}
}
#endregion #region 05.删除文件
/// <summary>
/// 删除文件
/// </summary>
/// <param name="Path">文件路径(web里相对路径,控制台在根目录下写)</param>
public static string FileDel(string Path)
{
try
{
string temp = FileOperateHelp.PathConvert(Path);
File.Delete(temp);
return "ok";
}
catch (Exception ex)
{
return ex.Message;
}
}
#endregion #region 06.移动文件
/// <summary>
/// 移动文件
/// </summary>
/// <param name="OrignFile">原始路径(web里相对路径,控制台在根目录下写)</param>
/// <param name="NewFile">新路径,需要写上路径下的文件名,不能单写路径(web里相对路径,控制台在根目录下写)</param>
public static string FileMove(string OrignFile, string NewFile)
{
try
{
OrignFile = FileOperateHelp.PathConvert(OrignFile);
NewFile = FileOperateHelp.PathConvert(NewFile);
File.Move(OrignFile, NewFile);
return "ok";
}
catch (Exception ex)
{
return ex.Message;
}
}
#endregion #region 07.复制文件
/// <summary>
/// 复制文件
/// </summary>
/// <param name="OrignFile">原始文件(web里相对路径,控制台在根目录下写)</param>
/// <param name="NewFile">新文件路径(web里相对路径,控制台在根目录下写)</param>
public static string FileCopy(string OrignFile, string NewFile)
{
try
{
OrignFile = FileOperateHelp.PathConvert(OrignFile);
NewFile = FileOperateHelp.PathConvert(NewFile);
File.Copy(OrignFile, NewFile, true);
return "ok";
}
catch (Exception ex)
{
return ex.Message;
}
}
#endregion #region 08.创建文件夹
/// <summary>
/// 创建文件夹
/// </summary>
/// <param name="Path">相对路径(web里相对路径,控制台在根目录下写)</param>
public static string FolderCreate(string Path)
{
try
{
Path = FileOperateHelp.PathConvert(Path);
// 判断目标目录是否存在如果不存在则新建之
if (!Directory.Exists(Path))
{
Directory.CreateDirectory(Path);
}
return "ok";
}
catch (Exception ex)
{
return ex.Message;
}
}
#endregion #region 09.递归删除文件夹目录及文件
/// <summary>
/// 递归删除文件夹目录及文件
/// </summary>
/// <param name="dir">相对路径(web里相对路径,控制台在根目录下写) 截止到哪删除到哪,eg:/a/ 连a也删除</param>
/// <returns></returns>
public static string DeleteFolder(string dir)
{ try
{
string adir = FileOperateHelp.PathConvert(dir);
if (Directory.Exists(adir)) //如果存在这个文件夹删除之
{
foreach (string d in Directory.GetFileSystemEntries(adir))
{
if (File.Exists(d))
File.Delete(d); //直接删除其中的文件
else
DeleteFolder(d); //递归删除子文件夹
}
Directory.Delete(adir, true); //删除已空文件夹
}
return "ok";
}
catch (Exception ex)
{
return ex.Message;
}
} #endregion #region 10.将相对路径转换成绝对路径
/// <summary>
/// 10.将相对路径转换成绝对路径
/// </summary>
/// <param name="strPath">相对路径</param>
public static string PathConvert(string strPath)
{
//web程序使用
if (HttpContext.Current != null)
{
return HttpContext.Current.Server.MapPath(strPath);
}
else //非web程序引用
{
strPath = strPath.Replace("/", "\\");
if (strPath.StartsWith("\\"))
{
strPath = strPath.TrimStart('\\');
}
return System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, strPath);
}
}
#endregion }

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中配置映射数据库,代码如下:

   public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCors(CorsOptions.AllowAll).MapSignalR();
//四. 性能优化
// 1. SQLServer版本(跨服务器通信代码配置)
string sqlConnectionString = "data source=localhost;initial catalog=SignalRDB;persist security info=True;user id=sa;password=123456;";
GlobalHost.DependencyResolver.UseSqlServer(sqlConnectionString); }
}

以上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 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
 

第六节:SignalR完结篇之依赖注入和分布式部署的更多相关文章

  1. IoC容器Autofac正篇之依赖注入(六)

    依赖注入,这个专业词我们可以分为两个部分来理解: 依赖,也就是UML中描述事物之间关系的依赖关系,依赖关系描述了事物A在某些情况下会使用到事物B,事物B的变化会影响到事物A: 注入,医生通过针头将药物 ...

  2. ABP module-zero +AdminLTE+Bootstrap Table+jQuery权限管理系统第十六节--SignalR与ABP框架Abp.Web.SignalR及扩展

    SignalR简介 SignalR是什么? ASP.NET SignalR 是为 ASP.NET 开发人员提供的一个库,可以简化开发人员将实时 Web 功能添加到应用程序的过程.实时 Web 功能是指 ...

  3. IoC容器Autofac正篇之依赖注入(七)

    依赖注入,这个专业词我们可以分为两个部分来理解: 依赖,也就是UML中描述事物之间关系的依赖关系,依赖关系描述了事物A在某些情况下会使用到事物B,事物B的变化会影响到事物A: 注入,医生通过针头将药物 ...

  4. Web API(六):使用Autofac实现依赖注入

    在这一篇文章将会讲解如何在Web API2中使用Autofac实现依赖注入. 一.创建实体类库 1.创建单独实体类 创建DI.Entity类库,用来存放所有的实体类,新建用户实体类,其结构如下: us ...

  5. Core篇——初探依赖注入

    目录 1.DI&&IOC简单介绍 2.UML类图中六种关联关系 3..net core 中DI的使用 4..net core DI初始化源码初窥 DI&&IOC简单介绍 ...

  6. [ASP.NET Core开发实战]基础篇02 依赖注入

    ASP.NET Core的底层机制之一是依赖注入(DI)设计模式,因此要好好掌握依赖注入的用法. 什么是依赖注入 我们看一下下面的例子: public class MyDependency { pub ...

  7. 【串线篇】依赖注入DI与控制反转IOC

    DI&IOC 在spring框架中DI与IOC说的其实是一回事 一句话:本来我接受各种参数来构造一个对象,现在只接受一个参数——已经实例化的对象. 也就是说我对对象的『依赖』是注入进来的,而和 ...

  8. 深入理解ASP.NET 5的依赖注入

    (此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 题记:ASP.NET 5整个底层都架构于依赖注入机制之下,今天介绍的文章详细介绍了内置依赖注 ...

  9. 理解依赖注入(IOC)和学习Unity

    资料1: IOC:英文全称:Inversion of Control,中文名称:控制反转,它还有个名字叫依赖注入(Dependency Injection). 作用:将各层的对象以松耦合的方式组织在一 ...

随机推荐

  1. python文章装饰器理解12步

    1. 函数 在python中,函数通过def关键字.函数名和可选的参数列表定义.通过return关键字返回值.我们举例来说明如何定义和调用一个简单的函数: def foo(): return 1 fo ...

  2. 进程间数据传递:Queue,Pipe 进程间数据共享:Manager

    1.使用multiprocessing模块的Queue实现数据传递 ''' 进程间通讯: Queue,用法跟线程里的Queue一样,put,get 线程queue没有做序列化,进程queue做序列化了 ...

  3. idea spring boot

    1 如何使用IntelliJ IDEA 配置Maven https://blog.csdn.net/westos_linux/article/details/78968012 2.Maven将中央仓库 ...

  4. Linux Swap交换分区探讨

    Swap交换分区概念 Linux divides its physical RAM (random access memory) into chucks of memory called pages. ...

  5. TCP/IP及内核参数优化调优

    Linux下TCP/IP及内核参数优化有多种方式,参数配置得当可以大大提高系统的性能,也可以根据特定场景进行专门的优化,如TIME_WAIT过高,DDOS攻击等等.如下配置是写在sysctl.conf ...

  6. [JLOI2015]骗我呢

    [JLOI2015]骗我呢 Tags:题解 作业部落 评论地址 TAG:数学,DP 题意 骗你呢 求满足以下条件的\(n*m\)的矩阵的个数对\(10^9+7\)取模 对于矩阵中的第\(i\)行第\( ...

  7. Spring Security Oauth2 的配置

    使用oauth2保护你的应用,可以分为简易的分为三个步骤 配置资源服务器 配置认证服务器 配置spring security 前两点是oauth2的主体内容,但前面我已经描述过了,spring sec ...

  8. java基础-开发工具IDEA

    常用快捷键 查找 查找:Ctrl + F Find In Path: Ctrl + F + Shift (比普通查找多了一个shift) Search EveryWhere : 双击Shift 视图 ...

  9. 解决import模块后提示无此模块的问题

    最近在工作中发现一个奇怪的问题: 明明已经装上了,但是还提示找不到该模块,没办法,我又去site-package文件下面看了: 发现Linux下自带的python2.7里面装上了该模块(我在root用 ...

  10. Shiro限制登录尝试次数

    /** * 认证信息.(身份验证) : Authentication 是用来验证用户身份 * * @param token * @return * @throws AuthenticationExce ...