这是本人在福州八中的研究性学习项目,闲得无聊发布在博客上,嘛,就是这样。
技术选型
服务端
先对需求进行评估,找出性能瓶颈。其服务器主要任务为接收弹幕并转发到客户端。
一条弹幕从用户手中发出,直到显示在屏幕上,其间延迟应低于1000ms。然而,用户得到其设备上“发送成功”的反馈时间,应不高于500ms。一般的网络环境中,客户端和服务端的延迟在200ms左右。也就是说,其需要在用户发送弹幕成功后立刻返回信息,将弹幕插入后台队列中,并最终向客户端主动推送。其实时性要求并不高,能在500ms里把服务端逻辑处理完即可。再加上其业务逻辑不复杂,故属于I/O密集型应用。
我选用iojs作为服务端。其异步处理的特性正好符合本项目的需求。再加上其生态环境良好,开发方便,故用之。作为Realtime应用,其需要向客户端主动推送信息。若自己在TCP协议之上扩展一套新协议,调试难度略高。故选用WebSocket协议。其数据库只担任存储数据服务,考虑到Web页面管理方便问题,选用MySQL。
客户端
既然已经选用iojs + WebSocket,那客户端自然用HTML相关技术了。传统的客户端开发技术也并不适合该项目,理由如下:
首先不考虑C / C++语言。为了保证开发效率,势必需要使用Qt等界面库,否则需要熟悉DirectX / OpenGL等相关技术。然而,考虑到可扩展性,为了实现更多的效果,势必需要使用脚本语言(如Lua)等对客户端进行扩展。开发难度高,开发周期长。所以,暂不考虑。
其次,因为该客户端有可能在Windows + PowerPoint和Mac + KeyNote下运行,所以需要考虑跨平台。很明显,为了跨平台,用C# / Java开发只能使用GTK# / GTK技术,而不能用WPF等。其开发效率相比之下显得太低。
现今,把JavaScript用在客户端开发方面的技术有不少,如nw.js / Electron。因对前者更为熟悉,故选用前者。
程序架构
服务端
iojs,通过Express中间件搭建一个HTTP服务器。得到弹幕请求后,通过关键词和用户是否被屏蔽判断是否允许发送弹幕,若否则返回错误。然后返回提交成功的信息,替换敏感词后进行进一步工作。将原始的弹幕记录到数据库,同时加入双向队列。队列大小一定。若过大,则需要从队列头出队列,原因是因为太老的弹幕没有意义。工作线程每隔一定时间自动从队列尾弹出需要的内容发送到客户端,服务端整套流程完成。最后,由nginx反向代理即可。
客户端
因为DOM有效率问题,一个节点的插入会导致整个页面的重新计算。所以不考虑CSS3 Animation和JavaScript Animation。在这里直接选用canvas,把整个页面设计为一个画布。在这个画布上自由发挥。
首先定义每一帧为一个Frame对象。每一帧上面都要有一个动画元素。定义一个Sprite对象作为动画元素的原型对象,拥有一个Draw方法的实现,用于在渲染每一帧的时候告诉自己如何呈现于Frame对象。
其次,定义继承于Sprite对象的Comment对象,这便是每一条弹幕。定义一个继承于Frame对象的CommentFrame对象,由其负责每一个Comment对象的管理。CommentFrame对象要自动渲染每一帧。为了保证效率,还需要一个Buffer,由CommentFrame对其进行管理。CommentFrame渲染各个Sprite对象到Buffer上,再由Buffer绘制到主画布上,实现每一帧的绘画。
再加一个socket模块自动获取消息,就算完成了。
负载测试
环境 | 网络 | 配置及备注 | QPS |
---|---|---|---|
Windows 10 x64 9926 i3-3220 @ 3.30GHz Node v0.12.0 (x64) MySQL 5.7.5-ml5-log (x64) | 本地 一个客户端连接 | 469.5 | |
563.09 | |||
504.59 | |||
420.6 | |||
Linux 3.10.0-123.9.3.el7.x86_64 E5-2630 @ 2.30GHz iojs v1.6.2 mariadb-server.x86_64 1:5.5.41-2.el7_0 | 阿里云杭州D 一个福州铁通客户端连接 | 默认配置 | 784.7 |
868.2 |
QPS意为Requests Per Second,即每秒处理请求数。
试用效果
在宿舍文化节试用,合计约一小时又三十分。
共有569人发送弹幕,筛选掉被拦截(如表白、色情词汇、脏话)弹幕,共得到4457条弹幕。校内人数为1357人,场内总人数估计约1200左右,约47%的人发送了弹幕。
达到了预期效果。
(下列图片来自当时参加活动的同学,已取得其允许,非常感谢)
解决问题
1. 跟踪用户
如何在不登录的情况下跟踪用户?若用随机Session + Cookie的话极易被清理,且有效期难以保证。有一些技术,如Canvas指纹可能是个好选择。(注意iOS设备的验证!如果是同样设置、同样型号的iPhone是否会重复?)
2. 连接稳定性
根据之前的测试,TCP连接由于网络环境过于复杂,稳定性比较差。约10分钟掉线一次。socket.io库本身自带心跳包,故用超时 + 自动重连解决问题。
3. 数据被画到界面上时的效率问题
当Comment对象过多时,每一个对象都要计算其位置并重绘。当同时有10000条以上时,这方面的卡顿会非常明显。结论是不需要考虑,数据量过大的弹幕没有意义。
4. 线程间通信与模块解耦合
程序架构上的问题,用事件解决。
5. 负载测试
发送弹幕接口采用HTTP POST,所以可直接用 ab进行测试。上有测试报告。由于一般连接的客户端少,所以没有很大的必要来测试其极限的连接数。
暴露问题
1. 无意义弹幕占比过大,有一部分人身攻击信息出现,总体弹幕素质不高。
2. 弹幕平均长度过长,容易遮挡其它弹幕。
3. 身份验证手段过于低级,以致一些人难以形成良好的弹幕观。
4. Nwjs对于窗口透明支持度并不高。
5. Linux下不能打开GPU加速。