This is a creation in Article, where the information may have evolved or changed.
Describe
Lhttp is a websocket server-based framework that provides an HTTP-like protocol to help developers develop long-connected applications.
The use of lhttp can greatly reduce the workload of server-side development, enabling very good modularity and decoupling of business functions.
You can customize any function you want.
Project Address
Characteristics
Simple to use, powerful
High performance, using GNATSD Message Queuing Publish 10,000 messages is time consuming 0.04s (Single-core cpu,1g memory).
Supports clustering, scale-out, and increased server access for higher service capabilities
Very container for customization and expansion
Can work well with HTTP services, such as sending messages using HTTP, forwarding messages to upstream HTTP servers, and so on. So even if you don't go the language can also develop some applications.
Chat Room Demo
Front-End SDK
Protocol stack:
+--------------------+| lhttp |+--------------------+| websocket |+--------------------+| TCP |+--------------------+
System architecture
+---------------------------------------+ | message center cluster (gnatsd) | +---------------------------------------+ ........|.................|...............|..................| +-------------+ +-------------+ +-------------+ | | |lhttp server | |lhttp server | |lhttp server | ... | lhttp 服务集群| +-------------+ +-------------+ +-------------+ | .....|..........._____| |___.............| |_________...... | | | | | <----使用websocket链接 +--------+ +--------+ +--------+ +--------+ +--------+ | client | | client | | client | | client | | client | +--------+ +--------+ +--------+ +--------+ +--------+
Quick Start
go get github.com/nats-io/natsgo get github.com/fanux/lhttp
Start GNATSD First:
Open another terminal, execute the client program, enter the command code:
cd bin./lhttpClient
Use the Docker quick experience
$ docker build -t lhttp:latest .$ docker run -p 9090:9090 -p 8081:8081 lhttp:latest
Open Browser, Access: http://localhost:9090 .
Open up to two windows and you can talk.
The WebSocket port is 8081 and can use its own WebSocket client to connectws://localhost:8081
You can also download images from Dockerhub:
$ docker run -p 9090:9090 -p 8081:8081 fanux/lhttp:latest
Protocol Introduction
LHTTP/1.0 Command\r\n --------起始行,协议名和版本,Command:非常重要,标识这条消息的命令码是什么,服务端也是根据命令码注册对应的处理器的。Header1:value\r\n --------首部Header2:value\r\n\r\nbody --------消息体
Case:
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}
With a tutorial, you need only three steps
Define your processor and need aggregationBaseProcessor
type ChatProcessor struct { *lhttp.BaseProcessor}
Implement three interfaces, what to do when the connection is open, what to do when the message arrives.
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}
Register your processor, here with the chat message body, that is chat对应 , this processor will only process LHTTP/1.0 chat\r\n.... this kind of message.
lhttp.Regist("chat",&ChatProcessor{&lhttp.BaseProcessor{}})
If the command code is "chat" Chatprocessor will handle it.
Here, for example, you receive a message and return it directly:
func (p *ChatProcessor)OnMessage(h *WsHandler) { h.Send(h.GetBody())}
Start the server
http.Handler("/echo",lhttp.Handler(lhttp.StartServer))http.ListenAndServe(":8081")
A complete example of a back-shot:
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)}
Subscribe/Publish
Here's how easy it is to develop a timely communication application with Lhttp
Let's say there are two clients, such as browser applications.
CLIENT1:
LHTTP/1.0 command\r\nsubscribe:channelID\r\n\r\nbody optional
Client1 sends the string as above through WebSocket to Lhttp, subscribes to thechannelId
CLIENT2:
LHTTP/1.0 command\r\npublish:channelID\r\n\r\nbody require
Client2 sends a message to the lhttp by sending it as a string on the WebSocket channelID . Because Client1 subscribes to Channelid, CLIENT1 will receive this message.
Client1 do not want to receive the message, then send the following string to the server can:
LHTTP/1.0 command\r\nunsubscribe:channelID\r\n\r\nbody optional
Subscription/release is lhttp built-in functionality, the service side line of code to obtain this service without writing, only need to use a specific header subscribe , publish andunsubscribe
Subscribe to multiple chat rooms at the same time.
LHTTP/1.0 chat\r\nsubscribe:channelID1 channelID2 channelID3\r\n\r\n
Publishing messages using HTTP
URL:/publish.
Method: POST.
HTTP body: Entire lhttp message
For example I want the send a message to the WHO subscribe channel_test by HTTP.
If I want to send a message to the person who subscribed to 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!")
This encapsulates a better tool 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 server
Upstream header allows Lhttp to send a message to the upstream HTTP server.
LHTTP/1.0 command\r\nupstream:POST http://www.xxx.com\r\n\r\nbody
In the case of the Post method, Lhttp sends the entire message body as HTTP body to http://www.xxx.com
If it is get,lhttp will ignore the message body
LHTTP/1.0 command\r\nupstream:GET http://www.xxx.com?user=user_a&age=26\r\n\r\nbody
What is the use of upstream:
If we do not want to change the Lhttp code, but want to store chat records.
A good decoupling can be achieved through the upstream:
and HTTP server can be implemented in other languages.
+----+ +----+ |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
So Jack publish the message when not only Mike can receive, back-end upstream server can also receive, we can process the message store logic in the back-end server, such as the message
Stored in an ordered collection of Redis.
Chunked message
Imagine a message that has both a picture and text and a voice. Lhttp's multipart first solved the problem
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 is implemented using Boundry, and Lhttp uses an offset to identify the tiles, which is more efficient and does not need to traverse the entire message body.
How to get chunked messages
such as client messages are as follows:
LHTTP/1.0 upload\r\nmultipart:0 14\r\n\r\nk1:v1\r\n\r\nbody1k2:v2\r\n\r\nbody2
Server-side code, the message exists in the list:
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{}})
First Filter Module development