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

Concorrência em Go - GopheRio #1

Concorrência em Go - GopheRio #1

Apresentação feita ao GopheRio #1 realizado na OLX.

Fernando Soliva

April 18, 2019
Tweet

More Decks by Fernando Soliva

Other Decks in Technology

Transcript

  1. Communicating Sequential Processes (CSP) Muitas das funcionalidades encontradas hoje em

    Go podem em muitas outras linguagens que se basearam em CSP de Tony Hoare. 1. Cada processo é criado para execução sequencial 2. Comunique dados, não compartilhe. Sem compartilhamento de estado. 3. Fácil escalabilidade, adicionando mais do mesmo. (workers) CSP - Conceitos chave
  2. coroutines coroutines foram descritas pela primeira vez para COBOL por

    Melvin Conway. Conway sugere que o propósito de uma coroutine é fazer com que um problema seja dividido em partes, ou, sub-tarefas (coroutines) e permitir que as mesmas operam de formas independentes, compartilhando pequenos fragmentos de dados.
  3. goroutines • thread leve gerenciada pelo Go Runtime • são

    executadas no mesmo endereço • M:N goroutines x threads Kavya Joshi @Gophercon 2017 - Understanding Channels
  4. goroutines Threads goroutines normalmente têm 8KB de tamanho na stack

    nas versões mais novas. Threds do SO geralmente possuem tamanho na stack fixo de 1-2 MB goroutines são gerenciadas pelo Go Runtime, tendo como consequência criação/destrução menos custosas. Threads têm configuração significativa e custo de criação/destrução, já que uma thread precisa solicitar muitos recursos do sistema operacional e retorná-los assim que estiver pronto. goroutines não têm nenhuma identidade, uma consequência de não existir TLS (Thread Local Storage) Threads têm identidade. Elas possuem TID, que identifica cada Thread dentro do processo. goroutines usam canais para se comunicar com outras goroutines com baixa latência Não existe um meio de comunicação fácil entre os threads. Há uma grande latência na comunicação entre threads.
  5. channels • buffered • unbuffered • semântica FIFO • armazena

    e comunica valores entre goroutines • pode bloquear / desbloquear goroutines
  6. channels Channel em Go nada mais é que um canal

    de transferência de dados no qual os dados podem ser passados ou lidos, fazendo com que uma goroutine possa enviar dados para um canal, enquanto outras goroutines possam ler os dados do mesmo canal. “do not communicate by sharing memory; instead, share memory by communicating.”
  7. unbuffered := make(chan int) a := <-unbuffered unbuffered <- 1

    go func() { <-unbuffered }() // bloqueia goroutine // bloqueia goroutine // sincronização créditos: cnblogs.com
  8. package main import ( "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" ) func main() {

    lambda.Start(processaEventoSQS) } func processaEventoSQS(evento events.SQSEvent) error { for _, mensagem := range evento { processa(mensagem) } return nil } func processa(mensagem events.SQSMessage) { //… } package main import ( "fmt" "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" ) func main() { lambda.Start(processaEventoSQS) } func processaEventoSQS(event events.SQSEvent) error { // cria um unbuffered channel canal := make(chan events.SQSMessage) // processa as mensagens em Go routines diferentes go processaMensagens(event.Records, canal) // escuta eventos de um canal até que o mesmo esteja fechado for resposta := range canal { fmt.Println(resposta.Body) } return nil }
  9. buffered := make(chan int, 1) a := <-buffered buffered <-

    1 go func() { <-buffered }() // bloqueia goroutine // nao bloqueia // sincronização créditos: Gaurav Agarwal buffered <- 2 // bloqueia goroutine
  10. package main import ( "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" ) func main() {

    lambda.Start(processaEventoSQS) } func processaEventoSQS(evento events.SQSEvent) error { for _, mensagem := range evento { processa(mensagem) } return nil } func processa(mensagem events.SQSMessage) { //… } package main import ( "fmt" "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" ) func main() { lambda.Start(processaEventoSQS) } func processaEventoSQS(event events.SQSEvent) error { // define máximo de goroutines maxBuffer := len(event.Records) // cria um buffered channel canal := make(chan events.SQSMessage, maxBuffer) // processa as mensagens em Go routines diferentes go processaMensagens(event.Records, canal) // escuta eventos de um canal até que o mesmo esteja fechado for resposta := range canal { fmt.Println(resposta.Body) } return nil }
  11. package main import ( "encoding/json" "fmt" “github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" ) func

    main() { lambda.Start(processaEventoSQS) } func processaEventoSQS(event events.SQSEvent) error { // cria parametro para definir maximo de workers maxWorkers := len(event.Records) // cria um buffered channel canal := make(chan events.SQSMessage, maxWorkers) // processa as mensagens em Go routines diferentes go processaMensagens(event.Records, canal) // escuta eventos de um canal até que o mesmo esteja fechado for resposta := range canal { fmt.Println(resposta.Body) } return nil } //especifica na sua função que somente canais de envio de mensagem serão aceitos como parâmetro func processaMensagens(mensagens []events.SQSMessage, canal chan<- events.SQSMessage) { for _, mensagem := range mensagens { // realiza processamento e notifica canal canal <- mensagem } // feche o canal close(canal) }
  12. select select = switch porém só para channels select {

    case mensagem := <-canal: fmt.Println(mensagem) case <-quit: return }
  13. package main import ( "fmt" "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" ) func main()

    { lambda.Start(processaEventoSQS) } func processaEventoSQS(event events.SQSEvent) error { // define máximo de goroutines maxBuffer := len(event.Records) // cria um buffered channel canal := make(chan events.SQSMessage, maxBuffer) // processa as mensagens em Go routines diferentes go processaMensagens(event.Records, canal) // escuta eventos de um canal até que o mesmo esteja fechado for resposta := range canal { fmt.Println(resposta.Body) } return nil } package main import ( "fmt" "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" ) func main() { lambda.Start(processaEventoSQS) } func processaEventoSQS(event events.SQSEvent) error { // cria canal de consumo canal := make(chan events.SQSMessage) // cria canal para termino do processo quit := make(chan bool) // processa as mensagens em Go routines diferentes go notificaMensagens(event.Records, canal, quit) processMensagens(canal, quit) //finaliza canais close(canal) close(quit) return nil }
  14. // código omitido func processaEventoSQS(event events.SQSEvent) error { // cria

    canal de consumo canal := make(chan events.SQSMessage) // cria canal para termino do proceso quit := make(chan bool) // processa as mensagens em Go routines diferentes go notificaMensagens(event.Records, canal, quit) processMensagens(canal, quit) //finaliza canais close(canal) close(quit) return nil } func processMensagens(canal chan events.SQSMessage, quit chan bool) { // um loop para processar o recebimento de mensagens for { // select para rotear em que caso deverá ser processado select { case mensagem := <-canal: fmt.Println(mensagem) case <-quit: return } } } //especifica na sua função que somente canais de envio de mensagem serão aceitos como parâmetro func notificaMensagens(mensagens []events.SQSMessage, canal chan<- events.SQSMessage, quit chan bool) { for _, mensagem := range mensagens { canal <- mensagem } quit <- true }
  15. // código omitido func processMensagens(canalEntradaA, canalEntradaB <-chan events.SQSMessage, canalSaida chan<-

    events.SQSMessage) { // um loop para processar o recebimento de mensagens ate o canalEntrada estar fechado for mensagem := range canalEntradaA { // select para rotear em que caso deverá ser processado select { // Envia para o primeiro canal não bloqueado case canalSaida <- mensagem: } } for mensagem := range canalEntradaB { // select para rotear em que caso deverá ser processado select { // Envia para o primeiro canal não bloqueado case canalSaida <- mensagem: } } } Rob Pike - Go Concurrency Patterns
  16. // código omitido func processMensagens(canalEntrada <- chan events.SQSMessage, canalSaidaA, canalSaidaB

    chan events.SQSMessage) { // um loop para processar o recebimento de mensagens ate o canalEntrada estar fechado for mensagem := range canalEntrada { // select para rotear em que caso deverá ser processado select { // Envia para o primeiro canal não bloqueado case canalSaidaA <- mensagem: case canalSaidaB <- mensagem: } } } Go Meetup Smotri+ 23.04.2015
  17. sync package sync package oferece funções e estruturas para sincronização

    como Mutex, Wait Groups, Pool, Locker, Cond e Once. “Values containing the types defined in this package should not be copied.” –Go Manual
  18. wait group Estrutura para sincronização de goroutines. • Add(n) sinaliza

    a quantidade de callback a goroutine principal deverá esperar. • Done() sinaliza a conclusão de uma goroutine, decrementando o valor de n especificado no Add(n). • Wait() Gera um lock na goroutine até que todos os callbacks tenham sido enviados através da chamada de Done().
  19. package main import ( "fmt" "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" ) func main()

    { lambda.Start(processaEventoSQS) } func processaEventoSQS(event events.SQSEvent) error { // cria canal de consumo canal := make(chan events.SQSMessage) // cria canal para termino do processo quit := make(chan bool) // processa as mensagens em Go routines diferentes go notificaMensagens(event.Records, canal, quit) processMensagens(canal, quit) //finaliza canais close(canal) close(quit) return nil } package main import ( "encoding/json" "fmt" "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" "sync" ) var wg sync.WaitGroup func main() { lambda.Start(processaEventoSQS) } func processaEventoSQS(event events.SQSEvent) error { // cria canal de consumo canal := make(chan events.SQSMessage) // cria canal para termino do processo quit := make(chan bool) // especifica a quantidade de goroutines a esperar o callback wg.Add(2) // processa as mensagens em Go routines diferentes go notificaMensagens(event.Records, canal, quit, &wg) go processMensagens(canal, quit, &wg) // espera a sincronização dessas goroutines para prosseguir com a conclusão de processamento wg.Wait() //finaliza canais close(canal) close(quit) return nil }