Шлюзы API являются важнейшим компонентом любой архитектуры микросервисов. Они служат единой точкой входа для всех клиентских запросов, направляя эти запросы в соответствующие микросервисы. В этой статье я буду использовать Golang и фреймворк Gin для создания шлюза API с пробным тестом.

Нажмите, чтобы стать средним участником и читать неограниченное количество историй!

Общие сведения о шлюзе API

Как обсуждалось в статье API Gateway vs gRPC Gateway: какой выбрать в микросервисах? Полное руководство по микросервисам(1)», шлюз API в микросервисах — это сервер, который выступает в роли внешнего интерфейса API, получает запросы API, перенаправляет их соответствующим внутренним микросервисам, а затем возвращает ответ запрашивающей стороне. По сути, это служба маршрутизации, которая занимается маршрутизацией запросов, композицией и преобразованием протоколов, позволяя микросервисам не знать о существовании друг друга.

Зачем шлюзу нужен обратный прокси

Обратный прокси-сервер в контексте шлюза API дает несколько преимуществ:

а. Маршрутизация

Обратный прокси-сервер может направлять клиентские запросы к соответствующей серверной службе на основе пути запроса, заголовков или других атрибутов. Это позволяет клиентам взаимодействовать с единым унифицированным API, а не с отдельными микросервисами.

б. Балансировка нагрузки

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

в. Безопасность

Размещая обратный прокси-сервер перед серверными службами, вы можете защитить их от прямого доступа к Интернету. Обратный прокси-сервер может применять политики безопасности, такие как ограничение скорости, белый список IP-адресов и аутентификация. снижение риска атак на ваши сервисы.

д. Кэширование

Обратный прокси-сервер может кэшировать ответы от ваших серверных служб и передавать эти кэшированные ответы клиентам. Это может значительно снизить нагрузку на ваши службы и улучшить время отклика.

е. Устойчивость

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

ф. Перевод протокола

Если ваши серверные службы используют разные протоколы (например, HTTP/2, gRPC или WebSocket), обратный прокси-сервер может выполнять преобразование между этими протоколами и протоколом HTTP/1.1, используемым большинством клиентов.

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

Шаги по созданию шлюза API

1. Установите Го и Джин

Прежде чем начать, убедитесь, что в вашей системе установлены Go и Gin. Вы можете скачать Go с официального сайта Golang и установить Gin, используя go get github.com/gin-gonic/gin.

2. Создайте новый проект Go

Создайте новый каталог для своего проекта и инициализируйте его с помощью go mod init, который создает новый модуль, инициализирует файл go.mod, а также определяет и устанавливает все зависимости.

3. Определите маршруты

Давайте сначала определим простой маршрут.

func routes() {
   // Set Gin to release mode
   gin.SetMode(gin.ReleaseMode)

   router := gin.Default()

   // Define the routes for the API Gateway
   router.Any("/service1/*path", createReverseProxy("http://localhost:8081"))
   router.Any("/service2/*path", createReverseProxy("http://localhost:8082"))

   // Start the API Gateway
   router.Run(":8080")
}

3. Создайте обратный прокси

Приведенный ниже код представляет собой простой обратный прокси.

func createReverseProxy(target string) gin.HandlerFunc {
   return func(c *gin.Context) {
      // Parse the target URL
      targetURL, _ := url.Parse(target)

      // Create the reverse proxy
      proxy := httputil.NewSingleHostReverseProxy(targetURL)

      // Modify the request
      c.Request.URL.Scheme = targetURL.Scheme
      c.Request.URL.Host = targetURL.Host
      c.Request.URL.Path = c.Param("path")

      // Let the reverse proxy do its job
      proxy.ServeHTTP(c.Writer, c.Request)
   }
}

В приведенном выше коде функция createReverseProxy создает обратный прокси-сервер для заданного целевого URL. Эта функция используется в качестве обработчика маршрутов, определенных в маршрутизаторе Gin. Когда делается запрос на один из этих маршрутов, обратный прокси-сервер перенаправляет запрос на целевой URL-адрес и отправляет ответ обратно клиенту.

