Асинхронная обработка уведомлений о сообщениях на основе RabbitMQ

сервер JavaScript RabbitMQ WebSocket

Деловая сцена

Простой бизнес-сценарий, такой как: веб-страница имеет несколько взаимосвязанных областей для асинхронного получения данных и отображения данных Как элегантно реализовать, что при обновлении данных одной области асинхронно уведомлять другие области для обновления данных?

Рисунок 1 Бизнес-сценарий

Когда состояние операции с данными списка изменяется, пусть указанная выше область статистики обновляется автоматически.
Распространенной реализацией является вызов метода «обновить статистику» в методе «действия», но этот метод нежелателен, потому что, когда бизнес-логика сложна, ее станет сложно поддерживать (по всей вероятности, возникнут ошибки).

решение

При изменении состояния «операции» отправляется уведомление (производитель), а бизнес (мессенджер), который заботится об этой операции, подписывается на сообщение и обрабатывает его. Реализуйте разделение бизнеса, подходящее для распределенного развертывания.

Асинхронный метод уведомления о сообщениях

  1. Короткий опрос Ajax
    Опрос Ajax в основном реализует загрузку данных через периодические асинхронные задачи обновления JS на стороне страницы, но этот метод имеет плохой эффект в реальном времени и большую нагрузку на серверную сторону.
  2. долгий опрос
    Долгий опрос также в основном осуществляется через механизм Ajax, но отличается от традиционных приложений Ajax тем, что серверная часть длительного опроса блокирует запрос при отсутствии данных до тех пор, пока не будут сгенерированы новые данные или не истечет время ожидания запроса, а затем клиент повторно установить соединение для получения данных. Это будет занимать ресурсы в течение длительного времени, а если сообщения будут отправляться часто, это вызовет большую нагрузку на сервер. 3. Двунаправленная связь WebSocket
  3. WebSocket — это новый протокол связи в HTML5, который может реализовать полнодуплексную связь между браузером и сервером. Если и браузер, и сервер поддерживают протокол WebSocket, реализованный таким образом push сообщений, несомненно, является наиболее эффективным и лаконичным. А последние версии IE, Firefox, Chrome и других браузеров уже поддерживают протокол WebSocket, а Apache Tomcat 7.0.27 и более поздние версии также поддерживают WebSocket.

STOMP

То есть простой (или потоковый) протокол обмена текстовыми сообщениями, который обеспечивает совместимый формат соединения, который позволяет клиентам STOMP взаимодействовать с любым брокером сообщений STOMP (брокером). Протокол STOMP широко используется на нескольких языках и платформах благодаря простому дизайну и простоте клиентской разработки. Многие компании предоставляют серверы и клиенты на основе STOMP, такие как сервер RabbitMQ, клиент stomp.js на основе браузера и т. д.

RabbitMQ

AMQP, Advanced Message Queuing Protocol, является открытым стандартом для протоколов прикладного уровня, разработанным для промежуточного программного обеспечения, ориентированного на сообщения. Промежуточное программное обеспечение сообщений в основном используется для разделения компонентов, отправителю сообщения не нужно знать о существовании потребителя сообщения, и наоборот. Основными функциями AMQP являются ориентированность на сообщения, очереди и маршрутизацию, надежность и безопасность. RabbitMQ — это реализация AMQP с открытым исходным кодом, серверная часть написана на языке Erlang, поддерживает различные клиенты, такие как: Python, Ruby, .NET, Java, JMS, C, PHP, ActionScript, XMPP, STOMP и т. д. Аякс. Он используется для хранения и пересылки сообщений в распределенных системах и хорошо работает с точки зрения простоты использования, масштабируемости и высокой доступности.

RabbitMQ Web STOMP

