1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/mirrors-grpc-framework

Клонировать/Скачать
PROTOCOL-HTTP2.md 25 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Отправлено 28.06.2025 08:23 b4cba57

gRPC через HTTP2

Введение

Данный документ представляет собой подробное описание реализации gRPC, передаваемой через HTTP2 фрейминг. Предполагается знакомство с спецификацией HTTP2.

Протокол

Производственные правила используют синтаксис ABNF.

Обзор

Следующее является общей последовательностью атомов сообщений в потоке запросов и ответов gRPC

  • Запрос → Request-Headers *Length-Prefixed-Message EOS
  • Ответ → (Response-Headers *Length-Prefixed-Message Trailers) / Trailers-Only

Запросы

  • Запрос → Request-Headers *Length-Prefixed-Message EOS

Request-Headers передаются как HTTP2 заголовки в фреймах HEADERS + CONTINUATION.

  • Request-Headers → Call-Definition *Custom-Metadata
  • Call-Definition → Method Scheme Path [Authority] TE [Timeout] Content-Type [Message-Type] [Message-Encoding] [Message-Accept-Encoding] [User-Agent]
  • Method → ":method POST"
  • Scheme → ":scheme " ("http" / "https")
  • Path → ":path" "/" Service-Name "/" {method name} ; Но см. примечание ниже.
  • Service-Name → {IDL-специфичное имя службы}
  • Authority → ":authority" {виртуальное имя хоста}
  • TE → "te" "trailers" ; Используется для обнаружения несовместимых прокси
  • Timeout → "grpc-timeout" TimeoutValue TimeoutUnit
  • TimeoutValue → {положительное целое число в виде ASCII строки длиной не более 8 цифр}
  • TimeoutUnit → Hour / Minute / Second / Millisecond / Microsecond / Nanosecond
    • Hour → "H"
    • Minute → "M"
    • Second → "S"
    • Millisecond → "m"
    • Microsecond → "u"
    • Nanosecond → "n"
  • Content-Type → "content-type" "application/grpc" [("+proto" / "+json" / {custom})]
  • Content-Coding → "identity" / "gzip" / "deflate" / "snappy" / {custom}
  • Message-Encoding → "grpc-encoding" Content-Coding
  • Message-Accept-Encoding → "grpc-accept-encoding" Content-Coding *("," Content-Coding)
  • User-Agent → "user-agent" {структурированная строка user-agent}
  • Message-Type → "grpc-message-type" {имя типа для схемы сообщения}
  • Custom-Metadata → Binary-Header / ASCII-Header
  • Binary-Header → {Header-Name "-bin" } {значение, закодированное в base64}
  • ASCII-Header → Header-Name ASCII-Value
  • Header-Name → 1*( %x30-39 / %x61-7A / "_" / "-" / ".") ; 0-9 a-z _ - .
  • ASCII-Value → 1*( %x20-%x7E ) ; пробел и печатаемые символы ASCII

HTTP2 требует, чтобы зарезервированные заголовки, начинающиеся с ":", появлялись перед всеми другими заголовками. Кроме того, реализации должны отправлять Timeout сразу после зарезервированных заголовков, а также отправлять заголовки Call-Definition перед отправкой Custom-Metadata.

Path чувствителен к регистру. Некоторые реализации gRPC могут позволять переопределять формат Path, показанный выше, но эта функциональность настоятельно не рекомендуется. gRPC не стремится нарушать работу пользователей, использующих этот вид переопределения, но мы не активно поддерживаем его, и некоторые функции (например, поддержка конфигурации службы) не будут работать, если путь не имеет указанного выше формата.

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

Если Content-Type не начинается с "application/grpc", gRPC серверы ДОЛЖНЫ отвечать HTTP статусом 415 (Unsupported Media Type). Это предотвратит другие клиенты HTTP/2 от интерпретации ошибочного ответа gRPC, который использует статус 200 (OK), как успешный.

Custom-Metadata — это произвольный набор пар ключ-значение, определенных на уровне приложения. Имена заголовков, начинающиеся с "grpc-", но не перечисленные здесь, зарезервированы для будущего использования gRPC и не должны использоваться приложениями как Custom-Metadata.

