8
votes

Relever Google Drive API Per-utilisateur Limit n'empêche pas les exceptions limites de taux

Je travaille avec un processus qui utilise l'API de lecteur pour télécharger des fichiers texte simples sur Google Drive. Le processus frappe fréquemment des exceptions limites de débit, même si le nombre réel de demandes est nulle part près de la limite de l'utilisateur pour l'installation d'API de lecteur dans la console APIS. En fait, la fixation de la limite de l'utilisateur ne semble pas affecter le taux dans lequel nous recevons des exceptions. Existe-t-il une autre limite (autre que la limite d'utilisateur) qui gouverne combien de demandes peuvent être faites par seconde? Peut-il être ajusté?

Le processus utilise un back-off exponential sur ces exceptions. Les actions sont donc finalement réussies. Nous ne faisons que environ 5 demandes par seconde et la limite par utilisateur est définie sur 100. p> xxx pré>

edit: voici une version "simplifiée" du code du développeur. Nous utilisons le compte de service avec la délégation de domaine comme décrit à l'adresse suivante: https://developers.google.com/drive/ Délégation . P>

package com.seto.fs.daemon;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.googleapis.json.GoogleJsonResponseException;
import com.google.api.client.http.FileContent;
import com.google.api.client.http.HttpBackOffIOExceptionHandler;
import com.google.api.client.http.HttpBackOffUnsuccessfulResponseHandler;
import com.google.api.client.http.HttpBackOffUnsuccessfulResponseHandler.BackOffRequired;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.client.testing.util.MockBackOff;
import com.google.api.client.util.DateTime;
import com.google.api.client.util.ExponentialBackOff;
import com.google.api.services.drive.Drive;
import com.google.api.services.drive.Drive.Files.Insert;
import com.google.api.services.drive.DriveScopes;
import com.google.api.services.drive.model.ChildList;
import com.google.api.services.drive.model.ChildReference;
import com.google.api.services.drive.model.File.Labels;
import com.google.api.services.drive.model.ParentReference;


public class Test {
    private static final int testFilesCount = 100;
    private static final int threadsCount = 3;
    private static final AtomicInteger rateLimitErrorsCount = new AtomicInteger(0);

    private static final String impersonatedUser = "<impersonatedUserEmail>";
    private static final String serviceAccountID = "<some-id>@developer.gserviceaccount.com";
    private static final String serviceAccountPK = "/path/to/<public_key_fingerprint>-privatekey.p12";

