Acceder a App Engine con la API Remote

ID de región

El REGION_ID es un código abreviado que Google asigna en función de la región que selecciones al crear tu aplicación. El código no corresponde a un país o provincia, aunque algunos IDs de región pueden parecerse a los códigos de país y provincia que se usan habitualmente. En las aplicaciones creadas después de febrero del 2020, REGION_ID.r se incluye en las URLs de App Engine. En las aplicaciones creadas antes de esa fecha, el ID de región es opcional en la URL.

Más información sobre los IDs de región

La biblioteca de la API remota permite que cualquier cliente de Python acceda a los servicios disponibles para las aplicaciones de App Engine.

Por ejemplo, si tu aplicación de App Engine usa Datastore o Google Cloud Storage, un cliente de Python podría acceder a esos recursos de almacenamiento mediante la API remota.

Puedes usar la API remota para acceder al almacén de datos de tu aplicación desde una aplicación que se ejecute en tu máquina local o desde un shell de API remota interactivo local. La API remota interactúa con servicios reales, por lo que este acceso usa cuota y recursos facturables.

Habilitar el acceso a la API remota en tu aplicación

La forma más sencilla de habilitar la API remota en tu aplicación es usar la directiva builtins en el archivo app.yaml de tu aplicación, que especifica la URL predeterminada /_ah/remote_api/. Sin embargo, puedes usar la directiva url en ese mismo archivo para especificar otra URL.

builtin

La directiva builtins del archivo app.yaml hace que la API remota esté disponible en la URL predeterminada /_ah/remote_api:

runtime: python27
api_version: 1
threadsafe: true

builtins:
- remote_api: on

URL

Si usas la directiva url en app.yaml, puedes especificar otra URL para usarla con la API remota:

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

Asegúrate de desplegar tu aplicación en App Engine después de hacer este cambio.

Usar el shell de la API remota

El SDK de Python incluye un shell de API remota que te permite invocar comandos de Python en los servicios de App Engine que usa tu aplicación. No tienes que proporcionar ninguna autenticación adicional, ya que se utilizan automáticamente las mismas credenciales que usaste para subir la aplicación a App Engine.

Para iniciar el shell de la API remota, haz lo siguiente:

  1. Invoca el siguiente comando desde una ventana de terminal en tu máquina local:

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

    Sustituye [SDK-INSTALL-DIRECTORY] por la ruta al SDK de App Engine para Python y [YOUR-PROJECT-ID] por el ID de tu proyecto.

  2. En el shell interactivo que se muestra, invoca los comandos de Python que quieras ejecutar. Por ejemplo, si tu aplicación usa Datastore, puedes invocar la siguiente consulta de ndb para obtener 10 registros:

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

Usar la API remota en un cliente local

También puedes usar la API remota en aplicaciones locales para acceder a los servicios que usa tu aplicación en App Engine.

Para usar la API remota en una aplicación local, sigue estos pasos:

  1. Habilita la API remota.

  2. Exporta la variable de entorno PYTHONPATH de tu directorio de Python. Por ejemplo:

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

    Sustituye esa ruta por los valores reales de la ubicación de Python.

  3. Añade la ubicación del SDK de App Engine para Python a PYTHONPATH:

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

    Sustituye la ruta del SDK que se muestra arriba por la ruta real del SDK de App Engine.

  4. En el código de tu cliente, importa dev_appserver y llama a dev_appserver.fix_sys_path() para asegurarte de que todos los módulos del SDK de App Engine se importan correctamente:

    try:
        import dev_appserver
        dev_appserver.fix_sys_path()
  5. Añade el siguiente código remote_api_stub a tu aplicación. Asegúrate de pasarle el ID de tu proyecto en el código:

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

    Si no usas la URL predeterminada /_ah/remote_api para la API Remote, tendrás que cambiar el código anterior para que refleje la URL que estés usando. Para ver la definición y la documentación de remote_api_stub.ConfigureRemoteApiForOAuth, consulta el archivo del SDK [SDK-INSTALL-DIRECTORY]/google/appengine/ext/remote_api/remote_api_stub.py.

  6. Añade las importaciones de App Engine y el código de Python necesarios para acceder a los servicios de App Engine que quieras. El siguiente código de muestra accede al almacén de datos del proyecto:

    
    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. Una vez que hayas desplegado tu aplicación en App Engine, inicia tu cliente de la API remota:

     python your-client.py YOUR-PROJECT-ID
    

    Sustituye your-client.py por tu módulo de cliente y YOUR-PROJECT-ID por el ID de tu proyecto. En este caso, se presupone que tu cliente acepta el ID de proyecto como entrada de línea de comandos, siguiendo el ejemplo de código client.py.

