Перейти к содержимому

Что такое структурированный протокол в python

  • автор:

Protocols (протоколы)

Понятие протокол введено для обозначения частичного или полного подчинения объектов тому или иному договору (интерфейсу).

В утиной типизации Python любой объект, вне зависимости от типа, может объявить о своём следовании договору путём поддержки протокола. Обычно поддержка протокола заявляется путём реализации одного или нескольких методов, характерных для него.

Синонимы поиска: Protocols (протоколы)

Статьи раздела
Rich Comparisons («богатые» сравнения) «Богатые» сравнения позволяют существенно обогатить/расширить возможн…
Протокол дескриптора Протокол регламентирует работу дескриптора.
Протокол доступа к атрибутам Протокол регламентирует методы обращения к атрибутам.
Протокол итератора Протокол регламентирует методы, описывающие итераторы.
Протокол консервирования Протокол регламентирует работу консервирования.
Протокол копирования Протокол регламентирует работу функций копирования объектов.
Протокол менеджера контекста Протокол регламентирует методы менеджеров контекста.
Протокол последовательности Протокол регламентирует работу с последовательностями.

[Из песочницы] Структурированный протокол обмена данных Protobuf или JSON во фронтенде? 25.02.2020 23:53

В новом проекте в нашей команде мы выбрали frontend framework VUE для нового продукта, бэкенд написан на PHP, и уже как 17 лет успешно работает.

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

Про бэкенд

Проект достаточно большой, и функционал очень замороченный, следовательно код написанный на DDD имел определенные структуры данных, они были сложные и объемные для некой универсальности в проекте в целом.

Про фронтенд

4 мес. разработки фронта мы использовать JSON в качестве ответа от сервера, мапили в State Vuex в удобном нам формате. Но для отдачи на сервер нам требовалось преобразовывать в обратную сторону, чтобы сервер смог прочитать и замапить свои DTO объекты (может показаться странным, но так надо :))

Проблемы

Вроде бы ничего, работали с тем что есть, состояние разрасталось до объектов больших размеров. Начали разбивать на еще меньшие модули в каждом из которых были свои состояния, мутации и т.п… API стало меняться вслед новым задачам от менеджеров, и все сложнее стало управлять всем этим, то там замапили не так, то поля изменились…

И тут мы начали думать об универсальных структурах данных на сервере и фронте чтобы исключить ошибки в парсингах, мапингах и т.п.

После некоторых поисков, мы пришли к двум вариантам:

  1. protocol-buffers
  2. Автогенерация JS DTO на стороне сервера для фронта, с дальнейшей обработкой JSON в эти DTO.

После пробы пера, было принято использовать Protobuf от google.

  1. Уже есть функционал который компилирует описанные структуры для множества платформ, в том числе и для PHP и для JS.
  2. Есть автогенератор документации по созданным структурам .proto
  3. Можно легко прикрутить некую версионность для структур.
  4. Облегчается поиск объектов при рефакторинге как на PHP так и на JS.
  5. И другие фишки как gRPC и т.п если потребуется.
Хватит болтовни, давайте посмотрим как все это выглядит

Как это выглядит на стороне PHP я не буду описывать, там примерно все тоже самое, объекты те же.

Покажу на примере простого клиентского JS и мини сервера на Node.js.

Для начала описываем структуры данных которые нам потребуется. Дока.

product.proto

syntax = "proto3"; package api; import "price.proto"; message Product < message Id < uint32 value = 1; >Id string name = 2; string text = 3; string url = 4; Price price = 5; > 
syntax = "proto3"; package api; message Price

service.proto

syntax = "proto3"; package api; import "product.proto"; service ApiService

Поясню немного про сервис, зачем он нужен, если даже не используется. Сервис описывается только ради документации в нашем случае, что принимает и что отдает, чтобы мы могли подставлять нужные объекты. Он нужен только для gRPC.

Далее скачивается генератор кода на основании структур.

И запускается команда генерации под JS.

