package service import ( "context" "fmt" "github.com/go-nunu/nunu-layout-advanced/internal/repository" "github.com/sourcegraph/conc/pool" "math/rand" "strconv" "strings" "sync" "time" ) type GenerateIpService interface { GenerateMultipleLoopbackIps(ctx context.Context, count ...int) ([]string, error) } func NewGenerateIpService( service *Service, gameShieldRepository repository.GameShieldRepository, ) GenerateIpService { return &generateIpService{ Service: service, gameShieldRepository: gameShieldRepository, } } type generateIpService struct { *Service gameShieldRepository repository.GameShieldRepository } // GenerateMultipleLoopbackIps 生成多个唯一127.0.0.0/8范围IP地址 // count 指定需要生成的IP地址数量,默认为10 // 返回生成的唯一IP地址数组和可能的错误 func (s *generateIpService) GenerateMultipleLoopbackIps(ctx context.Context, count ...int) ([]string, error) { requiredCount := 10 if len(count) > 0 && count[0] > 0 { requiredCount = count[0] } maxAttempts := 1000 // 最大尝试次数 attempts := 0 // 使用互斥锁保护结果集 var mu sync.Mutex collected := make([]string, 0, requiredCount) checkedSegments := make(map[string]struct{}, maxAttempts) // 使用一个固定的随机源以提高性能 rnd := rand.New(rand.NewSource(time.Now().UnixNano())) for len(collected) < requiredCount && attempts < maxAttempts { // 随机生成多个x/z段组合,批量处理 candidates := make([]struct{ x, z int }, 0, 5) for i := 0; i < 5 && len(candidates) < 5; i++ { x := rnd.Intn(256) z := rnd.Intn(256) segmentKey := fmt.Sprintf("%d-%d", x, z) // 跳过已检查过的段 mu.Lock() _, exists := checkedSegments[segmentKey] if !exists { checkedSegments[segmentKey] = struct{}{} mu.Unlock() candidates = append(candidates, struct{ x, z int }{x, z}) } else { mu.Unlock() } } // 使用conc/pool包并发处理候选IP段 // 创建一个工作池,限制并发数为4,并收集错误 p := pool.New().WithMaxGoroutines(4).WithErrors() // 存储结果 var results [][]string // 对每个候选项并发处理 for _, candidate := range candidates { // 创建副本避免闭包捕获循环变量 candidate := candidate p.Go(func() error { if ctx.Err() != nil { return ctx.Err() } x, z := candidate.x, candidate.z prefix := fmt.Sprintf("127.%d.%d.", x, z) // 查询数据库中该前缀已存在的IP existingIps, err := s.gameShieldRepository.GetGameShieldExistingIps(ctx, prefix) if err != nil { // 返回错误以便收集 return fmt.Errorf("获取IP %s 前缀已存在的记录失败: %w", prefix, err) } // 使用map标记已存在的第四段 existingFourth := make(map[int]bool, len(existingIps)) for _, ip := range existingIps { parts := strings.Split(ip, ".") if len(parts) == 4 { fourth, err := strconv.Atoi(parts[3]) if err == nil { // 排除127.0.0.1的情况 if x == 0 && z == 0 && fourth == 1 { continue } existingFourth[fourth] = true } } } // 快速估算可用数量 availableCount := 256 - len(existingFourth) // 排除127.0.0.1的特殊情况 if x == 0 && z == 0 && !existingFourth[1] { availableCount-- } // 如果可用数量太少,可以跳过 if availableCount <= 0 { return nil } // 预分配足够大的切片避免多次扩容 available := make([]string, 0, availableCount) // 如果可用数量很多,可以只收集部分,避免不必要的工作 collectLimit := min(availableCount, requiredCount*2) collectCount := 0 // 随机选择起始点,避免每次都从0开始 startPoint := rnd.Intn(256) for i := 0; i < 256 && collectCount < collectLimit; i++ { fourth := (startPoint + i) % 256 if existingFourth[fourth] { continue } // 确保不生成127.0.0.1 if x == 0 && z == 0 && fourth == 1 { continue } available = append(available, fmt.Sprintf("127.%d.%d.%d", x, z, fourth)) collectCount++ } // 随机打乱结果 for i := len(available) - 1; i > 0; i-- { j := rnd.Intn(i + 1) available[i], available[j] = available[j], available[i] } // 将结果添加到结果集 mu.Lock() results = append(results, available) mu.Unlock() return nil }) } // 等待所有goroutine完成并检查错误 if err := p.Wait(); err != nil { // 这里可以选择记录错误但继续尝试,或者直接返回错误 // 我们选择只有当收集的IP不足时才返回错误 if len(collected) < requiredCount { // 如果已经是最后一次尝试且收集的IP不足,返回错误 if attempts >= maxAttempts-1 { return nil, fmt.Errorf("生成IP地址失败: %w", err) } // 否则继续尝试下一批候选IP段 } } // 收集结果 for _, ips := range results { mu.Lock() for _, ip := range ips { if len(collected) >= requiredCount { break } collected = append(collected, ip) } mu.Unlock() } attempts++ // 如果已经收集足够的IP,跳出循环 mu.Lock() hasEnough := len(collected) >= requiredCount mu.Unlock() if hasEnough { break } } mu.Lock() defer mu.Unlock() if len(collected) < requiredCount { return nil, fmt.Errorf("无法生成足够的唯一IP地址,地址池可能已耗尽,仅生成了%d个", len(collected)) } // 最终随机排序 for i := len(collected) - 1; i > 0; i-- { j := rand.Intn(i + 1) collected[i], collected[j] = collected[j], collected[i] } return collected[:requiredCount], nil }