Советы по производительности

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

Сжатие с помощью gzip

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

Чтобы получить ответ в кодировке gzip, вы должны сделать две вещи: установить заголовок Accept-Encoding и изменить свой пользовательский агент, чтобы он содержал строку gzip . Вот пример правильно сформированных заголовков HTTP для включения сжатия gzip:

Accept-Encoding: gzip
User-Agent: my program (gzip)

Работа с частичными ресурсами

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

Существует два типа частичных запросов:

  • Частичный ответ : запрос, в котором вы указываете, какие поля включить в ответ (используйте параметр запроса fields ).
  • Patch : запрос на обновление, в котором вы отправляете только те поля, которые хотите изменить (используйте HTTP-глагол PATCH ).

Дополнительные сведения о выполнении частичных запросов приведены в следующих разделах.

Частичный ответ

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

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

Обратите внимание, что параметр fields влияет только на данные ответа; это не влияет на данные, которые вам нужно отправить, если они есть. Чтобы уменьшить объем данных, которые вы отправляете при изменении ресурсов, используйте запрос на исправление .

Пример

В следующем примере показано использование параметра fields с универсальным (вымышленным) «демонстрационным» API.

Простой запрос: этот HTTP- GET пропускает параметр fields и возвращает полный ресурс.

https://www.googleapis.com/demo/v1

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

{
  "kind": "demo",
  ...
  "items": [
  {
    "title": "First title",
    "comment": "First comment.",
    "characteristics": {
      "length": "short",
      "accuracy": "high",
      "followers": ["Jo", "Will"],
    },
    "status": "active",
    ...
  },
  {
    "title": "Second title",
    "comment": "Second comment.",
    "characteristics": {
      "length": "long",
      "accuracy": "medium"
      "followers": [ ],
    },
    "status": "pending",
    ...
  },
  ...
  ]
}

Запрос на частичный ответ: следующий запрос для того же ресурса использует параметр fields , чтобы значительно уменьшить объем возвращаемых данных.

https://www.googleapis.com/demo/v1?fields=kind,items(title,characteristics/length)

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

200 OK
{
  "kind": "demo",
  "items": [{
    "title": "First title",
    "characteristics": {
      "length": "short"
    }
  }, {
    "title": "Second title",
    "characteristics": {
      "length": "long"
    }
  },
  ...
  ]
}

Обратите внимание, что ответ представляет собой объект JSON, который включает только выбранные поля и окружающие их родительские объекты.

Подробная информация о том, как отформатировать параметр fields , описана далее, а затем более подробная информация о том, что именно возвращается в ответе.

Краткое описание синтаксиса параметров полей

Формат значения параметра запроса fields в общих чертах основан на синтаксисе XPath. Поддерживаемый синтаксис приведен ниже, а дополнительные примеры приведены в следующем разделе.

  • Используйте список, разделенный запятыми, чтобы выбрать несколько полей.
  • Используйте a/b , чтобы выбрать поле b , вложенное в поле a ; используйте a/b/c , чтобы выбрать поле c , вложенное в b .

    Исключение: для ответов API, которые используют оболочки «данных», где ответ вложен в объект data , который выглядит как data: { ... } , не включайте « data » в спецификацию fields . Включение объекта данных со спецификацией полей, такой как data/a/b , вызывает ошибку. Вместо этого просто используйте спецификацию fields , например a/b .

  • Используйте подселектор, чтобы запросить набор определенных подполей массивов или объектов, поместив выражения в круглые скобки " ( ) ".

    Например: fields=items(id,author/email) возвращает только идентификатор элемента и адрес электронной почты автора для каждого элемента в массиве элементов. Вы также можете указать одно подполе, где fields=items(id) эквивалентно fields=items/id .

  • При необходимости используйте подстановочные знаки в выборе полей.

    Например: fields=items/pagemap/* выбирает все объекты в карте страницы.

Дополнительные примеры использования параметра fields

Примеры ниже включают описание того, как значение параметра fields влияет на ответ.

Примечание. Как и все значения параметра запроса, значение параметра fields должно быть закодировано в URL-адресе. Для лучшей удобочитаемости в примерах в этом документе кодировка не указана.

Определите поля, которые вы хотите вернуть, или выберите поля .
Значение параметра запроса fields представляет собой список полей, разделенных запятыми, и каждое поле указывается относительно корня ответа. Таким образом, если вы выполняете операцию со списком , ответ представляет собой набор и обычно включает в себя массив ресурсов. Если вы выполняете операцию, которая возвращает один ресурс, поля указываются относительно этого ресурса. Если выбранное поле является массивом (или его частью), сервер возвращает выбранную часть всех элементов массива.

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

Всякий раз, когда возвращается вложенное поле, ответ включает в себя вложенные родительские объекты. Родительские поля не включают никаких других дочерних полей, если только они не выбраны явно.
context/facets/label Возвращает только поле label для всех элементов массива facets , которое само вложено в объект context .
items/pagemap/*/title Для каждого элемента в массиве items возвращает только поле title (если есть) всех объектов, которые являются дочерними элементами pagemap .

Вот несколько примеров на уровне ресурсов:
Примеры Эффект
title Возвращает поле title запрошенного ресурса.
author/uri Возвращает uri объекта author в запрошенном ресурсе.
links/*/href
Возвращает поле href всех объектов, являющихся потомками links .
Запрашивайте только части определенных полей, используя подвыборки .
По умолчанию, если в вашем запросе указаны определенные поля, сервер возвращает объекты или элементы массива целиком. Вы можете указать ответ, который включает только определенные подполя. Вы делаете это, используя синтаксис подвыбора " ( ) ", как в примере ниже.
Пример Эффект
items(title,author/uri) Возвращает только значения title и uri автора для каждого элемента в массиве items.

Обработка частичных ответов

После обработки допустимого запроса, включающего параметр запроса fields , сервер отправляет обратно код состояния HTTP 200 OK вместе с запрошенными данными. Если параметр запроса fields имеет ошибку или недействителен по иным причинам, сервер возвращает код состояния HTTP 400 Bad Request вместе с сообщением об ошибке, сообщающим пользователю, что было не так с выбором полей (например, "Invalid field selection a/b" ).

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

https://www.googleapis.com/demo/v1?fields=kind,items(title,characteristics/length)

Частичный ответ выглядит так:

200 OK
{
  "kind": "demo",
  "items": [{
    "title": "First title",
    "characteristics": {
      "length": "short"
    }
  }, {
    "title": "Second title",
    "characteristics": {
      "length": "long"
    }
  },
  ...
  ]
}

Примечание. Для API, которые поддерживают параметры запроса для разбиения данных на страницы (например, maxResults и nextPageToken ), используйте эти параметры, чтобы уменьшить результаты каждого запроса до управляемого размера. В противном случае выигрыш в производительности, возможный при частичном ответе, может быть не реализован.

Патч (частичное обновление)

Вы также можете избежать отправки ненужных данных при изменении ресурсов. Чтобы отправлять обновленные данные только для определенных полей, которые вы изменяете, используйте команду HTTP PATCH . Семантика исправления, описанная в этом документе, отличается (и проще) от предыдущей реализации частичного обновления GData.

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

Пример

В этом примере показан простой запрос на исправление для обновления только заголовка универсального (вымышленного) ресурса "Demo" API. У ресурса также есть комментарий, набор характеристик, статус и многие другие поля, но этот запрос отправляет только поле title , так как это единственное поле, которое модифицируется:

PATCH https://www.googleapis.com/demo/v1/324
Authorization: Bearer your_auth_token
Content-Type: application/json

{
  "title": "New title"
}

Ответ:

200 OK
{
  "title": "New title",
  "comment": "First comment.",
  "characteristics": {
    "length": "short",
    "accuracy": "high",
    "followers": ["Jo", "Will"],
  },
  "status": "active",
  ...
}

Сервер возвращает код состояния 200 OK вместе с полным представлением обновленного ресурса. Поскольку в запрос на исправление было включено только поле title , это единственное значение, которое отличается от предыдущего.

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

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

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

  • Добавить: чтобы добавить несуществующее поле, укажите новое поле и его значение.
  • Изменить: Чтобы изменить значение существующего поля, укажите поле и установите для него новое значение.
  • Удалить: чтобы удалить поле, укажите поле и установите для него значение null . Например, "comment": null . Вы также можете удалить весь объект (если он является изменяемым), установив для него значение null . Если вы используете клиентскую библиотеку API Java , вместо этого используйте Data.NULL_STRING ; подробности см. в разделе JSON null .

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

Использование исправления в цикле чтения-изменения-записи

Полезно начать с получения частичного ответа с данными, которые вы хотите изменить. Это особенно важно для ресурсов, использующих ETag, поскольку для успешного обновления ресурса необходимо указать текущее значение ETag в HTTP-заголовке If-Match . После получения данных вы можете изменить значения, которые хотите изменить, и отправить измененное частичное представление обратно с запросом исправления. Вот пример, в котором предполагается, что демо-ресурс использует ETags:

GET https://www.googleapis.com/demo/v1/324?fields=etag,title,comment,characteristics
Authorization: Bearer your_auth_token

Это частичный ответ:

200 OK
{
  "etag": "ETagString"
  "title": "New title"
  "comment": "First comment.",
  "characteristics": {
    "length": "short",
    "level": "5",
    "followers": ["Jo", "Will"],
  }
}

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

PATCH https://www.googleapis.com/demo/v1/324?fields=etag,title,comment,characteristics
Authorization: Bearer your_auth_token
Content-Type: application/json
If-Match: "ETagString"
{
  "etag": "ETagString"
  "title": "",                  /* Clear the value of the title by setting it to the empty string. */
  "comment": null,              /* Delete the comment by replacing its value with null. */
  "characteristics": {
    "length": "short",
    "level": "10",              /* Modify the level value. */
    "followers": ["Jo", "Liz"], /* Replace the followers array to delete Will and add Liz. */
    "accuracy": "high"          /* Add a new characteristic. */
  },
}