Этот подключаемый модуль можно рассматривать как мост между HTML5 WebSocket и протоколом STOMP, и его цель также состоит в том, чтобы позволить браузерам использовать RabbitMQ. Когда на сервере сообщений RabbitMQ включены подключаемые модули STOMP и Web STOMP, браузер может легко использовать клиент WebSocket или SockerJS для связи с сервером RabbitMQ.
RabbitMQ Web STOMP — это мост к протоколу STOMP, поэтому его синтаксис также полностью соответствует протоколу STOMP. STOMP — это протокол на основе фреймов, а HTTP рама похожа. Фрейм содержит команду, ряд необязательных заголовков и тело. Пользовательский агент STOMP-клиента может, конечно, играть две роли одновременно: как производитель, отправляющий сообщения на сервер через кадры SEND; как потребитель, отправляющий кадры SUBCRIBE получателю и получающий сообщения от сервера через СООБЩЕНИЕ кадров.
Для использования протокола STOMP с WebSocket на веб-странице достаточно скачать stomp.js, учитывая, что старая версия браузера не поддерживает WebSocket, SockJS Он обеспечивает поддержку моделирования для WebSocket.

Установка RabbitMQ

См. Установка RabbitMQ на CentOS7.

Решать проблему

Внедрить js-плагин

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

html тестовой страницы

<body>
	<div style="padding: 50px 100px">
		<button class="btn btn-success" type="button">
			待出票 <span class="badge" id="notout">0</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 btn-primary" type="button">
			已取消 <span class="badge" id="cancel">0</span>
		</button>

		<table class="table">
			<thead>
				<tr>
					<th>产品</th>
					<th>日期</th>
					<th>状态</th>
					<th>操作</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">出票</button></td>
				</tr>
				<tr>
					<td>CAN-CKG</td>
					<td>20/10/2017</td>
					<td class="status">待出票</td>
					<td><button class="btn btn-primary exStatus" type="button">出票</button></td>
				</tr>
				<tr>
					<td>CAN-CKG</td>
					<td>20/10/2017</td>
					<td class="status">待出票</td>
					<td><button class="btn btn-primary exStatus" type="button">出票</button></td>
				</tr>
				<tr>
					<td>CUT-PEK</td>
					<td>10/11/2017</td>
					<td class="status">已出票</td>
					<td><button class="btn btn-warning exStatus" type="button">退票</button></td>
				</tr>
				<tr>
					<td>PEK-CKG</td>
					<td>23/11/2017</td>
					<td class="status">待支付</td>
					<td><button class="btn btn-primary exStatus" type="button">取消</button></td>
				</tr>
				<tr>
					<td>PEK-CKG</td>
					<td>23/11/2017</td>
					<td class="status">待支付</td>
					<td><button class="btn btn-primary exStatus" type="button">取消</button></td>
				</tr>
				<tr>
					<td>SHA-PEK</td>
					<td>20/10/2017</td>
					<td class="status">已退票</td>
					<td></td>
				</tr>

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

Привязка событий страницы и инициализация данных

<script type="text/javascript">
		var countNotout=function(data){
			//真实业务可能是ajax向后台请求数据
			$('#notout').html($("td:contains('待出票')").length);
		};

		var countOut=function(data){
			//真实业务可能是ajax向后台请求数据
			$('#out').html($("td:contains('已出票')").length);
		};

		var countBreak=function(data){
			//真实业务可能是ajax向后台请求数据
			$('#break').html($("td:contains('已退票')").length);
		};


		var countCancel = function(data) {
			//真实业务可能是ajax向后台请求数据
			$('#cancel').html($("td:contains('已取消')").length);
		};

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

		//事件绑定
		$(".exStatus").click(function() {
			if ('出票' === $(this).html()) {
				$(this).removeClass().addClass('btn btn-warning exStatus');
				$(this).parents('tr').find(".status").html('已出票');
				$(this).html('退票');
				//发送通知
				sendMQ("doOut");
				return;
			}

			if ('退票' === $(this).html()) {
				$(this).parents('tr').find(".status").html('已退票');
				$(this).remove();
				//发送通知
				sendMQ("doBreak");
				return;
			}

			if ('取消' === $(this).html()) {
				$(this).parents('tr').find(".status").html('已取消');
				$(this).remove();
				//发送通知
				sendKeyMQ("doCancel");
				return;
			}

		});

	</script>

Подключитесь к серверу RabbitMQ и подпишитесь на уведомления о сообщениях.

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

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

	// 定义连接失败回调函数
	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>

Область применения

Эта рамочная модель может применяться, по крайней мере, в следующих ситуациях:

  1. между разными бизнес-модулями
  2. между разными языками разработки
  3. между разными серверами приложений
  4. между различными прикладными устройствами

Справочная документация

Документация Stomp Over Websocket