Monitorando seus serviços

Bem, foi uma semana complicada, por isso o hiato tão grande desde meu último post, uma nova proposta consumiu todo meu tempo na empresa que trabalho, mas pelo menos tive a oportunidade de testar um conceito que vou apresentar em um próximo post :)

Bem, todo mundo fala de SOA e junto com essa sigla dos infernos, vem mais um monte de outras siglas e aparentemente a preferida de todos é BAM (Business Activity Monitoring). Muitos dos clientes que visito, reclamam de a suite JBoss não tem um BAM integrado, como outras ferramentas. O grande problema de se usar BAM, é que o cliente deve primeiro conhecer o seu negócio antes mesmo de querer monitorar algo, e isso sabemos que não reflete a realidade na maioria das vezes.

Bem, não satisifeitos em não ter BAM, começam a reclamar do SAM (Service Activity Monitoring). Bem, monitoramento de serviços o JBoss WS tem, e em dois nívels: Endpoint management (coberto neste post) e Records management (próximo post).

Se você deseja monitorar algo no seu JBoss eu recomendo que adquira a versão com subscrição e com isso o JON, que apesar de ainda não possuir um monitoramento para webservices (funcionalidade prevista para setembro deste ano), é muito simples criar um plugin para monitorar os webservices do jboss através de JMX.

Bem, apresentei então o console de métricas do JBoss WS:

Métricas do JBoss WS (clique para fullsize)

Métricas do JBoss WS (clique para fullsize)

A reação do cliente foi de repúdio claro, afinal de contas o Oracle tem um console “bonitinho”, e então me apresentaram a seguinte tela:

Oracle SAM (clique para fullsize)

Oracle SAM (clique para fullsize)

PQP, É foda viu, escolher OC4J como broker de webservices por causa dessa telinha? So por que tem esse gráficozinho de velocímetro? Bem, cheguei em casa puto, e gastei cerca de duas horas para produzir a aplicação que vou apresentar abaixo. Peço singelas desculpas pela falta de conhecimento em JFreeChart, e principalmente pelos componentes de imagem do richfaces, mas acredito que o que vale é provar um conceito: É possível termos um console bacana de SAM no jboss, e bem mais barato que pagar uma licensa absurda por causa deste consolezinho de OC4J.

Bem, mãos na massa. A primeira classe é minha MeterImage, que gera o gráfico usando bibliotecas do JFreeChart:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
package com.furiousbob.ws;
 
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.awt.image.WritableRaster;
import java.io.Serializable;
 
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.DialShape;
import org.jfree.chart.plot.MeterInterval;
import org.jfree.chart.plot.MeterPlot;
import org.jfree.chart.plot.Plot;
import org.jfree.chart.plot.dial.ArcDialFrame;
import org.jfree.chart.plot.dial.DialBackground;
import org.jfree.chart.plot.dial.DialPlot;
import org.jfree.chart.plot.dial.DialPointer;
import org.jfree.chart.plot.dial.StandardDialScale;
import org.jfree.data.Range;
import org.jfree.data.general.ValueDataset;
 
public class MeterImage implements Serializable{
 
	private ValueDataset dataset;
	private JFreeChart chart;
	private String title;
 
 
 
	public void  createChart(){
		Plot plot = createPlot();
		chart = new JFreeChart(title,plot);
		chart.setBackgroundPaint(Color.WHITE);
	}
 
 
 
	private MeterPlot createPlot(){
		MeterPlot plot = new MeterPlot(dataset);
		plot.setBackgroundAlpha(0.0f);
        plot.setMeterAngle(180); 
        plot.setDialShape(DialShape.CHORD); 
        plot.setDialBackgroundPaint(Color.white);
        plot.setTickLabelsVisible(false);
        plot.setDialOutlinePaint(Color.black);
        plot.setOutlineStroke(new BasicStroke(6.0f));
        plot.setNeedlePaint(Color.black);
        plot.setDrawBorder(false);
 
        plot.addInterval(new MeterInterval("Good",new Range(0,55.0),new Color(0,0,0),null,Color.GREEN));
        plot.addInterval(new MeterInterval("Warn",new Range(55,80.0),new Color(0,0,0),null,Color.YELLOW));
        plot.addInterval(new MeterInterval("Bad",new Range(80,100.0),new Color(0,0,0),null,Color.RED));
        plot.setValuePaint(Color.black);
        plot.setUnits("%");
        return plot;
	}
 
