博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
及时通信最佳实践
阅读量:6927 次
发布时间:2019-06-27

本文共 6444 字,大约阅读时间需要 21 分钟。

描述

Lhttp是一个基于websocket服务端框架,提供一个类似http的协议去帮助开发者开发长连接的应用。

使用Lhttp可以大量减少服务端开发的工作量,实现非常好的模块化和业务功能的解耦合。

可以定制任何你想要的功能。

特点

  • 使用简单,功能强大

  • 性能高,使用gnatsd消息队列 publish 10000 条消息耗时0.04s(single-core CPU,1G memory).

  • 支持集群,横向扩展,通过增加服务器来获取更高的服务能力

  • 非常容器进行定制与扩展

  • 可以非常好的与http服务协同工作,如利用http发送消息,将消息转发给上游http服务器等。所以即便你不会go语言也可以开发一些应用。

图片描述

协议栈:

+--------------------+|       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/natsgo 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

打开浏览器,访问: http://localhost:9090.

打开两个窗口就可以聊起来了。

websocket 端口是 8081, 可以使用自己的websocket客户端去连 ws://localhost:8081

也可以从dockerhub上下载镜像:

$ docker run -p 9090:9090 -p 8081:8081 fanux/lhttp:latest

协议介绍

LHTTP/1.0 Command\r\n                --------起始行,协议名和版本,Command:非常重要,标识这条消息的命令码是什么,服务端也是根据命令码注册对应的处理器的。Header1:value\r\n                    --------首部Header2:value\r\n\r\nbody                                 --------消息体

事例:

LHTTP/1.0 chat\r\n                  命令码叫`chat`content-type:json\r\n               消息体使用json编码publish:channel_jack\r\n            服务端请把这条消息publish给jack (jack订阅了channel_jack)\r\n{    to:jack,    from:mike,    message:hello jack,    time:1990-1210 5:30:48}

使用教程,只需三步

定义你的处理器,需要聚合 BaseProcessor

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}

注册你的处理器,这里的chat 与消息体中的chat对应,也就是这个处理器仅会处理LHTTP/1.0 chat\r\n....这类消息.

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 command\r\nsubscribe:channelID\r\n\r\nbody optional

client1通过websocket向Lhttp发送如上字符串,就订阅了channelId

client2:

LHTTP/1.0 command\r\npublish:channelID\r\n\r\nbody require

client2通过websocket向Lhttp发送如上字符串,就向channelID发布了一条消息。 因为client1订阅了channelID,所以client1会收到这条消息。

client1不想再收消息,那么发如下字符串给服务端即可:

LHTTP/1.0 command\r\nunsubscribe:channelID\r\n\r\nbody optional

订阅/发布 是lhttp内置功能,服务端一行代码不用写即可获取这种服务,只需要使用特定首部subscribe,publishunsubscribe

同时订阅多个,如同时订阅多个聊天室。

LHTTP/1.0 chat\r\nsubscribe:channelID1 channelID2 channelID3\r\n\r\n

使用http发布消息

URL: /publish .

方法: POST .
http body: 整个lhttp消息
for example I want send a message to who subscribe channel_test by HTTP.
如我想发送一条消息给订阅了channel_test的人。

resp,err := http.POST("https://www.yourserver.com/publish", "text/plain",    "LHTTP/1.0 chat\r\npublish:channel_test\r\n\r\nhello channel_test guys!")

这里封装好了一个更好用的工具 Publish tools.go

//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 command\r\nupstream:POST http://www.xxx.com\r\n\r\nbody

如果是POST方法,lhttp会把整个消息体当作http的body发送给

如果是GET,lhttp会忽略消息体

LHTTP/1.0 command\r\nupstream:GET http://www.xxx.com?user=user_a&age=26\r\n\r\nbody

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 chat\r\nupstream:POST http://www.xxx.com/record\r\npublish:channel_mike\r\n\r\nhello mike,I am jack

mike:

LHTTP/1.0 chat\r\nsubscribe:channel_mike\r\n\r\n

这样jack publish消息时不仅mike可以收到,后端的upstream server也可以收到,我们可以在后端服务器中处理消息存储的逻辑,如将消息

存储到redis的有序集合中。

分块消息

试想一下,一条消息中既有图片也有文字还有语音怎么办? lhttp的multipart首部解决这个问题

LHTTP/1.0 upload\r\nmultipart:0 56\r\n\r\ncontent-type:text/json\r\n\r\n{filename:file.txt,fileLen:5}content-type:text/plain\r\n\r\nhello
content-type:text/json\r\n\r\n{filename:file.txt,fileLen:5}content-type:text/plain\r\n\r\nhello^                                                          ^|<---------------------first part------------------------->|<---------second part------------>|0                                                          56

http中是使用boundry实现的,lhttp使用偏移量标识分块,这样效率更高,不需要遍历整个消息体。

如何获取分块消息

如客户端消息如下:

LHTTP/1.0 upload\r\nmultipart:0 14\r\n\r\nk1:v1\r\n\r\nbody1k2:v2\r\n\r\nbody2

服务端代码,消息存在链表中:

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 processorlhttp.Regist("upload", &UploadProcessor{&lhttp.BaseProcessor{}})

转载地址:http://mcujl.baihongyu.com/

你可能感兴趣的文章
ONE
查看>>
Jmeter常见问题
查看>>
Contoso 大学 - 3 - 排序、过滤及分页
查看>>
Sass介绍及入门教程
查看>>
libCurl的文件上传
查看>>
Can't call commit when autocommit=true(转)
查看>>
一分钟了解:String & StringBuilder & StringBuffer
查看>>
POJ2891:Strange Way to Express Integers(解一元线性同余方程组)
查看>>
如何调试Excel VBA代码
查看>>
写给自己看的小设计2 - 对象设计通用原则(序)
查看>>
学习HTML5之表单
查看>>
cocos2d-x 3.0来做一个简单的游戏教程 win32平台 vs2012 详解献给刚開始学习的人们!...
查看>>
Selenium2(WebDriver)总结(三)---元素定位方法
查看>>
SQLServer 2012异常问题(一)--故障转移群集+镜像环境导致作业执行失败
查看>>
【转】android 最新 NDK r8 在window下开发环境搭建 安装配置与使用 详细图文讲解,完整实际配置过程记录(原创)...
查看>>
ocp 1Z0-043 1-60题解析
查看>>
POJ 2777 Count Color(线段树+位运算)
查看>>
Android英文文档翻译系列(6)——LocalBroadcastManager
查看>>
Unity3D骨骼动画的分解(CleanData.Ani详解)
查看>>
java thread dump日志分析
查看>>