Go实现基于WebSocket的弹幕服务
拉模式和推模式
拉模式
1、数据更新频率低,则大多数请求是无效的
2、在线用户量多,则服务端的查询负载高
3、定时轮询拉取,实时性低
推模式
1、仅在数据更新时才需要推送
2、需要维护大量的在线长连接
3、数据更新后可以立即推送
基于webSocket推送
1、浏览器支持的socket编程,轻松维持服务端长连接
2、基于TCP可靠传输之上的协议,无需开发者关心通讯细节
3、提供了高度抽象的编程接口,业务开发成本较低
webSocket协议与交互
通讯流程
客户端->upgrade->服务端
客户端<-switching<-服务端
客户端->message->服务端
客户端<-message<-服务端
实现http服务端
1、webSocket是http协议upgrade而来
2、使用http标准库快速实现空接口:/ws
webSocket握手
1、使用webSocket.Upgrader完成协议握手,得到webSocket长连接
2、操作webSocket api,读取客户端消息,然后原样发送回去
封装webSocket
缺乏工程化设计
1、其他代码模块,无法直接操作webSocket连接
2、webSocket连接非线程安全,并发读/写需要同步手段
隐藏细节,封装api
1、封装Connection结构,隐藏webSocket底层连接
2、封装Connection的api,提供Send/Read/Close等线程安全接口
api原理(channel是线程安全的)
1、SendMessage将消息投递到out channel
2、ReadMessage从in channel读取消息
内部原理
1、启动读协程,循环读取webSocket,将消息投递到in channel
2、启动写协程,循环读取out channel,将消息写给webSocket
// server.go
package main
import (
"net/http"
"github.com/gorilla/websocket"
"./impl"
"time"
)
var (
upgrader = websocket.Upgrader{
//允许跨域
CheckOrigin: func(r *http.Request) bool {
return true
},
}
)
func wsHandler(w http.ResponseWriter, r *http.Request) {
var (
wsConn *websocket.Conn
err error
conn *impl.Connection
data []byte
)
//Upgrade:websocket
if wsConn, err = upgrader.Upgrade(w, r, nil); err != nil {
return
}
if conn, err = impl.InitConnection(wsConn); err != nil {
goto ERR
}
go func() {
var (
err error
)
for {
if err =conn.WriteMessage([]byte("heartbeat")); err != nil {
return
}
time.Sleep(1 * time.Second)
}
}()
for {
if data, err = conn.ReadMessage(); err != nil {
goto ERR
}
if err = conn.WriteMessage(data); err != nil {
goto ERR
}
}
ERR:
//关闭连接
conn.Close()
}
func main() {
//http:localhost:7777/ws
http.HandleFunc("/ws", wsHandler)
http.ListenAndServe("0.0.0.0:7777", nil)
}
// connection.go
package impl
import (
"github.com/gorilla/websocket"
"sync"
"github.com/influxdata/platform/kit/errors"
)
var once sync.Once
type Connection struct {
wsConn *websocket.Conn
inChan chan []byte
outChan chan []byte
closeChan chan byte
isClosed bool
mutex sync.Mutex
}
func InitConnection(wsConn *websocket.Conn) (conn *Connection, err error) {
conn = &Connection{
wsConn:wsConn,
inChan:make(chan []byte, 1000),
outChan:make(chan []byte, 1000),
closeChan:make(chan byte, 1),
}
//启动读协程
go conn.readLoop()
//启动写协程
go conn.writeLoop()
return
}
//API
func (conn *Connection) ReadMessage() (data []byte, err error) {
select {
case data = <- conn.inChan:
case <- conn.closeChan:
err = errors.New("connection is closed")
}
return
}
func (conn *Connection) WriteMessage(data []byte) (err error) {
select {
case conn.outChan <- data:
case <- conn.closeChan:
err = errors.New("connection is closed")
}
return
}
func (conn *Connection) Close() {
// 线程安全的close,可重入
conn.wsConn.Close()
conn.mutex.Lock()
if !conn.isClosed {
close(conn.closeChan)
conn.isClosed = true
}
conn.mutex.Unlock()
}
//内部实现
func (conn *Connection) readLoop() {
var (
data []byte
err error
)
for {
if _, data, err = conn.wsConn.ReadMessage(); err != nil {
goto ERR
}
//阻塞在这里,等待inChan有空位置
//但是如果writeLoop连接关闭了,这边无法得知
//conn.inChan <- data
select {
case conn.inChan <- data:
case <-conn.closeChan:
//closeChan关闭的时候,会进入此分支
goto ERR
}
}
ERR:
conn.Close()
}
func (conn *Connection) writeLoop() {
var (
data []byte
err error
)
for {
select {
case data = <- conn.outChan:
case <- conn.closeChan:
goto ERR
}
if err = conn.wsConn.WriteMessage(websocket.TextMessage, data); err != nil {
goto ERR
}
conn.outChan <- data
}
ERR:
conn.Close()
}
Go实现基于WebSocket的弹幕服务的更多相关文章
- 使用JMeter测试基于WebSocket协议的服务
使用JMeter测试基于WebSocket协议的服务 :first-child{margin-top:0!important}img.plugin{box-shadow:0 1px 3px rgba( ...
- day112:MoFang:种植园使用websocket代替http&服务端基于flask-socketio提供服务&服务端响应信息&种植园页面显示初始化
目录 1.种植园使用websocket代替http 2.服务端基于socket提供服务 3.服务端响应信息 4.种植园页面展示 1.种植园使用websocket代替http 我们需要完成的种植园,是一 ...
- 基于 WebSocket 实现 WebGL 3D 拓扑图实时数据通讯同步(二)
我们上一篇<基于 WebSocket 实现 WebGL 3D 拓扑图实时数据通讯同步(一)>主要讲解了如何搭建一个实时数据通讯服务器,客户端与服务端是如何通讯的,相信通过上一篇的讲解,再配 ...
- 高效简易开发基于websocket 的通讯应用
websocket的主要是为了解决在web上应用长连接进行灵活的通讯应用而产生,但websocket本身只是一个基础协议,对于消息上还不算灵活,毕竟websocket只提供文本和二进制流这种基础数据格 ...
- Socket.IO – 基于 WebSocket 构建跨浏览器的实时应用
Socket.IO 是一个功能非常强大的框架,能够帮助你构建基于 WebSocket 的跨浏览器的实时应用.支持主流浏览器,多种平台,多种传输模式,还可以集合 Exppress 框架构建各种功能复杂 ...
- 基于 WebSocket 构建跨浏览器的实时应用
Socket.IO – 基于 WebSocket 构建跨浏览器的实时应用 Socket.IO 是一个功能非常强大的框架,能够帮助你构建基于 WebSocket 的跨浏览器的实时应用.支持主流浏览器,多 ...
- 分享基于 websocket 网页端聊天室
博客地址:https://ainyi.com/67 有一个月没有写博客了,也是因为年前需求多.回家过春节的原因,现在返回北京的第二天,想想,应该也要分享技术专题的博客了!! 主题 基于 websock ...
- 第一节:.Net版基于WebSocket的聊天室样例
一. 说在前面的话 该篇文章为实时通讯系列的第一节,基于WebSocket编写了一个简易版聊天样例,主要作用是为引出后面SignalR系列的用法及其强大方便之处,通过这个样例与后续的SignalR对比 ...
- 基于WebSocket实现聊天室(Node)
基于WebSocket实现聊天室(Node) WebSocket是基于TCP的长连接通信协议,服务端可以主动向前端传递数据,相比比AJAX轮询服务器,WebSocket采用监听的方式,减轻了服务器压力 ...
随机推荐
- Speech and Booth Demo in Maker Faire Shenzhen 2018
2018年10月12日-14日,受主办方的邀请,有幸参加了这次的Maker Faire Shenzhen 2018,并参与了Maker Faire Shenzhen 2018 论坛-创客的城市共创(C ...
- 【npm】伙计,给我来一杯package.json!不加糖
前言:夜深了,我熬了一锅热气腾腾的package.json,给大家端上来,希望大家喜欢 json和JS对象的区别 package.json,顾名思义,它是一个json文件,而不能写入JS对象. 所以我 ...
- Ubuntu允许root远程登录配置
1.背景 近期在本地的虚拟机VMware上安装了Ubuntu Server 17.04,由于系统是无界面的,所有操作都需要通过Linux命令进行操作.后来不想直接在服务器上操作,想通过远程工具Xshe ...
- 1分钟快速制作漂亮的H5本地记事本
大家好,以前给大家分享过一个五步骤制作精美的HTML5时钟的文章,点击回顾<五步教你制作漂亮精致的HTML时钟>,还有<一分钟教你如何实现唯美的文字描边>:今天给大家分享一个用 ...
- LabVIEW(七):多态VI
1.多态VI概念:可以处理多种不同数据类型的VI被称为“多态VI”.多态VI根据输入或输出的 数据类型,再选择调用一个针对这种数据类型实现功能的VI,这些针对某种数据类型实现功能的VI称作“实例VI” ...
- PHP之ThinkPHP框架(界面)
Thinkphp框架其精髓就在于实现了MVC思想,其中M为模板.V为视图.C为控制器,模板一般是公共使用类,在涉及数据库时,一般会跟数据表同名,视图会和控制器类里的方法进行名字的一一对应. 下载及配置 ...
- openfire的SSL双向认证增加android客户端证书库步骤
过程 需要新制作PKCS12证书库.CER证书.转换为androidBKS证书,最后把客户端的CER证书导入进im服务器的私钥库client.truststore,然后替换原证书. 新证书生成步骤 ...
- MySQL(4)---慢查询
慢查询 简介 开启慢查询日志,可以让MySQL记录下查询超过指定时间的语句,通过定位分析性能的瓶颈,才能更好的优化数据库系统的性能. 一.配置慢查询 1.参数说明 slow_query_l ...
- .Net 环境下比较各种数据库插入操作的性能
1.简介 再说Windows的异步I/O操作前,先聊聊一些题外话,能帮助我们更好的理解异步I/O操作,常规的Web程序,当用户发起一次请求,当请求通过管道到达客户端的这个过程,会唤起一个线程池线程(后 ...
- intellij idea maven project 无法显示dependencies
Intellj 自动载入Mave依赖是一个很人性化的功能,但不排除有时候会碰到问题,导致pom文件修改却没有触发自动重新载入的动作或者加载中途出现弱网的情况中断载入的,此时需要手动强制更新依赖. 如下 ...