sábado, 15 de maio de 2021

Threads - Sincronização e outros cuidados com a execução paralela

 

Nesse artigo vou falar sobre sincronização de métodos para evitar problemas com o uso de threads. O uso de threads serve para agilizar a execução de rotinas, porém devemos estar sempre atentos como o que pode ou não ser acessado de modo paralelo por threads diferentes sem causar algum problema de consistência nos dados.

      

      Esse código abaixo mostra o acesso a um método de uma classe de negócio qualquer. Não temos como garantir qual thread vai acessar primeiro o método, e por mais que a thread1 acesse primeiro, não há nenhuma garantia que ela vai concluir todo o seu trabalho antes que a thread2 também acesse o mesmo método.


public class SincronizacaoThreads {

    public static void main( String[] args ) {

        ClasseDeNegocio classeDeNegocio = new ClasseDeNegocio();

        // Passando a tarefa e o nome do Thread

        Thread thread1 = new Thread( new Runnable() {

            @Override

            public void run() {

                // executa uma regra de negócio

                classeDeNegocio.metodoA();

            }

        }, "thread 1" );

 

        Thread thread2 = new Thread( new Runnable() {

            @Override

            public void run() {

                // executa uma regra de negócio

                classeDeNegocio.metodoA();

            }

        }, "thread 2" );

        thread1.start();

        thread2.start();

    } 

}

 

public class ClasseDeNegocio {

    public void metodoA() {

        String nome = Thread.currentThread().getName();

        System.out.println( nome + " entrando no metodoA" );

        try {

            Thread.sleep( 9000 );

        } catch ( InterruptedException e ) {

            e.printStackTrace();

        }

        System.out.println( nome + " terminando execução do metodoA" );

        System.out.println( nome + " saindo do metodoA" );

    }

} 

          Ao executar na minha máquina o resultado foi:

 







Por isso, temos que tomar alguns cuidados ao usar threads. Digamos que no metodoA chamado na classe de negócio houvesse acesso a dados para depois processar alguma coisa e gravar no banco, já imaginou o problema? As duas threads poderiam buscar os mesmos dados e fazer uma duplicação de informação, cálculos errados, etc.

 

O que poderíamos fazer para evitar esses problemas?

·     Sincronizar o acesso ao método

·    Consultar os dados antes e depois passar para cada thread os dados a serem trabalhados já divididos em dois conjuntos distintos.

 

Qual o melhor a se fazer? Depende de cada caso! Pode haver casos que seja necessário as duas soluções, outros apenas uma delas basta. Sempre que puder, evite a sincronização, pois ela cria um gargalo, porém, não deixe de usar onde necessário, pois traria grandes transtornos.


Veja como ficaria a sincronização do método:


public class ClasseDeNegocio {

    public void metodoA() {

        synchronized ( this ) {

            String nome = Thread.currentThread().getName();

            System.out.println( nome + " entrando no metodoA" );

            try {

                Thread.sleep( 9000 );

            } catch ( InterruptedException e ) {

                e.printStackTrace();

            }

            System.out.println( nome + " terminando execução do metodoA" );

            System.out.println( nome + " saindo do metodoA" );

        }

    } 

}


Como sincronizamos todo o método (pode haver casos que apenas um trecho precisa ser sincronizado) podemos usar o synchronized na declaração do método.  


public class ClasseDeNegocio {

    public synchronized void metodoA() {

        String nome = Thread.currentThread().getName();

        System.out.println( nome + " entrando no metodoA" );

        try {

            Thread.sleep( 9000 );

        } catch ( InterruptedException e ) {

            e.printStackTrace();

        }

        System.out.println( nome + " terminando execução do metodoA" );

        System.out.println( nome + " saindo do metodoA" );

    }

}


Veja como ficou o resultado da execução sincronizada:








O método passou a ser uma operação atômica, ou seja, a execução não pode ser interrompida na metade para que outra chamada entre nele. Espero que esse post tenha sido útil, até a próxima.

3 formas básicas de usar Threads no Java

      Nesse post rápido eu mostro 3 formas de usar uma Thread no Java. 


public class UsingSimpleThreads {

    /* trabalhando com Threads */

    public static void main( String[] args ) {

        // usando uma classe que implementa Runnable

        MinhaTarefa tarefa = new MinhaTarefa();

        Thread threadMultiplicador = new Thread( tarefa );

        threadMultiplicador.start();


        // usando uma classe que estende a classe Thread

        MinhaTarefa2 multiplicador = new MinhaTarefa2();

        multiplicador.start();


        // usando uma classe anônima:

        new Thread( new Runnable() {


            @Override

            public void run() {

                System.out.println( "go!go!go!" );

            }

        } ).start();

    }

}


public class MinhaTarefa implements Runnable {

    @Override

    public void run() {

        // esse método o nosso thread executará

    }

}


public class MinhaTarefa2 extends Thread {

    @Override

    public void run() {

        // esse método o nosso thread executará

    }

}


quinta-feira, 13 de maio de 2021