    public static void main(String[] args) throws Exception {
        // Create HTTP transport
        HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
        // Create JsonFactory
        final JsonFactory jsonFactory = new JacksonFactory();

        // Create Google credential for service account
        final Credential credential = new GoogleCredential.Builder()
                .setTransport(httpTransport)
                .setJsonFactory(jsonFactory)
                .setServiceAccountScopes(Arrays.asList(DriveScopes.DRIVE))
                .setServiceAccountUser(impersonatedUser)
                .setServiceAccountId(serviceAccountID)
                .setServiceAccountPrivateKeyFromP12File(new File(serviceAccountPK))
                .build();

        // Create Drive client
        final Drive drive = new Drive.Builder(httpTransport, jsonFactory, new HttpRequestInitializer() {
            public void initialize(HttpRequest request) throws IOException {
                request.setContentLoggingLimit(0);
                request.setCurlLoggingEnabled(false);

                // Authorization initialization
                credential.initialize(request);

                // Exponential Back-off for 5xx response and 403 rate limit exceeded error
                HttpBackOffUnsuccessfulResponseHandler serverErrorHandler
                    = new HttpBackOffUnsuccessfulResponseHandler(new ExponentialBackOff.Builder().build());
                serverErrorHandler.setBackOffRequired(new BackOffRequired() {
                    public boolean isRequired(HttpResponse response) {
                        return response.getStatusCode() / 100 == 5
                            || (response.getStatusCode() == 403 && isRateLimitExceeded(
                                    GoogleJsonResponseException.from(jsonFactory, response)));
                    }
                });
                request.setUnsuccessfulResponseHandler(serverErrorHandler);

                // Back-off for socket connection error
                MockBackOff backOff = new MockBackOff();
                backOff.setBackOffMillis(2000);
                backOff.setMaxTries(5);
                request.setIOExceptionHandler(new HttpBackOffIOExceptionHandler(backOff));
            }
        }).setApplicationName("GoogleDriveUploadFile/1.0").build();

        // Get root folder id
        final String rootFolderId = drive.about().get().execute().getRootFolderId();

        // Query all children under root folder
        ChildList result = drive.children().list(rootFolderId).execute();

        // Delete all children under root folder
        for (ChildReference child : result.getItems()) {
            System.out.println("Delete child: " + child.getId());
            drive.files().delete(child.getId()).execute();
        }

        // Create a drive folder
        com.google.api.services.drive.model.File folderMetadata
            = new com.google.api.services.drive.model.File();
        folderMetadata.setMimeType("application/vnd.google-apps.folder")
            .setParents(Arrays.asList(new ParentReference().setId(rootFolderId)))
            .setTitle("DriveTestFolder");
        final com.google.api.services.drive.model.File driveTestFolder = drive.files().insert(folderMetadata).execute();

        // Create test files
        final List<File> testFiles = Collections.synchronizedList(createTestFiles());

        // Run threads to upload files to drive
        List<Thread> threads = new ArrayList<Thread>();
        for (int i = 0; i < threadsCount; i++) {
            Thread thread = new Thread(new Runnable() {
                public void run() {
                    while (testFiles.size() > 0) {
                        try {
                            File testFile = testFiles.remove(0);

                            // The file meta data
                            com.google.api.services.drive.model.File fileMetadata =
                                new com.google.api.services.drive.model.File()
                                .setTitle(testFile.getName()).setParents(Arrays.asList(new ParentReference().setId(driveTestFolder.getId())))
                                .setLabels(new Labels().setRestricted(false)).setMimeType("text/plain")
                                .setModifiedDate(new DateTime(testFile.lastModified()))
                                .setDescription("folder:MyDrive " + testFile.getName());

                            // Insert to drive
                            FileContent fileContent = new FileContent("text/plain", testFile);
                            Insert insertFileCommand = drive.files().insert(fileMetadata, fileContent)
                                    .setUseContentAsIndexableText(true);
                            insertFileCommand.getMediaHttpUploader().setDirectUploadEnabled(true);

                            insertFileCommand.execute();

                            System.out.println(testFile.getName() + " is uploaded");
                        } catch (IOException e) {
                            e.printStackTrace();
                        } catch (IndexOutOfBoundsException e) {
                            // ignore
                        }
                    }
                }
            });
            threads.add(thread);
        }

        long startTime = System.currentTimeMillis();

        for (Thread thread : threads) {
            thread.start();
        }

        for (Thread thread : threads) {
            thread.join();
        }

        System.out.println("Total time spent: " + (System.currentTimeMillis() - startTime)
                + "ms for " + testFilesCount + " files with " + threadsCount + " threads");
        System.out.println("Rate limit errors hit: " + rateLimitErrorsCount.intValue());
    }

    private static List<File> createTestFiles() throws Exception {

        // Create test files directory
        File testFolder = new File("TestFiles");
        testFolder.mkdir();

        // Create test files
        List<File> testFiles = new ArrayList<File>();
        for (int i = 0; i < testFilesCount; i++) {
            File testFile = new File("TestFiles/" + i + ".txt");
            FileOutputStream fops = new FileOutputStream(testFile);
            fops.write(testFile.getAbsolutePath().getBytes());
            fops.close();
            testFiles.add(testFile);
        }
        return testFiles;
    }

    private static boolean isRateLimitExceeded(GoogleJsonResponseException ex) {
        boolean result = false;
        if (ex.getDetails() != null && ex.getDetails().getErrors() != null
                && ex.getDetails().getErrors().size() > 0) {
            String reason = ex.getDetails().getErrors().get(0).getReason();
            result = "rateLimitExceeded".equals(reason) || "userRateLimitExceeded".equals(reason);
            if (result) {
                rateLimitErrorsCount.incrementAndGet();
                System.err.println("Rate limit error");
            }
        }

        return result;
    }

}


4 commentaires

Avez-vous trouvé une solution pour cela? J'ai le même problème et la réponse acceptée de Steve ci-dessous est une petite lumière sur les détails :-)


À l'époque, la limite inférieure a été déclarée 3 demandes par seconde pour les téléchargements. Malheureusement, il y a également un double comptage sur des demandes, la limite de téléchargement effectif est donc de 1,5 demandes de téléchargement par seconde. Je ne sais pas si cela a changé du tout récemment.