Обратите внимание, что HTTP2 не позволяет использовать произвольные последовательности октетов для значений заголовков, поэтому бинарные значения заголовков должны быть закодированы с использованием Base64 согласно https://tools.ietf.org/html/rfc4648#section-4. Реализации ДОЛЖНЫ принимать как заполненные, так и незаполненные значения и должны отправлять незаполненные значения. Приложения определяют бинарные заголовки, добавляя суффикс "-bin" к их именам. Библиотеки времени выполнения используют этот суффикс для обнаружения бинарных заголовков и правильно применяют кодирование и декодирование base64 при отправке и получении заголовков.

Порядок заголовков Custom-Metadata не гарантируется сохранением, за исключением значений с одинаковыми именами заголовков. Значения с одинаковыми именами заголовков могут быть объединены с использованием "," в качестве разделителя и считаться семантически эквивалентными. Реализации должны разделять Binary-Header на "," перед декодированием значений, закодированных в base64.

Значения ASCII-Value не должны иметь начальных или конечных пробелов. Если они содержат начальные или конечные пробелы, они могут быть удалены. Определенный диапазон символов для ASCII-Value строже, чем в HTTP. Реализации не должны выдавать ошибку при получении недействительного ASCII-Value, который является допустимым field-value в HTTP, но точное поведение не строго определено: они могут игнорировать значение или принять его. Если значение принято, следует убедиться, что приложение разрешено эхом отвечать этим значением как метаданными. Например, если метаданные предоставляются приложению в виде списка в запросе, приложение не должно вызывать ошибку, предоставляя тот же список как метаданные в ответе.

Серверы могут ограничивать размер Request-Headers, с рекомендуемым значением по умолчанию 8 КБ. Реализации рекомендуют вычислять общий размер заголовков аналогично HTTP/2's SETTINGS_MAX_HEADER_LIST_SIZE: сумма всех полей заголовков, для каждого поля сумма длины незакодированного имени поля и значения плюс 32, с учетом длины бинарных значений после кодирования base64.

Повторяющаяся последовательность элементов Length-Prefixed-Message передается в фреймах DATA

  • Length-Prefixed-Message → Compressed-Flag Message-Length Message
  • Compressed-Flag → 0 / 1 ; закодировано как 1 байт беззнакового целого числа
  • Message-Length → {длина Message} ; закодировано как 4 байта беззнакового целого числа (big endian)
  • Message → *{бинарный октет}

Значение Compressed-Flag равное 1 указывает на то, что последовательность бинарных октетов Message сжата с использованием механизма, объявленного в заголовке Message-Encoding. Значение 0 указывает на то, что кодирование байтов Message не происходило. Контексты сжатия НЕ сохраняются между границами сообщений, реализации должны создавать новый контекст для каждого сообщения в потоке. Если заголовок Message-Encoding опущен, то значение Compressed-Flag должно быть 0.

Для запросов конец потока (EOS) указывается наличием флага END_STREAM на последнем полученном фрейме DATA. В сценариях, где поток запроса нужно закрыть, но данных для отправки больше нет, реализации ДОЛЖНЫ отправить пустой фрейм DATA с этим флагом установленным.

Ответы

  • Ответ → (Response-Headers *Length-Prefixed-Message Trailers) / Trailers-Only
  • Response-Headers → HTTP-Status [Message-Encoding] [Message-Accept-Encoding] Content-Type *Custom-Metadata
  • Trailers-Only → HTTP-Status Content-Type Trailers
  • Trailers → Status [Status-Message] [Status-Details] *Custom-Metadata
  • HTTP-Status → ":status 200"
  • Status → "grpc-status" 1*DIGIT ; 0-9
  • Status-Message → "grpc-message" Percent-Encoded
  • Status-Details → "grpc-status-details-bin" {base64 encoded value} ; См. примечания ниже.
  • Percent-Encoded → 1*(Percent-Byte-Unencoded / Percent-Byte-Encoded)
  • Percent-Byte-Unencoded → 1*( %x20-%x24 / %x26-%x7E ) ; пробел и VCHAR, кроме %
  • Percent-Byte-Encoded → "%" 2HEXDIGIT ; 0-9 A-F

