Шлюзы 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, прочитайте следующие статьи.
Использование Golang Build TLS Unary gRPC! 7 статей, чтобы стать экспертом по gRPC(6)
Повышение уровня кодирования
Спасибо, что являетесь частью нашего сообщества! Перед тем, как ты уйдешь:
- 👏 Хлопайте за историю и подписывайтесь на автора 👉
- 📰 Смотрите больше контента в публикации Level Up Coding
- 💰 Бесплатный курс собеседования по программированию ⇒ Просмотреть курс
- 🔔 Подписывайтесь на нас: Twitter | ЛинкедИн | "Новостная рассылка"
🚀👉 Присоединяйтесь к коллективу талантов Level Up и найдите прекрасную работу