Skip to content

Concurrency

sungaowei edited this page Oct 16, 2026 · 2 revisions

并发控制 (Concurrency)

English Version | 简体中文


typesafe.Client 是并发安全的,可由任意数量的 Goroutine 同时复用。 客户端内部不保存任何特定请求的可变状态,初始化完成后其配置均为只读,底层的 http.Client 会自动管理高效的 HTTP 连接池(MaxIdleConnsPerHost: 100)。

推荐在整个应用程序生命周期中只创建一个 Client 实例并全局共享,每次请求调用时传入独立的 context.Context 控制生命周期:

var wg sync.WaitGroup
for _, ticket := range tickets {
	wg.Add(1)
	go func(ticket string) {
		defer wg.Done()
		response, err := client.SystemOne(ctx, ticket, questions)
		if err != nil {
			log.Printf("%s 评估失败: %v", ticket, err)
			return
		}
		fmt.Printf("%s -> %s\n", ticket, response.Choices()["category"].Choice)
	}(ticket)
}
wg.Wait()

控制并发度(有界并发)

无限制地为海量任务启动 Goroutine 会瞬间发起成百上千个并发网络连接,可能迅速耗尽系统文件描述符或触发服务端的速率限制。 推荐使用带缓冲的通道作为信号量,或采用工作协程池(Worker Pool)模式:

1. 信号量模式 (Semaphore)

sem := make(chan struct{}, 16) // 最多允许 16 个并发请求在传输中
var wg sync.WaitGroup

for _, ticket := range tickets {
	wg.Add(1)
	go func(t string) {
		defer wg.Done()
		sem <- struct{}{}        // 获取令牌
		defer func() { <-sem }() // 释放令牌

		response, err := client.SystemOne(ctx, t, questions)
		// 处理结果...
	}(ticket)
}
wg.Wait()

2. 工作协程池模式 (Worker Pool)

jobs := make(chan Ticket, 100)
var wg sync.WaitGroup

// 启动与 CPU 核心数相当的常驻工作 Goroutine
for range runtime.NumCPU() {
	wg.Add(1)
	go func() {
		defer wg.Done()
		for ticket := range jobs {
			response, err := client.SystemOne(ctx, ticket, questions)
			// 处理工单...
		}
	}()
}

// 投递任务
for _, t := range tickets {
	jobs <- t
}
close(jobs)
wg.Wait()

上下文取消与超时

每个 SDK 方法均接受 context.Context 参数。只要取消 Context 或达到 Deadline 超时,当前网络请求将被立即中断:

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

response, err := client.SystemOne(ctx, state, questions)

Important

被主动取消或超时的上下文绝不会触发重试:SDK 会立即返回失败,而不会在调用方已经放弃该请求时继续浪费时间重试。参见 重试策略 (Retry Policy)。


释放连接 (Close)

client.Close() 会释放底层空闲连接池所占用的资源。在程序准备优雅退出时调用一次即可。 该方法是幂等安全的,重复调用将直接返回第一次调用时的结果。

Clone this wiki locally