Response-Headers и Trailers-Only передаются в одном блоке фреймов HTTP2 HEADERS. Большинство ответов ожидается будут иметь как заголовки, так и трейлеры, но разрешены и только трейлеры для вызовов, которые вызывают немедленную ошибку. Статус должен быть отправлен в трейлерах даже если код статуса — OK.

Для ответов конец потока указывается наличием флага END_STREAM на последнем полученном фрейме HEADERS, который содержит трейлеры.

Реализации должны ожидать неработающие развертывания, которые отправляют HTTP статусы отличные от 200 в ответах, а также различные недопустимые типы контента и отсутствие Status и Status-Message. Реализации ДОЛЖНЫ синтезировать Status и Status-Message, чтобы передать их на уровень приложения при возникновении таких ситуаций.

Клиенты могут ограничивать размер Response-Headers, Trailers, и Trailers-Only, с рекомендуемым значением по умолчанию 8 КБ каждый.

Значение части Status — это десятичное целое число в виде ASCII строки без ведущих нулей.

Значение части Status-Message концептуально является строковым описанием ошибки Unicode, физически закодированной как UTF-8 с последующим процентным кодированием. Процентное кодирование определено в RFC 3986 §2.1, хотя форма, используемая здесь, имеет другие ограниченные символы. При декодировании недействительных значений реализации НЕ ДОЛЖНЫ выдавать ошибку или игнорировать сообщение. В худшем случае реализация может полностью прекратить декодирование статусного сообщения таким образом, что пользователь получит исходную форму процентного кодирования. Альтернативно, реализация может декодировать действительные части, оставляя сломанные %-кодировки без изменений или заменяя их символом замены (например, '?' или символ замены Unicode).

Status-Details допускается только если Status не равен OK. Если он установлен, он содержит дополнительную информацию об ошибке RPC. Если он содержит поле кода статуса, оно НЕ ДОЛЖНО противоречить заголовку Status. Потребитель ДОЛЖЕН проверить это требование.

Пример

Пример унарного вызова с показом последовательности фреймов HTTP2

Запрос

HEADERS (flags = END_HEADERS)
:method = POST
:scheme = http
:path = /google.pubsub.v2.PublisherService/CreateTopic
:authority = pubsub.googleapis.com
grpc-timeout = 1S
content-type = application/grpc+proto
grpc-encoding = gzip
authorization = Bearer y235.wef315yfh138vh31hv93hv8h3v

DATA (flags = END_STREAM)
<Length-Prefixed Message>

Ответ

HEADERS (flags = END_HEADERS)
:status = 200
grpc-encoding = gzip
content-type = application/grpc+proto

DATA
<Length-Prefixed Message>

HEADERS (flags = END_STREAM, END_HEADERS)
grpc-status = 0 # OK
trace-proto-bin = jher831yy13JHy3hc

User Agents

Хотя протокол не требует user-agent для работы, рекомендуется, чтобы клиенты предоставляли структурированную строку user-agent, которая дает базовое описание библиотеки вызова, версии и платформы для облегчения диагностики проблем в гетерогенных средах. Следующая структура рекомендуется разработчикам библиотек:

User-Agent → "grpc-" Language ?("-" Variant) "/" Version ?( " ("  *(AdditionalProperty ";") ")" )

Например:

grpc-java/1.2.3
grpc-ruby/1.2.3
grpc-ruby-jruby/1.3.4
grpc-java-android/0.9.1 (gingerbread/1.2.4; nexus5; tmobile)

Идемпотентность и повторные попытки

Если явно не указано иное, gRPC вызовы не считаются идемпотентными. Конкретно:

  • Вызовы, которые нельзя доказательно считать начавшимися, не будут повторяться.
  • Механизм подавления дубликатов отсутствует, так как он не требуется.
  • Вызовы, помеченные как идемпотентные, могут быть отправлены несколько раз.

Маппинг транспорта HTTP2

Идентификация потока

