Comparação entre VRaptor e Struts 2 utilizando JMeter

Bom dia pessoal,

Estamos iniciando um projeto aqui na empresa, e entramos em discussão sobre qual ferramenta utilizar. Criei dois projetos simples e nas mesmas condições, que utilizam Spring, fazem acesso ao banco com Hibernate e jogam os dados na tela. Fiz um teste com JMeter, com 10 usuários simultâneos e deixei rodar por 5 minutos para cada aplicação. Para a minha surpresa, o Struts respondeu muito mais rápido do que o VRaptor, respondendo coisa de mais de 4 vezes o número de requisições atendidas pelo VRaptor. Entendo que o VRaptor utiliza artifícios que consomem mais recursos do que os utilizados no Struts 2, mas achei a diferença entre os resultados muito alta.

Se possível, gostaria que algum dos desenvolvedores do VRaptor falasse sobre esses valores, prós e contras dessa comparação, e se meu teste está sendo “justo”, pois talvez eu tenha feito alguma configuração (ou deixado de fazer) que desfavoreça o VRaptor nesse teste.

Seguem as imagens dos resultados:

Resumo VRaptor:

Gráfico VRaptor:

Resumo Struts:

Gráfico Struts:

Vlw! :thumbup:

Pelo que notei você usou Hibernate por baixo dos frameworks. Ou seja, o seu teste não está usando apenas Vraptor, mas sim Vraptor + Hibernate. Nesse caso suponho que o Hibernate possa influenciar em algo.

Acho que um bom teste que você pode fazer é rodar o Vraptor com algum controller que faça alguma lógica ficticia qualquer, e no Struts2 fazer o mesmo.

Há algum tempo eu havia feito testes com o Vraptor e me foi bem satisfatório. Os testes que eu havia feito foram com o JSF, e nesse caso a balança pesou a favor do Vraptor, que teve um tempo de resposta muito maior. No meu caso a simulação foi de 100 usuários por 5 minutos, e a resposta foi satisfatória.

Atualmente esse sistema está em produção. O Vraptor atua apenas como controller acessando EJB remoto distribuído em cluster com 32 instâncias e aproximadamente ~150 usuários simultâneos fazendo ~3 milhões de consultas diárias. Eu já havia há algum tempo divulgado essas informações em outros tópicos.

http://guj.com.br/posts/list/139238.java#752484
http://guj.com.br/posts/list/140386.java#755987

No final do dia vou fazer uns testes com o JMeter de novo, usando o vr3.1.3 para ver se há alguma diferença, já que essa aplicação e meus testes iniciais foram feitos no 3.0x.

Oi Garcia,

Sim, usei o Hibernate (e o Spring também), mas porque para defender o uso do VRaptor, me foi proposto que eu deveria criar as aplicações no modelo como elas serão desenvolvidas, com Struts 2 ou VRaptor + Spring e Hibernate, então não estou testando somente o VRaptor, mas como ele trabalha junto aos demais frameworks. Pode ser que o VRaptor supere o Struts em um teste isolado, mas junto aos demais frameworks o Struts se saiu melhor, e nesse caso isso tem mais valor.

Repare que não estou criticando o VRaptor, estou buscando justificativas em que eu possa me basear para continuar a defender seu uso nesse projeto! Mesmo ele sendo mais lento que o Struts (se realmente é), creio que existam razões para tal. Ainda acho que o ganho na facilidade de desenvolvimento pesa bastante! :smiley:

Vlw! :thumbup:

Oi Juliano

Excelente trabalho. Posta pra gente o codigo do Struts e do VRaptor que ai a gente olha com calma as diferencas.

Vale lembrar que o framework MVC vai dificilmente ser responsavel pela performance do seu sistema ja que, teoricamente, tudo que ele faz é em tempo constante. O preco que voce pagara de comunicacao com banco de dados e queries sempre sera ordens de magnitude maior que isso.

Oi Paulo,

Obrigado! Vou postar para a sua avaliação!

As classes à seguir são iguais nos dois projetos, mas repare que não são as mesmas, vou comentar sobre as diferenças:

[code]@Component //somente no projeto com VRaptor
@Repository //somente no projeto com Struts
public class HibernateFuncionarioDao implements FuncionarioDao {
private HibernateTemplate template;

@Autowired //somente no projeto com Struts
public HibernateFuncionarioDao(HibernateTemplate template) {
	this.template = template;
}

public Funcionario find(Long id) {
	return template.get(Funcionario.class, id);
}

}

@Component //somente no projeto com VRaptor
@Service //somente no projeto com Struts
public class FuncionarioServiceImpl implements FuncionarioService {
private FuncionarioDao dao;

@Autowired //somente no projeto com Struts
public FuncionarioServiceImpl(FuncionarioDao dao) {
	this.dao = dao;
}

public Funcionario busca(Long id) {
	return dao.find(id);
}

}

@Entity
public class Funcionario {

@Id
@GeneratedValue
private Long id;

private String nome;
private String usuario;
private String senha;
private String email;

}[/code]As configurações para acesso ao banco são as mesmas no applicationContext.xml com BasicDataSource da apache, AnnotationSessionFactoryBean e HibernateTemplate, banco MySQL. Agora o Controller do VRaptor:

[code]@Resource
public class IndexController {

private final Result result;
private final FuncionarioService service;

public IndexController(Result result, FuncionarioService service) {
	this.result = result;
	this.service = service;
}

@Path("/")
public Funcionario index() {
	return service.busca(1L);
}

}[/code]E a Action do Struts. Fiz focando Zero Configuration, e usa o Spring como objectFactory:

[code]@Result(name = Action.SUCCESS, value = “/jsp/index/index.jsp”)
public class IndexAction implements Action {

private final FuncionarioService service;
private Funcionario funcionario;

public IndexAction(FuncionarioService service) {
	this.service = service;
}

public String execute() throws Exception {
	this.funcionario = service.busca(1L);
	return SUCCESS;
}

public Funcionario getFuncionario() {
	return funcionario;
}

}[/code]A página de resultado mostra os dados do funcionario:<br>${funcionario.id} <br>${funcionario.nome} <br>${funcionario.usuario} <br>${funcionario.senha} <br>${funcionario.email}
Com essa estrutura fiz os testes. Se faltou alguma informação é só avisar que eu posto também!

Vlw! :thumbup:

vc usou o HibernateCustomProvider no VRaptor?
ele é um OpenSessionInView… então ele vai abrir uma session e uma transaction em toda a requisição…

se vc usou isso no VRaptor, vc tem que usar um similar no Struts 2…

Oi Lucas,

Não, não usei o HibernateCustomProvider, deixei para o Spring cuidar disso. Tudo que usei coloquei no post anterior, procurei fazer as aplicações de forma similar, para realizar um teste justo!

Oi Juliano

Posta pra gente o application context xml, e o FuncionarioDao (e tambem sua implementacao, caso ela seja uma interface).

Juliano, vou te pedir mais um pouco

Voce pode zipar todo o codigo e colocar no www.drop.io pra gente baixar? Se estiver dificil para voce uploadar tudo, so coloque o codigo e os jars a gente reconstroi.

Eu quis dizer a mesma coisa que o Paulo falou, porém ele soube explicar melhor do que eu.

[quote=von.juliano]Oi Lucas,

Não, não usei o HibernateCustomProvider, deixei para o Spring cuidar disso. Tudo que usei coloquei no post anterior, procurei fazer as aplicações de forma similar, para realizar um teste justo![/quote]

Usando vraptor você não tem de acessar as coisas do Spring diretamente. O Vraptor usa o Spring apenas como DI, o resto é tudo no próprio Vraptor, como por exemplo o controle de transação. Acho que nesse caso há dois sping-contexts. Você declarou algum filter do Spring no seu web.xml? Se sim provavelmente há dois Springs rodando, por isso talvez a degradação de performance.

Oi Paulo, tá na mão!

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

<context:property-placeholder location="WEB-INF/jdbc.properties"/> // no Struts 2
<context:property-placeholder location="jdbc.properties"/> // no VRaptor

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
	destroy-method="close" p:driverClassName="com.mysql.jdbc.Driver" 
	p:url="${jdbc.url}" p:username="${jdbc.username}" p:password="${jdbc.password}">
</bean>

<bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
	<property name="sessionFactory" ref="sessionFactory" />
</bean>

<bean id="sessionFactory"
	class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
	<property name="dataSource" ref="dataSource" />
	<property name="hibernateProperties">
		<props>
			<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
			<prop key="hibernate.connection.release_mode">after_transaction</prop>
			<prop key="hibernate.show_sql">true</prop>
			<prop key="hibernate.format_sql">true</prop>  
		</props>
	</property>
	<property name="annotatedClasses">
		<list>
			<value>br.com.poc.model.Funcionario</value>
		</list>
	</property>
</bean>

<bean id="transactionManager"
	class="org.springframework.orm.hibernate3.HibernateTransactionManager"
	p:sessionFactory-ref="sessionFactory" />

<tx:annotation-driven/>

<aop:aspectj-autoproxy proxy-target-class="true"/>

<context:component-scan base-package="br.com.poc.struts2"/> // somente no Struts 2

[/code]E a FuncionarioDao, a implementação eu já postei, é a classe de nome HibernateFuncionarioDao:

[code]public interface FuncionarioDao {

Funcionario find(Long id);

}[/code]Qualquer coisa é só falar! Vlw! :thumbup:

Ahh, esqueci. Vale lembrar que o comportamento do vraptor é customizável. É importante saber se há alguma customização sua e também se você subiu o Vraptor padrão ou se há componentes opcionais carregados.

Cabe lembrar que muita coisa no Vraptor sobe apenas se você tiver tal jar no classpath, exemplo, bean validation e fileupload. Também é importante ver se há log ativado, quais as APIs que estão no classpath.

Outro caso a pensar é que no meu caso usando glassfishv3 o vraptor sobe ondemand. Ou seja, no primeiro acesso que eu fizer ele se inicializa. Isso pode então aumentar a barra do average.

otimo juliano. parece muito proximo mesmo, o caso do vraptor esta fazendo algo a mais. voce percebe algo pela saida do log?

