redu walled garden
DESCRIPTION
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.TRANSCRIPT
Walled GardenRedu
Apresentações
Guilherme Cavalcanti
github.com/guiocavalcanti
Tiago Ferreiragithub.com/fltiago
Plataforma de educação a distância construída em uma estrutura de rede social
redu.com.br
Walled Garden
Monolítica Serviços
Redu
Analytics
Core
App Store
Serviços
• Resposabilidades bem definidas
• API REST
• Privados
• On going process
• “Desmembrar” serviços
Analytics
Core
App Store
Vantagens
• Evolução indepentente
• Liberdade de tecnologia
• Menos overhead de comunicação entre pessoas
Analytics
Core
App Store
CoreVis Apps
Walled Garden
Untiedgithub.com/redu/untied
Acoplamento entre serviços
• Comunicação “um para muitos”
• Identidade de serviços
Acoplamento entre serviços
Core
Vis
Apps
N
...
spaghettiService
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
Disponibilidade e tolerância
Core
Vis
N
...
x
Message Bus
Ideia
• Canal de comunicação transversal entre todos os serviços
• Propagação de representações do domain model (RESTishy)
The real Walled Garden
CoreVis Apps
Message Bus
Messaging that just works
Upsides
• Tolerância a falhas (dos consumers)
• O trabalho do publisher termina ao enviar a mensagem para o exchanger
• Garantia de entrega
Untied Publishergithub.com/redu/untied
Doorkeeper
class DoorkeeperWithRepresenter include Untied::Doorkeeper
def initialize watch User, :after_create, represent_with: UserRepresenter watch Course, :after_create, represent_with: CourseRepresenter endend
Features
• ActiveRecord lifecycle
• Propaga representações, não domain models
• Multiplos adapters (Bunny & AMQP)
Untied Consumergithub.com/redu/untied
Observer
class Builder < Untied::Consumer::Observer observe :user, from: "social-‐network" def build(attrs) LeanUser.new(attrs) end alias_method :after_create, :buildend
• Lifecycle
• Configuração
• Framework agnostic
“Daemonizable”
$ ruby consumerd.rb start$ ruby consumerd.rb statusuntiedc: running [pid 52324]$ ruby consumerd.rb stopuntiedc: trying to stop process with pid 52324...untiedc: process with pid 52324 successfully stopped.
worker = Untied::Consumer::Worker.newworker.daemonize(pids_dir: pids_dir, log_dir: log_dir)
• gem “deamons”
• Monit, god, etc
Untied Pluginsgithub.com/redu/untied-consumer-sync
Padrões comuns
• Untied Consumer Sync
• Abstraí o padrão de replicar entidades através de vários serviços
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's core_id column name: LeanUser # Model name
• Configuração
• Definição de atributos
• Definição de mapeamentos
• ActiveRecord
• Mongoid
Vis
Objetivos
• Garantia de entrega
• Assíncrono e escalável
• Fail-safe
• Independente de Framework
Big Picture
Core VismongoDB
HTTP
Solução
• MongoDB
• Dados não estruturados
• Possibilidade de guardar estrutura de dados complexas
• Mais facilmente escalável
• Boa biblioteca de consulta
Solução
• Aplicação isolada (fail-safe)
• Interação através de uma API REST
• em-http-request (requisições assíncronas e paralelas)
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
Requisições
• Assíncronas
• Logs em arquivos
def send_async_info(params, url) if EM.reactor_running? do_request(params, url, true) else ... endend 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 endend
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 endend
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 endend
Lições aprendidas
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
Lições Aprendidas
• Lidar com requisições paralelas é difícil
• Pouco workers do nginx para muitas requisições paralelas
• Difícil depuração
Atualmente
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 representersgithub.com/apotonick/roar
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.buildend
Representermodule 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 endend
Futuro próximo
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
Referências
• https://github.com/redu/permit
• https://github.com/redu/untied
• https://github.com/redu/untied-consumer-sync