Accesso ad App Engine con l'API Remote

ID regione

REGION_ID è un codice abbreviato assegnato da Google in base alla regione selezionata quando crei l'app. Il codice non corrisponde a un paese o a una provincia, anche se alcuni ID regione possono sembrare simili ai codici di paesi e province di uso comune. Per le app create dopo febbraio 2020, REGION_ID.r è incluso negli URL di App Engine. Per le app esistenti create prima di questa data, l'ID regione è facoltativo nell'URL.

Scopri di più sugli ID regione.

La libreria API remota consente a qualsiasi client Python di accedere ai servizi disponibili per le applicazioni App Engine.

Ad esempio, se la tua applicazione App Engine utilizza Datastore o Google Cloud Storage, un client Python potrebbe accedere a queste risorse di archiviazione utilizzando l'API Remote.

Puoi utilizzare l'API Remote per accedere al datastore della tua applicazione da un'app in esecuzione sulla tua macchina locale o da una shell dell'API Remote interattiva locale. L'API Remote interagisce con servizi reali, pertanto questo accesso utilizza quota e risorse fatturabili.

Attivare l'accesso API remoto nell'app

Il modo più semplice per attivare l'API Remote per la tua applicazione è utilizzare la direttiva builtins nel file app.yaml per la tua app, che specifica l'URL predefinito /_ah/remote_api/. Tuttavia, puoi utilizzare la direttiva url nello stesso file per specificare un altro URL.

builtin

La direttiva builtins nel file app.yaml rende l'API Remote disponibile all'URL predefinito /_ah/remote_api:

runtime: python27
api_version: 1
threadsafe: true

builtins:
- remote_api: on

URL

L'utilizzo della direttiva url in app.yaml ti consente di specificare un URL diverso da utilizzare con l'API Remote:

