I am sure every web-developer has faced the task of prompt updating of the WEB UI by event on the back-end. A classic example is a web chat (if you have already written your fire web chat, you can skip reading further, most likely you already know everything below).
In X5, I encounter such tasks with an enviable frequency. I must say all kinds of chats, chatbots and other RealTime end-user interaction is trending now. In this article, I want to summarize my experience in this matter and share it with the readers of Habr.
Formulation of the problem
We need a universal and efficient transport to deliver events from the WEB UI (user's browser) to the server and back from the server to the WEB UI
Option # 1 - PERIODIC SURVEYS
the easiest and most ineffective way
The meaning is clear from the name, from the Client2 side periodically, for example, once every 1 second, requests such as "What is there?" Are sent to the server.
This approach has two significant drawbacks:
1-10 , , 1-10 , , , . , , 1-10 RPS.
β RealTime. , RealTime.
β2 -
long polling
β1, , , http-, (.. ), , , . , . , TimeOut
, , .
:
, .. http-, , , .
RealTime, .. , . , http . , , .. .
- β1.
β3 - SERVER SENT EVENT (SSE)
+ API
Server-Sent Events EventSource, . , EventSource . . , retry: ( )
!!! β !
, , , .
. , SSE , REST. http-. , , 1-10 ( ), - β1 :(, , , .
β . , . , , .
β4 - WEBSOCKET
WEBSOCKET SSE . SSE WEBSOCKET.
WEBSOCKET |
SSE |
: , |
: |
|
|
WebSocket |
HTTP |
FrontEnd <-> BackEnd Websocket Golang.
?
β1 - web- ( , app ..):
FrontEnd BackEnd (WS)
BackEnd (WS)
FrontEnd . (REST) (, )
3- .2, , .
2 - () , ,
BackEnd (WS)
FrontEnd (WS)
( )
:
(http://your_domain/ws)
Go Β«HUBΒ»,
http http://your_domain/ws :
Go ( , ws )
http ws βCLIENT_CONNECTEDβ
// Message ...
type Message struct {
Type string `json:"type,omitempty"`
Event string `json:"event"`
Data string `json:"data"`
}
Event β
Data β
Type β
Type = publish, Data , Event
Type = broadcast, Data
Type = subscribe, , Event
Type = unsubscribe, , Event
-
. , MacBook Pro i5 8Gb 12K RPS
- . . .
SDK:
- , , .
β , SDK .
sdk :
<script src="http://localhost:9000/sdk/js" async onload="initEventTube()"></script>
localhost:9000 β
:
function initEventTube(){
var options={
connection:{
host:'localhost',
port:'9000'
}
}
var eventTube=new EventTube(options);
window.EventTube=eventTube;
window.EventTube.connect();
}
:
var self=this;
var subscriptionId=null;
window.EventTube.sub('YOUR_EVENT_NAME',function(data){
//
console.log(data);
}).then(function(subId){
//
subscriptionId = subId;
onsole.log('subId:',subId);
},function(err){
//
console.log(err);
});
:
window.EventTube.pub('YOUR_EVENT_NAME', 'YOUR_EVENT_DATA');
:
window.EventTube.unsub('YOUR_EVENT_NAME', 'OPTIONAL_SUB_ID');
. OPTIONAL_SUB_ID, , , . SUB_ID (. Β« Β»)
:
$ git clone git@github.com:colber/eventtube-server.git your_dir
$ cd your_dir
$ go run main.go
Docker
$ docker pull ptimofeev/eventtube:latest $ docker run --name eventtube --rm -p 9000:9000 ptimofeev/eventtube
: localhost:9000
$ git clone git@github.com:colber/eventtube-client.git your_dir
$ cd your_dir
$ yarn install
$ yarn serve
http://localhost:8080