【问题标题】:Importing Entities Into Local GCP Datastore Emulator将实体导入本地 GCP 数据存储模拟器
【发布时间】:2019-01-13 23:49:37
【问题描述】:

我可以使用这个命令轻松地将实体导出到存储桶中:

gcloud datastore export --kinds="KIND1,KIND2" --namespaces="NAMESPACE1,NAMESPACE2" gs://${BUCKET}

根据文档导入可以这样完成:

gcloud datastore import gs://${BUCKET}/[PATH]/[FILE].overall_export_metadata

或者像这样:

curl \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H "Content-Type: application/json" \
https://datastore.googleapis.com/v1/projects/${PROJECT_ID}:import \
-d '{
"inputUrl": "gs://'${BUCKET}'/[PATH]/[FILE].overall_export_metadata",
}'

我的数据存储模拟器在localhost:8081 上运行,无论如何我可以使用此 curl 命令将数据导入模拟器吗?文档中没有关于它的任何内容,我尝试猜测很多网址但没有任何效果。

如果这是不可能的,有没有其他方法可以填充我的本地模拟器,或者更好地将本地应用引擎连接到生产数据存储区?

显然曾经有一种使用 csv 文件导出和导入的方法:

Google cloud datastore emulator init data

但这已经被弃用了。

【问题讨论】:

  • 一旦为gcloud datastore 命令设置环境以与本地数据存储模拟器一起工作(请参阅this answer),可能可以将实体导入模拟器.试一试(我实际上并没有尝试过,我不确定在那种模式下gcloud 命令是否能够访问 GCS 中的导出文件)。
  • @DanCornilescu 我刚刚尝试过,它给了我一个 404 /v1/projects/<project_id>:[import|export]。显然,模拟器只实现了subset of the Cloud Datastore Data API methods,导入/导出功能来自Admin API。
  • @Jofre bummer... 这使得答案是否定的:(
  • 显然,import/export features have been recently added to the emulator,但我没有测试过它们。

标签: google-cloud-platform google-cloud-datastore


【解决方案1】:

数据存储模拟器现在支持导入和导出:

进口:

curl -X POST localhost:8081/v1/projects/[PROJECT_ID]:import \
-H 'Content-Type: application/json' \
-d '{"input_url":"[ENTITY_EXPORT_FILES]"}'

导出:

curl -X POST localhost:8081/v1/projects/[PROJECT_ID]:export \
-H 'Content-Type: application/json' \
-d '{"output_url_prefix":"EXPORT_DIRECTORY"}'

https://cloud.google.com/datastore/docs/tools/emulator-export-import

【讨论】:

  • PROJECT_ID 是否匹配本地项目?
  • 我相信即使在本地工作时您也需要指定 PROJECT_ID。您应该将其设置为您使用的 GCP 项目的名称。
【解决方案2】:

由于 Datastore 模拟器似乎没有导入功能,因此您可以构建自己的。

就像在脚本中创建两个客户端一样简单,一个用于远程(云)数据存储,一个用于本地数据存储模拟器。由于云客户端库支持模拟器,您可以深入研究代码以了解如何正确建立连接。

我正是为 云客户端库做的,并想出了这个脚本:

package main

import (
        "context"
        "fmt"
        "os"
        "time"

        "cloud.google.com/go/datastore"
        "google.golang.org/api/iterator"
        "google.golang.org/api/option"
        "google.golang.org/grpc"
)

const (
        projectId = "<PROJECT_ID>"
        namespace = "<NAMESPACE>"
        kind      = "<KIND>"
        emulatorHost = "<EMULATOR_HOST>:<EMULATOR_PORT>"
)

func main() {

        ctx := context.Background()

        // Create the Cloud Datastore client
        remoteClient, err := datastore.NewClient(ctx, projectId, option.WithGRPCConnectionPool(50))
        if err != nil {
                fmt.Fprintf(os.Stderr, "Could not create remote datastore client: %v \n", err)
        }

        // Create the local Datastore Emulator client
        o := []option.ClientOption{
                option.WithEndpoint(emulatorHost),
                option.WithoutAuthentication(),
                option.WithGRPCDialOption(grpc.WithInsecure()),
                option.WithGRPCConnectionPool(50),
        }
        localClient, err := datastore.NewClient(ctx, projectId, o...)
        if err != nil {
                fmt.Fprintf(os.Stderr, "Could not create local datastore client: %v \n", err)
        }

        // Create the query
        q := datastore.NewQuery(kind).Namespace(namespace)

        //Run the query and handle the received entities
        start := time.Now() // This is just to calculate the rate
        for it, i := remoteClient.Run(ctx, q), 1; ; i++ {
                x := &arbitraryEntity{}

                // Get the entity
                key, err := it.Next(x)
                if err == iterator.Done {
                        break
                }
                if err != nil {
                        fmt.Fprintf(os.Stderr, "Error retrieving entity: %v \n", err)
                }

                // Insert the entity into the emulator
                _, err = localClient.Put(ctx, key, x)
                if err != nil {
                        fmt.Fprintf(os.Stderr, "Error saving entity: %v \n", err)
                }

                // Print stats
                go fmt.Fprintf(os.Stdout, "\rCopied %v entities. Rate: %v/s", i, i/int(time.Since(start).Seconds()))
        }
        fmt.Fprintln(os.Stdout)
}


// Declare a struct capable of handling any type of entity.
// It implements the PropertyLoadSaver interface
type arbitraryEntity struct {
        properties []datastore.Property
}

func (e *arbitraryEntity) Load(ps []datastore.Property) error {
        e.properties = ps
        return nil
}

func (e *arbitraryEntity) Save() ([]datastore.Property, error) {
        return e.properties, nil
}

有了这个,我得到了大约 700 个实体/秒的速率,但它可能会根据您拥有的实体而发生很大变化。

不要设置 DATASTORE_EMULATOR_HOST 环境变量,因为脚本会手动创建与本地模拟器的连接,并且您希望库自动连接到 Cloud Datastore。

脚本可以大大改进:远程和本地都使用 GRPC,因此您可以使用一些原始魔法来避免对消息进行编码解码。使用批处理进行上传也会有所帮助,以及使用 Go 的并发技巧。您甚至可以通过编程方式获取命名空间和种类,因此您不需要为每个实体运行它。

但是,我认为这个简单的概念证明可以帮助您了解如何开发自己的工具来运行导入。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-16
    • 2021-06-27
    • 2010-10-28
    • 2018-07-01
    • 2020-01-10
    • 1970-01-01
    相关资源
    最近更新 更多