Asynchronous message notification processing based on RabbitMQ

Posted by illzz on Mon, 24 Jan 2022 09:17:02 +0100

Business scenario

A simple business scenario, such as: there are multiple interrelated areas for asynchronous data acquisition and display on a web page. How to gracefully notify other areas to refresh data asynchronously when updating data in one area?

Figure 1 business scenario

When the operation status of list data changes, the above statistical area will be updated automatically.
The common way to achieve this is to call the method of refreshing statistics in the operation method, but this is not advisable because the logic becomes complex when it is complex, and it will be difficult to maintain (nine out of ten will come out of Bug).

Solution ideas

When the "operation" status changes, a notification (producer) is sent, and the business (messenger) concerned about the operation subscribes to the message and processes it. Realize business decoupling, suitable for distributed deployment.

Asynchronous message notification mode

  1. Ajax short polling
    Ajax polling mainly realizes data loading through JS timed asynchronous refresh task on the page side, but this method has poor real-time effect and great pressure on the server side.
  2. Long polling
    Long polling is mainly through Ajax mechanism, but different from traditional Ajax applications, the server side of long polling will block the request when there is no data until new data is generated or the request times out, and then the client will re-establish the connection to obtain data. This will occupy resources for a long time. If messages are sent frequently, it will bring great pressure to the server. 3. WebSocket two-way 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, the message push realized by this method is undoubtedly the most efficient and concise. Moreover, the latest versions of IE, Firefox, Chrome and other browsers have supported WebSocket protocol, and Apache Tomcat versions after 7.0.27 have also begun to support WebSocket.

STOMP

Simple (or streaming) text oriented messaging protocol, which provides an interoperable connection format and allows STOMP clients to interact with any STOMP message Broker. STOMP protocol is widely used in many languages and platforms because of its simple design and easy to develop client. Many companies provide STOMP based servers and clients, such as RabbitMQ server and browser based STOMP JS client, etc.

RabbitMQ

AMQP, namely Advanced Message Queuing Protocol, is an open standard of application layer protocol, which is designed for message oriented middleware. Message middleware is mainly used for decoupling between components. The sender of a message does not need to know the existence of the message consumer, and vice versa. The main feature of AMQP is message, queue and routing oriented, reliable and secure. RabbitMQ is an open source AMQP implementation. The server side is written in Erlang language and supports a variety of clients, such as Python, Ruby NET, Java, JMS, C, PHP, ActionScript, XMPP, STOMP, etc. Ajax is supported. It is used to store and forward messages in distributed systems, and performs well in ease of use, scalability, high availability and so on.

RabbitMQ Web STOMP

The plug-in can be understood as a bridge between HTML5 WebSocket and STOMP protocol. The purpose is also to enable browsers to use RabbitMQ. When the RabbitMQ message server opens the STOMP and Web STOMP plug-ins, the browser can easily use the WebSocket or SockerJS client to communicate with the RabbitMQ server.
RabbitMQ Web STOMP is a bridge to the STOMP protocol, so its syntax completely follows the STOMP protocol. STOMP is a frame based protocol, which is similar to the frame of HTTP. A frame contains a command, a series of optional headers and a body. The user agent of STOMP client can play two roles. Of course, it can also act at the same time: as a producer, send messages to the server through SEND frame; As a consumer, send the SUBCRIBE frame to the destination and get the message from the server through the MESSAGE frame.
In the Web page, using WebSocket to use STOMP protocol, you only need to download STOMP JS. Considering that the old version of the browser does not support WebSocket, SockJS provides simulation support for WebSocket.

RabbitMQ installation

See: RabbitMQ installed in CentOS7

solve the problem

Introducing js plug-ins

<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> 

Test page html

