582337768。群是一堆牛人,你有问题一般不过分,很多人都会解答一二。添加群的时候,请说明来自于 汉克博客园

汉克书

http://hankbook.cn

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理
package main

import (
	"context"
	"fmt"
	"math/rand"
	"sync"
	"time"
)

// 模拟的数据结构
type UserInfo struct {
	ID   int
	Name string
	Age  int
}

type OrderHistory struct {
	Orders []string
	Total  float64
}

type Recommendations struct {
	Items []string
}

// 模拟的数据库/API客户端
type APIClient struct{}

// 1. 获取用户信息(模拟数据库查询)
func (c *APIClient) GetUserInfo(ctx context.Context, userID int) (*UserInfo, error) {
	select {
	case <-ctx.Done():
		fmt.Println("GetUserInfo: 请求被取消")
		return nil, ctx.Err()
	case <-time.After(time.Duration(rand.Intn(2000)) * time.Millisecond): // 模拟0-2秒延迟
		return &UserInfo{ID: userID, Name: fmt.Sprintf("用户%d", userID), Age: 25}, nil
	}
}

// 2. 获取订单历史(模拟外部API调用)
func (c *APIClient) GetOrderHistory(ctx context.Context, userID int) (*OrderHistory, error) {
	// 模拟10%的失败率
	if rand.Intn(10) == 0 {
		return nil, fmt.Errorf("订单服务暂时不可用")
	}

	select {
	case <-ctx.Done():
		fmt.Println("GetOrderHistory: 请求被取消")
		return nil, ctx.Err()
	case <-time.After(time.Duration(800+rand.Intn(1200)) * time.Millisecond): // 0.8-2秒延迟
		return &OrderHistory{
			Orders: []string{"订单A", "订单B", "订单C"},
			Total:  299.99,
		}, nil
	}
}

// 3. 获取推荐内容(模拟缓存查询)
func (c *APIClient) GetRecommendations(ctx context.Context, userID int) (*Recommendations, error) {
	select {
	case <-ctx.Done():
		fmt.Println("GetRecommendations: 请求被取消")
		return nil, ctx.Err()
	case <-time.After(time.Duration(rand.Intn(1000)) * time.Millisecond): // 0-1秒延迟
		return &Recommendations{
			Items: []string{"推荐商品A", "推荐商品B", "推荐商品C"},
		}, nil
	}
}

// 聚合器:并发获取所有数据
type Aggregator struct {
	client *APIClient
}

// Result 包装每个请求的结果
type Result struct {
	DataType string      // 数据类型
	Data     interface{} // 实际数据
	Err      error       // 错误信息
}

// AggregateUserData 并发聚合用户数据
func (a *Aggregator) AggregateUserData(ctx context.Context, userID int) map[string]interface{} {
	// 创建带有超时的上下文(3秒后超时)
	ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
	defer cancel() // 确保释放资源

	// 创建带缓冲的channel,避免goroutine泄漏
	resultChan := make(chan Result, 3)

	var wg sync.WaitGroup

	// 1. 启动获取用户信息的goroutine
	wg.Add(1)
	go func() {
		defer wg.Done()
		userInfo, err := a.client.GetUserInfo(ctx, userID)
		resultChan <- Result{"user_info", userInfo, err}
	}()

	// 2. 启动获取订单历史的goroutine
	wg.Add(1)
	go func() {
		defer wg.Done()
		orders, err := a.client.GetOrderHistory(ctx, userID)
		resultChan <- Result{"order_history", orders, err}
	}()

	// 3. 启动获取推荐内容的goroutine
	wg.Add(1)
	go func() {
		defer wg.Done()
		recommendations, err := a.client.GetRecommendations(ctx, userID)
		resultChan <- Result{"recommendations", recommendations, err}
	}()

	// 独立的goroutine等待所有任务完成并关闭channel
	go func() {
		wg.Wait()
		close(resultChan)
	}()

	// 主goroutine收集结果
	results := make(map[string]interface{})

	// 关键部分:使用select监听多个channel
	for {
		select {
		case <-ctx.Done():
			// 上下文被取消或超时
			fmt.Printf("\n聚合请求超时或被取消: %v\n", ctx.Err())

			// 尝试收集已经完成的结果
			fmt.Println("已收集到的部分结果:")
			for k, v := range results {
				fmt.Printf("  %s: %v\n", k, v)
			}

			// 添加超时标记
			results["timeout"] = true
			return results

		case result, ok := <-resultChan:
			if !ok {
				// channel已关闭,所有结果收集完成
				fmt.Println("\n所有请求完成!")
				return results
			}

			// 处理单个结果
			if result.Err != nil {
				fmt.Printf("获取 %s 失败: %v\n", result.DataType, result.Err)
				results[result.DataType+"_error"] = result.Err.Error()
			} else {
				fmt.Printf("成功获取 %s\n", result.DataType)
				results[result.DataType] = result.Data
			}
		}
	}
}

func main() {
	fmt.Println("=== Go并发编程示例:API数据聚合 ===")

	client := &APIClient{}
	aggregator := &Aggregator{client: client}

	// 测试1: 正常情况
	fmt.Println("测试1: 正常请求(3秒超时)")
	fmt.Println("------------------------")
	start := time.Now()
	results := aggregator.AggregateUserData(context.Background(), 123)
	elapsed := time.Since(start)

	fmt.Printf("\n总耗时: %v\n", elapsed)
	fmt.Printf("收集到的数据类型数量: %d\n\n", len(results))

	// 显示部分结果
	if userInfo, ok := results["user_info"]; ok {
		fmt.Printf("用户信息: %+v\n", userInfo)
	}

	// 测试2: 模拟手动取消
	fmt.Println("\n\n测试2: 手动取消请求")
	fmt.Println("-------------------")
	ctx, cancel := context.WithCancel(context.Background())

	// 启动一个goroutine在500ms后取消请求
	go func() {
		time.Sleep(500 * time.Millisecond)
		fmt.Println("\n发送取消信号...")
		cancel()
	}()

	start = time.Now()
	results = aggregator.AggregateUserData(ctx, 456)
	fmt.Println("results", results)
	elapsed = time.Since(start)

	fmt.Printf("\n请求在 %v 后被取消\n", elapsed)

	// 测试3: 模拟外部中断
	fmt.Println("\n\n测试3: 模拟外部中断处理")
	fmt.Println("-----------------------")

	interruptChan := make(chan struct{}, 1)

	// 创建一个可以响应中断的上下文
	ctx, cancel = context.WithCancel(context.Background())
	defer cancel()

	// 模拟外部中断(如用户按Ctrl+C)
	go func() {
		time.Sleep(700 * time.Millisecond)
		fmt.Println("\n[系统] 收到中断信号")
		interruptChan <- struct{}{}
	}()

	// 启动聚合请求
	go func() {
		results := aggregator.AggregateUserData(ctx, 789)
		fmt.Printf("\n中断测试结果: %d个数据项\n", len(results))
	}()

	// 监听中断信号
	select {
	case <-interruptChan:
		fmt.Println("[系统] 正在取消所有请求...")
		cancel()                           // 取消上下文
		time.Sleep(100 * time.Millisecond) // 给goroutine一些时间响应
	case <-time.After(2 * time.Second):
		fmt.Println("[系统] 未收到中断信号")
	}

	time.Sleep(1 * time.Second) // 等待所有输出完成
	fmt.Println("\n=== 示例结束 ===")
}

posted on 2025-12-05 20:47  汉克书  阅读(16)  评论(0)    收藏  举报