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