./protoc --proto_path=/Users/user/dev/habr_protobuf/public/proto --js_out=import_style=commonjs,binary:/Users/user/dev/habr_protobuf/src/proto/ /Users/user/dev/habr_protobuf/public/proto/*.proto

Подробнее в доке.

После генерации появляется 3 JS файла, в которых уже все приведено к объектам, с функционалом сериализации в буфер и десериализации из буфера.

Далее описываем уже JS код.

import < Product >from '../proto/product_pb'; // В структуре мы описали что ждем от клиента Product.Id const instance = new Product.Id().setValue(12345); let message = instance.serializeBinary(); let response = await fetch('http://localhost:3008/api/getById', < method: 'POST', body: message >); let result = await response.arrayBuffer(); // Ждем от сервера объект Product, его и подставляем, он сам все сделает, // распарсит в нужные структуры и сабструктуры. const data = Product.deserializeBinary(result); console.log(data.toObject()); 

В принципе клиент готов.

На сервере заюзаем Express

const express = require('express'); const cors = require('cors'); const app = express(); app.use(cors()); // подключаем все нужные объекты для их заполнения и отправки на клиент. const Product = require('./src/proto/product_pb').Product; const Price = require('./src/proto/price_pb').Price; // обрабатываем буфер с клиента, т.к данные обмениваются только через него. app.use (function(req, res, next) < let data = []; req.on('data', function(chunk) < data.push(chunk); >); req.on('end', function() < if (data.length ) >); app.post('/api/getById', function (req, res) < // От клиента мы ожидаем Product.Id, его и подставляем const prId = Product.Id.deserializeBinary(req.raw); const // Далее куча "бизнес логики" и мы красивыми объектами оформляем ответ const product = new Product(); product.setId(new Product.Id().setValue(id)); product.setName('Sony PSP'); product.setUrl('http://mysite.ru/product/psp/'); const price = new Price(); price.setValue(35500.00); price.setTax(20); product.setPrice(price); // и отправляем обратно клиенту сериализованный объект Product res.send(Buffer.from(product.serializeBinary())); >); app.listen(3008, function () < console.log('Example app listening on port 3008!'); >); 
Что мы имеем в итоге
  1. Единая точка правды в виде сгенерированных объектов на основании структур, которые описываются один раз для множества платформ.
  2. Нет путаницы, есть понятная документация как в виде автосгенерированного HTML так и просто просмотра .proto файлов.
  3. Везде ведется работа с конкретными сущностями, без своих модификаций и т.п (а все мы знаем что фронтенд любит отсебятину :))
  4. Очень удобная работа данным протоколом обмениваться по веб сокетам.

Есть конечно небольшой минус, это скорость сериалиации и десериализации, вот пример.

Я взял lorem ipsum на 10 абзацев, получилось 5.5 кб данных с учетом заполненных объектов Price, Product. И погонял данные по Protobuf и JSON (все тоже самое только заполненные JSON схемы, вместо Protobuf объектов)

без учета самих запросов Protobuf parsing client 2.804999ms 1.8150000ms 0.744999ms server 1.993ms 0.495ms 0.412ms JSON client 0.654999ms 0.770000ms 0.819999ms server 0.441ms 0.307ms 0.242ms 

Всем спасибо за внимание:)

Протокол Buffers основные вещи, которые вы должны знать

Protocol Buffers – это двоичный протокол, разработанный в Google и сделанный общедоступным. Первой общедоступной версией была Protocol Buffers version 2. Самая последняя реализация на момент написания этой статьи – Protocol Buffers version 3. Версия 1 никогда не была общедоступной.

Буферы протокола – это независимый от языка, платформенно-независимый, расширяемый механизм Google для сериализации структурированных данных. Буферы протокола в настоящее время поддерживают сгенерированный код в Java, Python, Objective-C, C ++, C #, JS и других. Вы определяете, как вы хотите, чтобы ваши данные были структурированы один раз, затем вы можете использовать специальный сгенерированный исходный код, чтобы легко записывать и считывать ваши структурированные данные в различные потоки данных и из них, используя различные языки.

Причины смотреть на буфер протокола

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

Размер данных

Тот факт, что Protobuf является двоичным протоколом, дает ему очень хорошие характеристики. По умолчанию его пропускная способность намного выше по сравнению с текстовыми протоколами, например, JSON и XML. Это не должно быть сюрпризом. Однако, например, разница в пропускной способности между буферами протокола и JSON может быть уменьшена путем сжатия данных при отправке с использованием текстовых протоколов, и можно использовать некоторые другие приемы. Если это будет сделано, буферные протоколы будут по-прежнему лучше пропускной способности, но не с таким большим запасом, поэтому я советую всегда делать это, если вы используете текстовые форматы для передачи данных.

Загрузка процессора

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

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

использование

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

В случае протокольных буферов все начинается со схемы или, если быть более точным, прото-файл. Если этот файл протока используется как часть gRPC, то есть плагины maven и gradle для автоматической генерации классов и тому подобное для нас. В случае, если буферы протокола будут использоваться в качестве автономных, мы можем сгенерировать все классы и аналогичные вручную, используя компилятор буферов протокола.

Начальный файл прото

Давайте посмотрим на простой файл прото

Как создать и использовать протоколы в Python

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

Python snake interacting with a Python script.

Алексей Кодов
Автор статьи
23 июня 2023 в 18:49

Протоколы в Python являются основой для создания гибкого и расширяемого кода. В этой статье мы рассмотрим, что такое протоколы, как их создать и использовать в вашем коде.

Что такое протоколы?

Протоколы, также известные как интерфейсы, определяют ожидаемое поведение объекта. Они представляют собой набор методов, которые должны быть реализованы классом или объектом. В Python протоколы определены в модуле typing , и их можно создать с помощью класса Protocol .

Создание протокола

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

from typing import Protocol class MyProtocol(Protocol): def my_method(self, arg: int) -> str: pass

Использование протоколов

Протоколы могут быть использованы для определения типов в аннотациях функций и методов. Это позволяет гарантировать, что переданные объекты соответствуют определенному интерфейсу.

def my_function(obj: MyProtocol) -> None: result = obj.my_method(42) print(result)

Также протоколы могут быть использованы для проверки соответствия типов с помощью функции issubclass или isinstance .

class MyClass: def my_method(self, arg: int) -> str: return f"Hello, !" # Проверяем, что класс реализует протокол print(issubclass(MyClass, MyProtocol)) # True

Python-разработчик: новая работа через 9 месяцев
Получится, даже если у вас нет опыта в IT

Пример использования протоколов

Рассмотрим следующий пример. Мы создадим протокол Drawable , который определяет метод draw() . Затем создадим классы Circle и Rectangle , реализующие этот протокол.

from typing import Protocol class Drawable(Protocol): def draw(self) -> None: pass class Circle: def draw(self) -> None: print("Drawing a circle") class Rectangle: def draw(self) -> None: print("Drawing a rectangle") def draw_shape(shape: Drawable) -> None: shape.draw() circle = Circle() rectangle = Rectangle() draw_shape(circle) # Drawing a circle draw_shape(rectangle) # Drawing a rectangle

В этом примере функция draw_shape принимает объект типа Drawable и вызывает его метод draw() . Это позволяет гарантировать, что переданный объект реализует нужный интерфейс, и мы можем безопасно вызывать метод draw() .

Заключение

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

Не забывайте применять протоколы в своих проектах, и удачи вам в изучении Python! ��

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *