使用Go Hijack和jQuery轻松实现异步推送服务
首先要说明的是,这里实现的异步推送服务采用的是Long Polling方式,并不是Comet。
如果想用Comet来实现的话,可以参考这个开源项目:http://cometd.org/。不过其中的服务端实现只有Java版和Python版。如果要用Go来做后端的话需要自己实现Bayeux协议。
关于异步推送服务的解决方案的资料有很多,在这里就不在赘述了。当然,当前最先进的两个方案就是Long Polling和Comet。
1. 预备知识
1.1 Go语言
关于Go语言,其实要说的很多。但是为了不跑题,请大家移步到这里:http://code.google.com/p/golang-china/。另外,Go语言的官网地址是:http://golang.org/。
1.2 Hijack
Hijack其实是一个单词,虽然有很多人把它和电影《泰坦尼克号》中Rose的召唤联系到一起。Hijack被译为劫持,在“处理HTTP请求”的这个上下文中,就意味着可以让我们“劫持”(或者说“保持”)HTTP请求链接,做一些其他操作(比如根据需要修改HTTP响应的内容),然后再在之后的某个时间将响应“推送”回去。说到这,我想这就与Long Polling的运作方式很相似了。
Go语言的Hijack接口非常简单,我们在官方的文档站点上可以找到说明:http://godoc.org/net/http#Hijacker。本文中的核心代码也是来自于此文档。
1.3 jQuery
jQuery作为当今最流行的Javascript开发框架,我想基本上每一个做过Web开发的人都会知道,所以在这里我就不多说了。如果你不知道,可以看这里:http://jquery.com/。
2. 实战
2.1 需求
在本案例中,我需要做一个能实时查看当前授权码的页面,而且我不想手动刷新页面。另外,我还想记录一下从页面打开到当前时刻授权码改变过多少次。因为授权码在被使用后会自动变更一次,所以授权的变更次数就等于使用授权码服务的人数。
2.2 编写服务端代码
之前说了,我们使用Go语言来编写后端代码。我们要使用Go语言的官方http库。
其中,我们需要用http.HandleFunc来注册针对某个url的请求处理器。如下:
http.HandleFunc("/auth_code", getAuthCodeForAdmin) //向http服务器注册一个对指定url进行处理的函数。
在函数getAuthCodeForAdmin的签名中,有两个参数——http.Request对象指针和http.ResponseWriter对象。http.Request对象指针用来获取请求信息,http.ResponseWriter对象用来写入响应。
如果要使用Go的Hijack方式来处理HTTP请求,就需要先import其官方的http包:
import (
"net/http"
)
之后,我们在处理函数getAuthCodeForAdmin中先将http.ResponseWriter对象显式转换为http.Hijacker接口:
hj, ok := w.(http.Hijacker)
返回值中赋给“ok”变量的值代表转换是否成功,如果不成功就说明http.ResponseWriter对象未实现http.Hijacker接口。
如果转换成功,我们就可以调用http.Hijacker接口的Hijack方法来获取连接对象及其读写缓存对象了:
conn, bufrw, err := hj.Hijack()
返回值中,“conn”代表连接对象,“bufrw”代表该连接的读写缓存对象。
如果返回值“err”等于nil就说明获取成功,我就可以继续下面的事情了。但首先需要在函数推出前关闭连接,不论函数是否执行成功以及是否有错误发生:
defer conn.Close()
使用defer关键字意味着,让程序执行流程退出该函数前先执行紧随其后的语句或函数。这样就保证了资源的及时释放。
接下来,我们先观测新的授权码的出现,当其出现后我们就使用连接读写缓存对象bufrw返回给http客户端。从观测到返回给http客户端的时间并不确定,也许时间会很长,这也从侧面体现了Long Polling中的Long。看下面的代码:
nacChan := make(chan string)
triggerFunc := func(newAuthCode string) {
nacChan <- newAuthCode }
triggerId := fmt.Sprintf("long-polling|%s|%s|%d", loginName, groupName, time.Now().UnixNano())
request.AddNewAuthCodeTrigger(triggerId, triggerFunc)
defer request.DelNewAuthCodeTrigger(triggerId)
这段代码其中包含的东西很多,我们不需要全搞明白,只要知道这是为新授权码产生时间注册一个触发器就行了。
当新授权码被产生后,充当触发器的函数triggerFunc会被调用。它会向名为nacChan的Channel中添加一个元素。注意,这个Channel是字符串类型的,并且是阻塞式。阻塞式意味着获取元素的语句会一直阻塞,直到该Channel被添入元素。另外,当Channel中已有一个元素时,添加元素的语句也会被阻塞。我们在这里只用到了阻塞式Channel的前一个特性。元素获取语句是这样写的:
newAuthCode := <-nacChan
获取到新授权码后,程序会立即把它“push”给http客户端。
done := pushResponse(bufrw, newAuthCode)
函数pushResponse的完整定义如下:
func pushResponse(bufrw *bufio.ReadWriter, authCode string) bool {
_, err := bufrw.Write([]byte(authCode))
if err == nil {
err = bufrw.Flush()
}
if err != nil {
go_lib.LogErrorf("PushAuthCodeError: %s\n", err)
return false
}
return true
}
其中用到了很多“net/http”以外的包,关于它们的说明可以到Go文档站点http://godoc.org/中查找。另外,“go_lib”是我为了自己开发方便而写的一个函数库,源码在这里:https://github.com/hyper-carrot/go_lib,有兴趣的读者可以查看。
至此,基于Long Polling的异步推送服务的服务端就完成了。函数getAuthCodeForAdmin的完整代码可以参看:https://github.com/hyper-carrot/hypermind/blob/master/server.go#L244。
如果想用Comet来实现的话,可以参考这个开源项目:http://cometd.org/。不过其中的服务端实现只有Java版和Python版。如果要用Go来做后端的话需要自己实现Bayeux协议。
关于异步推送服务的解决方案的资料有很多,在这里就不在赘述了。当然,当前最先进的两个方案就是Long Polling和Comet。
1. 预备知识
1.1 Go语言
关于Go语言,其实要说的很多。但是为了不跑题,请大家移步到这里:http://code.google.com/p/golang-china/。另外,Go语言的官网地址是:http://golang.org/。
1.2 Hijack
Hijack其实是一个单词,虽然有很多人把它和电影《泰坦尼克号》中Rose的召唤联系到一起。Hijack被译为劫持,在“处理HTTP请求”的这个上下文中,就意味着可以让我们“劫持”(或者说“保持”)HTTP请求链接,做一些其他操作(比如根据需要修改HTTP响应的内容),然后再在之后的某个时间将响应“推送”回去。说到这,我想这就与Long Polling的运作方式很相似了。
Go语言的Hijack接口非常简单,我们在官方的文档站点上可以找到说明:http://godoc.org/net/http#Hijacker。本文中的核心代码也是来自于此文档。
1.3 jQuery
jQuery作为当今最流行的Javascript开发框架,我想基本上每一个做过Web开发的人都会知道,所以在这里我就不多说了。如果你不知道,可以看这里:http://jquery.com/。
2. 实战
2.1 需求
在本案例中,我需要做一个能实时查看当前授权码的页面,而且我不想手动刷新页面。另外,我还想记录一下从页面打开到当前时刻授权码改变过多少次。因为授权码在被使用后会自动变更一次,所以授权的变更次数就等于使用授权码服务的人数。
2.2 编写服务端代码
之前说了,我们使用Go语言来编写后端代码。我们要使用Go语言的官方http库。
其中,我们需要用http.HandleFunc来注册针对某个url的请求处理器。如下:
http.HandleFunc("/auth_code", getAuthCodeForAdmin) //向http服务器注册一个对指定url进行处理的函数。
在函数getAuthCodeForAdmin的签名中,有两个参数——http.Request对象指针和http.ResponseWriter对象。http.Request对象指针用来获取请求信息,http.ResponseWriter对象用来写入响应。
如果要使用Go的Hijack方式来处理HTTP请求,就需要先import其官方的http包:
import (
"net/http"
)
之后,我们在处理函数getAuthCodeForAdmin中先将http.ResponseWriter对象显式转换为http.Hijacker接口:
hj, ok := w.(http.Hijacker)
返回值中赋给“ok”变量的值代表转换是否成功,如果不成功就说明http.ResponseWriter对象未实现http.Hijacker接口。
如果转换成功,我们就可以调用http.Hijacker接口的Hijack方法来获取连接对象及其读写缓存对象了:
conn, bufrw, err := hj.Hijack()
返回值中,“conn”代表连接对象,“bufrw”代表该连接的读写缓存对象。
如果返回值“err”等于nil就说明获取成功,我就可以继续下面的事情了。但首先需要在函数推出前关闭连接,不论函数是否执行成功以及是否有错误发生:
defer conn.Close()
使用defer关键字意味着,让程序执行流程退出该函数前先执行紧随其后的语句或函数。这样就保证了资源的及时释放。
接下来,我们先观测新的授权码的出现,当其出现后我们就使用连接读写缓存对象bufrw返回给http客户端。从观测到返回给http客户端的时间并不确定,也许时间会很长,这也从侧面体现了Long Polling中的Long。看下面的代码:
nacChan := make(chan string)
triggerFunc := func(newAuthCode string) {
nacChan <- newAuthCode }
triggerId := fmt.Sprintf("long-polling|%s|%s|%d", loginName, groupName, time.Now().UnixNano())
request.AddNewAuthCodeTrigger(triggerId, triggerFunc)
defer request.DelNewAuthCodeTrigger(triggerId)
这段代码其中包含的东西很多,我们不需要全搞明白,只要知道这是为新授权码产生时间注册一个触发器就行了。
当新授权码被产生后,充当触发器的函数triggerFunc会被调用。它会向名为nacChan的Channel中添加一个元素。注意,这个Channel是字符串类型的,并且是阻塞式。阻塞式意味着获取元素的语句会一直阻塞,直到该Channel被添入元素。另外,当Channel中已有一个元素时,添加元素的语句也会被阻塞。我们在这里只用到了阻塞式Channel的前一个特性。元素获取语句是这样写的:
newAuthCode := <-nacChan
获取到新授权码后,程序会立即把它“push”给http客户端。
done := pushResponse(bufrw, newAuthCode)
函数pushResponse的完整定义如下:
func pushResponse(bufrw *bufio.ReadWriter, authCode string) bool {
_, err := bufrw.Write([]byte(authCode))
if err == nil {
err = bufrw.Flush()
}
if err != nil {
go_lib.LogErrorf("PushAuthCodeError: %s\n", err)
return false
}
return true
}
其中用到了很多“net/http”以外的包,关于它们的说明可以到Go文档站点http://godoc.org/中查找。另外,“go_lib”是我为了自己开发方便而写的一个函数库,源码在这里:https://github.com/hyper-carrot/go_lib,有兴趣的读者可以查看。
至此,基于Long Polling的异步推送服务的服务端就完成了。函数getAuthCodeForAdmin的完整代码可以参看:https://github.com/hyper-carrot/hypermind/blob/master/server.go#L244。
相关推荐
EdwardSiCong 2020-11-23
85477104 2020-11-17
hhanbj 2020-11-17
81427005 2020-11-11
seoppt 2020-09-13
honeyth 2020-09-13
WRITEFORSHARE 2020-09-13
84483065 2020-09-11
momode 2020-09-11
85477104 2020-08-15
83510998 2020-08-08
82550495 2020-08-03
tthappyer 2020-08-03
84901334 2020-07-28
tthappyer 2020-07-25
TONIYH 2020-07-22
tztzyzyz 2020-07-20
83510998 2020-07-18
81463166 2020-07-17