Estou desenvolvendo uma aplicação que faz várias requisições para um servidor apache e para fazer as requisições eu uso Socket por GET e cada requisição no meu aplicativo é uma thread.
Comecei a colocar a aplicação para fazer logs desta requisições, e verifiquei que nunca é executado uma requisição ao mesmo tempo da outra, alguém sabe o por que?
Desde já agradeço a ajuda de todos.
Mais uma dúvida sobre sockets....[RESOLVIDO]
21 Respostas
Acho que a questão pode estar na Threads, em vez de nos Sockets.
Quando vc abre múltiplas thread em Java isso não significa que elas vão executar ao mesmo tempo.
Quando vc utiliza o método start de uma Thread vc está pedindo para a JVM tomar conta da sua Thread, e dar atenção a ela de vez em quando.
Lembre-se que a JVM tem que cuidar de várias threads, além das que a sua aplicação abre. Para isso a JVM faz um time-slicing dando um pouco de processamento para cada thread.
Quando um thread faz uma operação de I/O (comunicar-se via socket, por exemplo) ela fica em estado bloqueado, aguardando a latência do dispositivo externo, e a JVM passa a dar atenção a outras threads ociosas.
Então o esperado é que as thread funcionem concorrendo por atenção da JVM.
Dependendo dos recursos de processamento em paralelo do seu processador, mais de uma thread pode receber atenção ao mesmo tempo.
Eu uso a API java.util.concurrent. E o meu processador tem 4 núcleos, e os processamentos das threads ocorrem em ms, que são os códigos, mas mesmo assim o envio destas requisições demoram a chegar no outro pc, vc teria alguma idéia para aumentar o desempenho?
Pelo o que você falou, quando eu abro um socket em uma thread todas as outras ficam esperando o seu resultado e com isso o desempenho do aplicativo pode estar baixo? Como posso mudar este erro, você teria alguma idéia?
Eu não disse que as outras threads ficam esperando o resultado - na verdade eu afirmei que:
E quanto a…
No seu lugar eu faria as seguintes considerações:
- Existe algum bloco de código synchronized executado pelas threads da sua aplicação Java ? Isso não é necessariamente ruim, mas pode tornar serial o processamento das múltiplas Threads.
- O problema pode estar na rede - verifique se o tempo de comunicação é aceitável rodando uma única thread de requisição.
- O problema pode estar no sistema operacional da aplicação requisitante - verifique a quantidade máxima de sockets que o seu sistema operacional suporta
- O problema pode estar no servidor Apache, que limita a quantidade de sockets recebidos, ou a quantidade de processos de atendimento de sockets.
No meu código eu uso o ArrayBlockingQueue, e não uso nenhum synchronized.
O tempo da rede é até legal.
Depois da sua resposta fui ver alguns números em relação ao OS e ao servidor, que estava recebendo as requisições, quanto ao OS ele tem para mais de 30.000 portas para fazer as requisições, mas o server que estava recebendo as requisições sá aceita 150 conexões, que é o “apache default”, acho que o problema pode ser isso, vou aumentar este número do servidor e fazer alguns testes, quando tiver os resultados posto aqui…
Aumentei o número de threads do apache e não tive um resultado muito bom… Sempre quando as requisições chegam a 150 ele passa a receber ou eu a enviar só 2 ou 3 por segundo…
Acho que erro está mesmo no meu código, eu gostaria de saber como eu posso ficar enviando uns 50 sockets por segundo.
Cara, posta a essencia do seu código ai,
pelo que vc esta dizendo teria que rodar tudo ao mesmo tempo mesmo…
Abs,
Oi Jedi_FeniX,
Em primeiro lugar eu só acredito que threads executam ao mesmo tempo SE houver mais de um microprocessador envolvido no esquema, fora isso só mediante uma análise profunda com envidencias.
Mas estou achando que este não é o seu problema.
O que eu tenho pra te dizer é o seguinte: se vc tiver um banco de dados participando desta sua história verifique o tempo de resposta dele o mais rápido possível, verifique indices, dimensionamento do pool de conexões, as instruções sql etc...
Resumindo, o banco de dados pode não estar conseguindo atender suas requisições quando suas threads estão sendo processadas.
Espero ter ajudado.
[]'s
A minha aplicação se relaciona com o banco sim, era um dos problemas, antigamente, o tempo de resposta do mesmo, mas isso foi corrigido a resposta do banco é boa e as threads só fazem requisições sockets.
Para controlar a conexão com o banco eu uso o jakarta dbcp, que cria um pool de conexão.
O computador que executa o aplicativo é um octacore, ou seja, 2 processadores com 4 núcleos.
O mais estranho é que os sockets são resolvidos rapidamente em milissegundos.
O Jedi…
cola ai algo pra nóis,
ao que tudo indica tem falha na sua implementação…
que a fez serial ao invés de multi processo.
quem sabe um bloco synchronized…
até não seu, de algum objeto do JDk que vc esteje usando…
mais no escuro não da pra ajudar não
Abs,
Vou colocar algumas partes do código, porque tem muita coisa e conforme precisarmos vou colocando o resto.
Vou explicar como funciona esta parte do aplicativo, que faz as requisições sockets.
Tenho 2 classes que controlam todo o processo. Estas 2 classes são 2 threads no meu aplicativo elas se chamam ControlData e ControlRequest.
Todas as 2 recebem 2 ArrayBlockingQueue, que funcionam como buffer e neles que vão ficar guardados as linhas do banco que eu enviei e as linhas que já foram enviadas, porque depois de enviar as linhas eu tenho que atualizar-las dependendo da resposta do server. Abaixo seguem as classes ControlData e ControlRequest.
ControlData.java:
pulic class ControlData{
//setters and getters....
/**
* Metodo para pegar as linhas do banco que serao enviadas pela control.ControlRequest.
* E atualiza as linhas que ja forma enviadas.
* O BufferSend e BufferSent são os ArrayBlockingQueue que funcionam como buffer.
* @access public
* @return void
*/
public void run(){
while(true){
try{
if(this.getBufferSend().size() < this.getService().getThread()){
this.elements = this.dao.selectWithLimit(this.getBufferSend().remainingCapacity(), this.getService().getId());
LOGGER.info(this.getToken() + " TOTAL PROCESS RETURNED: " + this.elements.size());
if(!this.elements.isEmpty()){
this.getBufferSend().addAll(this.elements);
LOGGER.info(this.getToken() + " TOTAL ELEMENTS IN BUFFER: " + this.getBufferSend().size());
}
//Metodo para atualizar todas as linhas que já foram processadas
this.dao().updateProcessedGroup(this.getBufferSent());
} else {
continue;
}
} catch(SQLException e1){
LOGGER.error(this.getToken() + e1.getMessage());
}
}
}
}
ControlRequest.java:
public class ControlRequest{
//setters and getters...
private void setExecutor(){
this.executorService = Executors.newCachedThreadPool();
}
/**
* Metodo que cria as threads que iram fazer as requisições sockets.
*/
public void run(){
long count = 0;
while(true){
try{
if(this.getBufferSend().size() > 0){
Iterator<MoQueue> iterator = this.getBufferSend().iterator();
while(iterator.hasNext()){
Future<MoQueue> result = this.getExecutor().submit(new ThreadRequest(this.getService(), iterator.next(), "THREAD" + count));
LOGGER.info(this.getToken() + " TAKE ELEMENT, TOTAL ELEMENTS IN BUFFER: " + this.getBufferSend().size());
this.getBufferSent().put(result.get());
this.getBufferSend().remove(result.get());
count++;
}
} else{
continue;
}
} catch(InterruptedException e){
LOGGER.error(this.getToken() + e.getMessage());
} catch(ExecutionException e1){
LOGGER.error(this.getToken() + e1.getMessage());
}
}
}
E para fazer as requisições eu uso a classe java.net.Socket.
Cara, testa o te código com algo que não seje esse
ArrayBlockingQueue,
pois se vc esta usando ele pra todas as requisições (Threads).
é ele o culpado de ser 1 por vez…(acredito :P)
pois tem muito lock e unlock internamente.
tenta usar uma lista simples só pra ver o que acontece,
e da um retorno ai pra nóis
Abs,
Vou testar com ArrayList. Eu uso o ArrayBlockingQueue porque eu preciso limitar o número de linhas que eu trago do banco. Se ficar sem esse bloqueio eu fico trazendo várias linhas do banco e isso não pode.
Coloquei o ArrayList e tive essa exception:
Exception in thread "pool-4-thread-2" java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
at java.util.AbstractList$Itr.next(AbstractList.java:343)
at control.ControlRequest.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:885)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:907)
at java.lang.Thread.run(Thread.java:619)
Acho que isso aconteceu porque 2 objetos tentaram acessar o ArrayList ao mesmo tempo. Teria alguma idéia de como resolver isso?
Isso mesmo, mais na verdade é que vc deve estar usando o FOR
novo do Java 5, o tal do FOR EACH…
use o FOR normal para testar
E aquele profiler? Rodou?
Na verdade eu uso o while, com o iterator.hasNext()…
Consegui rodar o profiler não…
Conversando com o dyorgio verifiquei que um dos erros era o ArrayBlockingQueue, que fica travando o apllicativo.
usei isEmpty
e percorra o próprio array list ao invés do iterator dele
Fiz de uma outra maneira que acabou dando certo. Em vez de colocar 2 threads para gerenciar esta parte coloquei tudo em um thread só, aonde, ela pega os registro do banco e envia as linhas em forma de thread, deu certo e o bom que fiquei com menos thread para a JVM gerenciar…heheh
Blz cara,
qeum bom que funcionou,
se vc puder colocar qual era o problema certinho
ai tira a nossa curiosidade 
Abs,
O problema era no ArrayBlockingQueue mesmo, tirei o ArrayBlockingQueue e coloquei um ArrayList e para controlar o tamanho do array eu chamo um ArrayList.size() e vejo quantos elementos eu tenho no Array,
Outra coisa que eu fiz também foi mudar Executros.newFixedPoolThread, para Executors.newCachedPoolThread e tirei 2 threads, uma que controlava a data e a outra que controlava o request, ficou tudo em uma classe, diminuindo assim um pouco a “porrada” na jvm. Esta foi uma das dicas que o ViniGodoy deu a um tempo atrás.
Agradeço a ajuda de todos.