Обратите внимание, что "http://localhost:8081" и "http://localhost:8082" — это фактические URL-адреса ваших микросервисов. Кроме того, микросервисы должны быть запущены и доступны через шлюз API.

После того, как вышеописанное закончено, давайте добавим часть startServer. Код следующий.

func startServer(router *gin.Engine) {
   // Start the API Gateway
   router.Run(":8080")
}

Теперь весь код шлюза API закончен, весь код находится в файле с именем api.go. Содержание кода ниже.

package reverseproxy

import (
   "net/http/httputil"
   "net/url"

   "github.com/gin-gonic/gin"
)

func createRouter() *gin.Engine {
   router := gin.Default()

   // Define the routes for the API Gateway
   router.Any("/service1/*path", createReverseProxy("http://localhost:8081"))
   router.Any("/service2/*path", createReverseProxy("http://localhost:8082"))

   return router
}

func startServer(router *gin.Engine) {
   // Start the API Gateway
   router.Run(":8080")
}

func createReverseProxy(target string) gin.HandlerFunc {
   return func(c *gin.Context) {
      // Parse the target URL
      targetURL, _ := url.Parse(target)

      // Create the reverse proxy
      proxy := httputil.NewSingleHostReverseProxy(targetURL)

      // Modify the request
      c.Request.URL.Scheme = targetURL.Scheme
      c.Request.URL.Host = targetURL.Host
      c.Request.URL.Path = c.Param("path")

      // Let the reverse proxy do its job
      proxy.ServeHTTP(c.Writer, c.Request)
   }
}

4. Протестируйте шлюз API

После того, как я создам шлюз API, я проведу для него тест. Прежде чем я начну тест, убедитесь, что вы установили следующие два пакета.

"github.com/jarcoal/httpmock"
"github.com/stretchr/testify/assert"

Если они не установлены, пожалуйста, в своем терминале выполните следующие команды.

go get github.com/jarcoal/httpmock

go get github.com/stretchr/testify/assert

Все содержимое тестового кода находится в файле с именем api_test.go. Код следующий.

package reverseproxy

import (
   "net/http"
   "net/http/httptest"
   "testing"

   "github.com/jarcoal/httpmock"
   "github.com/stretchr/testify/assert"
)

type closeNotifyingRecorder struct {
   *httptest.ResponseRecorder
   closeNotifyChan chan bool
}

func newCloseNotifyingRecorder() *closeNotifyingRecorder {
   return &closeNotifyingRecorder{
      httptest.NewRecorder(),
      make(chan bool, 1),
   }
}

func (cnr *closeNotifyingRecorder) CloseNotify() <-chan bool {
   return cnr.closeNotifyChan
}

func TestCreateRouter(t *testing.T) {
   // Activate the httpmock library
   httpmock.Activate()
   defer httpmock.DeactivateAndReset()

   // Mock the servers
   httpmock.RegisterResponder("GET", "http://localhost:8081/test",
      httpmock.NewStringResponder(200, "Service 1"))
   httpmock.RegisterResponder("GET", "http://localhost:8082/test",
      httpmock.NewStringResponder(200, "Service 2"))

   // Create the router
   router := createRouter()

   // Create a request to the first route
   req, _ := http.NewRequest("GET", "/service1/test", nil)
   resp := newCloseNotifyingRecorder()

   // Serve the request
   router.ServeHTTP(resp, req)

   // Check the status code
   assert.Equal(t, http.StatusOK, resp.Code)
   assert.Equal(t, "Service 1", resp.Body.String())

   // Create a request to the second route
   req, _ = http.NewRequest("GET", "/service2/test", nil)
   resp = newCloseNotifyingRecorder()

   // Serve the request
   router.ServeHTTP(resp, req)

   // Check the status code
   assert.Equal(t, http.StatusOK, resp.Code)
   assert.Equal(t, "Service 2", resp.Body.String())
}

