Внедрение инфраструктуры RPC с нуля (ноль)

Go

предисловие

задний план

Недавно я решил начать изучать язык го, но из-за отсутствия сценариев практического применения обучение всегда оставалось на уровне hello world, а учебники и материалы, которые я прочитал, не очень впечатляют. Поэтому я решил начать с реализации rpc, поставляемой вместе с go, и узнать, как язык go используется в реальных сценариях, включая обработку исключений, прокси и фильтрацию, использование подпрограммы go и т. д., а также кратко понять, как работает go. другие языковые реализации rpc, такие как thrift и grpc и т. д. Через некоторое время мое впечатление немного углубилось, и я начал медленно осознавать различия и сходства между языком go и языком java. Затем, для дальнейшего закрепления эффекта обучения, а также для обзора и отчета о моей карьере, я решил использовать язык go для создания относительно полной инфраструктуры RPC (или микросервиса) с нуля.

Фреймворк микросервисов и фреймворк RPC

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

исследовательская работа

Прежде чем начать это делать, вам нужно узнать о других существующих продуктах, и вы можете учиться на отличном опыте и методах.Вот несколько фреймворков, которые вы изучили изначально:

  • grpcПлатформа микросервисов, запущенная Google, поддерживает 10 языков и поддерживает двустороннюю потоковую связь на основе http2.
  • go-microПлатформа микросервисов с открытым исходным кодом, уникально поддерживающая асинхронный обмен сообщениями, функцию подпубликации, такую ​​​​как mq.
  • thrift-goThrift — RPC-фреймворк, подаренный Facebook компании apache (не включает в себя функции, связанные с управлением услугами). Согласно официальным документам, Thrift поддерживает RPC-вызовы на 20 языках.
  • rpcxrpcx — это микросервисная структура, разработанная китайцами с открытым исходным кодом.Особенности рекламы — «быстрая, простая в использовании, но мощная». Во введении на официальном сайте упоминается, что производительность в два раза выше, чем у grpc. Вот авторская (должна быть)блог

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

Pluggable Interfaces

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

анализ спроса

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

  • Поддерживает вызовы RPC, включая синхронные и асинхронные вызовы.
  • Связанные функции для поддержки управления услугами, в том числе:
    • Регистрация и обнаружение службы
    • Балансировка нагрузки службы
    • Ограничение тока и предохранители
    • Аутентификация
    • Мониторинг и отслеживание ссылок
    • Проверки работоспособности, включая сквозные контрольные сигналы и проверки реестра экземпляров службы.
  • Поддержка подключаемых модулей.Для функций с несколькими реализациями (таких как балансировка нагрузки) реализация должна предоставляться в виде подключаемых модулей, а пользовательские подключаемые модули должны поддерживаться. Что касается нефункциональных требований, таких как более высокая производительность и достаточная стабильность, мы пока не будем на них сосредотачиваться.

Системный дизайн

Слоистый

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

  • service — это пользовательский интерфейс, такой как инициализация и запуск экземпляров клиента и сервера и т. д.
  • клиент и сервер представляют экземпляры клиента и сервера, которые отвечают за отправку запросов и возврат ответов.
  • selector означает балансировку нагрузки, или loadbanlancer, который отвечает за решение, на какой сервер отправить запрос
  • registery означает центр регистрации.После того, как сервер инициализирован или даже запущен, ему необходимо зарегистрировать свою информацию в центре регистрации, чтобы клиент мог найти нужный сервер из центра регистрации.
  • кодек означает кодирование и декодирование, то есть преобразование объектов и двоичных данных друг в друга
  • протокол представляет собой протокол связи, то есть то, как составлены двоичные данные.Многие функции в структуре RPC требуют поддержки уровня протокола
  • транспортное средство связи, оно отвечает за конкретную сетевую связь, отправляет двоичные данные, собранные в соответствии с протоколом, через сеть и считывает данные из сети в соответствии с методом, указанным протоколом.

Упомянутые выше слои, кроме сервисных, на самом деле могут предоставлять несколько реализаций, поэтому все они должны быть реализованы в виде плагинов.

Таким образом, в соответствии с уровнем, который мы разделяем, процесс клиента от отправки запроса до получения ответа, вероятно, выглядит следующим образом:

Логика сервера аналогична, поэтому рисовать здесь не буду.

цепочка фильтров

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

протокол сообщения

Затем разрабатывается специальный протокол сообщений.Так называемый протокол сообщений, вероятно, представляет собой соглашение между двумя компьютерами для связи друг с другом. Например, протокол TCP определяет определенный формат пакета данных TCP.Например, первые 2 байта представляют исходный порт, 3-й и 4-й байты представляют порт назначения, за которыми следуют порядковый номер и порядковый номер подтверждения, и так далее. на. В нашей структуре RPC нам также необходимо определить собственный протокол. Вообще говоря, сетевые протоколы делятся на головную и основную части: головная часть — это некоторые метаданные, т. е. данные, требуемые самим протоколом, а тело — это данные, передаваемые с верхнего уровня, которые нужно передать только в неизменном виде.

Далее мы пытаемся определить наш собственный протокол:

-------------------------------------------------------------------------------------------------
|2byte|1byte  |4byte       |4byte        | header length |(total length - header length - 4byte)|
-------------------------------------------------------------------------------------------------
|magic|version|total length|header length|     header    |                    body              |
-------------------------------------------------------------------------------------------------

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

  • Начните с магического числа в два байта, чтобы мы могли быстро идентифицировать незаконные запросы.
  • Байт указывает версию протокола, которая в настоящее время всегда может быть установлена ​​на 0.
  • 4 байта указывают общую длину оставшейся части тела сообщения (общая длина)
  • 4 байта указывают длину заголовка сообщения (длина заголовка)
  • Заголовок сообщения (header), длина которого определяется по ранее проанализированной длине (header length)
  • Тело сообщения (тело), ​​длина которого равна общей длине проанализированного перед минусом длины заголовка сообщения (общая длина — 4 — длина заголовка)

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

type Header struct {
        Seq uint64 //序号, 用来唯一标识请求或响应
        MessageType byte //消息类型,用来标识一个消息是请求还是响应
        CompressType byte //压缩类型,用来标识一个消息的压缩方式
        SerializeType byte //序列化类型,用来标识消息体采用的编码方式
        StatusCode byte //状态类型,用来标识一个请求是正常还是异常
        ServiceName string //服务名
        MethodName string  //方法名
        Error string //方法调用发生的异常
        MetaData map[string]string //其他元数据
}

Эпилог

На этом первая статья подходит к концу, главное подготовиться и разобраться в идеях, если есть какие-то неправильные или необоснованные моменты, дайте еще совет.