及时通信最佳实践
描述Lhttp是一个基于websocket服务端框架,提供一个类似http的协议去帮助开发者开发长连接的应用。 使用Lhttp可以大量减少服务端开发的工作量,实现非常好的模块化和业务功能的解耦合。 可以定制任何你想要的功能。 项目地址 特点
聊天室demo前端sdk
协议栈:+--------------------+ | lhttp | +--------------------+ | websocket | +--------------------+ | TCP | +--------------------+ 系统架构+---------------------------------------+ | message center cluster (gnatsd) | +---------------------------------------+ ........|.................|...............|.................. | +-------------+ +-------------+ +-------------+ | | |lhttp server | |lhttp server | |lhttp server | ... | lhttp 服务集群 | +-------------+ +-------------+ +-------------+ | .....|..........._____| |___.............| |_________...... | | | | | <----使用websocket链接 +--------+ +--------+ +--------+ +--------+ +--------+ | client | | client | | client | | client | | client | +--------+ +--------+ +--------+ +--------+ +--------+ 快速入门go get github.com/nats-io/nats go get github.com/fanux/lhttp 先启动gnatsd: cd bin ./gnatsd & ./lhttpd 打开另一个终端,执行客户端程序,输入命令码: cd bin ./lhttpClient 使用docker快速体验$ docker build -t lhttp:latest . $ docker run -p 9090:9090 -p 8081:8081 lhttp:latest 打开浏览器,访问: 打开两个窗口就可以聊起来了。 websocket 端口是 8081,可以使用自己的websocket客户端去连 也可以从dockerhub上下载镜像: $ docker run -p 9090:9090 -p 8081:8081 fanux/lhttp:latest 协议介绍LHTTP/1.0 Commandrn --------起始行,协议名和版本,Command:非常重要,标识这条消息的命令码是什么,服务端也是根据命令码注册对应的处理器的。 Header1:valuern --------首部 Header2:valuern rn body --------消息体 事例: LHTTP/1.0 chatrn 命令码叫`chat` content-type:jsonrn 消息体使用json编码 publish:channel_jackrn 服务端请把这条消息publish给jack (jack订阅了channel_jack) rn { to:jack,from:mike,message:hello jack,time:1990-1210 5:30:48 } 使用教程,只需三步
type ChatProcessor struct { *lhttp.BaseProcessor }
type ChatProcessor struct { } func (p ChatProcessor)OnOpen(h *WsHandler) { //your logic } func (p ChatProcessor)OnClose(h *WsHandler) { //your logic } func (p ChatProcessor)OnMessage(h *WsHandler) { //your logic }
lhttp.Regist("chat",&ChatProcessor{&lhttp.BaseProcessor{}}) 如果命令码是 "chat" ChatProcessor 会处理它。
func (p *ChatProcessor)OnMessage(h *WsHandler) { h.Send(h.GetBody()) } 启动服务器http.Handler("/echo",lhttp.Handler(lhttp.StartServer)) http.ListenAndServe(":8081") 一个完整的回射例子:type ChatProcessor struct { *lhttp.BaseProcessor } func (p *ChatProcessor) OnMessage (h *lhttp.WsHandler) { log.Print("on message :",h.GetBody()) h.Send(h.GetBody()) } func main(){ lhttp.Regist("chat",&ChatProcessor{&lhttp.BaseProcessor{}}) http.Handle("/echo",lhttp.Handler(lhttp.StartServer)) http.ListenAndServe(":8081",nil) } 订阅/发布下面来看用Lhttp开发及时通信应用有多简单 假设有两个客户端,这里的客户端比如浏览器应用。 client1: LHTTP/1.0 commandrn subscribe:channelIDrn rn body optional client1通过websocket向Lhttp发送如上字符串,就订阅了 client2: LHTTP/1.0 commandrn publish:channelIDrn rn body require client2通过websocket向Lhttp发送如上字符串,就向 client1不想再收消息,那么发如下字符串给服务端即可: LHTTP/1.0 commandrn unsubscribe:channelIDrn rn body optional 订阅/发布 是lhttp内置功能,服务端一行代码不用写即可获取这种服务,只需要使用特定首部 同时订阅多个,如同时订阅多个聊天室。 LHTTP/1.0 chatrn subscribe:channelID1 channelID2 channelID3rn rn 使用http发布消息URL: /publish . resp,err := http.POST("https://www.yourserver.com/publish","text/plain","LHTTP/1.0 chatrnpublish:channel_testrnrnhello channel_test guys!") 这里封装好了一个更好用的工具 //func Publish(channelID []string,command string,header map[string]string,body string) (err error) { //} //send message to who subscribe mike. Publish("mike","yourCommand",nil,"hello mike!") 上游服务器upstream首部可以让lhttp向上游的http服务器发送一条消息。 LHTTP/1.0 commandrn upstream:POST http://www.xxx.comrn rn body 如果是POST方法,lhttp会把整个消息体当作http的body发送给 http://www.xxx.com LHTTP/1.0 commandrn upstream:GET http://www.xxx.com?user=user_a&age=26rn rn body upstream有什么用:如我们不想改动lhttp的代码,但是想存储聊天记录。 通过upstream可以实现很好的解耦: 并且http server可以用其它语言实现. +----+ +----+ |jack| |mike| +----+ +----+ |_____________ _______| | | +------------+ |lhttp server| +------------+ |(http request with chat record) V +------------+ | http server| upstream server(http://www.xxx.com/record) +------------+ (save chat record) jack: LHTTP/1.0 chatrn upstream:POST http://www.xxx.com/recordrn publish:channel_mikern rn hello mike,I am jack mike: LHTTP/1.0 chatrn subscribe:channel_mikern rn 这样jack publish消息时不仅mike可以收到,后端的upstream server也可以收到,我们可以在后端服务器中处理消息存储的逻辑,如将消息 存储到redis的有序集合中。 分块消息试想一下,一条消息中既有图片也有文字还有语音怎么办? lhttp的multipart首部解决这个问题 LHTTP/1.0 uploadrn multipart:0 56rn rn content-type:text/jsonrn rn {filename:file.txt,fileLen:5} content-type:text/plainrn rn hello content-type:text/jsonrnrn{filename:file.txt,fileLen:5}content-type:text/plainrnrnhello ^ ^ |<---------------------first part------------------------->|<---------second part------------>| 0 56 http中是使用boundry实现的,lhttp使用偏移量标识分块,这样效率更高,不需要遍历整个消息体。 如何获取分块消息如客户端消息如下: LHTTP/1.0 uploadrnmultipart:0 14rnrnk1:v1rnrnbody1k2:v2rnrnbody2 服务端代码,消息存在链表中: type UploadProcessor struct { *lhttp.BaseProcessor } func (*UploadProcessor) OnMessage(ws *lhttp.WsHandler) { for m := ws.GetMultipart(); m != nil; m = m.GetNext() { log.Print("multibody:",m.GetBody()," headers:",m.GetHeaders()) } } //don't forget to tegist your command processor lhttp.Regist("upload",&UploadProcessor{&lhttp.BaseProcessor{}}) 首部过滤模块开发(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |