The business scenario

In a simple service scenario, for example, a Web page has multiple related areas for displaying data asynchronously. How can data updates in one area be asynchronously notified to other areas?

Figure 1 Business scenario

Let the upper statistics area update automatically when the status of the list data operation changes. A common way to do this is to call the “refresh statistics” method in the “action” method, but this is not desirable because it becomes difficult to maintain (and probably buggy) when the business logic is complex.

Solution idea

When the “action” status changes, a notification is sent (producer), which is subscribed to and processed by the business concerned with the action (message maker). Realize service decoupling, suitable for distributed deployment.

Asynchronous message notification

  1. Ajax short polling Ajax polling is mainly used to load data by asynchronously updating the JS periodically on the page. However, this method has poor real-time performance and great pressure on the server.
  2. Long polling is also mainly Ajax, but unlike traditional Ajax applications, the long polling server blocks the request until new data is generated or the request times out, and then the client reestablishes the connection to retrieve the data. This can tie up resources for long periods of time and put a lot of strain on the server if messages are sent frequently. 3. WebSocket bidirectional communication
  3. WebSocket is a new communication protocol in HTML5, which can realize full duplex communication between browser and server. If both browser and server support WebSocket protocol, message push implemented in this way is undoubtedly the most efficient and concise. The latest versions of Internet Explorer, Firefox, and Chrome all support WebSocket. Apache Tomcat 7.0.27 and later also support WebSocket.

STOMP

Simple (or Streaming) Text Orientated Messaging Protocol, which provides an interoperable connection format Allows STOMP clients to interact with any STOMP message Broker. STOMP is widely used in multiple languages and platforms due to its simple design and ease of client development. Many companies provide STOMp-based servers and clients, such as RabbitMQ server, browser-based stomp.js client, etc.

RabbitMQ

AMQP (Advanced Message Queuing Protocol) is an open standard of application-layer protocols designed for message-oriented middleware. Message-oriented middleware is primarily used for decoupling between components so that the sender of a message does not need to be aware of the message consumer and vice versa. The main features of AMQP are message -, queue – and routing-oriented, reliable and secure. RabbitMQ is an open source IMPLEMENTATION of AMQP. The server is written in Erlang and supports a variety of clients, such as Python, Ruby,.NET, Java, JMS, C, PHP, ActionScript, XMPP, STOMP, and Ajax. It is used to store and forward messages in distributed systems, and has good performance in ease of use, scalability, and high availability.

RabbitMQ Web STOMP

The plugin can be understood as a bridge between HTML5 WebSocket and STOMP to enable browsers to use RabbitMQ. With STOMP and Web STOMP enabled on the RabbitMQ message server, the browser can easily communicate with the RabbitMQ server using a WebSocket or SockerJS client.

RabbitMQ Web STOMP is a bridge to STOMP and therefore follows STOMP syntax. STOMP is a frame-based protocol, similar to HTTP frame. A Frame contains a command, an optional set of headers, and a body. The user agent of STOMP Client can play two roles, or possibly both: as a producer, sending messages to the server via SEND Frame; As the consumer, send the SUBCRIBE Frame to the destination and get the MESSAGE from the server through the MESSAGE Frame.

To use STOMP with WebSocket in a Web page, you only need to download stomp.js. Considering that older browsers do not support WebSocket, SockJS provides simulated support for WebSocket.

The RabbitMQ installation

See: RabbitMQ installed on CentOS7

To solve the problem

Introducing JS plugins

<script SRC ="jquery/jquery-1.9.1.min.js"></script> <script SRC ="rabbitmq/sockjs-0.3.js"></script> <script src="rabbitmq/stomp.js"></script> <link rel="stylesheet" href="bootstrap/css/bootstrap.min.css"> <script src="bootstrap/js/bootstrap.min.js"></script>Copy the code

Test page HTML

<body> <div style="padding: <span class="badge" ID =" notOut ">0</span> </span> </button> <button class=" BTN bTN-warning "type="button"> <span class="badge" ID ="out">0</span> </button> <button class=" BTN" Btn-danger "type="button"> <span class="badge" id="break">0</span> </button> <button class=" bTN-primary" <span class="badge" id="cancel">0</span> </button> <table class="table"> <thead> <tr> <th> Date of < th > < / th > < th > state < / th > < th > action < / th > < / tr > < thead > < tbody > < tr > < td > PEK - CKG < / td > < td > 23/11/2017 < / td > < td Class ="status"> </td> <td><button class=" BTN btn-primary exStatus" type="button"> </td> </tr> <tr> <td>CAN -ckg </ TD >< TD >< td>< td class="status"> </ TD >< TD >< td class=" BTN bTN-primary exStatus" Type = "button" > ticket < / button > < / td > < / tr > < tr > < td > CAN - CKG < / td > < td > 20/10/2017 < / td > < td class = "status" > stay ticket < / td > < td > < button Class =" BTN btn-primary exStatus" type="button"> </button></ tr> <tr> < TD > cut-pek </ TD >< TD >10/11/2017</ TD >< TD Class ="status"> issued </ TD >< TD ><button class=" BTN BTN - Warning exStatus" type="button"> Returned </button></ TD ></ tr> <td> pek-ckg </ TD >< TD >23/11/2017</ TD >< td class="status"> </ TD >< TD ><button class=" BTN bTN-primary exStatus" Type = "button" > cancel < / button > < / td > < / tr > < tr > < td > PEK - CKG < / td > < td > 23/11/2017 < / td > < td class = "status" > to pay < / td > < td > < button Class =" BTN btn-primary exStatus" type="button" </button></ tr> <tr> < TD > SHA-pek </ TD >< TD >20/10/2017</ TD >< TD Class = "status" > refunded < / td > < td > < / td > < / tr > < / tbody > < / table > < / div > < / body >Copy the code

Page event binding and data initialization

<script type="text/javascript"> var countNotout=function(data){// Real business may be ajax to the background request data $(' # notout '). The HTML ($(" td: the contains (' stay out tickets') "). The length). }; Var countOut=function(data){$('#out').html($(" TD :contains(' contains ')").length); }; Var countBreak=function(data){var countBreak=function(data){$('#break').html($(" TD :contains(' returned ')").length); }; Var countCancel = function(data) {$('#cancel').html($(" TD :contains(' cancelled ')").length); }; $(document).ready(function(){ countNotout(); countOut(); countBreak(); countCancel(); }); / / event binding $(". ExStatus "). Click (function () {if (' ticket '= = = $(this). The HTML ()) {$(this). RemoveClass (). The addClass (' BTN BTN - warning exStatus'); $(this). Parents (' tr.), find (" status "). The HTML (' has a ticket); $(this). The HTML (' refund '); // Send notification sendMQ("doOut"); return; } the if (' refund '= = = $(this). The HTML ()) {$(this). The parents (' tr.), find (" status "). The HTML (' refunded'); $(this).remove(); // Send notification sendMQ("doBreak"); return; } the if (' cancelled '= = = $(this). The HTML ()) {$(this). The parents (' tr.), find (" status "). The HTML (' cancelled'); $(this).remove(); SendKeyMQ ("doCancel"); return; }}); </script>Copy the code

Connect to the RabbitMQ server and subscribe to message notifications

<script type="text/javascript"> //connection rabbitmq var username = 'kaven'; var password = 'kaven123'; // Stomp.js boilerplate if (location.search == '? ws') { var ws = new WebSocket('ws://master:15674/ws'); console.log('Using WebSocket... '); } else { var ws = new SockJS('http://master:15674/stomp'); console.log('Using SockJS... '); } // Init Client var client = Stomp.over(ws); // SockJS does not support heart-beat: disable heart-beats client.heartbeat.outgoing = 0; client.heartbeat.incoming = 0; / / Declare on_connect var on_connect = function (x) {/ / subscription model client. The subscribe (". / exchange/amq fanout/rabbitmq_routingkey ",  function(d) { countOut(d.body); }); / / a subscription model client. The subscribe (". / exchange/amq fanout/rabbitmq_routingkey ", function (d) {countBreak (db ody); }); / / a subscription model client. The subscribe (". / exchange/amq fanout/rabbitmq_routingkey ", function (d) {countNotout (db ody); }); / / the client Direct mode. The subscribe ("/exchange/amq. Direct/rabbitmq_orderCancel ", function (d) {countCancel (db ody); }); }; Var on_error = function(error) {console.log(error. Headers. Message); }; // Conect to RabbitMQ client.connect(username, password, on_connect, on_error, '/'); var sendMQ = function(data) { client.send('/exchange/amq.fanout/rabbitmq_routingkey', {"content-type" : "text/plain"}, data); }; var sendKeyMQ = function(data) { client.send('/exchange/amq.direct/rabbitmq_orderCancel', {"content-type" : "text/plain"}, data); }; </script>Copy the code

Scope of application

This framework model can be applied to at least the following situations:

  1. Between different business modules
  2. Between different development languages
  3. Between application servers
  4. Between different application devices

Reference documentation

Stomp Over Websocket documentation