Acelera tus API

Feb 21 2023
La creación de nuevos objetos puede ser una operación relativamente costosa, especialmente si implica asignar memoria en el montón, por lo que la reutilización de objetos puede ayudar a reducir la sobrecarga y mejorar el rendimiento. Al mantener un conjunto de objetos preasignados y reutilizarlos según sea necesario, podemos evitar la sobrecarga de asignar y desasignar memoria (el recolector de basura desasigna las variables no utilizadas en la memoria).

La creación de nuevos objetos puede ser una operación relativamente costosa, especialmente si implica asignar memoria en el montón, por lo que la reutilización de objetos puede ayudar a reducir la sobrecarga y mejorar el rendimiento. Al mantener un conjunto de objetos preasignados y reutilizarlos según sea necesario, podemos evitar la sobrecarga de asignar y desasignar memoria (el recolector de basura desasigna las variables no utilizadas en la memoria).

Esto puede ser especialmente útil para las API que necesitan manejar una gran cantidad de solicitudes, donde la creación y destrucción de objetos en cada solicitud puede ser un cuello de botella importante. Al reutilizar objetos, podemos reducir la cantidad de tiempo de CPU y memoria requerida, lo que puede generar un mejor rendimiento y la capacidad de manejar más solicitudes.

Para lograr esto, podemos usar el sync.Poolpaquete en Go para implementar la agrupación de objetos. Es sync.Poolun paquete de Go incorporado que proporciona un mecanismo seguro para subprocesos para almacenar y recuperar objetos de un grupo.
Puede saltar directamente al código ..!!

Manejadores:-


package handlers

import (
 "encoding/json"
 "net/http"
 "sync"
)

type Request struct {
 Name    string `json:"name"`
 Id      string `json:"id"`
 Country string `json:"country"`
 Age     int    `json:"age"`
}

type ObjectPool struct {
 ReqPool *sync.Pool
}

var objPool ObjectPool

func init() {
 // init object pool
 objPool.ReqPool = &sync.Pool{
  New: func() interface{} {
   return new(Request)
  },
 }

}


func HandlerWithoutSyncPool() http.Handler {
 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  req :=new(Request)
  err := json.NewDecoder(r.Body).Decode(&req)
  if err != nil {
   w.WriteHeader(http.StatusBadRequest)
   return
  }
  w.WriteHeader(http.StatusOK)
  err = json.NewEncoder(w).Encode(req)
  if err != nil {
   w.WriteHeader(http.StatusInternalServerError)
   return
  }

 })
}

func HandlerWithSyncPool() http.Handler {
 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  req := objPool.ReqPool.Get().(*Request)

 //to make sure it return object to pool
  defer objPool.ReqPool.Put(req)

  err := json.NewDecoder(r.Body).Decode(&req)
  if err != nil {
   w.WriteHeader(http.StatusBadRequest)
   return
  }

  w.WriteHeader(http.StatusOK)
  
 })
}

Nota: los resultados varían según el tipo de objeto que estamos agrupando

package handlers

import (
 "bytes"
 "encoding/json"
 "net/http"
 "net/http/httptest"
 "testing"
)

var tests = []struct {
 req Request
}{
 {
  req: Request{Name: "test", Country: "testCountry", Id: "id1", Age: 10},
 },
}


func BenchmarkHandlerWithSyncPool(b *testing.B) {

 for _, test := range tests {
  data, _ := json.Marshal(test)

  b.RunParallel(func(pb *testing.PB) {

   for pb.Next() {
    _, _ = http.Post("http://127.0.0.1:8888/withpool", "application/json", bytes.NewBuffer(data))
   }
  })

 }

}

func BenchmarkHandlerWithoutSyncPool(b *testing.B) {
 for _, test := range tests {

  data, _ := json.Marshal(test)

  b.RunParallel(func(pb *testing.PB) {

   for pb.Next() {
    _, _ = http.Post("http://127.0.0.1:8888/withoutpool", "application/json", bytes.NewBuffer(data))
   }
  })
 }

}


go test -bench=BenchmarkHandlerWithoutSyncPool -benchtime=5s -cpu=1
goos: darwin
goarch: amd64
pkg: testsyncpool/handlers
cpu: Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
BenchmarkHandlerWithoutSyncPool     23067    1004690 ns/op

      
                

go test -bench=BenchmarkHandlerWithSyncPool -benchtime=5s -cpu=1 
goos: darwin
goarch: amd64
pkg: testsyncpool/handlers
cpu: Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
BenchmarkHandlerWithSyncPool     77290      76521 ns/op

      
                

package main

import (
 "log"
 "net/http"
 "net/http/pprof"
 "testsyncpool/handlers"
)

func main() {
 r := http.NewServeMux()
 //r.Handle("/withoutpool", handlers.HandlerWithoutSyncPool())

 r.Handle("/withpool", handlers.HandlerWithSyncPool())

 // Register pprof handlers
 r.HandleFunc("/debug/pprof/", pprof.Index)
 r.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
 r.HandleFunc("/debug/pprof/profile", pprof.Profile)
 r.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
 r.HandleFunc("/debug/pprof/trace", pprof.Trace)
 err := http.ListenAndServe(":8888", r)
 if err != nil {
  log.Fatal(err)
 }
}

Por favor, siéntase libre de agregar cualquier comentario/sugerencia/errores, etc...