В приведенном выше коде я создаю тип closeNotifyingRecorder, который встраивает httptest.ResponseRecorder и добавляет метод CloseNotify, необходимый для реализации интерфейса http.CloseNotifier. Я использую этот новый тип вместо httptest.ResponseRecorder при обслуживании запроса.

Я также создал фиктивный сервер, который всегда отвечает статусом 200 OK. Затем мы создаем маршрутизатор и отправляем запросы на маршруты. Поскольку маршруты настроены на прокси на http://localhost:8081 и http://localhost:8082, и эти серверы не реализованы в моей статье, маршрутизатор должен ответить статусом 404 Not Found. Я проверяю, что код состояния ответов — 404 Not Found.

Обратите внимание, что это обходной путь только для целей тестирования. Метод CloseNotify в этом примере на самом деле не уведомляет о закрытии клиентского соединения. В реальном проекте вы должны использовать ResponseWriter, который правильно реализует интерфейс http.CloseNotifier.

Обратите внимание, мой тест предполагает, что функция createReverseProxy всегда работает корректно. Если функция может дать сбой (например, если целевой URL-адрес недействителен), вам также следует самостоятельно написать тесты для этих случаев сбоя.

Кроме того, данный тест фактически не проверяет функциональность обратного прокси-сервера. Для этого вам нужно будет запустить серверы с адресами http://localhost:8081 и http://localhost:8082 и проверить соответствие ответов от маршрутов ответам от этих серверов. Однако запуск серверов для тестирования может быть сложным и, как правило, не рекомендуется. Вместо этого вы можете рассмотреть возможность использования имитации HTTP-клиента, который может имитировать поведение серверов. Я расскажу оhttpmock в своих следующих статьях.

Скриншот результатов теста ниже.

Заключение

Это очень простой пример, и он не включает в себя такие функции, как балансировка нагрузки, разрыв цепи, ограничение скорости или аутентификация, которые вы обычно найдете в готовом к работе API-шлюзе. Тем не менее, это должно стать хорошей отправной точкой для создания шлюза API с использованием Go и Gin. Удачного кодирования :)

В третьей части этой серии я реализую реальный пример шлюза API с серверами микросервисов.

Список статей полного руководства по микросервисам приведен ниже.

Шлюз API или шлюз gRPC: какой из них выбрать? Полное руководство по микросервисам(1)

Создайте простой шлюз API, используя Gin с пробными тестами! Полное руководство по микросервисам(2)

Шлюз API и два микросервиса с тестами! Полное руководство по микросервисам(3)

Спасибо, что читаете. Если вам понравилась моя статья, хлопайте в ладоши и подписывайтесь на меня. Я с удовольствием отвечу на все ваши вопросы, если вы спросите меня в комментарии. Нажмите на следующую ссылку, чтобы стать средним участником.

Нажмите, чтобы стать средним участником и читать неограниченное количество историй!

Если вы не знакомы с gRPC, прочитайте следующие статьи.

REST или gRPC: какой из них больше подходит для микросервисов? 7 статей, чтобы стать экспертом по gRPC(1)

Используя Golang, создайте унарный gRPC с модульными тестами! 7 статей, чтобы стать экспертом по gRPC(2)

Использование сервера сборки Golang для потоковой передачи gRPC с модульными тестами! 7 статей, чтобы стать экспертом по gRPC(3)

Использование Golang для потоковой передачи gRPC клиента с модульными тестами! 7 статей, чтобы стать экспертом по gRPC(4)

Используя Golang, создайте службу чата, используя gRPC с двунаправленной потоковой передачей и модульными тестами! 7 статей, чтобы стать экспертом по gRPC(5)

Использование Golang Build TLS Unary gRPC! 7 статей, чтобы стать экспертом по gRPC(6)

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

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

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

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