Auf App Engine mit Remote API zugreifen

Die Google Cloud CLI enthält ein remote_api-Paket, mit dem Sie transparent von jeder Go-Anwendung aus auf App Engine-Dienste zugreifen können. Beispiel: Sie können die Remote API verwenden, um von einer Anwendung, die auf Ihrem lokalen Computer ausgeführt wird, oder von einer anderen Ihrer App Engine-Anwendungen aus auf Datastore zuzugreifen.

Informationen zum Inhalt des remote_api-Pakets finden Sie in der Referenz zum remote_api-Paket.

Remote API aktivieren

Fügen Sie zuerst den remote_api-url-Handler zur Datei app.yaml hinzu. Beispiel:

- url: /_ah/remote_api
  script: _go_app

Dadurch wird die URL /_ah/remote_api Ihrer Go-Anwendung zugeordnet. Der Remote API-Handler gewährt nur Administratoren der Anwendung Zugriff auf diese URL.

Anschließend importieren Sie das remote_api-Paket in eines der Pakete Ihres Projekts. Fügen Sie dazu folgende Zeile zu einer Ihrer .go-Quelldateien hinzu:

import _ "google.golang.org/appengine/remote_api"

Während der Programminitialisierung registriert das remote_api-Paket einen Handler für den Pfad /_ah/remote_api. Der Unterstrich in der Importdeklaration bedeutet: „Dieses Paket importieren, aber nicht direkt verwenden“. Ohne den Unterstrich erhalten Sie beim Kompilieren die Fehlermeldung „Importiert, aber nicht verwendet“.

Stellen Sie schließlich das Update in App Engine bereit. Beispiel:

gcloud app deploy app.yaml

Remote API in einem lokalen Client verwenden

Mit der Remote API können lokale Anwendungen geschrieben werden, die App Engine-Dienste verwenden und auf Datenspeicher zugreifen. Beachten Sie dabei, dass die Verwendung der Remote API zu einer Kontingentnutzung in der Anwendung führt, auf die Sie zugreifen.

Stellen Sie vor Beginn sicher, dass die Remote API in Ihrer App Engine-Anwendung aktiviert ist. Die lokale Anwendung kann die Remote API verwenden. Dazu erstellt sie einen Kontext mit remote_api.NewRemoteContext und verwendet diesen anstelle des regulären App Engine-Kontexts in allen API-Aufrufen.

// Copyright 2011 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.

package main

import (
	"log"
	"time"

	"golang.org/x/net/context"
	"golang.org/x/oauth2/google"

	"google.golang.org/appengine/datastore"
	"google.golang.org/appengine/remote_api"
)

func main() {
	const host = "<your-app-id>.appspot.com"

	ctx := context.Background()

	hc, err := google.DefaultClient(ctx,
		"https://www.googleapis.com/auth/appengine.apis",
		"https://www.googleapis.com/auth/userinfo.email",
		"https://www.googleapis.com/auth/cloud-platform",
	)
	if err != nil {
		log.Fatal(err)
	}

	remoteCtx, err := remote_api.NewRemoteContext(host, hc)
	if err != nil {
		log.Fatal(err)
	}

	q := datastore.NewQuery("Greeting").
		Filter("Date >=", time.Now().AddDate(0, 0, -7))

	log.Println(q.Count(remoteCtx))
}

Sie müssen die folgenden Pakete abrufen, um diesen Code auszuführen:

$ go get google.golang.org/appengine/...
$ go get golang.org/x/oauth2/...

Sie müssen den Hostnamen Ihres Servers und einen http.Client im Aufruf von NewRemoteContext angeben. Der bereitgestellte http.Client ist für die Übergabe der erforderlichen Authentifizierungsinformationen in jeder Anfrage verantwortlich.

Im obigen Beispiel stellt der DefaultClient aus dem Paket golang.org/x/oauth2/google OAuth 2-Anmeldedaten über Standardanmeldedaten für Anwendungen bereit.

Beschränkungen und Best Practices

Bei dem Modul „remote_api“ wurde großer Wert darauf gelegt, dass es sich nach Möglichkeit genau so wie der native App Engine-Datenspeicher verhält. In einigen Fällen führt dies allerdings zu einer geringeren Effizienz. Bei der Verwendung von remote_api müssen einige Punkte berücksichtigt werden:

Umlauf bei jeder Datenspeicher-Anforderung

Da Sie über HTTP auf den Datenspeicher zugreifen, sind der Aufwand und die Latenz etwas höher als beim lokalen Zugriff. Zur Erhöhung der Geschwindigkeit und zur Verringerung der Last sollten Sie versuchen, die Anzahl der durchgeführten Umläufe zu begrenzen, indem Sie „get“- und „put“-Anforderungen als Stapelvorgänge ausführen und Entitäten stapelweise aus den Abfragen abrufen. Dies hilft nicht nur bei der Verwendung von remote_api, sondern auch bei der Verwendung des Datenspeichers im Allgemeinen, da ein Batchvorgang nur als einzelner Datenspeichervorgang betrachtet wird.

Quotenverbrauch für „remote_api“-Anfragen

Da remote_api über HTTP ausgeführt wird, führt jeder ausgeführte Datenspeicheraufruf neben der Verwendung des erwarteten Datenspeicher-Kontingents zu einer Kontingentverwendung eingehender und ausgehender Byte für HTTP-Anforderungen. Beachten Sie dies, wenn Sie remote_api für Bulk-Updates verwenden.

1-MB-Obergrenze für API

Wie bei der nativen Ausführung gilt auch hier die 1-MB-Obergrenze für API-Anforderungen und -Antworten. Bei besonders großen Entitäten müssen Sie möglicherweise die jeweils abgerufenen oder abgelegten Daten begrenzen, um unter dieser Obergrenze zu bleiben. Diese Vorgabe steht leider im Widerspruch zu der Reduzierung der Umläufe. Daher sollten die Batches jeweils so groß gewählt werden, wie es möglich ist, ohne die Größenbeschränkungen für Anforderung oder Antwort zu überschreiten. Bei den meisten Entitäten ist dies jedoch vermutlich kein Problem.

Iteration über Abfragen vermeiden

Wenn Sie Abfragen durchlaufen, ruft das SDK jeweils 20 Entitäten aus dem Datenspeicher und ruft immer dann einen neuen Stapel ab, wenn es die vorhandenen verbraucht hat. Da jeder Stapel von remote_api in einer separaten Anforderung abgerufen werden muss, kann hierbei nicht dieselbe Effizienz erreicht werden. Stattdessen führt remote_api für jeden Stapel eine völlig neue Abfrage aus und verwendet dabei die Offset-Funktion, um zu weiteren Ergebnissen vorzudringen.

Wenn Sie wissen, wie viele Entitäten Sie benötigen, können Sie den gesamten Abruf in einer einzigen Anforderung durchführen, indem Sie die benötigte Anzahl anfordern:

Wenn Sie nicht wissen, wie viele Entitäten Sie benötigen, können Sie Cursors verwenden, um große Ergebnismengen effizient zu iterieren. Dadurch können Sie gleichzeitig die Obergrenze von 1.000 Entitäten umgehen, die für normale Datenspeicherabfragen gilt.

Geringere Effizienz bei Transaktionen

Zum Implementieren von Transaktionen über remote_api werden Informationen zu den in der Transaktion abgerufenen Entitäten sowie Kopien der Entitäten, die in der Transaktion abgelegt oder gelöscht wurden, angesammelt. Wenn die Transaktion in einem Commit-Vorgang übergeben wird, werden alle diese Informationen an den App Engine-Server gesendet. Dort müssen erneut alle Entitäten, die in der Transaktion verwendet wurden, abgerufen werden, es muss überprüft werden, dass sie nicht geändert wurden, und dann müssen alle Änderungen, die in der Transaktion vorgenommen wurden, abgelegt und gelöscht werden und schließlich muss ein Commit-Vorgang durchgeführt werden. Bei einem Konflikt macht der Server die Transaktion rückgängig und benachrichtigt die Client-Seite, die dann den Vorgang noch einmal von vorne beginnen muss.

Diese Vorgehensweise funktioniert und dupliziert die von den Transaktionen bereitgestellten Funktionen exakt im lokalen Datenspeicher, ist jedoch ziemlich ineffizient. Wenn nötig, sollten Sie unbedingt Transaktionen verwenden. Versuchen Sie jedoch zugunsten der Effizienz die Anzahl und Komplexität der ausgeführten Transaktionen möglichst gering zu halten.