Criando chave SSH dentro do Gitlab Runner para automação do Deploy - CI/CD

      Nesse post eu mostro como criar uma chave SSH no Gitlab Runner para usá-la no Servidor de Deploy como host conhecido, permitindo o deploy sem a necessidade de login e senha, permitindo a automação do processo.


      Aqui assumimos que já se tem o Gitlab Runner configurado e rodando  na máquina desejada.


     Entramos no terminal do container:


$ docker exec - it gitlab-runner bash


     Mudamos para o usuário gitlab-runner


$ su gitlab-runner


     Executamos o comando que cria a chave e seguimos os passos seguintes apenas dando enter para que fique com as configurações padrão.


$ ssh-keygen

     

     Agora que a chave foi criada. Vamos copiá-la para configurá-la na máquina de deploy.


$ cd  /home/gitlab-runner/.ssh

cat id_rsa.pub


      Copiamos o conteúdo e vamos para o servidor de deploy. Nele vamos procurar o arquivo authorized_keys onde devemos colocar o conteúdo copiado. Esse arquivo fica no diretório raiz do usuário na pasta .ssh/, pode ser que já tenham outras chaves nele ou que seja ncessário criar do zero esse arquivo. 


     Ao colar lá a chave ssh pública, volte no container do Gitlab Runner e faça o primeiro acesso para testar. Lembre-se de estar com o usuário gitlab-runner.


$ docker exec - it gitlab-runner bash

$ su gitlab-runner

$ ssh usuario@ip_da_maquina_deploy


     Como é a primeira vez, aceite a conexão ssh. Pronto, agora o processo de deploy é pra funcionar normalmente.

terça-feira, 11 de maio de 2021

Erro ao Criar Runner Gitlab no Docker Toolbox - Error response from daemon: invalid mode: \MinGW\msys\1.0\etc\gitlab-runner.

      Nesse post eu mostro como resolver um problema de criar um Runner do Gitlab para quem usa o Docker Toolbox. Sabe o que é o Docker Toolbox? Um projeto (que até foi descontinuado, porém eu uso devido a problemas que tive com o Docker rodando direto na minha máquia e outras coisas que uso) que instala as ferramentas Docker (Docker Engine, Docker Machine, etc)  dentro de uma "caixa" que abstrai as características da sua máquina e roda o Docker independentemente delas. Como tudo no mundo, isso tem vantagens e desvantagens. Como o Docker Toolbox roda dentro dessa "caixa" algumas coisas funcionam um pouco diferentes. Os volumes são uma delas.

     Após baixar a imagem do Runner com o comando:


$ docker pull gitlab/gitlab-runner:latest


     Quando vamos executar o comando que sobe  o Runner criando os volumes:


$   docker run -d --name gitlab-runner --restart always -v /Users/Shared/gitlab-runner/config:/etc/gitlab-runner -v /var/run/docker.sock:/var/run/docker.sock gitlab/gitlab-runner:latest


     Recebemos o seguinte erro:


c:\Program Files\Docker Toolbox\docker.exe: Error response from daemon: invalid mode: \MinGW\msys\1.0\etc\gitlab-runner. 

See 'c:\Program Files\Docker Toolbox\docker.exe run --help'.


     E procurando a solução, encontrei e foi bem simples, basta adicionar uma barra "/" a mais no início dos caminhos do volume, ficando assim:


$ docker run -d --name gitlab-runner --restart always -v //Users/Shared/gitlab-runner/config://etc/gitlab-runner -v //var/run/docker.sock://var/run/docker.sock gitlab/gitlab-runner:latest 


     Pronto. Agora funciona tranquilo.


https://stackoverflow.com/questions/50540721/docker-toolbox-error-response-from-daemon-invalid-mode-root-docker

sexta-feira, 7 de maio de 2021

Exemplo de teste com JUnit e Mockito

       Nesse post eu trago um exemplo de testes usando Mockito para simular o comportamento de uma classe DAO, substituindo seu acesso ao banco por dados criados específicamente para os testes, de maneira que possamos testar apenas o comportamento desejado e não a integração com banco de dados, etc. Esse exemplo é do curso que fiz da Alura sobre testes com Mockito.


/**

 * Resumo:

 * 1 - Cria as propriedades SERVICE(s) e DAO(s)

 * 2 - No metodo inicializar:

 *      1 - mocka o(s) DAO(s)

 *      2 - inicia o(s) SERVICE(s) com os respectivos DAO(s) mockados

 * 3 - Cria os métodos de teste

 */

class FinalizarLeilaoServiceTest {

 

    private FinalizarLeilaoService finalizarLeilaoService;

 

    private LeilaoDao leilaoDao;

 

    @BeforeEach

    void inicializar() {

        // inicializa o mock e passa pro service

        leilaoDao = Mockito.mock( LeilaoDao.class );

        finalizarLeilaoService = new FinalizarLeilaoService( leilaoDao );

    }

 

    @Test

