Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Redu Walled Garden

Redu Walled Garden

Apresentará as motivações e lições aprendidas na criação de uma aplicação de larga escala orientada a serviços. Entenda como fatores como performance, estilos de comunicação e produtividade da equipe relacionam-se à estruturação de uma aplicação.

Guilherme Cavalcanti

September 14, 2013
Tweet

More Decks by Guilherme Cavalcanti

Other Decks in Programming

Transcript

  1. Serviços • Resposabilidades bem definidas • API REST • Privados

    • On going process • “Desmembrar” serviços Analytics Core App Store
  2. Vantagens • Evolução indepentente • Liberdade de tecnologia • Menos

    overhead de comunicação entre pessoas Analytics Core App Store
  3. Disponibilidade e tolerância • Como lidar com não disponibilidade do

    serviço? • Estratégia de retry no cliente • Em casos de falhas de HTTP
  4. Ideia • Canal de comunicação transversal entre todos os serviços

    • Propagação de representações do domain model (RESTishy)
  5. Upsides • Tolerância a falhas (dos consumers) • O trabalho

    do publisher termina ao enviar a mensagem para o exchanger • Garantia de entrega
  6. Doorkeeper class  DoorkeeperWithRepresenter    include  Untied::Doorkeeper    def  initialize  

         watch  User,  :after_create,  represent_with:  UserRepresenter        watch  Course,  :after_create,  represent_with:  CourseRepresenter    end end
  7. Observer class  Builder  <  Untied::Consumer::Observer    observe  :user,  from:  "social-­‐network"

         def  build(attrs)        LeanUser.new(attrs)    end    alias_method  :after_create,  :build end • Lifecycle • Configuração • Framework agnostic
  8. “Daemonizable” $  ruby  consumerd.rb  start $  ruby  consumerd.rb  status untiedc:

     running  [pid  52324] $  ruby  consumerd.rb  stop untiedc:  trying  to  stop  process  with  pid  52324.. untiedc:  process  with  pid  52324  successfully  sto worker  =  Untied::Consumer::Worker.new worker.daemonize(pids_dir:  pids_dir,  log_dir:   • gem “deamons” • Monit, god, etc
  9. Padrões comuns • Untied Consumer Sync • Abstraí o padrão

    de replicar entidades através de vários serviços
  10. Como? Untied::Consumer::Sync.configure  do  |config|    config.model_data  =  "mappings.yml" end User:

     #  Payload's  type    attributes:  #  Needed  attributes        -­‐  id        -­‐  login        -­‐  first_name        -­‐  last_name    mappings:        id:  core_id  #  Maps  payload's  id  key  to  model'    name:  LeanUser  #  Model  name • Configuração • Definição de atributos • Definição de mapeamentos • ActiveRecord • Mongoid
  11. Vis

  12. Solução • MongoDB • Dados não estruturados • Possibilidade de

    guardar estrutura de dados complexas • Mais facilmente escalável • Boa biblioteca de consulta
  13. Solução • Aplicação isolada (fail-safe) • Interação através de uma

    API REST • em-http-request (requisições assíncronas e paralelas)
  14. Implementação inicial • Cliente da API (VisClient) bem simples •

    Uso do em-http-request para envio de requisições de forma assíncrona e paralela • Falhas logadas em arquivos • Uso extensivo de Observers
  15. Requisições • Assíncronas • Logs em arquivos def  send_async_info(params,  url)

       if  EM.reactor_running?        do_request(params,  url,  true)    else        ...    end end   def  do_request(params,  url,  self_reactor)    http  =  EM::HttpRequest.new(url).post({:body  =>  params.to_json  })      http.callback  do        ...        rescue              log.error  "log  goes  here..."        end        EM.stop  unless  self_reactor    end      http.errback    do        ...        rescue            log.error  "log  goes  here..."        end        EM.stop  unless  self_reactor    end end
  16. Requisições • Paralelas • Logs em arquivos def  send_multi_request  

     ...    em  do        multi  =  EventMachine::MultiRequest.new                enrollments.each_with_index  do  |enroll,  idx|            multi.add  idx,  EM::HttpRequest.new(url).post({                :body  =>  enroll.to_json  })        end          multi.callback  do            multi.responses[:errback].each  do  |err|                logger.error  "logs  goes  here..."            end              EM.stop  unless  @running        end    end end
  17. Parâmetros • Preenchidos manualmente • Acesso a múltiplos modelos def

     fill_enroll_params(enrollment_id,  type)    ...    if  enrollment        course  =  enrollment.subject.space.course        params  =  {            :user_id  =>  enrollment.user_id,            :type  =>  type,            :lecture_id  =>  nil,            :subject_id  =>  enrollment.subject_id,            :space_id  =>  enrollment.subject.space.id,            :course_id  =>  course.id,            ...            :created_at  =>  enrollment.created_at,            :updated_at  =>  enrollment.updated_at        }    else        nil    end end
  18. Lições Aprendidas • Não havia interface única para envio das

    notificações, ou seja, código espalhado e de difícil manutenção • O em-http-request é construído em cima do EventMachine • Logar em arquivos com propósito de recuperar falhas é uma bola de neve
  19. Lições Aprendidas • Lidar com requisições paralelas é difícil •

    Pouco workers do nginx para muitas requisições paralelas • Difícil depuração
  20. Atualmente • Delayed Job lida com falhas e reenvios de

    uma forma atômica • VisClient responsável por criar Jobs e construir os parâmetros • Simples depuração e detecção de falhas • Uso de representers github.com/apotonick/roar
  21. New vis client • Envio de requisições (Faraday) • Lógica

    de criação de jobs • Lógica de parametrização def  self.notify_delayed(resource,  type,  args)    elements  =  args.respond_to?(:map)  ?  args  :  [args]    notifier_builder  =  NotifierBuilder.new(resource,                                            type,  elements)    notifier_builder.build end
  22. Representer module  Vis    module  EnrollmentVisRepresenter        include

     Roar::Representer::JSON          property  :user_id        property  :subject_id        property  :space_id        property  :course_id        property  :created_at        property  :updated_at          def  space_id            self.subject.space.id        end          def  course_id            self.subject.space.course.id        end    end end
  23. Futuro • Vis vai se tornar provedora de Relatórios e

    Visualizações • não haverá API de consulta • relatórios e visualizações estarão em Vis, front-end incluso • inclusão será feita através de iframe