Skip to content

avcsilva/BET-Distribuida

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

55 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

BET Distribuída

Este projeto foi desenvolvido como parte do terceiro problema da disciplina MI - Concorrência e Conectividade do curso de graduação em Engenharia de Computação na UEFS

Sobre o projeto

O projeto desenvolvido consiste em um sistema de apostas online que permite que usuários criem seus próprios eventos e apostem em outros que estejam disponíveis, funcionando sobre o modelo de sistemas distribuídos e tendo em sua formação a presença de até 3 servidores. O sistema é composto por dois principais componentes: os clientes, responsáveis por criar eventos e realizar depósitos para poder apostar e participar de eventos disponíveis, e os servidores, que realizam o processamento e o armazenamento dos eventos cadastrados em suas estruturas de dados, bem como a validação das participações realizadas e as devidas vinculações entre usuários e eventos. Tanto o cliente quanto os servidores foram desenvolvidos na linguagem de programação Go, recomendada por sua eficiência em projetos que envolvem comunicação em redes e tratamento adequado de concorrência. Para uma comunicação baseada em arquitetura REST com a linguagem Go, foi utilizado o framework Gin, que facilita a criação de rotas e a comunicação entre os componentes do sistema.

Equipe:

Sumário

Fundamentação teórica

A construção do projeto foi planejada para garantir sua eficiência e viabilidade dentro do período estipulado para o desenvolvimento. Diversas abordagens alternativas, apesar de apresentarem elevado potencial, foram descartadas devido a restrições de tempo, priorizando soluções práticas e eficazes.

Golang

A definição do código-fonte foi embasada no propósito geral da aplicação, que se concentra na comunicação cliente-servidor em uma arquitetura de servidores distribuídos. Um dos principais desafios identificados foi a questão da concorrência. Para lidar com essa problemática, optou-se pela utilização da linguagem Go (Golang). Essa linguagem é amplamente reconhecida por sua eficiência em lidar com aplicações concorrentes, sendo especialmente adequada para o desenvolvimento de servidores web e sistemas que demandam otimização de recursos de hardware.

Além disso, a linguagem Go destaca-se por seu uso extensivo no desenvolvimento de containers, como o Docker. Sua capacidade de compilação rápida e execução em diferentes arquiteturas torna-a ideal para ambientes de contêineres, oferecendo vantagens significativas, como o baixo consumo de memória e CPU. Essas características foram determinantes para a escolha da linguagem Go como base para o desenvolvimento do projeto.

Docker

Com o objetivo de garantir a portabilidade da aplicação para diferentes computadores e simplificar sua execução para o usuário final, foi empregado o uso de Docker. Essa tecnologia permite a criação de um container contendo todos os requisitos necessários para a execução da aplicação, eliminando a necessidade de configurações manuais por parte do usuário. Com isso, basta que o usuário execute o container para utilizar o sistema, independentemente de sua infraestrutura local.

Token Ring

Como forma de tratar a concorrência distribuída na comunicação entre os 3 servidores e clientes, foi implementado um protocolo de comunicação baseado no modelo token ring. Esse modelo, em sua idealização original, é utilizado em redes de computadores para garantir a passagem de um token entre os dispositivos conectados, permitindo que apenas o dispositivo com o token possa transmitir dados. No contexto do projeto, o token é utilizado para coordenar as operações dos servidores, garantindo que apenas um servidor possa realizar operações de leitura e escrita de dados por vez, evitando conflitos e garantindo a consistência dos dados. A definição do protocolo implementado especificamente para este projeto será melhor explicitada no tópico sobre a concorrência distribuída, no qual serão detalhadas as regras e o funcionamento do protocolo.

Comunicação RESTful

A comunicação entre os servidores e os clientes foi projetada com base no modelo RESTful, utilizando requisições HTTP para a troca de informações. O modelo RESTful é amplamente utilizado em aplicações web para garantir a interoperabilidade entre sistemas e a simplicidade na comunicação entre os componentes. Por meio de requisições HTTP, os clientes podem enviar solicitações aos servidores e receber respostas com os dados solicitados, permitindo a interação entre os componentes de forma eficiente e segura. A escolha do modelo RESTful para a comunicação do projeto foi motivada pela sua simplicidade e eficácia na troca de informações, garantindo a integridade e a segurança dos dados transmitidos.

