Remote API로 App Engine에 액세스

Google Cloud CLI에는 Go 애플리케이션에서 App Engine 서비스에 투명하게 액세스할 수 있도록 하는 remote_api 패키지가 포함되어 있습니다. 예를 들어 Remote API를 사용하여 로컬 머신에서 실행되는 앱 또는 다른 App Engine 앱에서 Datastore에 액세스할 수 있습니다.

remote_api 패키지의 콘텐츠를 보려면 remote_api 패키지 참조를 확인하세요.

Remote API 사용 설정

먼저 app.yamlremote_api url 핸들러를 추가합니다. 예를 들면 다음과 같습니다.

- url: /_ah/remote_api
  script: _go_app

그러면 URL /_ah/remote_api가 Go 앱에 매핑됩니다. 이 URL에 대한 액세스는 Remote API 핸들러에 의해 애플리케이션 관리자로 제한됩니다.

그런 다음 .go 소스 파일에 다음 줄을 추가하여 프로젝트의 패키지 중 하나에서 remote_api 패키지를 가져옵니다.

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

프로그램이 초기화되는 동안 remote_api 패키지는 /_ah/remote_api 경로의 핸들러를 등록합니다. 가져오기 선언의 밑줄 표시는 '이 패키지를 가져오지만 직접 사용하지 않을 것'을 의미합니다. 밑줄이 없으면 컴파일 시 '가져 왔지만 사용되지 않음' 오류 메시지가 표시됩니다.

마지막으로 업데이트를 App Engine에 배포합니다. 예를 들면 다음과 같습니다.

gcloud app deploy app.yaml

로컬 클라이언트에서 Remote API 사용

Remote API는 App Engine 서비스를 사용하고 Datastore에 액세스하는 로컬 애플리케이션을 작성하는 데 사용할 수 있습니다. Remote API를 사용하면 액세스 중인 애플리케이션에서 할당량이 사용된다는 점에 유의해야 합니다.

시작하기 전에 App Engine 애플리케이션에서 Remote API가 사용 설정되어 있는지 확인하세요. 로컬 애플리케이션에서 Remote API를 사용하려면 remote_api.NewRemoteContext로 컨텍스트를 생성하고 모든 API 호출에서 일반 App Engine 컨텍스트 대신 사용하면 됩니다.

// 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))
}

이 코드를 실행하려면 다음 패키지를 가져와야 합니다.

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

서버의 호스트 이름과 NewRemoteContext에 대한 호출의 http.Client를 제공해야 합니다. 제공된 http.Client는 각 요청에 필요한 인증 정보를 전달해야 합니다.

위의 샘플에서 golang.org/x/oauth2/google 패키지의 DefaultClient애플리케이션 기본 사용자 인증 정보를 통해 OAuth 2 사용자 인증 정보를 제공합니다.

제한사항 및 권장사항

remote_api 모듈은 기본 App Engine Datastore와 완전히 동일하게 작동되도록 하기 위해 최대한으로 시도합니다. 즉, 경우에 따라 효율성이 떨어지는 작업을 수행하기도 한다는 의미입니다. 따라서 remote_api를 사용할 때는 다음 사항에 주의하시기 바랍니다.

모든 Datastore 요청에 왕복이 필요함

HTTP를 통해 Datastore에 액세스하는 경우 로컬에서 액세스할 때보다 오버헤드와 지연 시간이 좀 더 늘어날 수 있습니다. 시간을 단축하고 부하를 줄이기 위해서는 get과 put 작업을 일괄 처리하고 쿼리 항목을 일괄적으로 가져와 왕복 횟수를 제한해야 합니다. 일괄 작업은 단일 Datastore 작업으로만 간주되기 때문에 이 방식은 remote_api뿐만 아니라 일반적으로 Datastore를 사용할 때도 유용합니다.

remote_api 요청 시 할당량이 사용됨

remote_api가 HTTP를 통해 작동하므로 Datastore를 호출할 때마다 HTTP 요청에 대한 할당량(바이트 단위 입출력)뿐만 아니라 일반적으로 예상되는 Datastore 할당량이 차감됩니다. 일괄 업데이트에 remote_api를 사용하는 경우 이 점을 염두에 두시기 바랍니다.

1MB API 제한 적용

기본 실행에서와 마찬가지로 API 요청 및 응답에는 1MB 제한이 계속 적용됩니다. 특히 항목 크기가 큰 경우 이 한도가 초과되지 않도록 한 번에 수행하는 fetch 또는 put 작업 횟수를 제한해야 할 수 있습니다. 이 경우 왕복을 최소화하는 접근 방식과 상충되기 때문에 요청 또는 응답 크기의 제한을 넘지 않는 범위 내에서 가장 큰 배치를 사용하는 것이 좋습니다. 그러나 대부분의 항목에서는 이 제한이 문제가 되지 않습니다.

쿼리 반복 방지

쿼리를 반복 실행하는 경우 SDK에서는 Datastore의 항목을 20개 단위의 배치로 가져오며 기존의 항목이 소모될 때마다 새 배치를 가져옵니다. 각 배치는 remote_api를 통해 별도 요청으로 가져와야 하기 때문에 효율성이 떨어질 수밖에 없습니다. 대신 remote_api는 오프셋 기능을 사용하여 배치별로 완전히 새로운 쿼리를 실행하므로 결과의 정확성이 높아집니다.

필요한 항목 수를 알고 있다면 필요한 숫자를 요청하는 방식으로 하나의 요청에서 전체 가져오기를 처리할 수 있습니다.

원하는 항목 수를 모르는 경우에는 커서를 사용하여 큰 결과 집합을 효율적으로 반복할 수 있습니다. 이렇게 하면 일반적인 Datastore 쿼리에 적용되는 1,000개 항목 제한을 피할 수 있습니다.

트랜잭션의 효율성이 떨어짐

remote_api를 통해 트랜잭션을 구현하기 위해 트랜잭션 내에서 가져온 항목에 대한 정보와 트랜잭션 내에서 배치 또는 삭제한 항목의 사본이 축적됩니다. 트랜잭션이 커밋되면 이 모든 정보가 App Engine 서버로 전송되며, 이때 트랜잭션에 사용된 모든 항목을 다시 가져와 변경되지 않았는지 확인한 다음 트랜잭션에 적용된 모든 변경사항에 대해 put 및 delete 작업을 수행한 후 커밋합니다. 충돌이 발생하면 서버가 트랜잭션을 롤백하고 클라이언트 측에 이를 알립니다. 그러면 클라이언트가 전체 프로세스를 처음부터 다시 반복해야 합니다.

이 접근 방식은 제대로 작동하며 트랜잭션에서 제공된 기능을 로컬 Datastore에 정확하게 복제하지만, 다소 비효율적입니다. 따라서 어떤 경우에도 꼭 필요한 경우에만 트랜잭션을 사용해야 하며 효율성을 위해 실행할 트랜잭션의 수와 복잡성을 제한해야 합니다.