	public BufferedImage getImage(){
		createChart();
		return chart.createBufferedImage(300, 300);
	}
 
	public ValueDataset getDataset() {
		return dataset;
	}
 
	public void setDataset(ValueDataset dataset) {
		this.dataset = dataset;
	}
 
	public String getTitle() {
		return title;
	}
 
	public void setTitle(String title) {
		this.title = title;
	}
}

Bem, preciso de um webservice para ser monitorado, então um SampleService quase HelloWorld:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.furiousbob.services;
 
import java.util.Random;
 
import javax.ejb.Stateless;
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
 
import org.jboss.wsf.spi.annotation.WebContext;
 
@Stateless
@WebService
@WebContext(contextRoot="/furiousbob/services",urlPattern="/SampleService")
@SOAPBinding(use = SOAPBinding.Use.LITERAL, style = SOAPBinding.Style.DOCUMENT)
public class SampleServiceBean implements SampleService {
 
	@WebMethod
	public String sayHello(String name) {
		int delay = 0;
		try {
			Random r = new Random();
			delay = r.nextInt(3000);
			Thread.sleep(delay);
		} catch (Exception e) {
			// TODO: handle exception
		}
		return "Hello " + name +"! I've waited " + delay + " ms before saying hello back!";
	}
 
}

Notem que o método realiza um sleep de até 3s de forma randomica, fiz isto, para gerar valores alternados para poder coletar dados na minha aplicação: bobsam

A primeira tela, bem simples, lista todos serviços disponibilizados no servidor:

Listando serviços disponíveis no servidor (clique para fullsize)

Listando serviços disponíveis no servidor (clique para fullsize)

<ui:define name="body">
 
    <h:messages globalOnly="true" styleClass="message"/>
 
    <rich:panel>
    <f:facet name="header">Welcome!</f:facet>
		<rich:dataTable value="#{sam.registredEndpoints}" var="_endpoint">
			<rich:column>
				<f:facet name="header">Service name</f:facet>
				<s:link value="#{_endpoint}" view="/sam.xhtml">
					<f:param name="endpoint" value="#{_endpoint}"/>
				</s:link>
 
			</rich:column>
 
		</rich:dataTable>   
    </rich:panel>
 
</ui:define>

Agora, como vamos buscar nossos endpoints? JMX, o JBoss WS expõe seus endpoints através de um objeto EndpointRegistry onde é possível ver todos serviços listados, a classe WebServiceManagement possui o método responsável por isso, note o trecho de código nas linhas 83-97.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
package com.furiousbob.ws;
 
 
import java.io.IOException;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
import javax.imageio.ImageIO;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.Query;
 
import org.jboss.mx.util.MBeanProxyExt;
import org.jboss.mx.util.MBeanServerLocator;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Out;
import org.jboss.seam.annotations.Scope;
import org.jboss.wsf.framework.management.ManagedEndpointMBean;
import org.jboss.wsf.framework.management.ManagedEndpointRegistry;
import org.jboss.wsf.framework.management.ManagedEndpointRegistryMBean;
import org.jboss.wsf.spi.deployment.Endpoint;
import org.jfree.data.general.DefaultValueDataset;
import org.jfree.data.general.ValueDataset;
 
@Name("sam")
@Scope(ScopeType.CONVERSATION)
public class WebServiceManagementAction implements Serializable {
 
	private MeterImage meterImage = new MeterImage();
 
	private ValueDataset dataset;
 
 
	private String endpoint;
 
	@Out(required=false,scope=ScopeType.CONVERSATION)
	@In(required=false)
	private ManagedEndpointMBean mep;
 