Gin

Para a implementação da comunicação RESTful, foi utilizado o framework Gin, que facilita a criação de rotas e a comunicação entre os componentes do sistema. O Gin é um framework web leve e de alto desempenho, desenvolvido em Go, que oferece uma série de funcionalidades para a criação de APIs RESTful de forma simples e eficiente. Com o Gin, é possível definir rotas, métodos de requisição e tratamento de erros de forma rápida e intuitiva, permitindo a implementação de uma comunicação eficaz entre os servidores e os clientes. A escolha do Gin para a implementação da comunicação RESTful foi motivada pela sua facilidade de uso e pela sua eficiência na criação de APIs web, garantindo a integridade e a segurança das informações transmitidas.

Blockchain

Uma abordagem alternativa considerada, porém descartada, foi a utilização de blockchain para garantir a segurança. A blockchain é uma tecnologia que permite o armazenamento de informações de forma descentralizada e segura, garantindo a integridade dos dados e a transparência das operações. No contexto do projeto, a blockchain poderia ser utilizada para registrar as apostas realizadas pelos clientes e os eventos criados, garantindo que as informações fossem imutáveis e auditáveis. Além disso, a blockchain poderia ser utilizada para garantir a segurança das transações financeiras, evitando fraudes e garantindo a integridade dos saldos dos clientes. Para sua implementação, seria necessário o desenvolvimento de contratos inteligentes e a integração com uma rede blockchain, como a Ethereum. Com isso, teriam sido utilizadas as ferramentas Ganache e Truffle para o desenvolvimento e testes dos contratos inteligentes (feitos em Solidity), e a conexão com a rede Ethereum para a execução das transações, utilizando-se para isso a biblioteca go-ethereum.

No entanto, devido à complexidade dessa abordagem e ao curto período disponível para o desenvolvimento, optou-se por uma solução mais prática e viável, baseada em servidores distribuídos e comunicação RESTful. A blockchain, apesar de oferecer vantagens significativas em termos de segurança e transparência, demandaria um tempo considerável para sua implementação e testes, o que poderia comprometer a entrega do projeto dentro do prazo estabelecido. Dessa forma, a decisão de utilizar servidores distribuídos e comunicação RESTful foi tomada com base na viabilidade e eficiência da solução, garantindo a entrega de um sistema funcional e eficaz dentro do prazo estipulado.

Testes

Para garantir a eficiência e a segurança do sistema, foram realizados testes unitários e de integração em todas as etapas do desenvolvimento. Os testes unitários foram utilizados para verificar o funcionamento correto de cada componente do sistema, garantindo que as operações fossem realizadas de acordo com as especificações e que os dados fossem armazenados e recuperados corretamente. Os testes de integração foram utilizados para verificar a comunicação entre os componentes do sistema, garantindo que as requisições HTTP fossem enviadas e recebidas corretamente e que os dados fossem transmitidos de forma segura e eficiente. Com a realização desses testes, foi possível identificar e corrigir eventuais falhas e garantir a qualidade e a eficiência do sistema como um todo.

Os testes realizados para este projeto, cujos resultados serão apresentados posteriormente, foram realizados a partir da execução de múltiplos terminais simultâneamente, simulando a existência de 3 servidores distintos, além de múltiplos clientes conectados a cada um deles. Os testes foram realizados em ambiente local, utilizando a mesma máquina para a execução dos servidores e dos clientes.

Arquitetura do sistema

O sistema utiliza uma arquitetura distribuída RESTful baseada em três servidores independentes, podendo cada um realizar todo processamento necessário para realização das apostas e cadastro de usuários e eventos. A arquitetura garante a disponibilidade e tolerância a falhas, uma vez que a operação do sistema é mantida mesmo com a falha de um dos servidores, e as informações são recuperadas na inicialização.

Todos os dados recorrentes de processamento das apostas e eventos são atribuídos aos servidores, assim, as informações referentes aos clientes permanecerão salvas e seguras quando o mesmo se desconectar e desejar retornar futuramente.