ponha tudo pra gente no drop.io que vamos dar uma investigada passo a passo.

esta muito estranho o struts demorar 4 milisegundos para uma requisicao inteira, que inclusive dispara um select no banco de dados ja que nao tem second level cache (a nao ser que no caso do struts ele nao estivesse usando session per request, e sim uma session global, ai ficaria tudo no 1st level cache, o que nao parece o caso). eu nao consigo executar esse select em 4 milisegundos aqui na minha maquina num metodo main, sem tomcat, sem servlet, sem framework mvc…

Paulo, a url é http://drop.io/vraptor_struts2 os dois projetos estão lá!

E não reparei nada de diferente no log. Nos projetos que coloquei no drop.io eu tinha removido os logs para o teste com o JMeter.

Garcia,

Eu optei por deixar para o Spring fazer o controle de transação, entre outras coisas. Dependendo do que você utiliza, implementar ComponentFactory não é suficiente para solucionar o problema, e aí você precisa usar o Spring “na unha”. Como disse o Lucas Cavalcanti em resposta a outro post meu:

[quote=Lucas Cavalcanti]pq o spring controla várias coisas usando estruturas internas dele, então se vc não usar o jeito dele de criar as classes, ele pode não configurar alguma coisa…

tenta usar o jdotemplate do jeito do spring, configurando no applicationContext.xml que provavelmente vai funcionar[/quote]
No post você pode ler sobre qual foi o problema, que foi resolvido deixando para o Spring mesmo.

[quote=garcia-jj]Ahh, esqueci. Vale lembrar que o comportamento do vraptor é customizável. É importante saber se há alguma customização sua e também se você subiu o Vraptor padrão ou se há componentes opcionais carregados.

Cabe lembrar que muita coisa no Vraptor sobe apenas se você tiver tal jar no classpath, exemplo, bean validation e fileupload. Também é importante ver se há log ativado, quais as APIs que estão no classpath.[/quote]
Não há nenhuma configuração excepcional, e os jars que coloquei são os que vem no blank-project do VRaptor + os do hibernate + os do Spring. Coloquei a url do drop.io no post de cima, baixa os projetos pra você avaliar também! :smiley:

Fiquei curioso com isso, pois o desempenho foi uma surpresa para mim também. Vou investigar isso, e se eu puder ajudar dou meus pitacos aqui.

Abraços

analisei os códigos e percebi que o Struts faz cache de páginas, ou de requisições (não sei direito)…
ou seja, só a primeira requisição é feita de verdade, todas as outras ele pega do cache…

o vraptor ainda não faz isso, então a cada requisição ele vai no banco e procura o funcionario, enquanto o struts só faz isso uma vez…

um teste mais realista seria para cada requisição carregar um id diferente (acho que dá pra passar um parâmetro diferente em cada requisição no jmeter)…

e ainda é uma boa oportunidade de incluir a população dos parâmetros no seu teste…

faça o IndexController do VRaptor receber um Funcionario, e manda um funcionario.id como parâmetro na requisição…

faça o similar no Struts 2…

mande sempre parâmetros diferentes…
se possível popule o seu banco para que ele tenha um funcionario para cada id q vc for passar como parâmetro

falei besteira sobre o cache… tava acessando a uri errada :stuck_out_tongue:

mas o teste que eu falei pra vc fazer eh um bom teste tbm…

nos meus testes aqui (1000 req, 10 concorrentes, no apache bench):

  • Struts 2:
Percentage of the requests served within a certain time (ms)
  50%     39
  66%     47
  75%     53
  80%     57
  90%     68
  95%     82
  98%    102
  99%    128
 100%    383 (longest request)

VRaptor:

Percentage of the requests served within a certain time (ms)
  50%    228
  66%    250
  75%    268
  80%    278
  90%    313
  95%    348
  98%    432
  99%    508
 100%    701 (longest request)

chuto que ao adicionar população de parâmetros o VRaptor vai ficar na mesma e o Struts vai ficar um pouco mais lento…

bom, colocar a passagem de parâmetros meio que dá na mesma mesmo…

mais uma estatística:

VRaptor: Time per request: 23.706 [ms] (mean, across all concurrent requests)
Struts: Time per request: 5.012 [ms] (mean, across all concurrent requests)

ou seja, se vc não tem uma quantidade de requests simultâneos muito grande na sua aplicação, não vai ser isso que vai decidir a performance da sua aplicação… um usuario esperar 23 ms ou 5 ms dá na mesma…

e se vc precisa de muitos requests simultâneos, não vai ser a escolha do framework que vai permitir a escalabilidade da sua aplicação, vai ser alguma outra estratégia como mais de um servidor e um proxy reverso na frente

de qualquer forma vamos investigar o código do vraptor pra ver se existe algum gargalo de performance

Muito obrigado von.juliano por compartilhar seus resultados com a gente =)

Lucas, não precisa agradecer, testar e compartilhar os resultados é o mínimo que eu posso fazer! :mrgreen:

Agradeço a atenção, e o que eu poder fazer pra ajudar a melhorar o VRaptor, estou à disposição!

Flw! :thumbup: