当前位置:网站首页>I have seven schemes to realize web real-time message push, seven!

I have seven schemes to realize web real-time message push, seven!

2022-07-25 05:10:00 InfoQ

Technical communication , official account : Programmer Xiaofu

Hello everyone , I'm Xiaofu ~

I have a friend ~

Made a small broken station , Now we need to realize an on-site letter web Message push function , Yes , This is the little red dot in the figure below , A very common function .

null
But he hasn't figured out how to do it , Here I help him sort out several schemes , And simple implementation .

null
Case download
, Remember Star  Oh

What is message push (push)

There are many push scenes , For example, someone pays attention to my official account , Then I will receive a push message , To attract me to click to open the application .

Message push (
push
) It usually refers to the operation staff of the website , Through some kind of tool to the user's current web page or mobile device APP Active message push .

Message push is generally divided into
web End message push
and
Mobile message push
.

null
The above one belongs to mobile message push ,web End message push is common, such as in station messages 、 Number of unread messages 、 Monitor the number of alarms , It is also widely used .

null
Before implementation , Let's analyze the needs ahead , In fact, the function is very simple , Just trigger an event ( Actively sharing resources or actively pushing messages in the background ),web The notification red dot on the page will be real-time
+1
That's all right. .

There are usually several message push tables on the server , It is used to record different types of messages pushed by users triggering different events , The front end actively queries ( PULL ) Or passive reception ( PUSH ) The number of all unread messages of the user .

null
Message push is nothing more than push (
push
) Ho La (
pull
) Two forms , Next, let's learn about it one by one .

Short polling

polling (
polling
) It should be the simplest way to implement message push , Here we divide polling into
Short polling
and
Long polling
.

Short polling is easy to understand , Specified time interval , From the browser to the server
HTTP
request , The server returns unread message data to the client in real time , The browser will render and display .

A simple JS The timer can handle it , Request unread messages per second interface , The returned data can be displayed .

setInterval(() => {
 //  Method request
 messageCount().then((res) => {
 if (res.code === 200) {
 this.messageCount = res.data
 }
 })
}, 1000);

The effect is OK , The implementation of short polling is simple , The disadvantages are also obvious , Because push data does not change frequently , Whether or not a new message is generated at the back end , Clients will make requests , It is bound to cause great pressure on the server , Waste bandwidth and server resources .

null

Long polling

Long polling is an improved version of short polling above , While minimizing the waste of server resources , Ensure the relative timeliness of messages . Long polling is widely used in middleware , such as
Nacos
and
apollo
Configuration center , Message queue
kafka
RocketMQ
Long polling is useful in .

Nacos The configuration center interaction model is push still pull?
In this article, I introduced in detail
Nacos
Implementation principle of long polling , Interested friends can have a look .

This time I use
apollo
The configuration center implements long polling , Applied a class
DeferredResult
, It's in
servelet3.0
After the Spring Encapsulation provides an asynchronous request mechanism , Straight meaning is to delay the result .

null
DeferredResult
It allows the container thread to quickly release the occupied resources , Do not block the request thread , So as to accept more requests and improve the throughput of the system , Then start the asynchronous worker thread to process the real business logic , Processing completion call
DeferredResult.setResult(200)
Submit response results .

Next, we use long polling to push messages .

Because a ID It may be monitored by multiple long polling requests , So I used
guava
Provided by the package
Multimap
The structure stores long polling , One key Can correspond to multiple value. Once you're listening to key change , All corresponding long polls will respond . The front end gets the status code of non request timeout , Be aware of data changes , Actively query the unread message number interface , Update page data .

@Controller
@RequestMapping("/polling")
public class PollingController {

 //  Store and monitor a Id Long polling set for
 //  Thread synchronization structure
 public static Multimap<String, DeferredResult<String>> watchRequests = Multimaps.synchronizedMultimap(HashMultimap.create());

 /**
 *  official account : Programmer Xiaofu
 *  Set listening
 */
 @GetMapping(path = &quot;watch/{id}&quot;)
 @ResponseBody
 public DeferredResult<String> watch(@PathVariable String id) {
 //  Delay object setting timeout
 DeferredResult<String> deferredResult = new DeferredResult<>(TIME_OUT);
 //  Remove when the asynchronous request completes  key, Prevent memory overflow
 deferredResult.onCompletion(() -> {
 watchRequests.remove(id, deferredResult);
 });
 //  Registrar polling request
 watchRequests.put(id, deferredResult);
 return deferredResult;
 }

 /**
 *  official account : Programmer Xiaofu
 *  Change data
 */
 @GetMapping(path = &quot;publish/{id}&quot;)
 @ResponseBody
 public String publish(@PathVariable String id) {
 //  Data changes   Take out the monitor ID All long polling requests , And respond one by one
 if (watchRequests.containsKey(id)) {
 Collection<DeferredResult<String>> deferredResults = watchRequests.get(id);
 for (DeferredResult<String> deferredResult : deferredResults) {
 deferredResult.setResult(&quot; I updated &quot; + new Date());
 }
 }
 return &quot;success&quot;;
 }

When the request exceeds the set timeout , Will throw out
AsyncRequestTimeoutException
abnormal , It's directly used here
@ControllerAdvice
Global capture and unified return , After obtaining the agreed status code, the front end sends a long polling request again , Call back and forth like this .

@ControllerAdvice
public class AsyncRequestTimeoutHandler {

 @ResponseStatus(HttpStatus.NOT_MODIFIED)
 @ResponseBody
 @ExceptionHandler(AsyncRequestTimeoutException.class)
 public String asyncRequestTimeoutHandler(AsyncRequestTimeoutException e) {
 System.out.println(&quot; Asynchronous request timed out &quot;);
 return &quot;304&quot;;
 }
}

So let's test that out , First, the page initiates a long polling request
/polling/watch/10086
Listening to messages changes , Request suspended , Do not change data until timeout , A long polling request was initiated again ; Then change the data manually
/polling/publish/10086
, Long polling is responded , After the front-end processing business logic is completed, the request is initiated again , And so on and so on .

null
Compared with short polling, long polling improves the performance a lot , But there will still be more requests , This is one of its imperfections .

iframe flow

iframe Flow is to insert a hidden
<iframe>
label , By means of
src
Number of request messages in API Interface , This creates a long connection between the server and the client , The server continues to
iframe
To transmit data .

The data transmitted is usually
HTML
、 Or embedded
javascript
Script , To achieve the effect of updating the page in real time .

null
It's easy to implement , There is only one front end
<iframe>
The label is done

<iframe src=&quot;/iframe/message&quot; style=&quot;display:none&quot;></iframe>

The server is directly assembled html、js Script data to
response
Just write

@Controller
@RequestMapping(&quot;/iframe&quot;)
public class IframeController {
 @GetMapping(path = &quot;message&quot;)
 public void message(HttpServletResponse response) throws IOException, InterruptedException {
 while (true) {
 response.setHeader(&quot;Pragma&quot;, &quot;no-cache&quot;);
 response.setDateHeader(&quot;Expires&quot;, 0);
 response.setHeader(&quot;Cache-Control&quot;, &quot;no-cache,no-store&quot;);
 response.setStatus(HttpServletResponse.SC_OK);
 response.getWriter().print(&quot; <script type=\&quot;text/javascript\&quot;>\n&quot; +
 &quot;parent.document.getElementById('clock').innerHTML = \&quot;&quot; + count.get() + &quot;\&quot;;&quot; +
 &quot;parent.document.getElementById('count').innerHTML = \&quot;&quot; + count.get() + &quot;\&quot;;&quot; +
 &quot;</script>&quot;);
 }
 }
}

But I personally don't recommend it , Because it will show on the browser that the request has not been loaded , The icon will rotate , It's a killer of obsessive-compulsive disorder .

null

SSE ( My way )

Many people may not know , The server pushes messages to the client , In fact, in addition to using
WebSocket
Outside this familiar mechanism , There is also a server that sends events (
Server-sent events
), abbreviation
SSE
.

SSE
It is based on
HTTP
Agreed , We know in general HTTP The protocol cannot make the server actively push messages to the client , but SSE The exception is , It changes a way of thinking .

null
SSE Open a one-way channel between the server and the client , The response of the server is no longer a one-time packet, but
text/event-stream
Type of data flow information , When there is data change, it is streamed from the server to the client .

The overall implementation idea is a little similar to online video playback , The video stream will be continuously pushed to the browser , You can also understand it as , It takes a long time for the client to complete one time ( The Internet is not working ) The download .

null
SSE
And
WebSocket
The effect is similar , Can establish the communication between the server and the browser , Realize that the server pushes messages to the client , But it's still a little different :

  • SSE  Is based on HTTP Agreed , They do not require special protocols or server implementations to work ;
    WebSocket
    A separate server is required to handle the Protocol .
  • SSE  One-way communication , Only one-way communication from the server to the client ;webSocket Full duplex communication , That is, both sides of the communication can send and receive information at the same time .
  • SSE  Simple implementation and low development cost , There is no need to introduce other components ;WebSocket Data transmission requires secondary analysis , The development threshold is higher .
  • SSE  Disconnection and reconnection are supported by default ;WebSocket You need to do it yourself .
  • SSE  Only text messages can be sent , Binary data needs to be encoded before transmission ;WebSocket Binary data transfer is supported by default .

SSE  And  WebSocket  How to choose ?

Technology is not good or bad , Only which is more suitable

SSE It seems to have been unknown to everyone , Part of the reason is the emergence of WebSockets, This provides richer protocols to perform bidirectional 、 Full duplex communication . For game 、 Instant messaging and scenarios that require two-way near real-time updates , Having two-way channels is more attractive .

however , In some cases , There is no need to send data from the client . And you only need some server operation updates . such as : mail 、 Number of unread messages 、 Status update 、 Stock market 、 Monitoring quantity and other scenarios ,
SEE
It has more advantages in terms of the difficulty and cost of implementation . Besides ,SSE  have
WebSockets
Lack of multiple functions in design , for example :
Automatically reconnect
event ID
and
Send any event
The ability of .

The front end only needs to be done once HTTP request , Take the only ID, Open the event flow , Just listen to the events pushed by the server

<script>
 let source = null;
 let userId = 7777
 if (window.EventSource) {
 //  Establishing a connection
 source = new EventSource('http://localhost:7777/sse/sub/'+userId);
 setMessageInnerHTML(&quot; Connect the user =&quot; + userId);
 /**
 *  Once the connection is established , It will trigger open event
 *  Another way of writing :source.onopen = function (event) {}
 */
 source.addEventListener('open', function (e) {
 setMessageInnerHTML(&quot; Establishing a connection ...&quot;);
 }, false);
 /**
 *  The client receives the data from the server
 *  Another way of writing :source.onmessage = function (event) {}
 */
 source.addEventListener('message', function (e) {
 setMessageInnerHTML(e.data);
 });
 } else {
 setMessageInnerHTML(&quot; Your browser does not support it SSE&quot;);
 }
</script>

The implementation of the server is simpler , Create a
SseEmitter
Objects in the
sseEmitterMap
Conduct management

private static Map<String, SseEmitter> sseEmitterMap = new ConcurrentHashMap<>();

/**
 *  Create connection
 *
 * @date: 2022/7/12 14:51
 * @auther:  official account : Programmer Xiaofu
 */
public static SseEmitter connect(String userId) {
 try {
 //  Set timeout ,0 No expiration date . Default 30 second
 SseEmitter sseEmitter = new SseEmitter(0L);
 //  Register callback
 sseEmitter.onCompletion(completionCallBack(userId));
 sseEmitter.onError(errorCallBack(userId));
 sseEmitter.onTimeout(timeoutCallBack(userId));
 sseEmitterMap.put(userId, sseEmitter);
 count.getAndIncrement();
 return sseEmitter;
 } catch (Exception e) {
 log.info(&quot; Create a new sse Abnormal connection , The current user :{}&quot;, userId);
 }
 return null;
}

/**
 *  Send a message to the specified user
 *
 * @date: 2022/7/12 14:51
 * @auther:  official account : Programmer Xiaofu
 */
public static void sendMessage(String userId, String message) {

 if (sseEmitterMap.containsKey(userId)) {
 try {
 sseEmitterMap.get(userId).send(message);
 } catch (IOException e) {
 log.error(&quot; user [{}] Push exception :{}&quot;, userId, e.getMessage());
 removeUser(userId);
 }
 }
}

We simulate the server pushing messages , Look at the message received by the client , It's in line with our expectations .

null
Be careful :
 SSE I won't support it
IE
browser , Good compatibility with other mainstream browsers .

null

MQTT

What is?  MQTT agreement ?

MQTT
  Full name (Message Queue Telemetry Transport): One is based on publishing / subscribe (
publish
/
subscribe
) Mode
Lightweight
Communication protocol , Get the message by subscribing to the corresponding topic , It's the Internet of things (
Internet of Thing
) A standard transport protocol in .

The protocol will the publisher of the message (
publisher
) With subscribers (
subscriber
) To separate , Therefore, it can be used in an unreliable network environment , Provide reliable messaging services for remotely connected devices , The way of use is different from the traditional MQ It's kind of similar .

null
TCP
The protocol is on the transport layer ,
MQTT
  The protocol is in the application layer ,
MQTT
  The agreement is built on
TCP/IP
Agreement on , That is to say, as long as you support
TCP/IP
Where the protocol stack , You can use
MQTT
agreement .

Why use  MQTT agreement ?

MQTT
Why is the protocol in the Internet of things (IOT) So popular in ? Not other agreements , For example, we are more familiar with  
HTTP
What about the agreement? ?

  • First
    HTTP
    Protocol, which is a synchronization protocol , After the client requests, it needs to wait for the response of the server . And in the Internet of things (IOT) Environment , The equipment will be subject to the influence of the environment , Such as low bandwidth 、 High network latency 、 Unstable network communication, etc , Obviously, asynchronous messaging protocol is more suitable for
    IOT
    Applications .
  • HTTP
    Is one-way , If you want to get a message, the client must initiate a connection , And in the Internet of things (IOT) In the application , Devices or sensors are often clients , This means that they cannot passively receive commands from the network .
  • Usually you need to send a command or message , Send to all devices on the network .
    HTTP
    It is not only difficult to realize such a function , And the cost is very high .

Concrete MQTT Introduction and practice of the agreement , I won't go into details here , You can refer to my previous two articles , What is written inside is also very detailed .

MQTT Introduction to the agreement

I didn't expect  springboot + rabbitmq  Smart home , It will be so simple

MQTT Implement message push

Unread message ( Little red dot ), front end   And  RabbitMQ  Real time message push practice , The thief is simple ~

Websocket

websocket
It should be a familiar way to push messages , We are talking about SSE It's also with websocket Compared .

WebSocket It's a kind of
TCP
A protocol for full duplex communication over a connection , Establish communication channels between clients and servers . The browser and server only need to shake hands once , A persistent connection can be created directly between the two , And two-way data transmission .

null
springboot Integrate websocket, First introduced
websocket
Related toolkits , and SSE Compared with the additional development costs .

<!--  introduce websocket -->
<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

Server use
@ServerEndpoint
The annotation indicates that the current class is a websocket The server , The client can use the
ws://localhost:7777/webSocket/10086
To connect to WebSocket Server side .

