Golang中读写Cloud Firestore时遇到高延迟(>500ms)问题如何解决

Golang中读写Cloud Firestore时遇到高延迟(>500ms)问题如何解决 我的 Firestore 集合包含少于 100 个文档,我正在使用 firestore-go-SDK 来建立连接。以下是我用来查询 Firestore 的代码。

func connectFirestore() {
	ctx := context.Background()

	// 设置您的 Google Cloud Platform 项目 ID。
	projectID := <Project-ID>

	client, err := firestore.NewClient(ctx, projectID)
	if err != nil {
		log.Println("Failed to create client: " + err.Error())
	}
	defer client.Close()

	startTime := time.Now()
	iter := client.Collection("User").Documents(ctx)
	for {
		_, err := iter.Next()
		if err == iterator.Done {
			break
		} else if err != nil {
            log.Println("Error in iterator ", err)
			break
		}
	}
	endTime := time.Now()
	log.Println("Latency of DB call ", endTime.Sub(startTime))

}

当我尝试使用 Node.js 应用程序访问同一个集合时,我得到的延迟小于 10 毫秒。NodeJS 代码如下:

async function firebaseGetData() {
    const db = new Firestore({
        projectId: <Project-ID>,
        keyFilename: <Project-Key-File>,
    });
    firebaseGetData(db)
    var startTime = Date.now()
    const snapshot = await db.collection('User').get();
    var startTime = Date.now()
    snapshot.forEach((doc) => {
        console.log(doc.id, '=>', doc.data());
        var endTime = Date.now()
        var latency = endTime - startTime
        console.log("Latency " + latency)
    });
}

更多关于Golang中读写Cloud Firestore时遇到高延迟(>500ms)问题如何解决的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang中读写Cloud Firestore时遇到高延迟(>500ms)问题如何解决的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Golang中处理Firestore高延迟问题,通常与连接池、上下文配置和批量获取方式有关。以下是优化方案:

func connectFirestoreOptimized() {
    // 使用带超时的上下文,避免无限期等待
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    projectID := <Project-ID>
    
    // 配置客户端选项,启用连接池
    client, err := firestore.NewClient(ctx, projectID, option.WithGRPCConnectionPool(10))
    if err != nil {
        log.Fatalf("Failed to create client: %v", err)
    }
    defer client.Close()

    startTime := time.Now()
    
    // 使用GetAll一次性获取所有文档,而不是迭代器
    docs, err := client.Collection("User").Documents(ctx).GetAll()
    if err != nil {
        log.Fatalf("Failed to get documents: %v", err)
    }
    
    endTime := time.Now()
    log.Printf("Latency of DB call: %v, Documents fetched: %d", 
        endTime.Sub(startTime), len(docs))
}

// 另一种优化方案:使用带重试的查询
func connectFirestoreWithRetry() {
    ctx := context.Background()
    projectID := <Project-ID>
    
    // 配置重试策略
    retryPolicy := []gax.CallOption{
        gax.WithRetry(func() gax.Retryer {
            return gax.OnCodes([]codes.Code{
                codes.DeadlineExceeded,
                codes.Unavailable,
            }, gax.Backoff{
                Initial:    100 * time.Millisecond,
                Max:        2000 * time.Millisecond,
                Multiplier: 1.5,
            })
        }),
    }
    
    client, err := firestore.NewClient(ctx, projectID, 
        option.WithGRPCDialOption(grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(1024*1024*5))),
    )
    if err != nil {
        log.Fatalf("Failed to create client: %v", err)
    }
    defer client.Close()

    startTime := time.Now()
    
    // 使用带重试的查询
    iter := client.Collection("User").Documents(ctx)
    iter = firestore.NewDocumentIterator(iter, retryPolicy...)
    
    var count int
    for {
        _, err := iter.Next()
        if err == iterator.Done {
            break
        }
        if err != nil {
            log.Printf("Error in iterator: %v", err)
            break
        }
        count++
    }
    
    endTime := time.Now()
    log.Printf("Latency with retry: %v, Documents: %d", 
        endTime.Sub(startTime), count)
}

// 使用批量读取优化小数据集
func batchReadFirestore() {
    ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
    defer cancel()

    client, err := firestore.NewClient(ctx, <Project-ID>)
    if err != nil {
        log.Fatalf("Failed to create client: %v", err)
    }
    defer client.Close()

    startTime := time.Now()
    
    // 预取所有文档ID(如果已知)
    // 然后使用批量读取
    collectionRef := client.Collection("User")
    
    // 如果文档ID已知,可以直接批量获取
    // docRefs := []*firestore.DocumentRef{...}
    // docs, err := client.GetAll(ctx, docRefs)
    
    // 对于未知文档,使用查询优化
    query := collectionRef.Limit(100).OrderBy(firestore.DocumentID)
    docs, err := query.Documents(ctx).GetAll()
    
    if err != nil {
        log.Fatalf("Failed to batch read: %v", err)
    }
    
    endTime := time.Now()
    log.Printf("Batch read latency: %v, Documents: %d", 
        endTime.Sub(startTime), len(docs))
}

关键优化点:

  1. 使用WithTimeout上下文避免无限等待
  2. 配置WithGRPCConnectionPool启用连接池
  3. 使用GetAll()替代迭代器减少RPC调用次数
  4. 对于小数据集,一次性获取比迭代更高效
  5. 考虑添加重试机制处理临时网络问题

Node.js SDK可能默认启用了某些优化,而Go SDK需要显式配置。

回到顶部