Введение

Есть много способов оптимизировать код для повышения эффективности, и одним из них является сокращение запущенных процессов. В этой статье мы увидим, как мы можем оптимизировать код Go, уменьшив количество повторяющихся процессов, используя один из пакетов Go, Singleflight.

Проблема

Допустим, у вас есть веб-приложение, у него 10 запросов в секунду (RPS). Основываясь на данных, вы знаете, что некоторые из этих запросов имеют один и тот же шаблон, который на самом деле может генерировать один и тот же результат, что означает, что у вас на самом деле есть избыточные процессы.

Из иллюстрации выше мы знаем, что user 1 и user 2 хотят одного и того же, но в итоге мы (в основном) обрабатываем оба запроса по отдельности.

Решение

Singleflight — один из пакетов Go, который может решить такую ​​проблему, как описано в документации, он предоставляет механизм подавления повторяющихся вызовов функций.

Круто, так что мы действительно можем уменьшить количество обрабатываемых функций, если знаем, что функции, которые мы собираемся вызывать, дублируются, давайте посмотрим, как мы можем использовать это в реальном случае.

Выполнение

Мы собираемся создать две программы, server.go и client.go.

server.go — будет действовать как веб-служба, которая может получать запросы в конечной точке /api/v1/get_something с параметром name.

// server.go

package main

import (
 "fmt"
 "net/http"
)

func main() {
 http.HandleFunc("/api/v1/get_something", func(w http.ResponseWriter, r *http.Request) {
  name := r.URL.Query().Get("name")
  response := processingRequest(name)
  fmt.Fprint(w, response)
 })

 err := http.ListenAndServe(":8080", nil)
 if err != nil {
  fmt.Println(err)
 }
}

func processingRequest(name string) string {
 fmt.Println("[DEBUG] processing request..")
 return "Hi there! You requested " + name
}

client.goбудет действовать как клиент, который делает 5 одновременных запросов к веб-службе (вы можете установить число в переменной totalRequests)

// client.go

package main

import (
 "io/ioutil"
 "log"
 "net/http"
 "sync"
)

func main() {

 var wg sync.WaitGroup

 endpoint := "http://localhost:8080/api/v1/get_something?name=something"
 totalRequests := 5

 for i := 0; i < totalRequests; i++ {
  wg.Add(1)
  go func(i int) {
   defer wg.Done()
   makeAPICall(endpoint)
  }(i)
 }
 wg.Wait()

}

func makeAPICall(endpoint string) {
 resp, err := http.Get(endpoint)
 if err != nil {
  log.Fatalln(err)
 }

 body, err := ioutil.ReadAll(resp.Body)
 if err != nil {
  log.Fatalln(err)
 }

 result := string(body)
 log.Printf(result)
}

Сначала мы можем запустить server.go, а затем продолжить client.go. Увидим в терминале скрипта сервера что-то вроде этого:

[DEBUG] processing request..
[DEBUG] processing request..
[DEBUG] processing request..
[DEBUG] processing request..
[DEBUG] processing request..

Терминал клиентского скрипта будет таким:

Hi there! You requested something
Hi there! You requested something
Hi there! You requested something
Hi there! You requested something
Hi there! You requested something

Это имеет смысл, так как мы отправили пять запросов от клиентов и обработали эти пять запросов на сервере.

Теперь давайте реализуем Singleflight в нашем коде, чтобы он был более эффективным.

// server.go

package main

import (
 "fmt"
 "net/http"

 "golang.org/x/sync/singleflight"
)

var g = singleflight.Group{}

func main() {
 http.HandleFunc("/api/v1/get_something", func(w http.ResponseWriter, r *http.Request) {
  name := r.URL.Query().Get("name")
  response, _, _ := g.Do(name, func() (interface{}, error) {
   result := processingRequest(name)
   return result, nil
  })

  fmt.Fprint(w, response)
 })

 err := http.ListenAndServe(":8080", nil)
 if err != nil {
  fmt.Println(err)
 }
}

func processingRequest(name string) string {
 fmt.Println("[DEBUG] processing request..")
 return "Hi there! You requested " + name
}

После того, как мы перезапустили сервер и снова запустили программу клиента, терминал в скрипте сервера показывает это:

[DEBUG] processing request..
[DEBUG] processing request..

Терминал клиентского скрипта показывает это:

Hi there! You requested something
Hi there! You requested something
Hi there! You requested something
Hi there! You requested something
Hi there! You requested something

Замечательно! Все клиенты получают ожидаемые ответы, но теперь наш сервер обработал только два запроса. Представьте себе размер эффективности, которую вы принесете, если будете обрабатывать тысячи однотипных запросов, это потрясающе!

Заключение

В этой статье мы узнали о возможностях Singleflight в оптимизации нашего кода. Не только при обработке веб-запроса, но вы также можете расширить его использование для других вещей, таких как получение данных из базы данных и т. д.

Есть также вещи, которые я не осветил в этой статье, например, что произойдет, если процесс в Singleflight выйдет из строя, и как мы можем смягчить это. Это может быть темой, которую я хочу исследовать позже в следующих статьях.

Наконец, большое спасибо, если вы все еще читаете до этого момента, пожалуйста, поделитесь статьей, если вы найдете ее полезной или, возможно, знаете кого-то, кому она нужна.

Повышение уровня кодирования

Спасибо, что являетесь частью нашего сообщества! Перед тем, как ты уйдешь:

  • 👏 Хлопайте за историю и подписывайтесь на автора 👉
  • 📰 Смотрите больше контента в публикации Level Up Coding
  • 🔔 Подписывайтесь на нас: Twitter | ЛинкедИн | "Новостная рассылка"

🚀👉 Присоединяйтесь к коллективу талантов Level Up и найдите прекрасную работу