Servidor

Os servidores são responsáveis pelo processamento e armazenamento de todas as informações referentes ao funcionamento do sistema. Sendo elas, os eventos cadastrados e seus devidos detalhes, as apostas feitas sobre eles, tal como os clientes cadastrados e suas respectivas participações.

Os clientes são cadastrados automaticamente com seus nomes de usuário assim que realizam seu primeiro acesso, o sistema armazenará seus dados de saldo e permite realizar depósitos, tal como utilizar os valores disponíveis em suas apostas. As informações de cadastro dos clientes são enviadas a todos os servidores ligados, permitindo que todos tenham conhecimento das participações em eventos e clientes existentes. Dessa forma, um usuário cliente pode acessar seus dados de saldo pela conexão com qualquer um dos três servidores, necessitando apenas informar o mesmo nome de usuário inserido inicialmente.

Os eventos podem ser participados por qualquer cliente desde que o evento esteja disponível, ou seja, que ainda não tenha sido finalizado e seu resultado tenha sido divulgado.

As ações do servidor incluem:

  1. O servidor exibe no terminal um log de debugs, próprio do framework Gin utilizado, mostrando as requisições recebidas e respondidas, bem como os erros que possam ocorrer.
  2. Realizar cadastro e reidentificação de clientes, permitindo que os mesmos possam acessar seus saldos e eventos criados em qualquer servidor.
    1. Em caso de cadastro, o registro será encaminhado aos outros servidores, para que todos possuam as informações de todos os clientes.
  3. Listar os clientes já previamente conectados e cadastrados, tais como os registrar a partir de ID’s, que lhes são atribuídos no momento de suas conexões.
  4. Enviar para o cliente a listagem de todos os eventos disponíveis para participação.
  5. Efetivar a participação de um cliente em um evento, verificando se o evento está disponível e, em caso positivo, realizando a operação e retornando uma mensagem de confirmação, caso contrário, retornar uma mensagem de erro.
    1. Caso a aposta seja registrada, o servidor deve atualizar a lista de participantes no evento, atualizar dados do cliente (como saldo e eventos relacionados) e realizar todo o processamento relacionado aos valores de prêmio do evento. Todos os dados salvos e gerados devem ser repassados aos outros servidores.
  6. Enviar para o cliente a listagem de seus eventos relacionados, podendo ser eventos em que o cliente participou ou eventos que o cliente criou.
    1. Caso o cliente não possua nenhum evento a ser relacionado, será devolvida uma mensagem indicando esse fato.
  7. Realizar operações de saque ou depósito de valores para o cliente, atualizando seu saldo e repassando as informações aos outros servidores.
É utilizado o protocolo stateful, salvando as informações em variaveis no sistema dos servidores, porém é importante frisar que tais informações armazenadas estarão disponíveis apenas enquanto os servidores estiverem funcionando. No momento do desligamento de todos os servidores, todos os registros serão retornados a seus valores padrões.

Cliente

É a parte do sistema com o qual o usuário irá interagir para realizar suas solicitações, como realizar saques ou depósitos, cadastrar eventos e fazer apostas. É responsável por oferecer uma interface baseada em terminal para possibilitar que os usuários possam visualizar as informações e inserirem as ações que desejam realizar. Por meio dessa parte do sistema será possível:
  1. Indicar com qual servidor se deseja conectar para interação, por meio de endereço IP e porta de conexão.
  2. Realizar o cadastro de um novo cliente ou reconectar ao sistema, informando seu nome de usuário.
  3. Realizar operações de depósito e saque de valores, atualizando o saldo do cliente.
  4. Solicitar a lista de eventos disponíveis.
  5. Fazer apostas sobre os eventos disponíveis.
  6. Consultar a lista de eventos relacionados ao cliente.
  7. Criar um novo evento, informando seu nome, descrição, opções de aposta e valor de premiação.
O cliente utiliza o protocolo stateless, não possui nenhum armazenamento de dados e realiza processamento apenas para o envio e recebimento de mensagens, tal como processa a exibição da lista eventos disponíveis.