- url: /some-URL/*
  script: google.appengine.ext.remote_api.handler.application

Assicurati di eseguire il deployment dell'applicazione in App Engine dopo aver apportato questa modifica.

Utilizzo della shell dell'API remota

L'SDK Python include una shell API remota, che consente di richiamare comandi Python sui servizi App Engine utilizzati dalla tua applicazione. Non è necessario fornire un'autenticazione aggiuntiva, in quanto vengono utilizzate automaticamente le stesse credenziali utilizzate per caricare l'app su App Engine.

Per avviare la shell dell'API remota:

  1. Esegui il seguente comando da una finestra del terminale sulla tua macchina locale:

    SDK-INSTALL-DIRECTORY/remote_api_shell.py -s YOUR-PROJECT-ID. REGION_ID.r.appspot.com

    Sostituisci [SDK-INSTALL-DIRECTORY] con il percorso dell'SDK App Engine per Python e [YOUR-PROJECT-ID] con l'ID del tuo progetto.

  2. Nella shell interattiva visualizzata, invoca i comandi Python che vuoi eseguire. Ad esempio, se la tua applicazione utilizza Datastore, puoi invocare la seguente query ndb per recuperare 10 record:

     >>> from google.appengine.ext import ndb
     >>>
     >>> # Fetch 10 keys from the datastore
     >>> ndb.Query().fetch(10, keys_only=True)
    

Utilizzo dell'API Remote in un client locale

Puoi anche utilizzare l'API Remote nelle applicazioni locali per accedere ai servizi utilizzati dalla tua app in esecuzione in App Engine.

Per utilizzare l'API Remote in un'applicazione locale:

  1. Abilita l'API remota.

  2. Esporta la variabile di ambiente PYTHONPATH per la directory Python, ad esempio:

     export PYTHONPATH=/usr/somedir/v3/bin/python2.7
    

    Sostituisci il percorso con i valori effettivi della posizione di Python.

  3. Aggiungi la posizione dell'SDK App Engine per Python a PYTHONPATH:

     export GAE_SDK_ROOT="/usr/local/home/mydir/google_appengine"
     export PYTHONPATH=${GAE_SDK_ROOT}:${PYTHONPATH}
    

    Sostituisci il percorso dell'SDK mostrato sopra con il percorso effettivo dell'SDK App Engine.

  4. Nel codice client, importa dev_appserver e chiama dev_appserver.fix_sys_path() per assicurarti che tutti i moduli dell'SDK App Engine si importino correttamente:

    try:
        import dev_appserver
        dev_appserver.fix_sys_path()
  5. Aggiungi il seguente codice remote_api_stub all'applicazione, assicurandoti di trasmettere il tuo ID progetto nel codice:

    remote_api_stub.ConfigureRemoteApiForOAuth(
        '{}.appspot.com'.format(project_id),
        '/_ah/remote_api')

    Se non utilizzi l'URL predefinito /_ah/remote_api per l'API Remote, devi modificare il codice riportato sopra in modo che rifletta l'URL che stai utilizzando. Per la definizione e la documentazione di remote_api_stub.ConfigureRemoteApiForOAuth, consulta il file SDK [SDK-INSTALL-DIRECTORY]/google/appengine/ext/remote_api/remote_api_stub.py.

  6. Aggiungi le importazioni di App Engine e il codice Python necessari per accedere ai servizi di App Engine che ti interessano. Il seguente codice campione accede al datastore del progetto:

    
    import argparse
    
    try:
        import dev_appserver
        dev_appserver.fix_sys_path()
    except ImportError:
        print('Please make sure the App Engine SDK is in your PYTHONPATH.')
        raise
    
    from google.appengine.ext import ndb
    from google.appengine.ext.remote_api import remote_api_stub
    
    
    def main(project_id):
        remote_api_stub.ConfigureRemoteApiForOAuth(
            '{}.appspot.com'.format(project_id),
            '/_ah/remote_api')
    
        # List the first 10 keys in the datastore.
        keys = ndb.Query().fetch(10, keys_only=True)
    
        for key in keys:
            print(key)
    
    
    if __name__ == '__main__':
        parser = argparse.ArgumentParser(
            description=__doc__,
            formatter_class=argparse.RawDescriptionHelpFormatter)
        parser.add_argument('project_id', help='Your Project ID.')
    
        args = parser.parse_args()
    
        main(args.project_id)
  7. Dopo aver eseguito il deployment dell'applicazione in App Engine, avvia il client API remoto:

     python your-client.py YOUR-PROJECT-ID
    

    Sostituisci your-client.py con il tuo modulo client e YOUR-PROJECT-ID con il tuo ID progetto. Questo presuppone che il cliente accetti l'ID progetto come input della riga di comando, seguendo l'esempio di codice client.py.

Limitazioni e best practice

Il modulo remote_api fa di tutto per assicurarsi che, per quanto possibile, si comporti esattamente come il datastore App Engine nativo. In alcuni casi, ciò significa fare cose meno efficienti di quanto potrebbero essere. Quando utilizzi remote_api, tieni presente quanto segue:

Ogni richiesta di datastore richiede un round trip

Poiché accedi al datastore tramite HTTP, il sovraccarico e la latenza sono leggermente superiori rispetto all'accesso locale. Per velocizzare il processo e diminuire il carico, prova a limitare il numero di viaggi di andata e ritorno che esegui raggruppando get e put e recuperando batch di entità dalle query. Questo è un buon consiglio non solo per remote_api, ma anche per l'utilizzo del datastore in generale, perché un'operazione batch è considerata solo una singola operazione di Datastore. Ad esempio, invece di:

for key in keys:
  rec = key.get()
  rec.foo = bar
  rec.put()

Puoi:

records = ndb.get_multi(keys)
for rec in records:
  rec.foo = bar
  ndb.put_multi(records)

Entrambi gli esempi hanno lo stesso effetto, ma il secondo richiede solo due viaggi di andata e ritorno in totale, mentre il primo richiede due viaggi di andata e ritorno per ogni entità.

Quota di utilizzo delle richieste a remote_api

Poiché remote_api opera tramite HTTP, ogni chiamata al datastore comporta un utilizzo della quota per le richieste HTTP, i byte in entrata e in uscita, nonché la consueta quota del datastore. Tieni presente questo aspetto se utilizzi remote_api per eseguire aggiornamenti collettivi.

Si applicano i limiti dell'API di 1 MB

Come per l'esecuzione nativa, si applica ancora il limite di 1 MB per le richieste e le risposte dell'API. Se le entità sono particolarmente grandi, potresti dover limitare il numero di elementi che recuperi o inserisci alla volta per rimanere al di sotto di questo limite. Purtroppo, questo è in conflitto con la minimizzazione dei round trip, quindi il consiglio migliore è utilizzare i batch più grandi possibili senza superare i limiti di dimensioni della richiesta o della risposta. Tuttavia, per la maggior parte delle persone questo non è un problema.

Evita di eseguire l'iterazione sulle query

Un pattern comune con l'accesso al datastore è il seguente:

q = MyModel.query()
for entity in q:
  # Do something with entity

In questo modo, l'SDK recupera le entità dal datastore in batch di 20, recuperando un nuovo batch ogni volta che esaurisce quelli esistenti. Poiché ogni batch deve essere recuperato in una richiesta separata da remote_api, non è in grado di eseguire questa operazione in modo efficiente. Al contrario, remote_api esegue una query completamente nuova per ogni batch, utilizzando la funzionalità di offset per visualizzare altri risultati.

Se conosci il numero di entità di cui hai bisogno, puoi eseguire l'intero recupero in un'unica richiesta chiedendo il numero necessario:

entities = MyModel.query().fetch(100)
for entity in entities:
  # Do something with entity

Se non sai quante entità ti serviranno, puoi utilizzare i cursori per eseguire in modo efficiente l'iterazione su insiemi di risultati di grandi dimensioni. In questo modo, puoi anche evitare il limite di 1000 entità imposto alle normali query del datastore.

Le transazioni sono meno efficienti

Per implementare le transazioni tramite remote_api, accumula informazioni sulle entità recuperate all'interno della transazione, insieme a copie delle entità inserite o eliminate all'interno della transazione. Quando la transazione viene eseguita, invia tutte queste informazioni al server App Engine, dove deve recuperare di nuovo tutte le entità utilizzate nella transazione, verificare che non siano state modificate, quindi inserire ed eliminare tutte le modifiche apportate dalla transazione ed eseguirne il commit. Se si verifica un conflitto, il server esegue il rollback della transazione e invia una notifica al client, che deve ripetere la procedura da capo.

Questo approccio funziona e duplica esattamente la funzionalità fornita dalle transazioni nel datastore locale, ma è piuttosto inefficiente. Utilizza le transazioni se necessarie, ma cerca di limitare il numero e la complessità di quelle che esegui per motivi di efficienza.