ส่งคําขอแบบกลุ่ม

เอกสารนี้แสดงวิธีจัดกลุ่มการเรียก API ไว้ด้วยกันเพื่อลดจำนวนการเชื่อมต่อที่ไคลเอ็นต์ต้องทำ

เอกสารนี้เกี่ยวข้องกับการส่งคำขอแบบกลุ่มโดยใช้ไลบรารีของไคลเอ็นต์ Java โดยเฉพาะ ตัวอย่างพื้นฐานมีอยู่ใน ไลบรารีของไคลเอ็นต์ Google API สำหรับ .NET ระบบแบบกลุ่มสำหรับ Google Play EMM API ใช้ HTTP เดียวกัน ไวยากรณ์เป็นระบบการประมวลผลแบบกลุ่ม OData

ภาพรวม

คำขอแต่ละรายการที่ลูกค้าดำเนินการผ่าน Google Play EMM API จะทำให้เกิดค่าใช้จ่ายในการดำเนินการจำนวนหนึ่ง Google Play EMM API รองรับการทำงานแบบกลุ่ม เพื่อช่วยให้ไคลเอ็นต์ของคุณรวมการเรียก API หลายรายการไว้ในคำขอเดียวได้

ต่อไปนี้คือตัวอย่างสถานการณ์ที่คุณอาจต้องใช้การรวมกลุ่ม

  • โดเมนเพิ่งลงทะเบียนใหม่และมีข้อมูลที่ต้องอัปโหลดมากมาย
  • ผู้ใช้ทำการเปลี่ยนแปลงข้อมูลขณะที่แอปพลิเคชันของคุณออฟไลน์ ดังนั้นแอปพลิเคชันของคุณต้องซิงค์ข้อมูลจำนวนมากไว้ในเครื่องกับเซิร์ฟเวอร์

ในกรณีเช่นนี้ คุณอาจจัดกลุ่มการโทรเข้าด้วยกันเป็นคําขอเดียวแทนที่จะส่งการโทรแต่ละครั้งแยกกัน คุณยังสามารถจัดกลุ่มคำขอสำหรับผู้ใช้หลายคน หรือสำหรับ Google API หลายรายการก็ได้

อย่างไรก็ตาม ระบบจำกัดให้คุณโทรเข้ามาได้ไม่เกิน 1,000 ครั้งในคำขอเป็นกลุ่มเดียว หากต้องการโทรมากกว่าจำนวนนั้น ให้ใช้คำขอแบบกลุ่มหลายรายการ

รายละเอียดกลุ่ม

คำขอแบบกลุ่มประกอบด้วยการเรียก API หลายรายการรวมกันเป็นคำขอ 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 แยกต่างหาก

การตอบกลับคำขอแบบกลุ่ม

เซิร์ฟเวอร์จะดำเนินการกับแต่ละคำขอที่แยกกัน และจัดกลุ่มผลลัพธ์เป็นคำตอบเดียวซึ่งประกอบด้วยอาร์เรย์เดียว ไลบรารีของไคลเอ็นต์จะแยกคำตอบนี้ออกเป็นคำตอบแต่ละรายการ และแต่ละคำตอบจะส่งไปยังฟังก์ชัน Callback ที่ส่งไปยัง queue() Callback คืออินเทอร์เฟซที่กำหนดเมธอดสำหรับความล้มเหลวและเมธอดสู่ความสำเร็จ ตัวอย่างเช่น callback1 จะถูกนำมาใช้เป็นตัวอย่างดังต่อไปนี้

private class InstallsCallback implements JsonBatchCallback<InstallsListResponse> {

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

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

หมายเหตุ: เซิร์ฟเวอร์อาจดำเนินการเรียกคุณตามลำดับใดก็ได้ ดังนั้นอย่าอาศัยผลในการส่งคำขอตามลำดับที่ระบุไว้ในคำขอ หากต้องการแน่ใจว่ามีการเรียก 2 ครั้งตามลำดับที่ระบุ คุณไม่สามารถส่งคำขอเดียวได้ ให้ส่งคำขอแรกไปด้วยตัวเอง และรอการตอบกลับก่อนที่จะส่งรายการที่ 2

ตัวอย่างคำขอแบบกลุ่ม

ตัวอย่างต่อไปนี้แสดงวิธีแสดงรายการแอปทั้งหมดที่ติดตั้งในอุปกรณ์ของผู้ใช้ทั้งหมด การเรียกใช้ครั้งแรกจะใช้ในการรับรหัสขององค์กรและผู้ใช้ และต้องดำเนินการตามลำดับ เมื่อได้ ID อุปกรณ์ทั้งหมดที่มี 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();
  }
}