Ответ
Метаданные в gRPC — это набор пар "ключ-значение", которые передаются вместе с RPC-вызовом, но не являются частью бизнес-логики сообщения (Protobuf). Они очень похожи на HTTP-заголовки и служат для передачи служебной информации.
Основные сценарии использования:
- Аутентификация и авторизация: Передача токенов (JWT, API-ключи) для проверки прав доступа.
- Трассировка и мониторинг: Передача ID трассировки (trace ID) и ID запроса (request ID) для сквозного отслеживания вызовов в микросервисной архитектуре.
- Отладка: Передача отладочной информации, например, имени клиента или версии.
- Управление потоком: Передача информации о сжатии или других параметрах вызова.
Как работать с метаданными в Go:
Метаданные передаются через объект context.Context.
Пример отправки метаданных клиентом:
import "google.golang.org/grpc/metadata"
// 1. Создаем метаданные
md := metadata.Pairs(
"authorization", "Bearer my-secret-token",
"request-id", "some-unique-id",
)
// 2. Прикрепляем их к контексту
ctx := metadata.NewOutgoingContext(context.Background(), md)
// 3. Выполняем RPC-вызов с этим контекстом
response, err := client.SomeRPC(ctx, request)
Пример чтения метаданных на сервере:
import "google.golang.org/grpc/metadata"
func (s *server) SomeRPC(ctx context.Context, req *pb.Request) (*pb.Response, error) {
// 1. Извлекаем метаданные из входящего контекста
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return nil, status.Errorf(codes.InvalidArgument, "Missing metadata")
}
// 2. Читаем значения по ключу (ключи автоматически приводятся к нижнему регистру)
tokens := md.Get("authorization")
if len(tokens) > 0 {
fmt.Println("Auth Token:", tokens[0])
}
// ... остальная логика
return &pb.Response{}, nil
}
Ключевые особенности:
- Ключи метаданных нечувствительны к регистру и автоматически приводятся к
lowercase. - Для передачи бинарных данных ключ должен иметь суффикс
-bin. - Существуют также трейлеры (trailers) — это метаданные, которые сервер отправляет в самом конце обработки запроса, вместе со статусом. Они полезны для передачи информации, которая стала известна только по завершении вызова (например, итоговое время обработки).
Ответ 18+ 🔞
А, ну вот, смотри, опять эти ваши метаданные, блядь! Это ж как заголовки в HTTP, только для гРПЦшных дел. Представь себе, ты везешь посылку — это твоё Protobuf-сообщение, бизнес-логика, всё такое. А метаданные — это, сука, наклейки на коробке: «Хрупкое», «Срочно», «Токен внутри, ёпта». Саму посылку не трогают, но всем курьерам понятно, что с ней делать.
Зачем это, блядь, нужно?
- Кто ты такой, сука? Токены всякие, ключики — запихнул в метаданные, и сервер тебя опознает. Авторизация, аутентификация, в рот меня чих-пых.
- Где ты, засранец, пропадаешь? В микросервисах, когда один запрос по десяти сервисам скачет, нужно его отследить. Кидаешь
trace-idв метаданные — и вот он, маршрут, как на ладони. Мониторинг, трассировка, мать их. - Чё за хуйня происходит? Для отладки. Версию клиента запилил, или флаг «debug» — и сервер тебе в ответ может подробнее сопли размазать.
- Как тебя жрать? Настройки всякие: «сожми меня покрепче» или «не тормози».
Как с этим в Go ебаться?
Клиент, когда шлёт запрос, должен прилепить эти наклейки к контексту. Всё через библиотеку metadata.
Клиент, прикинь, клеит наклейки:
import "google.golang.org/grpc/metadata"
// 1. Делаем метаданные. Прям пары «ключ-значение», как в комиссионке.
md := metadata.Pairs(
"authorization", "Bearer my-secret-token",
"request-id", "some-unique-id",
)
// 2. Лепим этот пакет наклеек в контекст, который полетит на сервер.
ctx := metadata.NewOutgoingContext(context.Background(), md)
// 3. Вызываешь удалённую процедуру с этим контекстом — и всё, понеслась.
response, err := client.SomeRPC(ctx, request)
Сервер, сука, принимает и читает:
import "google.golang.org/grpc/metadata"
func (s *server) SomeRPC(ctx context.Context, req *pb.Request) (*pb.Response, error) {
// 1. Выковыриваем метаданные из контекста, который прилетел.
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
// Во, приплыли. Контекст пустой, метаданных нет. Отказ, блядь.
return nil, status.Errorf(codes.InvalidArgument, "Missing metadata")
}
// 2. Достаём по ключу. Запомни, ключи тут автоматом в нижний регистр идут!
tokens := md.Get("authorization")
if len(tokens) > 0 {
fmt.Println("Auth Token:", tokens[0]) // Вот он, родной, токен.
}
// ... дальше твоя бизнес-логика, ебать её в сраку.
return &pb.Response{}, nil
}
Важные нюансы, чтоб не обосраться:
- Регистр, блядь, не важен. Написал
Authorization,AUTHORIZATIONилиAuThOrIzAtIoN— всё равно станетauthorization. Не выёбывайся. - Бинарные данные — если шлёшь какую-нибудь хуйню не текстовую, а бинарную, в ключе должен быть суффикс
-bin. Правила, блядь. - Трейлеры — это, сука, метаданные на выхлопе. Сервер их шлёт в самом конце, когда уже всё сделал. Типа «итоговое время обработки — 5 секунд, ёпта». Удобно для пост-анализа, когда результат уже известен.
Вот и вся магия. Не такая и сложная хуйня, если разобраться. Главное — не забывай контекст правильный прокидывать, а то запрос улетит голый, и сервер тебя пошлёт нахуй.