O Cache de Segundo Nível no Hibernate – Parte 3
Neste artigo mostraremos como configurar um cache para atuar de forma replicada, analisando a influência que as otimizações desenvolvidas têm sobre o desempenho.
O Cache de Segundo Nível no Hibernate – Parte 1
O Cache de Segundo Nível no Hibernate – Parte 2
Finalizando esta série de artigos do C2N, mostraremos também como configurar um cache para atuar de forma replicada, analisando a influência que as otimizações desenvolvidas têm sobre o desempenho do cache e volume do tráfego de dados na rede.
Deste modo, este artigo é útil para desenvolvedores e arquitetos que utilizam o EhCache como solução de C2N e pretendem estender a estrutura de cache para sincronizar dados entre diversas JVMs, entendendo em quais situações a replicação pode ou não ser adequada e qual impacto é gerado na rede pela transmissão de dados.
Ao longo desta série de artigos a respeito do C2N focamos em detalhes importantes que afetam de forma significativa o desempenho de leitura e escrita no EhCache. Verificamos na parte 2 do artigo que ao passar a armazenar os dados do C2N em disco, os recursos passarão a ser serializados, o que adiciona uma nova camada de I/O e processamento para consultar ou inserir elementos. Como alternativa para melhorar a taxa de transferência entre disco e memória, foram propostas alterações estruturais nas classes do Hibernate que compõem as chaves e valores que são mantidos em cache, de forma a promover uma representação mais compacta e eficiente nas traduções Objeto ↔ stream de bytes.
Nesta última parte, mostraremos como utilizar as classes desenvolvidas anteriormente sem precisar alterar o código fonte do Hibernate, empregando um Java Agent para transformar, em tempo de execução, as classes do C2N. Aproveitando-se das otimizações de serialização promovidas pelas representações compactas, vamos avaliar como as mesmas influenciam o comportamento do EhCache quando configurado para replicar seus dados entre diversas JVMs.
Configurando e instalando o Java Agent na JVM
Um Java Agent permite que sejam registrados na JVM objetos do tipo ClassFileTransformer (do pacote java.lang.instrument), que capturam o evento de carregamento de classes e possibilita que as mesmas sejam transformadas antes de serem efetivamente apresentadas para a aplicação.
A Figura 1 mostra o processo de ClassLoading quando um Java Agent é conectado à JVM e registra um transformador de classes. Toda classe que for carregada após a instalação do agente passará pelo ClassFileTransformer, que pode decidir se vai ou não modificá-la.
Figura 1. ClassLoading com transformação.
Um Java Agent nada mais é que uma classe empacotada em um arquivo JAR, o qual deve especificar no arquivo META-INF/MANIFEST.MF algumas propriedades para que a JVM reconheça e instale o agente.
O agente que vamos utilizar define quatro propriedades, indicadas na Listagem 1.
Listagem 1. Configuração do arquivo de manifesto para o Java Agent.
Agent-Class: br.jm.agent.HibernateAgent
Premain-Class: br.jm.agent.HibernateAgent
Can-Redefine-Classes: false
Can-Retransform-Classes: false
As classes declaradas como Agent-Class e Premain-Class determinam quem irá invocar os métodos agentmain() e premain() exibidos na Listagem 2. A chamada desses métodos é realizada diretamente pela JVM e funciona como a captura de um evento que disponibiliza uma instância de java.lang.instrument.Instrumentation, a qual permite que sejam registrados transformadores de classe através do método addTransformer(). Não é possível invocar diretamente estes métodos, pois é a API de instrumentação quem os executa quando necessário, passando os argumentos 'corretos'!
Na Listagem 1 definimos a mesma classe, HibernateAgent, para atender ambos os métodos, agentmain() e premain(), e sua implementação completa pode ser vista na Listagem 2.
Listagem 2. Implementação do Java Agent
public class HibernateAgent{
static boolean registered;
public static void agentmain(String args, Instrumentation inst){
//Ações quando o agente é instalado. No caso, apenas instalar o
//otimizador se já não tiver sido instalado em premain
if(!registered){
inst.addTransformer(new HibernateTransformer());
registered=true;
}
}
public static void premain(String args, Instrumentation inst){
//Ações antes do método main de uma aplicação executar. No caso,
//apenas instalar o otimizador
inst.addTransformer(new HibernateTransformer());
registered=true;
}
//Apenas para instalação em tempo de execução
public statics synchronized void install(){
if(registered){
return;
}
VirtualMachine vm = getVM();
String jar = createTempJar();
vm.loadAgent(jar);
vm.detach();
}
static VirtualMachine getVM() throws
AttachNotSupportedException, IOException {
String vmName = ManagementFactory.getRuntimeMXBean().getName();
int p = vmName.indexOf('@');
String pid = vmName.substring(0, p);
return VirtualMachine.attach(pid);
}
//cria um arquivo jar com as informações do agente
static String createTempJar() throws IOException {
final File file = new File(System.getProperty("user.home"),
"agent.jar");
if (file.exists()) {
file.delete();
}
file.createNewFile();
try (final ZipOutputStream zout =
new ZipOutputStream(new FileOutputStream(file));//
final PrintWriter writer = new PrintWriter
(new OutputStreamWriter(zout))) {
zout.putNextEntry(new ZipEntry("META-INF/MANIFEST.MF"));
writer.println("Agent-Class: br.jm.agent.HibernateAgent");
writer.println("Premain-Class: br.jm.agent.HibernateAgent");
writer.println("Can-Redefine-Classes: false");
writer.println("Can-Retransform-Classes: false");
}
return file.getAbsolutePath();
}
}"
Artigos relacionados
-
Artigo
-
Artigo
-
Artigo
-
Artigo
-
Artigo