<body>
	<div style="padding: 50px 100px">
		<button class="btn btn-success" type="button">
			To be issued <span class="badge" id="notout">0</span>
		</button>
		<button class="btn btn-warning" type="button">
			Tickets issued<span class="badge" id="out">0</span>
		</button>
		<button class="btn btn-danger" type="button">
			Ticket refunded <span class="badge" id="break">0</span>
		</button>
		<button class="btn btn-primary" type="button">
			Cancelled <span class="badge" id="cancel">0</span>
		</button>

		<table class="table">
			<thead>
				<tr>
					<th>product</th>
					<th>date</th>
					<th>state</th>
					<th>operation</th>
				</tr>
			</thead>
			<tbody>
				<tr>
					<td>PEK-CKG</td>
					<td>23/11/2017</td>
					<td class="status">To be issued</td>
					<td><button class="btn btn-primary exStatus" type="button">Issue a ticket</button></td>
				</tr>
				<tr>
					<td>CAN-CKG</td>
					<td>20/10/2017</td>
					<td class="status">To be issued</td>
					<td><button class="btn btn-primary exStatus" type="button">Issue a ticket</button></td>
				</tr>
				<tr>
					<td>CAN-CKG</td>
					<td>20/10/2017</td>
					<td class="status">To be issued</td>
					<td><button class="btn btn-primary exStatus" type="button">Issue a ticket</button></td>
				</tr>
				<tr>
					<td>CUT-PEK</td>
					<td>10/11/2017</td>
					<td class="status">Tickets issued</td>
					<td><button class="btn btn-warning exStatus" type="button">to refund a ticket</button></td>
				</tr>
				<tr>
					<td>PEK-CKG</td>
					<td>23/11/2017</td>
					<td class="status">To be paid</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 be paid</td>
					<td><button class="btn btn-primary exStatus" type="button">cancel</button></td>
				</tr>
				<tr>
					<td>SHA-PEK</td>
					<td>20/10/2017</td>
					<td class="status">Ticket refunded</td>
					<td></td>
				</tr>

			</tbody>
		</table>
	</div>
</body> 

Page event binding and data initialization

<script type="text/javascript">
		var countNotout=function(data){
			//The real business may be ajax requesting data from the background
			$('#notout').html($("td:contains('To be issued ')") length);
		};

		var countOut=function(data){
			//The real business may be ajax requesting data from the background
			$('#out').html($("td:contains('issued ')") length);
		};

		var countBreak=function(data){
			//The real business may be ajax requesting data from the background
			$('#break').html($("td:contains('refunded ')") length);
		};


		var countCancel = function(data) {
			//The real business may be ajax requesting data from the background
			$('#cancel').html($("td:contains('cancelled ')") length);
		};

		$(document).ready(function(){
			countNotout();
			countOut();
			countBreak();
			countCancel();
		});

		//Event binding
		$(".exStatus").click(function() {
			if ('Issue a ticket' === $(this).html()) {
				$(this).removeClass().addClass('btn btn-warning exStatus');
				$(this).parents('tr').find(".status").html('Tickets issued');
				$(this).html('to refund a ticket');
				//Send notification
				sendMQ("doOut");
				return;
			}

			if ('to refund a ticket' === $(this).html()) {
				$(this).parents('tr').find(".status").html('Ticket refunded');
				$(this).remove();
				//Send notification
				sendMQ("doBreak");
				return;
			}

			if ('cancel' === $(this).html()) {
				$(this).parents('tr').find(".status").html('Cancelled');
				$(this).remove();
				//Send notification
				sendKeyMQ("doCancel");
				return;
			}

		});

	</script> 

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.subscribe("/exchange/amq.fanout/rabbitmq_routingkey",
				function(d) {
					countOut(d.body);
				});
		//subscription model 
		client.subscribe("/exchange/amq.fanout/rabbitmq_routingkey",
				function(d) {
					countBreak(d.body);
				});
		//subscription model 
		client.subscribe("/exchange/amq.fanout/rabbitmq_routingkey",
				function(d) {
					countNotout(d.body);
				});

		//Direct mode
		client.subscribe("/exchange/amq.direct/rabbitmq_orderCancel",
				function(d) {
					countCancel(d.body);
				});
	};

	// Define connection failure callback function
	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> 

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 different application servers
  4. Between different application devices

Write at the end

A god document explains java multithreading, locking, JMM, JUC and high concurrency design patterns

Finally, I'd like to share with you an immortal document on Java high concurrency core programming compiled by front-line development Daniel, which mainly contains knowledge points: multithreading, thread pool, built-in lock, JMM, CAS, JUC, high concurrency design mode, Java asynchronous callback, completable future class, etc.

First, let's look at the catalogue


The following is a detailed directory



Second, let's look at what each section contains

Multithreading principle and practice;

The core principle of Java built-in lock;
CAS principle and JUC atomic class;

The principle of visibility and order;

The principle and practice of JUC explicit lock;

AQS abstracts the core principle of synchronizer;

JUC container class;

High concurrency design pattern;

Asynchronous callback mode of high concurrency core mode;

Completabilefuture asynchronous callback;

Because the content of the article is too much to be reflected one by one. Each chapter has more detailed content. You need a small partner of the full version of the document. You can click three times and get the free way below!

Topics: Java Javascript html Ajax