Simultaneidade com Goroutines
Neste artigo, falarei sobre uma pequena biblioteca go que escrevemos para facilitar o uso da goroutine e a escrita de código claro. Vamos ver como este pacote saiu.
Como equipe de indexação Trendyol, tínhamos um projeto escrito em Scala que coletava dados de vários microsserviços. Para reduzir a carga de trabalho da equipe atual e a complexidade do projeto, formamos a equipe Indexing Offer, da qual também faço parte.
Minha equipe será responsável pelos negócios relacionados a preço e estoque. Isso significa que a nova equipe trabalhará com 8 microsserviços e 4 equipes diferentes por enquanto. É claro que, com essa divisão, transferimos não apenas uma API, mas também os consumidores do negócio relevante para a nova equipe.
Temos várias APIs escritas em Go e observamos que o Go funciona bem em termos de tempo de inicialização, uso de memória e uso de CPU. Obtemos alto rendimento dessas APIs com poucos recursos. Como resultado, decidimos escrever nossa nova API usando Go.
Vamos voltar ao nosso tópico principal. Temos as seguintes situações ao enviar solicitações aos serviços.
- Precisamos dividir a solicitação em partes menores e enviá-las.
- Alguns serviços são independentes uns dos outros, então podemos enviar requisições paralelas.
Exemplo em Scala
// Future.sequence
def ChunkRequestAndGetAll(ids: Array[String]): Future[Seq[String]] = {
Future.sequence(ids.grouped(chunkSize).map(chunkedIds => {
Future {
APICall(chunkedIds)
}
}).toSeq)
}
// for-yield
def ForYieldExample() = {
val f1: Future[String] = Future {APICall("call")}
val f2: Future[Int] = Future {AnotherAPICall()}
val result = for {
r1 <- f1
r2 <- f2
} yield (anyFunc(r1, r2))
Await.result(result, 5.seconds)
}
Pedaços menores e enviá-los
Este exemplo demonstra como dividir uma solicitação e enviá-la em paralelo. Os resultados de todas as operações paralelas são coletados em uma lista e a função retorna um erro se alguma operação falhar.
func ChunkRequestAndGetAll(ids []string) ([]string, error) {
var chunkOfIds [][]string = chunk(ids, chunkSize)
ch := make(chan result, len(chunkOfIds))
for i := range chunkOfIds {
chunkedIds := chunkOfIds[i]
go func() {
response, apiErr := APICall(chunkedIds)
ch <- result{response, apiErr}
}()
}
log.Println("Waiting for results...")
results := make([]string, len(ids))
var err error
for i := 0; i < len(ids); i++ {
rs := <-ch
if rs.err != nil {
err = rs.err
break
}
results[i] = rs.response
}
return results, err
}
func ChunkRequestAndGetAll(ids []string) ([]string, error) {
var chunkOfIds [][]string = chunk(ids, chunkSize)
futures := make([]*future.Future[string], len(chunkOfIds))
for i := range chunkOfIds {
chunkedIds := chunkOfIds[i]
futures[i] = future.Run(func() (string, error) {
return APICall(chunkedIds)
})
}
log.Println("Waiting for result...")
results, err := future.GetAll(futures)
return results, err
}
O bloco de código abaixo demonstra como enviar uma solicitação paralela para dois serviços diferentes
/***--- With directly use channel and goroutine ---***/
func DirectlyUse() error {
ch1 := make(chan result[string])
go func() {
response, err := APICall("call")
ch1 <- result[string]{response, err}
}()
ch2 := make(chan result[int64])
go func() {
response, err := AnotherAPICall()
ch2 <- result[int64]{response, err}
}()
res1 := <-ch1
if res1.err == nil {
return res1.err
}
res2 := <-ch2
if res2.err != nil {
return res2.err
}
//somethings
}
/***--- With future struct ---***/
func WithFutureStruct() error {
fut1 := future.Run(func() (string, error) {
return APICall("call")
})
fut2 := future.Run(func() (int64, error) {
return AnotherAPICall()
})
res1, err1 := fut1.Get()
res2, err2 := fut2.Get()
if err1 == nil {
return err1
}
if err2 != nil {
return err2
}
//somethings
}
/***--- Alternative future struct ---***/
func WithFutureStruct() error {
fut1 := future.Run(func() (any, error) {
return APICall("call")
})
fut2 := future.Run(func() (any, error) {
return AnotherAPICall()
})
err := future.WaitFor(fut1, fut2)
if err != nil {
return err
}
result1 := future.GetResult[string](fut1)
result2 := future.GetResult[int64](fut2)
//somethings
}
Obrigado a Emre Odabas por seu apoio e encorajamento ao escrever este artigo.





































![O que é uma lista vinculada, afinal? [Parte 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)