Merci pour la réponse; J'ai passé la journée aujourd'hui à transmettre toute l'application à Amazon S3 à la place. L'API est fantastique et tout fonctionne maintenant. C'est absolument c ** ps sur Google Drive.


Une mise à jour pour ceci? Je suis dans le même bateau ... Nulle part près du quota d'utilisation autorisé que je vois dans la console, pourtant obtenir de nombreuses erreurs.


3 Réponses :


0
votes

Cela signifie que quelque chose ne va pas dans votre configuration. Vous pouvez utiliser une API d'entraînement avec un quota faible comme ce que vous rencontrez même lorsque votre configuration de l'API est fausse.

  1. Veuillez vérifier si vous avez allumé l'API de lecteur dans Console APIS > Services
  2. Veuillez vérifier si vous utilisez une touche API correcte ou un flux d'autorisation.

    Si vous ne trouvez toujours pas ce qui vous entraîne cette erreur, veuillez joindre un code minimum pour reproduire cette erreur.


7 commentaires

Merci pour votre réponse! L'API d'entraînement est allumée. Sans cela sur nous ne pourrions pas ajuster les quotas. Nous allons regarder à nouveau sur les touches de l'API.


Qu'est-ce qui pourrait être faux qui permettrait toujours certaines des demandes de réussir? Que devrions-nous rechercher?


Veuillez valider votre jeton d'accès en getting Googleapis.com/oautheat2/v1/tokeninfo ? Access_token = [YourToken]


J'ai frappé cette URL avec le jeton le processus demandé. Il renvoie ce qui suit. {"émis_to": "46454168024.apps.googleusercontent.com", "Public": "46454168024.apps.googleusercontent.com", "Scope": " googleapis.com/auth/drive ", "expire_in": 3426, "Access_type": "hors ligne"}


Je ne vois pas où nous définissons un quotauuser ou des utilisateurs comme décrit dans la documentation ici . Cela pourrait être lié à notre problème.


L'utilisation de SetQuauser sur InsertFilecommand n'a pas abaissé nos exceptions de limite de taux. Quoi d'autre pourrait affecter les limites de taux?


@Burcudogan, cela aiderait-il si nous demandions plus de quota de conduite à l'aide de l'interface utilisateur, même si nous ne recevons nulle part près de ce que notre paramètre actuel est?



3
votes

Il existe une limite inférieure spécifiquement pour les téléchargements, et c'est par utilisateur de toutes les applications. Verra si nous pouvons poster la limite dans les documents.


2 commentaires

Quelle est la limite de téléchargement par utilisateur? Je ne peux pas le trouver. Est-il possible de configurer ou de changer cela? En tant que client de domaine payant, est-ce possible de changer?


Une mise à jour pour ceci? Je vérifie la console API et tout ressemble bien au-dessous des demandes allemandes / SEC - mais j'obtiens une quantité démesurée de 403 exceptions de Ratelimit. Je suis honnêtement abasourdi que ce n'est pas montré dans les docs plutôt que d'avoir des personnes qui le font comprendre eux-mêmes ....



0
votes

Suivi de la réponse de Steve Bazyl ...

Cette exception d'API Google a été générée par un programme C # ETL qui a rendu des demandes de lignes de feuille individuelles à charger dans une table de base de données SQL.

EX = {"google.apis.requests.requesTerrorRRROR \ r \ Ninsufficities jetons pour quota 'lecturegroupe' et limite "utilisateur-100 'de service 'Sheets.Joogleapis.com' pour le consommateur ...

La limite " UTILISATEUR-100SS-100S " apparaît sur le texte suivant de la Google API V4" Limites d'utilisation "Page .

Cette version de Google Feuilles API a une limite de 500 demandes par 100 secondes par projet, et 100 demandes par 100 secondes par Utilisateur . Les limites pour les lectures et les écrivies sont suivies séparément. Il y a aucune limite d'utilisation quotidienne.

Fondamentalement, un mécanisme d'étranglement est nécessaire pour éviter ces demandes par des contraintes de l'unité TIME, une réduction des demandes effectuées, ou une combinaison des deux.

Ce Google Page mentionne également que ces limites de quota peuvent être augmentées avec un " Compte de facturation ".


0 commentaires