Статьи

Постоянный мониторинг отказов потоков от MMC


Консоль Mule Management Console (MMC) — это отличный инструмент для мониторинга экземпляров Mule, отправки оповещений, если что-то не так, как должно быть, и многих других интересных функций.
Однако на момент публикации этой статьи он не поддерживает интеллектуальную отправку предупреждений, когда поток постоянно терпит неудачу.

Позвольте мне объяснить более подробно. Рассмотрим простой прокси-сервер веб-службы в Mule:

<?xml version="1.0" encoding="UTF-8"?>

<mule xmlns:http="http://www.mulesoft.org/schema/mule/http" xmlns:cxf="http://www.mulesoft.org/schema/mule/cxf" xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
  xmlns:spring="http://www.springframework.org/schema/beans" version="EE-3.3.1"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="
http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd 
http://www.mulesoft.org/schema/mule/cxf http://www.mulesoft.org/schema/mule/cxf/current/mule-cxf.xsd 
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd 
http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd ">
	
    <flow name="CxfProxyFlow" doc:name="CxfProxyFlow">
        <http:inbound-endpoint exchange-pattern="request-response" host="localhost" port="8081" doc:name="HTTP" path="proxy"/>
        <cxf:proxy-service doc:name="SOAP" payload="body"/>
        <cxf:proxy-client doc:name="SOAP" enableMuleSoapHeaders="true" payload="body"/>
        <http:outbound-endpoint exchange-pattern="request-response" host="localhost" port="8081" doc:name="HTTP" path="service"/>
    </flow>

</mule>

Это правда, что мониторинг целевой службы полностью выходит за рамки MMC. Однако ваш прокси-поток будет постоянно терпеть неудачу всякий раз, когда сообщение получено из-за исключения из-за соединения, когда целевой сервис недоступен. Если ваш экземпляр Mule скрывает внутренние службы, то ваша группа поддержки должна ответить на вопросы о том, почему ваша служба не работает.

Так что мы можем с этим поделать? Очевидным вариантом является получение оповещения о том, что ваш поток постоянно терпит неудачу, чтобы кто-то мог на него повлиять. Это можно сделать с помощью функции регулярного выражения в журнале. Проблема в том, что вы получите предупреждение для каждого исключения, и это может потенциально генерировать много предупреждений. Кроме того, если у вас другое оповещение, оно будет скрыто в длинном списке оповещений, сгенерированных регулярным выражением журнала.

К сожалению, для решения этой проблемы нет функции MMC «из коробки», поэтому мы должны решить эту проблему самостоятельно. К счастью, Mule предоставляет некоторые API, которые дают нам хорошую статистическую информацию. Используя эти API, мы написали небольшой класс, который вычисляет соотношение сообщений об ошибках, поступающих в потоке, к сообщениям, которые были успешно обработаны. Полный код Java нашего класса показан в следующем разделе.

package com.ricston.monitor;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.mule.construct.Flow;

public class ConsistentlyFailingMonitor {
  
	private long lastRecordedEventsReceived = 0;
	private long lastRecordedFailedEvents = 0;
	private float threshold = 0.5f;
	private float lastRecordedRatio;
	private boolean constantlyFailing = false;
	private String constantlyFailingString = Boolean.toString(constantlyFailing);
	
	private Flow flow;
	protected Log logger = LogFactory.getLog(getClass());
	private int count;
	
	private final String logLineLastRecorded = "lastRecordedEventsReceived: %s -- lastRecordedFailedEvents: %s";
	private final String logLineNewEvents = "newEventsReceived: %s -- newFailedEvents: %s";
	private final String logLineDiffEvents = "diffEventsReceived: %s -- diffFailedEvents: %s";
	private final String logLineRatioFailed = "Ratio: %s -- Failing: %s";