Все вызовы gRPC должны указывать внутренний ID. Мы будем использовать HTTP2 stream-id как идентификаторы вызовов в этой схеме. ПРИМЕЧАНИЕ: Эти ID контекстуальны открытой сессии HTTP2 и не будут уникальными в рамках процесса, обрабатывающего более одной сессии HTTP2; их также нельзя использовать как GUIDs.

Фреймы данных

Границы фреймов DATA не связаны с границами Length-Prefixed-Message, и реализации не должны делать предположений о их совпадении.

Ошибки

Когда во время RPC происходит ошибка приложения или среды выполнения, статус и сообщение статуса передаются в трейлерах.

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

Применяется следующее соответствие кодов ошибок RST_STREAM кодам ошибок gRPC.

HTTP2 Code GRPC Code
NO_ERROR(0) INTERNAL - Явный статус GRPC OK должен был быть отправлен, но это может использоваться для агрессивного lameduck в некоторых сценариях.
PROTOCOL_ERROR(1) INTERNAL
INTERNAL_ERROR(2) INTERNAL
FLOW_CONTROL_ERROR(3) INTERNAL
SETTINGS_TIMEOUT(4) INTERNAL
STREAM_CLOSED Нет соответствия, так как нет открытого потока для передачи. Реализации должны записывать.
FRAME_SIZE_ERROR INTERNAL
REFUSED_STREAM UNAVAILABLE - Указывает на то, что никакой обработки не произошло и запрос можно повторить, возможно в другом месте.
CANCEL(8) Отображается как отмена вызова при отправке клиентом. Отображается как CANCELLED при отправке сервером. Обратите внимание, что серверы должны использовать этот механизм только тогда, когда им нужно отменить вызов, но последовательность байтов полезной нагрузки неполная.
COMPRESSION_ERROR INTERNAL
CONNECT_ERROR INTERNAL
ENHANCE_YOUR_CALM RESOURCE_EXHAUSTED ...с дополнительными деталями ошибки от среды выполнения для указания на то, что исчерпан ресурс — пропускная способность.
INADEQUATE_SECURITY PERMISSION_DENIED … с дополнительными деталями для указания на то, что доступ был отклонен из-за недостаточной безопасности протокола для вызова.
Безопасность

Спецификация HTTP2 требует использования TLS 1.2 или выше при использовании TLS с HTTP2. Она также накладывает некоторые дополнительные ограничения на допустимые шифры в развертываниях для избежания известных проблем, а также требует поддержки SNI. Также ожидается использование HTTP2 в сочетании с проприетарными механизмами транспортной безопасности о которых спецификация не может сделать осмысленных рекомендаций.

Управление соединением
Фрейм GOAWAY

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

Серверы должны отправлять GOAWAY перед завершением соединения для надежного информирования клиентов о том, какая работа была принята сервером и выполняется.

Фрейм PING

И клиенты, и серверы могут отправлять фрейм PING, на который собеседник должен ответить точным эхом того, что получил. Это используется для утверждения того, что соединение все еще активно, а также для предоставления средства оценки конечной задержки. Если серверный PING не получает ответа в пределах времени ожидания среды выполнения все активные вызовы на сервере будут закрыты со статусом CANCELLED. Истекший клиентский PING приведет к закрытию всех вызовов со статусом UNAVAILABLE. Обратите внимание, что частота PING сильно зависит от сетевой среды; реализации свободны регулировать частоту PING на основе сетевых и прикладных требований.

Сбой соединения

Если на клиенте обнаруживается сбой соединения все вызовы будут закрыты со статусом UNAVAILABLE. Для серверов открытые вызовы будут закрыты со статусом CANCELLED.

Приложение A - gRPC для Protobuf

Служебные интерфейсы, объявленные protobuf легко мапятся на gRPC с помощью расширений генерации кода для protoc. Следующее определяет маппинг для использования.

  • Service-Name → ?( {proto package name} "." ) {service name}
  • Message-Type → {полностью квалифицированное имя proto сообщения}
  • Content-Type → "application/grpc+proto"
  • Status-Details → {google.rpc.Status proto сообщение}

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/mirrors-grpc-framework.git
git@api.gitlife.ru:oschina-mirror/mirrors-grpc-framework.git
oschina-mirror
mirrors-grpc-framework
mirrors-grpc-framework
master