拉模式和推模式

拉模式

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()
}

最新文章

  1. BZOJ 3531: [Sdoi2014]旅行 [树链剖分]
  2. mono for android学习过程系列教程(7)
  3. java中map插入相同的key
  4. 深入分析MVC中通过IOC实现Controller依赖注入的原理
  5. Xcode 8 : iOS xib is missing from working copy、iOS misssing file
  6. KMP快速字符串匹配
  7. iOS.StaticLibrary.1-avoid-duplicate-symbol-in-static-library[draft]
  8. ASP.NET MVC的路由
  9. .Net Core 学习 (1) - ASP.NET Core 总览
  10. Windows Phone获取WiFi BSSID
  11. css动画特效与js动画特效(一)------2017-03-24
  12. Azure Storage Rest API Demo
  13. js模块化编程之彻底弄懂CommonJS和AMD/CMD!
  14. 照葫芦画瓢系列之Java --- Maven的配置
  15. c/c++ 用普利姆(prim)算法构造最小生成树
  16. 用mybatis中的insert方法插入数据,返回值为1,但数据库却没有数据
  17. 让shell脚本中的echo输出带颜色
  18. jquery判断某个属性是否存在 hasAttr
  19. ATmega8仿真——LED 数码管的学习
  20. Linux 目录结构说明

热门文章

  1. [转]linux VLAN配置(vconfig)
  2. MySQL--BNL/ICP/MRR/BKA
  3. 全国计算机等级考试二级Python语言程序设计考试大纲
  4. Ubuntu18.04版本设置root账户
  5. 记录python题
  6. 导出到word
  7. Error running &#39;Unnamed&#39;: Address localhost:1099 is already in use
  8. Maven - 在Eclipse中创建Maven项目
  9. odoo开发笔记 -- 应用服务器&amp;数据库服务器分开部署
  10. tar (child): bzip2: Cannot exec: No such file or directory报错