Сервер отвечает кодом состояния HTTP 200 OK и частичным представлением обновленного ресурса:

200 OK
{
  "etag": "newETagString"
  "title": "",                 /* Title is cleared; deleted comment field is missing. */
  "characteristics": {
    "length": "short",
    "level": "10",             /* Value is updated.*/
    "followers": ["Jo" "Liz"], /* New follower Liz is present; deleted Will is missing. */
    "accuracy": "high"         /* New characteristic is present. */
  }
}

Создание запроса на исправление напрямую

Для некоторых запросов на исправление вам необходимо основывать их на данных, которые вы получили ранее. Например, если вы хотите добавить элемент в массив и не хотите потерять ни один из существующих элементов массива, вы должны сначала получить существующие данные. Точно так же, если API использует ETag, вам необходимо отправить предыдущее значение ETag с вашим запросом, чтобы успешно обновить ресурс.

Примечание. Вы можете использовать HTTP-заголовок "If-Match: *" , чтобы принудительно выполнить исправление при использовании ETag. Если вы сделаете это, вам не нужно читать перед записью.

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

PATCH https://www.googleapis.com/demo/v1/324?fields=comment,characteristics
Authorization: Bearer your_auth_token
Content-Type: application/json

{
  "comment": "A new comment",
  "characteristics": {
    "volume": "loud",
    "accuracy": null
  }
}

С этим запросом, если поле комментария имеет существующее значение, новое значение перезаписывает его; в противном случае устанавливается новое значение. Аналогично, если была характеристика объема, ее значение перезаписывается; если нет, то он создается. Поле точности, если установлено, удаляется.

Обработка ответа на патч

После обработки допустимого запроса на исправление API возвращает код ответа HTTP 200 OK вместе с полным представлением измененного ресурса. Если ETag используются API, сервер обновляет значения ETag при успешной обработке запроса на исправление, как это происходит с PUT .

Запрос исправления возвращает полное представление ресурса, если только вы не используете параметр fields для уменьшения объема возвращаемых данных.

Если запрос исправления приводит к новому состоянию ресурса, которое синтаксически или семантически недопустимо, сервер возвращает код состояния HTTP 400 Bad Request или 422 Unprocessable Entity , а состояние ресурса остается неизменным. Например, если вы попытаетесь удалить значение обязательного поля, сервер вернет ошибку.

Альтернативная запись, когда команда PATCH HTTP не поддерживается

Если ваш брандмауэр не разрешает HTTP-запросы PATCH , выполните HTTP-запрос POST и установите для заголовка переопределения значение PATCH , как показано ниже:

POST https://www.googleapis.com/...
X-HTTP-Method-Override: PATCH
...

Разница между патчем и обновлением

На практике, когда вы отправляете данные для запроса на обновление, использующего HTTP-команду PUT , вам нужно отправить только те поля, которые являются обязательными или необязательными; если вы отправляете значения для полей, установленных сервером, они игнорируются. Хотя это может показаться еще одним способом частичного обновления, этот подход имеет некоторые ограничения. При обновлениях, использующих HTTP-команду PUT , запрос завершается ошибкой, если вы не укажете обязательные параметры, и очищает ранее установленные данные, если вы не укажете необязательные параметры.

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