@Component
@Slf4j
@ServerEndpoint(&quot;/websocket/{userId}&quot;)
public class WebSocketServer {
 // A connection session with a client , It is needed to send data to the client
 private Session session;
 private static final CopyOnWriteArraySet<WebSocketServer> webSockets = new CopyOnWriteArraySet<>();
 //  Used to store the number of online connections
 private static final Map<String, Session> sessionPool = new HashMap<String, Session>();
 /**
 *  official account : Programmer Xiaofu
 *  Link the method successfully called
 */
 @OnOpen
 public void onOpen(Session session, @PathParam(value = &quot;userId&quot;) String userId) {
 try {
 this.session = session;
 webSockets.add(this);
 sessionPool.put(userId, session);
 log.info(&quot;websocket news :  There are new connections , The total number is :&quot; + webSockets.size());
 } catch (Exception e) {
 }
 }
 /**
 *  official account : Programmer Xiaofu
 *  Method called upon receipt of a client message
 */
 @OnMessage
 public void onMessage(String message) {
 log.info(&quot;websocket news :  Received client message :&quot; + message);
 }
 /**
 *  official account : Programmer Xiaofu
 *  This is a single message
 */
 public void sendOneMessage(String userId, String message) {
 Session session = sessionPool.get(userId);
 if (session != null && session.isOpen()) {
 try {
 log.info(&quot;websocket eliminate :  Single point message :&quot; + message);
 session.getAsyncRemote().sendText(message);
 } catch (Exception e) {
 e.printStackTrace();
 }
 }
 }
}

Front end initialization is on WebSocket Connect , And monitor the connection status , Receive data from the server or send data to the server .

<script>
 var ws = new WebSocket('ws://localhost:7777/webSocket/10086');
 //  Get connection status
 console.log('ws Connection status :' + ws.readyState);
 // Monitor whether the connection is successful
 ws.onopen = function () {
 console.log('ws Connection status :' + ws.readyState);
 // If the connection is successful, send a data
 ws.send('test1');
 }
 //  Answer the information sent back by the server and process the display
 ws.onmessage = function (data) {
 console.log(' Received a message from the server :');
 console.log(data);
 // Close after communication WebSocket Connect
 ws.close();
 }
 //  Listen for connection close events
 ws.onclose = function () {
 //  Monitor the whole process websocket The state of
 console.log('ws Connection status :' + ws.readyState);
 }
 //  Monitor and process error event
 ws.onerror = function (error) {
 console.log(error);
 }
 function sendMessage() {
 var content = $(&quot;#message&quot;).val();
 $.ajax({
 url: '/socket/publish?userId=10086&message=' + content,
 type: 'GET',
 data: { &quot;id&quot;: &quot;7777&quot;, &quot;content&quot;: content },
 success: function (data) {
 console.log(data)
 }
 })
 }
</script>

Page initialization establishment websocket Connect , Then we can have two-way communication , It's not bad

null
null

Custom push

Above we gave me 6 The principle and code implementation of these schemes , But in the actual business development process , Don't blindly take it directly , It is still necessary to choose the appropriate scheme in combination with the characteristics of its own system business and the actual scenario .

The most direct way to push is to use the third push platform , After all
The demand that money can solve is not a problem
, No complex development, operation and maintenance , Directly available , To save time 、 And effort 、 worry , image goEasy、 Aurora push is a very good three-party service provider .

Generally, large companies have self-developed message push platforms , Like what we achieved this time web In station letter is just a contact on the platform , SMS 、 mail 、 WeChat official account 、 Small programs can be accessed through any channel that can reach users .

null
The message push system is quite complicated , Such as maintenance and audit of message content 、 Circle the push crowd 、 Touch filter intercept ( Push rule frequency 、 time interval 、 Number 、 Black and white list 、 Key words and so on )、 There are many modules for push failure compensation , Technically, it involves a large amount of data 、 There are also many high concurrency scenarios . So our implementation method today is just a little fuss in front of this huge system .

Github Address

I have implemented the cases mentioned in this article one by one , I put it in
Github
On , Feel useful  
Star
  Let's go. !

Portal :https://github.com/chengxy-nds/Springboot-Notebook/tree/master/springboot-realtime-data

Whether you're new to the business 、 Or a programmer with years of experience , I believe this interview outline will give you a lot of help , Pay attention to the company  『 
Programmer Xiaofu
 』 , reply  『 
offer
 』  Get it yourself , Wish you all  offer  To get the soft !

null
Hundreds of e-book technologies have been sorted out , Students in need can , Pay attention to the public name and reply  [ 
666
 ]  Self taking .



原网站

版权声明
本文为[InfoQ]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/201/202207191816478301.html