	public synchronized void monitor() {
		count++;
		
		// quick fix for spring task scheduler scheduling our tasks twice
		if (count % 2 == 0) {
			
			//retrieve values of events received and events failed
			long newEventsReceived = flow.getStatistics().getTotalEventsReceived();
			long newFailedEvents = flow.getStatistics().getExecutionErrors();
			
			//calculate difference between now and last recorded statistics
			long diffEventsReceived = newEventsReceived - lastRecordedEventsReceived;
			long diffFailedEvents = newFailedEvents - lastRecordedFailedEvents;
			
			//some logging
			logger.debug(String.format(logLineLastRecorded, lastRecordedEventsReceived, lastRecordedFailedEvents));
			logger.debug(String.format(logLineNewEvents, newEventsReceived, newFailedEvents));
			logger.debug(String.format(logLineDiffEvents, diffEventsReceived, diffFailedEvents));
			
			//store last recorded values
			lastRecordedEventsReceived = newEventsReceived;
			lastRecordedFailedEvents = newFailedEvents;
			
			//if we did not receive any good events since last time recorded, lets check if we received error events 
			if (diffEventsReceived == 0) {
				
				//if we received only error events, set failed to true
				if (diffFailedEvents > 0) {
					lastRecordedRatio = 1;
					updateConstantlyFailing(true);
				} 
				//if we received no events at all, then set failed to false
				else {
					lastRecordedRatio = 0;
					updateConstantlyFailing(false);
				}
			} 
			//if we received good events, lets calculate the ratio of bad events against good events
			//and update the failed status accordingly (checking with the threshold)
			else {
				lastRecordedRatio = (float) diffFailedEvents / diffEventsReceived;
				updateConstantlyFailing(lastRecordedRatio > threshold);
			}
			
			//some more logging
			logger.debug(String.format(logLineRatioFailed, lastRecordedRatio, constantlyFailingString));
		}
	}

	protected void updateConstantlyFailing(boolean value) {
		constantlyFailing = value;
		constantlyFailingString = Boolean.toString(constantlyFailing);
	}

	//all getters and setters
}

Идея состоит в том, чтобы метод monitor () выполнялся каждые пару секунд. При каждом выполнении мы получаем обновленную статистику ошибок выполнения по сравнению с выполненными. Из этих значений мы вычитаем те же значения, которые были записаны в предыдущем прогоне, чтобы получить значения только для этого периода. Когда этот расчет завершен, мы делим ошибки на общее количество событий, чтобы получить соотношение двух значений. Если отношение превышает определенный порог (по умолчанию установлено значение 0,5), тогда переменной класса String  постоянноFailingString присваивается значение true .

Подготовив код мониторинга, мы должны дать Муле команду выполнять этот код каждые несколько секунд; 10 секунд например. Нам также нужно сообщить ConsistentlyFailingMonitor, какой поток контролировать. Весна очень удобна для этого.

<?xml version="1.0" encoding="UTF-8"?>

<mule xmlns:http="http://www.mulesoft.org/schema/mule/http" xmlns:cxf="http://www.mulesoft.org/schema/mule/cxf" xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
  xmlns:spring="http://www.springframework.org/schema/beans" xmlns:task="http://www.springframework.org/schema/task" version="EE-3.3.1"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="
http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd 
http://www.mulesoft.org/schema/mule/cxf http://www.mulesoft.org/schema/mule/cxf/current/mule-cxf.xsd 
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd 
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.1.xsd
http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd  ">

	<spring:beans>
		 <spring:bean id="CxfProxyFlowMonitor" class="com.ricston.monitor.ConsistentlyFailingMonitor"> 
		 	<spring:property name="flow" ref="CxfProxyFlow" /> 
		 </spring:bean> 

		<task:scheduler id="CxfProxyFlowMonitorScheduler" pool-size="1" />
		
		<task:scheduled-tasks scheduler="CxfProxyFlowMonitorScheduler">
			<task:scheduled ref="CxfProxyFlowMonitor" method="monitor" fixed-delay="10000" />
		</task:scheduled-tasks>
	</spring:beans>

</mule>

Чтобы рассмотреть, что мы сделали до сих пор:

Сначала мы определили наш bean-компонент мониторинга и подключили CxfProxyFlow к отслеживаемому потоку. Во-вторых, мы определили планировщик задач Spring и запланированное задание, чтобы наш код мониторинга выполнялся каждые 10 секунд.

Таким образом, на этом этапе у нас есть класс, отслеживающий наш поток на предмет постоянно сбойных сообщений. Это все хорошо, но нам нужен способ поднять предупреждение, если наш поток терпит неудачу. Пару недель назад я показал вам, как создать оповещение на основе атрибута JMX . Мы можем использовать ту же технику, поэтому все, что нам действительно нужно сделать, — это представить боб мониторинга как MBean, настроить оповещение, и мы готовы. Чтобы выставить наш компонент как MBean, мы снова можем использовать Spring:

<?xml version="1.0" encoding="UTF-8"?>

<mule xmlns:http="http://www.mulesoft.org/schema/mule/http"  xmlns:cxf="http://www.mulesoft.org/schema/mule/cxf" xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
  xmlns:spring="http://www.springframework.org/schema/beans" version="EE-3.3.1"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="
http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd 
http://www.mulesoft.org/schema/mule/cxf http://www.mulesoft.org/schema/mule/cxf/current/mule-cxf.xsd 
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd
http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd  ">

	<spring:beans>

		 <spring:bean id="CxfProxyFlowMonitorExporter" class="org.springframework.jmx.export.MBeanExporter">
			<spring:property name="beans">
				<spring:map>
					<spring:entry
						key="Mule.${app.name}:category=custom,name=CxfProxyFlowMonitor" value-ref="CxfProxyFlowMonitor" />
				</spring:map>
			</spring:property>
		</spring:bean> 

	</spring:beans>
	
</mule>

Чтобы рассмотреть, что мы сделали до сих пор:

Сначала мы определили наш bean-компонент мониторинга и подключили CxfProxyFlow к отслеживаемому потоку. Во-вторых, мы определили планировщик задач Spring и запланированное задание, чтобы наш код мониторинга выполнялся каждые 10 секунд.

Таким образом, на этом этапе у нас есть класс, отслеживающий наш поток на предмет постоянно сбойных сообщений. Это все хорошо, но нам нужен способ поднять предупреждение, если наш поток терпит неудачу. Пару недель назад я показал вам, как создать оповещение на основе атрибута JMX . Мы можем использовать ту же технику, поэтому все, что нам действительно нужно сделать, — это представить боб мониторинга как MBean, настроить оповещение, и мы готовы. Чтобы выставить наш компонент как MBean, мы снова можем использовать Spring:

<?xml version="1.0" encoding="UTF-8"?>

<mule xmlns:http="http://www.mulesoft.org/schema/mule/http"  xmlns:cxf="http://www.mulesoft.org/schema/mule/cxf" xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
  xmlns:spring="http://www.springframework.org/schema/beans" version="EE-3.3.1"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="
http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd 
http://www.mulesoft.org/schema/mule/cxf http://www.mulesoft.org/schema/mule/cxf/current/mule-cxf.xsd 
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd
http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd  ">

	<spring:beans>

		 <spring:bean id="CxfProxyFlowMonitorExporter" class="org.springframework.jmx.export.MBeanExporter">
			<spring:property name="beans">
				<spring:map>
					<spring:entry
						key="Mule.${app.name}:category=custom,name=CxfProxyFlowMonitor" value-ref="CxfProxyFlowMonitor" />
				</spring:map>
			</spring:property>
		</spring:bean> 

	</spring:beans>
	
</mule>

Тогда единственной оставшейся задачей является настройка оповещения JMX в MMC .

Здесь мы достигли системы, которая помогает диагностировать постоянно сбойные потоки. Он не будет выдавать оповещение для каждого исключения и потенциально наводнит вашу систему оповещениями. Вместо этого он будет генерировать одно предупреждение при достижении порога сообщений об ошибках.

Наслаждайтесь!