    void deveriaFinalizarLeilao() {

        List<Leilao> leiloes = leiloes();

 

        // defino o retorno de metodos do DAO mockado

        Mockito.when( leilaoDao.buscarLeiloesExpirados() ).thenReturn( leiloes );

 

        // metodo testado

        finalizarLeilaoService.finalizarLeiloesExpirados();

 

        // resultados esperados

        Leilao leilao = leiloes.get( 0 );

        Assert.assertTrue( leilao.isFechado() );

        Assert.assertEquals( new BigDecimal( "900" ), leilao.getLanceVencedor().getValor() );

        // Testa se determinado método do DAO foi chamado

        Mockito.verify( leilaoDao ).salvar( leilao );

    }

 

    //método com dados hardcode para teste

    private List<Leilao> leiloes() {

        List<Leilao> leiloes = new ArrayList<>();

 

        Leilao leilao = new Leilao( "Celular", new BigDecimal( "500" ), new Usuario( "Alguém" ) );

 

        Lance primeiro = new Lance( new Usuario( "Outro Alguém" ), new BigDecimal( "600" ) );

        Lance segundo = new Lance( new Usuario( "Mais Alguém" ), new BigDecimal( "900" ) );

        Lance terceiro = new Lance( new Usuario( "Um Alguém" ), new BigDecimal( "500" ) );

 

        leilao.propoe( primeiro );

        leilao.propoe( segundo );

        leilao.propoe( terceiro );

        leiloes.add( leilao );

        return leiloes;

    }

}



terça-feira, 20 de abril de 2021

Exemplo de função SQL para atualizar vários registros que usam sequence

       Nesse artigo trago um exemplo de uma função para atualização de vários registros com dados de uma sequence. Caso não seja criada uma função o que acontece é que apesar de chamar-mos a sequence, todos os os registros receberiam o mesmo valor. Por isso se faz necessário o uso de uma função.


CREATE OR REPLACE FUNCTION cria_num_boletim_pacientes_em_atendimento()

 RETURNS void

 LANGUAGE plpgsql

AS $function$

declare

internacao record;

        begin

for internacao in

(select il.id_internacao from t_internacao_leito il

inner join t_internacao it on il.id_internacao = it.id_internacao

where it.id_tipo_atendimento = 2 and il.data_alta is null

group by il.id_internacao)

loop

update t_internacao set num_boletim = (select nextval('s_numero_boletim'))  where id_internacao = internacao.id_internacao;

end loop;

         end;

$function$;

sexta-feira, 16 de abril de 2021

Usando JUnit para testar acesso a webservice

 Essa postagem traz um código simples de testes para um acesso a webservice:


import javax.ws.rs.client.Client;

import javax.ws.rs.client.ClientBuilder;

import javax.ws.rs.client.WebTarget;


import org.junit.Assert;

import org.junit.Test;


public class teste {


    @Test

    public void testaQueAConexaoComOServidorFunciona() {

        Client client = ClientBuilder.newClient();

        WebTarget target = client.target( "http://www.mocky.io" );

        String conteudo = target.path( "/v2/52aaf5deee7ba8c70329fb7d" ).request().get( String.class );

        System.out.println( conteudo );

        Assert.assertTrue( conteudo.contains( "Rua Vergueiro 3185" ) );

    }

}

sábado, 6 de março de 2021

Mudando Domínio no Wordpress

       Nesse post eu vou deixar os comandos para substituir Domínios no WordPress.


UPDATE wp_options SET option_value = replace(option_value, 'Existing URL', 'New URL') WHERE option_name = 'home' OR option_name = 'siteurl';

UPDATE wp_posts SET post_content = replace(post_content, 'Existing URL', 'New URL');

UPDATE wp_postmeta SET meta_value = replace(meta_value,'Existing URL','New URL');

UPDATE wp_usermeta SET meta_value = replace(meta_value, 'Existing URL','New URL');

UPDATE wp_links SET link_url = replace(link_url, 'Existing URL','New URL');

UPDATE wp_comments SET comment_content = replace(comment_content , 'Existing URL','New URL');


     Fonte:

     https://betterstudio.com/blog/search-and-replace-phpmyadmin/

sexta-feira, 11 de dezembro de 2020

Lendo conteúdo de uma URL no Java

       Nesse artigo vou mostrar como ler um conteúdo de uma URL via Java. Isso é útil para ler um XML ou JSON disponibilizado por um site um sistema externo por exemplo.


public void lerConteudoURL() {
       
        String noticia;
        try {
            URL url = new URL("endereço_web");

            HttpURLConnection.setFollowRedirects(true);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setConnectTimeout(3000);
            connection.setRequestProperty("Accept-Charset", "text/xml; charset=UTF-8");
            int codeMessage = connection.getResponseCode();


            if(HttpURLConnection.HTTP_OK == codeMessage) {
                InputStream buffer = new BufferedInputStream(connection.getInputStream());
                BufferedReader reader = new BufferedReader(new InputStreamReader(buffer, "UTF-8"));
                String line = reader.readLine();
               
                while (line !=null) {
                   
                    //conteúdo da linha
                    System.out.println(line);
                   
                    //pega a próxima linha
                    line = reader.readLine();
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        }

}