Вообще-то мы хотели назвать статью «Неудачный опыт…», потому что, как будет рассказано ниже, с использованием Tyk всё оказалось не так просто. Но, во-первых, даже неудачный опыт — это тоже опыт. Во-вторых, нам не хотелось бы превращать заголовок в спойлер, чтобы заинтересованные специалисты всё же имели стимул прочитать статью, а не делать споропалительных выводов из одного лишь названия. Тем более с развитием Tyk многие выявленные проблемы могут стать неактуальными.
Предыстория
Имеется проект для крупного заказчика, в реализации которого принимали участие специалисты «Рексофт». Задача проекта — разработка системы планирования и распределения ресурсов в реальном времени. Реализован он на базе микросервисной архитектуры, где back-end сервисы предоставляют GraphQL API.
Возникла необходимость расположить сервисы за шлюзом API, который взял бы на себя функции аутентификации пользователей и управления доступом на основе ролей (role-based access control, RBAC). Бонусом могли бы стать такие функции, как ограничение трафика по заданной квоте и другие способы защиты от атак.
Также, поскольку у нас GraphQL, то шлюз должен уметь работать с web-сокетами, чтобы обеспечить работоспособность GraphQL-подписок.
Для реализации RBAC шлюз должен уметь делать разбор GraphQL-запросов, потому что разграничивать доступ предполагается на уровне полей в GraphQL типах. Как известно, запросы и мутации в GraphQL также являются полями типов Query и Mutation соответственно, так что такой способ разграничения наиболее естественен для GraphQL.
Выбор шлюза
При поиске кандидата мы руководствовались следующими критериями.
Во-первых, это должен быть open-source проект, как минимум в своей базовой части. Проекты с закрытым кодом, и требующие лицензионного ключа с ограниченным сроком действия, по известным причинам опасны для использования или попросту недоступны в России.
Во-вторых, проект должен быть достаточно зрелым и стабильным. Само собой предполагается наличие мало-мальски развитого сообщества, чтобы оперативно решать вопросы. Ну, и хочется хотя бы минимальной бесплатной поддержки со стороны команды разработчиков.
В-третьих, желательно, чтобы GraphQL-стек был реализован не в виде внешнего плагина, а был составной частью проекта. В первую очередь из соображений производительности, потому что у наших сервисов довольно объёмное GraphQL API.
Всем этим требованиям (кроме второго пункта, о чём ниже) на первый взгляд удовлетворяет Tyk (произносится как «тайк»):
Хотя под маркой Tyk предлагается целая облачная платформа, в её основе лежит проект API Gateway — собственно сам шлюз, и этот проект — чистый open-source под лицензией MPL v2.0. Если верить официальному сайту — это намеренный шаг и даже философия авторов проекта, что делает им честь.
Tyk запущен в 2014 году и с тех пор дорос до версии 4. Проект представлен на сайте CNCF в качестве silver member и получил неплохое финансирование, о чём даже упоминалось на Хабре.
У проекта помимо раздела Issues на Github есть неплохой форум. Личный опыт показывает, что первая линия поддержки отвечает оперативно, зачастую в тот же день, и часть проблем помогает решить, не прибегая к помощи разработчиков. Увы, при обнаружении ошибок или по нестандартным проблемам помощи ждать придётся значительно дольше.
Шлюз целиком написан на Go, и наш опыт эксплуатации показал хорошую производительность и стабильную работу.
Tyk предоставляет богатую встроенную функциональность в качестве шлюза и прокси-сервера, есть и возможность подключения плагинов.
Но нас в первую очередь интересовали его возможности по работе с GraphQL. И тут у Tyk согласно документации тоже всё прекрасно:
Стек GraphQL также написан на Go, это предполагает отличную производительность, что в общем-то и подтвердилось в опыте использования.
Tyk позволяет настраивать доступ к GraphQL API на основе ролей пользователей и полей в GraphQL типах. Выше мы уже упомянули, что это наиболее естественный и гибкий способ реализации RBAC в GraphQL. Что сказать, 10 из 10.
Естественно Tyk обеспечивает валидацию запросов, причём можно ограничить глубину вложенности полей во избежание особого рода атак.
И, наконец, вишенка на торте, просто killer feature проекта — это встроенная поддержка API Federation. Если в двух словах, это такой способ комбинации API от нескольких поставщиков. Весьма многообещающий подход к построению API в многосервисных проектах, на который мы возлагали большие надежды.
Конфигурирование
Вдохновившись официальной документацией, мы приступили к работе. Сразу же выявились недостатки официальной документации, в которых некоторые нюансы конфигурации просто-напросто не освещены. Спасибо разработчикам, написавшим небольшой демонстрационный проект, который мы смогли взять за образец.
Толику информации мы смогли выловить на официальном форуме, хотя тема GraphQL там отнюдь не доминирует. Чувствуется, что эта функциональность была добавлена в Tyk недавно и пока не пользуется особым спросом.
Наконец, в особых случаях нам приходилось открывать исходники и там находить ответы на вопросы.
Впрочем, скажем прямо, это ожидаемо для любого open-source проекта, тем более учитывая его бесплатность.
Попутно заметим, что помимо самого API Gateway, который бесплатен и доступен в исходниках, имеется его платное расширение, предоставляющее удобные средства по конфигурированию шлюза. Возможно, оно сняло бы большую часть вопросов по настройке. К сожалению, у нас не было возможности проверить его в деле, да и текущая ситуация с лицензиями не добавляет желания рисковать.
В конце концов мы успешно справились с конфигурированием и запустили Tyk в тестовую эксплуатацию.
API Federation
На удивление почти не было проблем с самой сложной частью — API Federation, когда несколько поставщиков GraphQL API «сливаются» в единую точку входа и при этом имеют возможность взаимодействовать друг с другом, используя и дополняя элементы «чужого» API.
На момент наших экспериментов была обнаружена только одна серьёзная проблема. При подгрузке данных из поставщика API запросы отправлялись по отдельности для каждого возвращаемого объекта. Пакетная обработка была заявлена, но фактически не работала. Это приводило к просадке — в разы — производительности в объёмных запросах.
К слову, данная проблема исправлена в недавно вышедшей версии 4.3.
Ошибки парсинга GraphQL
Но самые серьёзные проблемы поджидали нас там, где не ждали — в подсистеме, отвечающей за разбор и валидацию GraphQL-запросов.
Например, Tyk как-то странно отрабатывает запрос интроспекции, возвращая не то, что нужно. Мы быстро нашли пути обхода этой проблемы: достаточно добавить в запрос ещё одно какое-нибудь поле. Но «звоночек» был настораживающим.
Далее, Tyk не знает, что у полей в input-типах могут быть значения по умолчанию. Из-за этого корректные запросы с такими типами отвергались по причине якобы отсутствующих обязательных полей.
В каких-то случаях Tyk отвергает корректные запросы по неясной причине. Иногда помогает перенести часть параметров из самого запроса в variables, иногда — обратное. Какой-то системы мы в этом найти не смогли и некоторое время просто жили с этим, модифицируя очередной проблемный запрос в ту или иную сторону.
При этом выросла нагрузка на наших разработчиков, потому что при любой проблеме клиентов с нашим API приходилось сперва разбираться, не проблема ли это в Tyk.
Наконец, Tyk как-то странно работает с GraphQL-интерфейсами. Рассмотрим следующий намеренно упрощённый пример:
query {
getResponse {
...on Response {
__typename
result
}
...on IResponse {
__typename
result
}
}
}
В примере выше тип Response
реализует интерфейс IResponse
. Ожидается, что запрос вернёт два поля — __typename
и result
. В реальности Tyk возвращает такое вот:
{
"data": {
"getResponse": {
"__typename": "Response",
"result": 123,
"__typename": "Response",
"result": 123
}
}
}
И это ещё не самая большая проблема. В более сложных случаях Tyk может вообще не вернуть данные или даже упасть в процессе обработки HTTP-запроса, вернув ошибку сокета.
Тут уже встал вопрос, что первично: наше GraphQL API или способность Tyk с ним работать. Мы не могли позволить, чтобы шлюз API, точнее его ошибки реализации диктовали нам, какие средства GraphQL использовать, а от каких воздерживаться.
В итоге, нам пришлось отказаться от использования Tyk в качестве шлюза GraphQL API.
Заключение
Не смотря на выявленные проблемы в целом Tyk показал себя неплохим инструментом с хорошей стабильностью и производительностью, и при этом с небольшим потреблением памяти.
По объему заявленной функциональности он, по нашему мнению, один из лучших среди open-source решений. При этом 100% этой функциональности доступно в бесплатной версии без каких-либо ограничений.
Нельзя отрицать наличие недоработок, а иногда просто «детских болезней» в GraphQL стеке. Но если у вас GraphQL API небольшого размера и сложности, возможно, вы никогда с ними не столкнётесь.
В целом хорошее впечатление осталось от общения со службой поддержки на официальном форуме. За недолгое время нашего опыта с Tyk было выявлено и исправлено несколько ошибок. К сожалению, исправление других критических для нас ошибок затянулось, что и не позволило в итоге взять Tyk текущей (4.3) версии в работу.
Будем следить за дальнейшем развитием Tyk и пожелаем успехов его авторам.