Limitaciones y prácticas recomendadas

El módulo remote_api hace todo lo posible para que se comporte exactamente igual que el almacén de datos nativo de App Engine. En algunos casos, esto implica hacer cosas que son menos eficientes de lo que podrían ser. Cuando uses remote_api, ten en cuenta lo siguiente:

Todas las solicitudes del almacén de datos requieren un recorrido de ida y vuelta

Como accedes al almacén de datos a través de HTTP, hay un poco más de sobrecarga y latencia que cuando accedes a él de forma local. Para agilizar las cosas y reducir la carga, intenta limitar el número de viajes de ida y vuelta que haces agrupando las operaciones de obtención e inserción, y obteniendo lotes de entidades de las consultas. Este consejo no solo es útil para remote_api, sino también para usar el almacén de datos en general, ya que una operación por lotes solo se considera una operación de almacén de datos. Por ejemplo, en lugar de lo siguiente:

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

puedes hacer esto:

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

Ambos ejemplos tienen el mismo efecto, pero el segundo solo requiere dos viajes de ida y vuelta en total, mientras que el primero requiere dos viajes de ida y vuelta por cada entidad.

Solicitudes de la cuota de uso del módulo remote_api

Como remote_api funciona a través de HTTP, cada llamada a Datastore que hagas incurrirá en el uso de cuota para las solicitudes HTTP, los bytes entrantes y salientes, así como la cuota de Datastore habitual. Tenlo en cuenta si usas remote_api para hacer actualizaciones en bloque.

Aplicación de un límite de 1 MB para el API

Al igual que cuando se ejecuta de forma nativa, se sigue aplicando el límite de 1 MB en las solicitudes y respuestas de la API. Si tus entidades son especialmente grandes, puede que tengas que limitar el número que obtienes o insertas a la vez para no superar este límite. Por desgracia, esto entra en conflicto con la minimización de los viajes de ida y vuelta, por lo que lo más recomendable es usar los lotes más grandes posibles sin superar los límites de tamaño de las solicitudes o las respuestas. Sin embargo, esto no supondría ningún problema para la mayoría de las entidades.

Evitación de la iteración de las consultas

A continuación, se muestra un patrón común para el acceso al almacén de datos:

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

Cuando lo haces, el SDK obtiene entidades del almacén de datos en lotes de 20 y obtiene un nuevo lote cada vez que se agotan los anteriores. Como cada lote se debe obtener en una solicitud independiente mediante remote_api, no puede hacerlo de forma tan eficiente. En su lugar, remote_api ejecuta una consulta completamente nueva para cada lote, usando la función de desplazamiento para obtener más resultados.

Si sabes cuántas entidades necesitas, puedes hacer toda la obtención en una sola solicitud indicando el número que necesitas:

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

Si no sabes cuántas entidades vas a querer, puedes usar cursores para iterar de forma eficiente en conjuntos de resultados grandes. También te permite evitar el límite de 1000 entidades impuesto en las consultas normales de Datastore.

Las transacciones son menos eficientes

Para implementar transacciones a través de remote_api, se acumula información sobre las entidades obtenidas en la transacción, junto con copias de las entidades que se insertaron o eliminaron en la transacción. Cuando se confirma la transacción, se envía toda esta información al servidor de App Engine, donde se deben obtener de nuevo todas las entidades que se han usado en la transacción, verificar que no se han modificado, insertar y eliminar todos los cambios que ha realizado la transacción y confirmarla. Si hay un conflicto, el servidor revierte la transacción y notifica al extremo del cliente, que tiene que repetir el proceso desde el principio.

Este enfoque funciona y duplica exactamente la funcionalidad que ofrecen las transacciones en el almacén de datos local, pero es bastante ineficiente. Usa transacciones siempre que sea necesario, pero intenta limitar el número y la complejidad de las transacciones que ejecutes para mejorar la eficiencia.