Não sei se coloquei no local certo essa questão.
Pesquisei muito pela internet, e achei algumas idéias, mas nenhuma delas resolve o problema completamente.
Tenho uma biblioteca legada desenvolvilda em C++, estou desenvolvendo uma biblioteca JNI e uma aplicação
web para usar essa biblioteca legada.
O maior problema é que ao criar uma nova instancia da classe JNI (interface), ela usa a mesma instancia da classe DLL (.so).
Como a aplicação legada tem váriaveis para cada usuário em C++, ao realizar mais de uma chamada via web,
eh usada a mesma instância e misturada as sessões. Na classe em JNI, tenho vários método que fazem um pass throught (conversão de tipos),
para a biblioteca legada.
Esse problema tem duas soluções que jah apliquei.
A primeira é separar todas as chamadas a classe nativa em blocos syncronized, portanto o método
só é acessado por uma thread por vez.
A segunda é lá na DLL JNI, criei um MAP<sessionId, Objeto> na qual o objeto é a classe legada, e sempre quando chamo qualquer método
da classe JNI passo o id da sessão do usuário e verifico se o usuário tem um objeto já, se não crio um novo. Assim todos tem sua própria sessão
lá em baixo também.
As duas funcionaram como deveriam, mais agora estou com outro problema, se eu usar o syncronized vou colocar um gargalo, pois a aplicação legada,
leva bastante tempo em cada processamento, enquantos os outros usuário ficam esperando. Usei o Jmeter e não deu muito certo não. 10 throughtput por minuto.
Na segunda opção acreditei que seria diferente, pois não teria gargalo, mais não, ele estava processando a mesma quantidade por minuto que quando usava o syncronized.
Cheguei a conclusão que pode ser a fila do processador mesmo.
Minha dúvida é, na aplicação JNI provavelmente é um thread só, certo?
É possivel eu criar threads separados para cada instancia da aplicação legada, para daí poderem ter um melhor rendimento e maquinas multiprocessadas?
Na verdade essa aplicação será muito acessada…
Achei difícil entender seu texto pelo modo como usou os pontos e as vírgulas.
Mas fiquei curioso. Pelo que acho que entendi, você tem algo que precisa ser feito por uma aplicação em C++ que é muito solicitada e que não dá conta dos acessos concorrentes.
Sugestões:
(que você provavelmente já fez) Mensurar tudo e localizar exatemente onde está o gargalo
Criar várias instâncias da tal DLL em máquinas diferentes
Desculpe pela pontuação.
Bom, já cheguei a conclusão que realmente a fila (ou o gargalo) é formado durante o processamento, por mais que eu consiga
criar mais de uma instancia da biblioteca legada, estou sendo parado pelo fato de estar trabalhando com apenas um thread.
O que você quiz dizer com criar várias instâncias da tal DLL em máquinas diferentes? Como acessa-las? Via socket ou algo parecido?
Como assim chamar a DLL de modo assincrono? Tenho opção de como chamar a DLL?
Usando Threads ou outro mecanismo qualquer mas não deixar o cliente esperando sem resposta nenhuma. Pacifique o cliente com alguma resposta parcial ou use AJAX.
Mas ainda desconfio que a arquitetura pode serreestudada de forma a distribuir as tarefas que demoram muito por outras máquinas ou outros processadores.
Oque está como gargalo não é o processador e sim o método da classe que possui os métodos nativos
que estão marcados como syncronized, pois se não estiver eu consigo melhorar a resposta por alguns
minutos, mais rápidamente a jvm caí por causa do acesso simultâneo ao método JNI.
É possivel tornar a classe JNI Thread Safe sem comprometer o desempenho.
É possível fazer muita coisa concorrente sem usar synchronized. Aí entram os conhecimentos obtidos na leitura dos livros obrigatórios Concurrent programming in Java e Java Concurrency in Practice além da api java.util.concurrent.
São muitas as medidas que precisam ser tomadas ou experiementadas. Não dá para resumir aqui. Só recomendo que estude o uso de variáveis atômicas e avalie a possibilidade de uso de barreiras.
Pelo que imagino, esse código C++ não é capaz de atender vários usuários simultaneamente em várias threads devido ao fato de nunca ter sido pensado para fazer isso.
Provavelmente você terá de fazer o seguinte:
Separar vários processos (em C++, Java, Delphi etc. ) que carreguem cada um deles uma instância dessa DLL. Cada um dos processos pode, por exemplo, abrir um socket (em uma porta diferente para cada processo) e responder a requisições. É mais fácil se o processo puder ser acessado via Web Services, por exemplo, que usar sockets. (Não é preciso um web server para que um processo exponha um Web Service).
Fazer com que a sua aplicação Web se contacte com um desses processos - como escolher qual processo está “menos ocupado” fica por sua conta.
Vou estudar sobre Java Concurrency e vou ver oque posso fazer para melhorar
o desempenho da aplicação. Creio que alterar a parte java para melhorar a concorrência não vai resolver
meu problema, pois o processo lá em baixo eh muito pesado e eh utilizado pelos usuários frequentemente.
Obrigado pela dica.
Na verdade essa bibilioteca já é utilizada em aplicações desktop em C++ e existe uma biblioteca
em PHP utilizando ZEND que consegue fazer um uso simultaneo dessa dll. As aplicações PHP
funcionam perfeitamente.
thingol,
Eu acredito que a aplicação legada suporte sim vários threads. Ainda pesquisando pela documentação
JNI da SUN e la diz que eh possível sim tornar uma aplicação JNI thread safe para evitar acesso errado de memória,
daí teoricamente não seria necessário definir na minha aplicação web o acesso JNI como syncronized. Tornando
assim o acesso mais rápido, para isso é necessário seguir esses passos:
[i]Threads
Here are basics of how to make JNI threadsafe:
* The JNIEnv pointer is valid only for the thread associated with it. Don?t pass it around.
* Local references are only valid in the thread that created them.
* You can convert a local reference to global reference to share it.
* You have the same sorts of synchronised problems in JNI you do in regular Java, it is just you deal with them with MonitorEnter and MonitorExit. For those who like to line dangerously, it is also possible to use the native thread mechanisms.[/i]
Quanto a idéia de WebService realmente já pensamos nisso e estamos estudando. É provavel que iremos utilizar essa saída,
mais de outro modo, criando um Web Service na parte C++.
Vou continuar procurando uma saída…
Obrigado pelas respostas.
Levei em consideração essa frase da ultima mensagem que eu mandei, e consegui resolver meu problema.
Bom, minha aplicação JNI agora eh Thread Safe, a parte JNI da aplicação não deve ter variáveis globais, pois
existe apenas uma instancia desse objeto. Cada thread que entra em um método, possui suas váriaveis locais.
Tirei todos os syncronized da minha aplicação e preparei a aplicação legada para ser thread safe também.
Nas partes críticas que exigiam um thread de cada vez, optei por usar o lock do C++.
Agora minha aplicação é thread safe por inteiro, podendo processar vários threads ao mesmo tempo e sem causar
gargalo. Agora a aplicação está respondendo 80 throughput/min. Sem quebrar.
Valeu pessoal pela dica.
Quem tiver dúvida estou a disposição para ajudar pelo email bogosoundz@gmail.com