إرسال طلبات مجمّعة

يعرض هذا المستند كيفية تجميع طلبات البيانات من واجهة برمجة التطبيقات معًا لتقليل عدد الاتصالات التي يجب أن يجريها العميل.

يدور هذا المستند تحديدًا حول إجراء طلب مجمّع باستخدام مكتبة برامج Java. يتوفر مثال أساسي أيضًا في مكتبة برامج Google API لنظام .NET يستخدم النظام المجمّع لواجهة برمجة التطبيقات Google Play EMM API بروتوكول HTTP نفسه. كنظام المعالجة المجمّعة OData.

نظرة عامة

ينتج عن كل طلب يقدّمه عميلك من خلال واجهة برمجة التطبيقات Google Play EMM API مقدار معيّن من النفقات العامة. تتيح واجهة برمجة التطبيقات Google Play EMM API إمكانية تجميع طلبات البيانات، وذلك للسماح لعميلك بوضع العديد من طلبات البيانات من واجهة برمجة التطبيقات في طلب واحد.

في ما يلي بعض الأمثلة على الحالات التي قد تحتاج فيها إلى استخدام التجميع:

  • تم تسجيل أحد النطاقات للتو، وعليه الآن الكثير من البيانات لتحميلها.
  • أجرى أحد المستخدمين تغييرات على البيانات أثناء عدم اتصال التطبيق بالإنترنت، لذا يحتاج تطبيقك إلى مزامنة قدر كبير من البيانات المحلية مع الخادم.

في مثل هذه الحالات، يمكنك تجميعها معًا في طلب واحد بدلاً من إرسال كل مكالمة على حدة. يمكنك أيضًا تجميع طلبات لعدة مستخدمين أو لعدة واجهات Google APIs.

ومع ذلك، يمكنك إجراء 1000 اتصال في طلب مجمّع واحد. وإذا كنت بحاجة إلى إجراء مكالمات أكثر من ذلك، استخدِم طلبات مُجمَّعة متعددة.

تفاصيل الدفعة

يتألف الطلب المجمّع من طلبات بيانات متعددة من واجهة برمجة التطبيقات مدمَجة في طلب JSON-RPC واحد. يوضِّح هذا القسم بنية الطلب المجمّع بالتفصيل، مع توفير مثال في القسم التالي.

ملاحظة: يتم احتساب مجموعة الطلبات البالغ عددها n المجمَّعة ضمن حدّ الاستخدام على أنّها n طلب، وليس كطلب واحد. يتم تقسيم الطلب المجمّع إلى مجموعة من الطلبات قبل معالجته.

تنسيق الطلب المجمّع

تحتوي مكتبة برامج Java على طلبات لإنشاء طلبات لكل طلب بيانات من Google Play EMM API. على سبيل المثال، لإدراج جميع التطبيقات المثبتة على أحد الأجهزة، يمكنك استخدام ما يلي:

AndroidEnterprise enterprise = ...;
InstallsListResponse response = enterprise.installs().list(enterpriseId, userId, deviceId)
  .execute();

هناك استدعاء batch() إضافي يمكنه إضافة طلبات متعددة إلى قائمة الانتظار، كما هو موضّح هنا:

AndroidEnterprise enterprise = ...;
BatchRequest batchRequest = enterprise.batch();
enterprise.installs().list(enterpriseId, userId, deviceId1).queue(batchRequest, callback1);
enterprise.installs().list(enterpriseId, userId, deviceId2).queue(batchRequest, callback2);
enterprise.installs().list(enterpriseId, userId, deviceId3).queue(batchRequest, callback3);
batchRequest.execute();
عند استدعاء الدالة batchRequest.execute()، يتم إرسال جميع الطلبات في قائمة الانتظار مرة واحدة إلى الخادم على هيئة مصفوفة JSON. ويطبِّق الخادم معلَمات طلب البحث وعناوينه الخارجية (حسب الحاجة) على كل جزء، ثم يتعامل مع كل جزء كما لو كان طلب JSON منفصلاً.

الرد على طلب مجمّع

ينفذ الخادم كل طلب منفصل، ويجمع النتيجة في استجابة واحدة مكونة من صفيف واحد. تقسّم مكتبة البرامج هذه الاستجابة إلى استجابات فردية، ويتم إرسال كل ردّ إلى دالة معاودة الاتصال التي يتم تمريرها إلى queue(). رد الاتصال هو واجهة تحدد طريقة الفشل وطريقة النجاح. على سبيل المثال، سيتم تنفيذ callback1 كمثال لما يلي:

private class InstallsCallback implements JsonBatchCallback<InstallsListResponse> {

  @Override
  public void onSuccess(InstallsListResponse response, HttpHeaders responseHeaders) {
    ...
  }

  @Override
  public void onFailure(GoogleJsonError e, HttpHeaders responseHeaders) {
    ...
  }
}

ملاحظة: قد يُجري الخادم المكالمات بأي ترتيب، لذلك لا تعتمد على تلقّي النتائج بالترتيب المحدّد في طلبك. إذا كنت تريد التأكّد من إجراء مكالمتَين بترتيب معيّن، لا يمكنك إرسالهما في طلب واحد. بدلاً من ذلك، أرسل الطلب الأول وحده، وانتظِر ردًا قبل إرسال الطلب الثاني.

