当时这个项目是2018年中旬的时候帮一家传媒公司开发的,里面功能很多,但涉及到的技术点并不多,可能是客户需要的功能比较少的原因吧。但还是相当繁琐的,因为里面有很多功能点。
霸屏,抢红包,互动,众筹,提现,打赏,打赏动画,代理等。
项目为.net 4.5,因为当时在考虑大屏与用户要实时的保持同步,用户与用户要保持连接,所以没选择PHP(不是因为php不好,而是我们公司的技术栈为.net,只能用php做一些简单的cms和网站,php大神勿喷)。开始想使用的是轮询,想着有延时,而且并发上来就得炸。后来考虑长连接和H5 WebSocket,起初已经开始着手自己实现了,后来了解到微软对此有专门的解决方案,那就是【SignalR】 ,而且使用起来很简单,稳定性也比较靠谱,会根据用户的所处环境,自动选择轮询,长连接还是WebSocket。
文章最下面是项目效果,当时客户开展了20多家酒吧吧,巅峰的时候也就不到1000个人在线,当时用的服务器是8g,8核的,cpu没超过30%,内存在4个G左右,可能是用户规模还没上来。数据库使用的是Sql server 2008R2,iis 8.5。抢红包这块使用的是redis+lua脚本,比较靠谱。因为当时的霸屏会出现队列的情况,所以也是使用的redis+lua实现的。
设计思路就是每个酒吧,设置一个单独的聊天室,每个用户通过二维码进入之后绑定进该酒吧的聊天室,聊天信息与霸屏信息推送至大屏。刚好signalr都有类型的功能,signalr怎么使用就不在复述了,网上搜下很多,这里只谈下设计思路。
下面是js用户进入的代码
$.connection.hub.qs = { nId:nid }; //后台生成的针对酒吧唯一ID
var chat = $.connection.chatHub;
chatClientCore = chat.client;
$.connection.hub.start().done(function () {
chatServerCore = chat.server;
console.log("连接成功!");
});
$.connection.hub.disconnected(function () {
if (reConnect == undefined || reConnect==null) {
reConnectServer();//重新连接
}
});
当用户连接入聊天室的时候,在后台将其加入至该酒吧的集合中
public override Task OnConnected()
{
//数据连接的时候
//UserInfo u = new SessionRedisHub(Context).GetLoginUser();
string cid = Context.ConnectionId;
//此处nid从前太连接是传过来的参数获取
int nId = Convert.ToInt32(Context.QueryString["nId"]);
Groups.Add(cid, "node_" + nId);
return base.OnConnected();
}
这里谈到一个细节,就是如果需要私聊在连接上来的时候就得把cid给保存起来。我们项目中是使用redis做session的,具体实现细节,网上也能搜到,这里没记录网页地址。这里需要着重提一下,就是自己实现的话需要在Startup.ConfigureSignalR中注册IUserIdProvider,代码如下
public static void ConfigureSignalR(IAppBuilder app)
{
var userIdProvider = new MyUserFactory();
GlobalHost.DependencyResolver.Register(typeof(IUserIdProvider), () => userIdProvider);
//var config = new HubConfiguration();
//app.MapSignalR("/chat",config);
app.MapSignalR();
}
//MyUserFactory
public class MyUserFactory : IUserIdProvider
{
/// <summary>
/// 获取当前登陆用户存储的sessionid
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
public string GetUserId(IRequest request)
{
string cnnId = new SessionRedis(request).SessionId;
if (!string.IsNullOrEmpty(cnnId))
return cnnId;
return "404";
}
}
发送私聊的时候直接取sessionID进行user发送就可以了
//群聊代码
ChatToNodeInfo nInfo = ChatCorrelation.GetChatNode(cId);
Clients.OthersInGroup("node_" + nInfo.MNId).BPtoScreen(nInfo);
//私聊代码
Clients.User(new SessionRedisHub(Context).SessionId).test(cPageInfo);
//推送大屏
IHubContext ScreenHub = GlobalHost.ConnectionManager.GetHubContext<ScreenHub>();
nInfo.PostTime = DateTime.Now.ToString("MM-dd HH:mm");
ScreenHub.Clients.Group("screen_"+nInfo.MNId).BPtoScreen(nInfo);
大屏和聊天室类似,就是加入的组名不能相同,因为大屏都是单方面接受推送,聊天室的内容想推送至大屏的话,只需要获取到大屏的Hub即可完成推送。
大体实现方案就是这样,使用signal之后实现起来还是比较简单的,具体细节还得看具体业务。
抢红包和霸屏在时间上控制都是使用redis做的,下次有时间在写。
项目部分图片