Concorrência Distribuída

Por utilizar do framework Gin, o sistema usufrui de funcionalidades tal como a de concorrência, que permite que múltiplas requisições sejam processadas simultaneamente, garantindo que o sistema possa lidar com diversos usuários conectados e requisições ao mesmo tempo. A concorrência é uma característica essencial para sistemas distribuídos, uma vez que a comunicação entre os componentes do sistema ocorre de forma assíncrona e não sequencial, permitindo que o servidor possa atender a múltiplas requisições de clientes simultaneamente.

Entretanto, fora as funcionalidades do Gin, foi implementado para este projeto um modelo de comunicação baseada no protocolo token ring, que permite que os servidores possam se comunicar entre si de maneira ordenada e compartilhar informações sobre os clientes e eventos cadastrados. O protocolo token ring é um modelo de comunicação em que os servidores se comunicam de forma circular e ordenada, enviando e recebendo mensagens entre si e coordenando suas operações, garantindo que todas as informações estejam atualizadas em todos os servidores e que não haja operações concorrentes entre si ocorrendo de forma simultânea. Dessa forma, o protocolo token ring permite que os servidores possam realizar operações de forma segura e eficiente, garantindo a consistência dos dados e a integridade do sistema.

O protocolo token ring deste projeto foi implementado seguindo as seguintes regras:

  • No sistema em execução, haverá a presença de um único token que circulará entre os servidores, permitindo que cada servidor possa realizar operações de leitura e escrita de forma ordenada e segura.
  • Cada servidor, só poderá realizar operações de leitura e escrita de dados solicitadas por clientes quando estiver com posse do token, garantindo que não haja operações concorrentes entre os servidores.
  • Requisições realizadas por outros servidores, tal como solicitação de registros atuais ou atualização de algum dado de cliente ou evento deverão ser executadas mesmo sem a presença do token, pelo fato de ser apenas uma sincronização dos dados entre os servidores.
  • Um servidor manterá posse do token por um tempo mínimo, definido como 1 segundo, ou até que sua operação seja finalizada por completo. Tendo as duas condições satisfeitas, o token será passado para o próximo servidor na ordem de comunicação.
  • A ordem de comunicação entre os servidores é fixa e circular, sendo o servidor A o primeiro a receber o token, seguido pelo servidor B e, por fim, o servidor C, que por sua vez deverá reiniciar o ciclo passando o token novamente para o servidor A.
    1. Caso um servidor esteja off-line, o token será passado para o próximo servidor na ordem de comunicação, garantindo que as operações possam ser realizadas mesmo com a falha de um dos servidores.
    2. Sempre que um servidor recebe o token, este envia para os outros servidores um sinal de confirmação de existência do token dentro do sistema. Este sinal será utilizado para verificar se o token está em circulação, explicado na regra seguinte.
  • Cada servidor fará uma verificação de existência de token em circulação sobre o sistema. Essa validação é feita verificando se houve detecção de um token pelo servidor ou de um sinal de existência de token dentro de um espaço de tempo de 3 segundos. Caso o tempo seja excedido e nenhuma das condições seja satisfeita, haverá a geração de um novo token.
    1. Para evitar que haja uma possível geração de múltiplos tokens, foi adicionada uma contagem de tempo adicional para cada servidor, de forma que haja um espaço de tempo levemente maior para que o token seja detectado por outro servidor. Para isso, foi determinado que o servidor A espere por mais 0,1 segundo, o servidor B por mais 0,3 segundos e o servidor C por mais 0,6 segundos.
    2. Ao gerar o token, o servidor enviará o sinal de confirmação de existência do token para os outros servidores e o manterá, tal qual o padrão estabelecido (com um tempo mínimo e verificação de tarefa realizada), para seu próprio funcionamento.
    3. A verificação de existência de token é realizada constantemente e à todo momento de funcionamento do sistema, sendo útil tanto para o caso de inicialização do sistema (como para a geração do primeiro token) quanto para a manutenção do token em circulação (em caso de falha de um servidor que estivesse em posse do token).
  • Distribuição do prêmio dos eventos

    A distribuição de prêmios é realizada de forma simples e justa entre os participantes dos eventos. Para facilitar a explicação, os participantes são divididos em duas categorias: ganhadores e perdedores. O valor total da premiação corresponde a uma porcentagem, definida pelo criador do evento, sobre o total arrecadado com as apostas. Essa porcentagem pode variar entre 50% a 100%.

    A premiação destinada aos ganhadores é distribuída proporcionalmente, com base na contribuição de cada um para o montante total arrecadado apenas pelos ganhadores. Em outras palavras, a quantia que cada vencedor apostou determina sua parcela do prêmio total, garantindo uma divisão justa e proporcional ao valor que ele contribuiu.

    Se um jogador A tiver apostado equivalente a 30% do valor arrecadado apenas pelo total de arrecadação de ganhadores, ele receberá 30% do ganho da premiação total, ou seja, a porcentagem da soma das apostas dos ganhadores e perdedores.

    Fórmulas

    • Premiação: Soma de todas as apostas para a premiação menos a porcentagem do criador do evento.
    • Valor Total dos Ganhadores (VTG): Valor total arrecadado pelos participantes que ganharam.

    Exemplo

    • Considere 5 jogadores e suas apostas:
      • A = R$: 15
      • B = R$: 715
      • C = R$: 45
      • D = R$: 40
      • E = R$: 185
    • Os jogadores A, C e D ganharam.
    • Criador fica com 30%.

    Cálculos

    • Premiação: (A + B + C + D + E) = 1000
    • VTG: (A + C + D) = 100
    • Valor que o criador receberá:
      • VCR = (30 / 100) * Premiação = (30 / 100) * 1000 = 300
    • Atualizando o valor da premiação:
      • Premiação = Premiação - VCR = 1000 - 300 = 700
    • Atribuindo ganho aos jogadores:
      • Porcentagem por Jogador (PPJ): (Valor pago * 100) / VTG
      • Ganho por Jogador (GPJ): (PPJ * Premiação) / 100

    Exemplo para o Jogador A que apostou 15:

    • PPJ = (15 * 100) / 100 = 15, ou seja, 15%
    • GPJ = (15 * 700) / 100 = 105, ou seja, ele ganhará R$: 105

    Demais jogadores

    • C = R$: 315
    • D = R$: 280

    Os demais jogadores não receberão.

    Resultados

    Tendo sido testado em ambiente local com uso de diversos terminais em paralelo para simular a conexão simultânea dos 3 servidores e de múltiplos clientes, foi possível averiguar que o sistema consegue lidar corretamente e de forma eficiente com as diversas requisições ocorrendo simultaneamente, não apresentando nenhum tipo de atraso ou travamento. Além disso, foi possível comprovar que cada servidor foi capaz de reconhecer corretamente cada cliente a partir de seu nome de usuário, sendo possível a recuperação dos dados e eventos de cada usuário em qualquer um dos servidores.

    Da maneira como projeto foi concebido, um cliente que tenha sua conexão perdida não consegue reconhecer o erro relatado em tempo real, mantendo a execução do programa na etapa em que parou, até que se tente enviar alguma requisição. Somente após a tentativa de enviar algo, o programa reconhece a perda da conexão e exibe uma mensagem de erro, solicitando em seguida um endereço alvo para realizar uma nova conexão. Caso o cliente receba de volta sua conexão com a rede, como tendo seu cabo de rede posto de volta, após o servidor ter encerrado sua conexão, esta não será iniciada novamente de forma automática. O usuário do cliente deverá indicar novamente o endereço alvo para poder se reconectar ao servidor e recuperar seus dados.

    Com os testes, foi possível comprovar o funcionamento da implementação do protocolo token ring, que permitiu que os servidores se comunicassem de forma ordenada e segura, garantindo a consistência dos dados e a integridade do sistema. O protocolo token ring foi capaz de coordenar as operações dos servidores de forma eficiente, garantindo que as operações de leitura e escrita de dados fossem realizadas de forma segura e sem conflitos, mesmo com a presença de múltiplos servidores e clientes conectados simultaneamente, além de realizar corretamente a passagem do token entre os servidores e a geração do mesmo dentro do sistema.

    Uma considerável porção do código fonte do projeto possui documentação sobre suas operações, indicando o que cada parte ou linha de código deve estar realizando para o funcionamento do sistema.

    Execução do Projeto

    Abrir o Terminal

    Este projeto deve ser executado no terminal do sistema operacional ou em IDEs Ambientes de Desenvolvimento Integrado (Integrated Development Environments).

    Para abrir o terminal:

  • No Windows, pressione as teclas Windows + R, digite cmd na janela que abrir e confirme.
  • No Linux, pressione as teclas Ctrl + Alt + T para abrir o terminal.
  • Com o terminal aberto, navegue até o diretório onde os arquivos foram baixados utilizando o comando cd, por exemplo,

    cd C:\BET-Distribuida\Server

    Sem docker

    Para executar o projeto sem Docker, primeiramente, é necessário configurar o ambiente de trabalho instalando a linguagem de programação Go. Em seguida, faça o download dos arquivos disponibilizados neste repositório.

    Deve ser aberto um terminal para cada código, e cada um possui um diretório diferente.

    O primeiro arquivo a ser executado deve ser o servidor. Embora o cliente possa ser iniciado primeiro, o servidor é quem informa o endereço da conexão.

    Para iniciar o servidor, insira o seguinte comando no terminal:

    go run servidor.go Ao iniciar a execução, será exigida uma entrada, que será qual dos três servidores deseja iniciar. Após isso, o servidor estará funcionando e exibirá o log de debugs do Gin. Com isso, não será mais possível interagir diretamente com o servidor, apenas visualizar suas saídas.

    Cliente

    Para iniciar o cliente, insira o comando no terminal:

    go run cliente.go Ao iniciar a execução, será solicitado que insira o endereço da conexão, seguindo-se para as etapas de cadastro e uso normal do sistema, tal como explicitado nos tópicos anteriores.

    O menu do cliente será exibido, permitindo que o usuário interaja com o sistema utilizando os números do teclado para selecionar as opções desejadas.

    Com Docker

    Para executar o projeto, com Docker é necessário ter o docker instalado na sua máquina, em seguida baixar os arquivos dispostos neste repositório.

    Servidor

    Para utilizar os arquivos em contêiner é necessário primeiro criar a imagem docker.

    Utilize o comando para gerar a imagem:

    docker build -t server . Para executar a imagem, roda a aplicação em container, utilize:

    docker run -it -p 8088:8088 server O código será executado e exibirá o endereço e porta, similar ao funcionamento sem docker, e os mesmo procedimentos deverão ser seguido

    Cliente

    Para iniciar o cliente, crie a imagem utilizando o comando a seguir:

    docker build -t client . Para executar a imagem:

    docker run -it --rm client Logo após, será solicitado que você insira o endereço da conexão exatamente como foi informado pelo servidor, incluindo todos os pontos separadores.

    O menu do cliente será exibido, permitindo que o usuário interaja com o sistema utilizando os números do teclado para selecionar as opções desejadas.

    Conclusão

    De acordo com os resultados obtidos nos testes, é possível afirmar que o produto cumpre com o que se propõe inicialmente. Com a execução correta do servidor e do cliente, é possível realizar a criação de eventos e aplicação de apostas sobre estes mesmo que haja a presença de diversos usuários simultâneos, sendo o servidor encarregado de realizar todo o processamento. Todos os servidores têm seus registros atualizados após cada requisição e há no sistema tratamento de concorrência para o caso de requisições coincidentes de múltiplos usuários.

    Ainda é possível aprimorar o sistema, como implementando uma interface mais amigável para o usuário cliente. Porém, o projeto ainda consegue lidar adequadamente com suas outras propostas, sendo assim bem favorável para a sua utilização.

    About

    Esse projeto visa desenvolver um sistema distribuído de apostas online. O sistema deve ser descentralizado e permitir que usuários cadastrem e apostem em eventos, além de garantir confiabilidade sobre o próprio e nas manipulações de valores entre os usuários.

    Resources

    Stars

    Watchers

    Forks

    Releases

    No releases published

    Packages

     
     
     

    Contributors

    Languages