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

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

Сжатие с использованием gzip

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

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

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

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

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

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

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

Более подробная информация о подаче частичных запросов представлена ​​в следующих разделах.

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

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

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

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

Пример

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

Простой запрос: В этом 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: { ... } , не включайте " data " в спецификацию fields . Включение объекта данных со спецификацией полей, например, data/a/b приводит к ошибке. Вместо этого используйте спецификацию fields , например, a/b .

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

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

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

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

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

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

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

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

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

При возврате вложенного поля ответ включает в себя окружающие его родительские объекты. Родительские поля не включают в себя другие дочерние поля, если они также не выбраны явно.
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.

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

Пример

В этом примере показан простой запрос на изменение только заголовка общего (вымышленного) ресурса API "Demo". Ресурс также содержит комментарий, набор характеристик, статус и множество других полей, но этот запрос отправляет только поле 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 частичного ответа в сочетании с параметром patch позволяет еще больше повысить эффективность запросов на обновление. Запрос patch уменьшает только размер запроса. Частичный ответ уменьшает размер ответа. Поэтому, чтобы уменьшить объем данных, передаваемых в обоих направлениях, используйте запрос patch с параметром fields .

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

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

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

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

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

Полезно начать с получения частичного ответа с данными, которые вы хотите изменить. Это особенно важно для ресурсов, использующих ETags, поскольку для успешного обновления ресурса необходимо указать текущее значение ETag в заголовке HTTP If-Match . После получения данных вы можете изменить нужные значения и отправить измененное частичное представление обратно с запросом на исправление. Вот пример, предполагающий, что ресурс Demo использует 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 использует ETags, для успешного обновления ресурса необходимо отправить предыдущее значение ETag вместе с запросом.

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

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

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 а также полное описание измененного ресурса. Если API использует ETags, сервер обновляет значения ETags после успешной обработки запроса на внесение изменений, как и в случае с PUT .

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

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

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

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

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

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

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

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