	public void paint(OutputStream out, Object data){
		updateDataset();
		meterImage.setDataset(dataset);
		Pattern p = Pattern.compile("jboss.ws:context=(.*),endpoint=(.*)");
		Matcher matcher = p.matcher(endpoint);
		matcher.find();
		meterImage.setTitle("Performance indicator for: " + matcher.group(2));
		try {
			ImageIO.write(meterImage.getImage(), "png", out);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
 
 
	private void updateDataset(){
		long average = mep.getAverageProcessingTime();
		average = (average*100)/3000;
		this.dataset = new DefaultValueDataset(average);
	}
 
 
 
 
	public String getEndpoint() {
		return endpoint;
	}
 
 
 
 
 
	public void setEndpoint(String endpoint) {
		this.endpoint = endpoint;
	}
 
 
	public List<String> getRegistredEndpoints(){
		List<String> endpoints = new LinkedList<String>();
		try {
			MBeanServer server = MBeanServerLocator.locate();
			ManagedEndpointRegistryMBean registry = (ManagedEndpointRegistryMBean)MBeanProxyExt.create(ManagedEndpointRegistryMBean.class, "jboss.ws:service=EndpointRegistry");
			Set<ObjectName> names = registry.getEndpoints();
			for(ObjectName o : names){
				endpoints.add(o.getCanonicalName());
			}
 
		} catch (Exception e) {
			e.printStackTrace();
		}
		return endpoints;
	}
 
 
	public void create(){
		try {
			MBeanServer server = MBeanServerLocator.locate();
			this.mep = (ManagedEndpointMBean)MBeanProxyExt.create(ManagedEndpointMBean.class, endpoint);
		} catch (Exception e) {
 
		}
	}
 
}

O método public List getRegistredEndpoints() retorna uma lista com os nomes de todos webservices registrados em nosso servidor. Agora, basta selecionarmos nosso serviço para apresentarmos o seu gráfico. A listagem abaixo mostra nossa página sam.xhtml

1
2
3
4
5
6
7
8
9
10
11
12
13
<ui:define name="body">
 
    <h:messages globalOnly="true" styleClass="message"/>
 
    <rich:panel>
    <f:facet name="header">Service Activity Monitoring for #{sam.endpoint}</f:facet>
	<a4j:mediaOutput element="img" cacheable="false" createContent="#{sam.paint}" value="#{meterImage}" mimeType="image/png" >
		<a4j:actionparam name="cid" value="#{conversation.id}"></a4j:actionparam>
	</a4j:mediaOutput>
 
    </rich:panel>
 
</ui:define>

Bem, pelo pouco que aprendi de suporte a imagens com richfaces é que preciso implementar o método paint. Este método é o responsável por invocar o ManagedEndpoint, buscar as métricas que desejo plotar (no caso estou verificando o tempo médio de processamento do serviço) e então gerar a imagem. O resultado final é a imagem abaixo:

Gráfico de monitoramento (clique para fullsize)

Gráfico de monitoramento (clique para fullsize)

Claro que se não gerarmos alguns acessos ao nosso serviço não será possível ver algum resultado. Para isso eu uso o soap-ui que dentre outras inúmeras funcionalidades , possui um assistente para gerar testes de carga, a figura abaixo mostra um teste de carga feito com o soap-ui:

Testes de carga com SOAP-ui (clique para fullsize)

Testes de carga com SOAP-ui (clique para fullsize)

Após 1 min, com 100 threads no modelo de variância (que randomiza o número de acessos concorrentes) o soap-ui gerou 4174 requisições no meu webservice (Alguém ainda duvida que JBoss WS é escalável????).

Ao acessar a página de monitoramento do meu webservice, temos agora uma figura que representa o estado de nosso serviço:

Gráfico de monitoramento (clique para fullsize)

Gráfico de monitoramento (clique para fullsize)

Bem, agora não tem mais motivo para ficar achando que não dá pra ter SAM com JBoss não é? Eu (que sou um cara TOSCO assim como meu amigo e mentor Edgar Silva) consegui criar em 2 horas, isso porque apanhei do JFreeChart :P, uma aplicação simples para SAM.

Bem, é isso ai pessoal, Inté a próxima.

Leave a comment