مثال على طلب مجمّع

يوضح المثال التالي كيفية سرد جميع التطبيقات المثبتة على كل أجهزة مستخدم معين. تُستخدم الطلبات الأولى للحصول على معرّف المؤسسة والمستخدم، وبالتالي يجب تنفيذها بشكل تسلسلي. بعد الحصول على جميع أرقام تعريف الأجهزة من خلال enterprise.devices().list()، يمكننا إجراء طلب مجمّع لاسترداد جميع التطبيقات على جميع أجهزة المستخدم في آنٍ واحد.

package com.google.playenterprise.example;

import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.googleapis.batch.BatchRequest;
import com.google.api.client.googleapis.batch.json.JsonBatchCallback;
import com.google.api.client.googleapis.json.GoogleJsonError;
import com.google.api.client.http.HttpHeaders;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson.JacksonFactory;
import com.google.api.services.androidenterprise.AndroidEnterprise;
import com.google.api.services.androidenterprise.AndroidEnterprise.Installs;
import com.google.api.services.androidenterprise.AndroidEnterpriseScopes;
import com.google.api.services.androidenterprise.model.Device;
import com.google.api.services.androidenterprise.model.DevicesListResponse;
import com.google.api.services.androidenterprise.model.Enterprise;
import com.google.api.services.androidenterprise.model.Install;
import com.google.api.services.androidenterprise.model.InstallsListResponse;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * Lists all the apps installed on all devices of a given user.
 */
public class ListAllInstalls {
  private AndroidEnterprise enterprise;
  private final List<String> installList = new ArrayList<>();

  public static void main(String[] argv) throws Exception {
    if (argv.length != 2) {
      throw new IllegalArgumentException("Usage: ListAllInstalls email jsonFilename");
    } else if (!argv[0].contains("@")) {
      throw new IllegalArgumentException("First parameter should be a valid email.");
    }
    new ListAllInstalls().run(argv[0], argv[1]);
  }

  private void run(String userEmail, String jsonKeyPath) throws IOException {
    enterprise = createAndroidEnterprise(jsonKeyPath);

    // Get the enterprise id, user id, and user devices.
    String domain = userEmail.split("@")[1];
    List<Enterprise> results = enterprise.enterprises().list(domain).execute().getEnterprise();
    if (results.isEmpty()) {
      throw new RuntimeException("No enterprise found.");
    }
    String enterpriseId = results.get(0).getId();
    String userId = enterprise
        .users()
        .list(enterpriseId, userEmail)
        .execute()
        .getUser()
        .get(0)
        .getId();
    List<Device> devices = getAllDevices(enterpriseId, userId);

    // Batch all calls to get installs on all user devices.
    gatherAllInstalls(enterpriseId, userId, devices);

    for (String entry : installList) {
      // Do something.
      System.out.println(entry);
    }
  }

  private List<Device> getAllDevices(String enterpriseId, String userId) throws IOException {
    DevicesListResponse devices = enterprise.devices().list(enterpriseId, userId).execute();
    return devices.getDevice();
  }

  private void gatherAllInstalls(String enterpriseId, String userId, List<Device> devices)
      throws IOException {
    BatchRequest batchRequest = enterprise.batch();
    for (Device device : devices) {
      Installs.List list = enterprise
          .installs().list(enterpriseId, userId, device.getAndroidId());
      // Each callback can take the specifics of the associated request in its constructor.
      list.queue(batchRequest, new InstallsCallback(device.getAndroidId()));
    }
    // Executes all the queued requests and their callbacks, single-threaded.
    batchRequest.execute();
  }

  private class InstallsCallback extends JsonBatchCallback<InstallsListResponse> {
    private final String androidId;

    InstallsCallback(String androidId) {
      this.androidId = androidId;
    }

    @Override
    public void onSuccess(InstallsListResponse response, HttpHeaders responseHeaders) {
      for (Install install : response.getInstall()) {
        installList.add(androidId + "," + install.getProductId());
      }
    }

    @Override
    public void onFailure(GoogleJsonError e, HttpHeaders responseHeaders) {
      throw new RuntimeException("Error fetching a device");
    }
  }

  private AndroidEnterprise createAndroidEnterprise(String jsonKeyPath) throws IOException {
    HttpTransport httpTransport = new NetHttpTransport();
    JsonFactory jsonFactory = new JacksonFactory();

    InputStream is = new BufferedInputStream(new FileInputStream(jsonKeyPath));
    final Credential credential = GoogleCredential.fromStream(is, httpTransport, jsonFactory)
        .createScoped(AndroidEnterpriseScopes.all());

    HttpRequestInitializer httpRequestInitializer = new HttpRequestInitializer() {
      @Override
      public void initialize(HttpRequest request) throws IOException {
        credential.initialize(request);
      }
    };
    return new AndroidEnterprise.Builder(httpTransport, jsonFactory, httpRequestInitializer)
        .build();
  }
}