From f8737684ef9795dcd3bdd174fe74d9be601d966e Mon Sep 17 00:00:00 2001 From: wood chen Date: Sun, 13 Oct 2024 11:57:07 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8E=BB=E9=87=8D,=20=E6=97=A0=E9=87=8D?= =?UTF-8?q?=E5=A4=8D=E9=80=89=E6=8B=A9(3=E6=AC=A1=E5=86=85),=20=E6=B4=97?= =?UTF-8?q?=E7=89=8C=E7=AE=97=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.go | 75 +++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 62 insertions(+), 13 deletions(-) diff --git a/main.go b/main.go index b6b7442..5ac4598 100644 --- a/main.go +++ b/main.go @@ -4,7 +4,6 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "log" "math/rand" "net" @@ -20,16 +19,62 @@ const ( port = ":5003" cacheDuration = 24 * time.Hour requestTimeout = 10 * time.Second + noRepeatCount = 3 // 在这个次数内不重复选择 ) var ( csvPathsCache map[string]map[string]string lastFetchTime time.Time - csvCache = make(map[string][]string) + csvCache = make(map[string]*URLSelector) mu sync.RWMutex rng *rand.Rand ) +type URLSelector struct { + URLs []string + CurrentIndex int + RecentUsed map[string]int +} + +func NewURLSelector(urls []string) *URLSelector { + return &URLSelector{ + URLs: urls, + CurrentIndex: 0, + RecentUsed: make(map[string]int), + } +} + +func (us *URLSelector) ShuffleURLs() { + for i := len(us.URLs) - 1; i > 0; i-- { + j := rng.Intn(i + 1) + us.URLs[i], us.URLs[j] = us.URLs[j], us.URLs[i] + } +} + +func (us *URLSelector) GetRandomURL() string { + if us.CurrentIndex == 0 { + us.ShuffleURLs() + } + + for i := 0; i < len(us.URLs); i++ { + url := us.URLs[us.CurrentIndex] + us.CurrentIndex = (us.CurrentIndex + 1) % len(us.URLs) + + if us.RecentUsed[url] < noRepeatCount { + us.RecentUsed[url]++ + // 如果某个URL使用次数达到上限,从RecentUsed中移除 + if us.RecentUsed[url] == noRepeatCount { + delete(us.RecentUsed, url) + } + return url + } + } + + // 如果所有URL都被最近使用过,重置RecentUsed并返回第一个URL + us.RecentUsed = make(map[string]int) + return us.URLs[0] +} + func main() { source := rand.NewSource(time.Now().UnixNano()) rng = rand.New(source) @@ -89,7 +134,7 @@ func loadCSVPaths() error { jsonPath := filepath.Join("public", "url.json") log.Printf("Attempting to read file: %s", jsonPath) - data, err := ioutil.ReadFile(jsonPath) + data, err := os.ReadFile(jsonPath) if err != nil { return fmt.Errorf("failed to read url.json: %w", err) } @@ -108,36 +153,40 @@ func loadCSVPaths() error { return nil } -func getCSVContent(path string) ([]string, error) { +func getCSVContent(path string) (*URLSelector, error) { mu.RLock() - content, exists := csvCache[path] + selector, exists := csvCache[path] mu.RUnlock() if exists { - return content, nil + return selector, nil } fullPath := filepath.Join("public", path) log.Printf("Attempting to read file: %s", fullPath) - fileContent, err := ioutil.ReadFile(fullPath) + fileContent, err := os.ReadFile(fullPath) if err != nil { return nil, fmt.Errorf("error reading CSV content: %w", err) } lines := strings.Split(string(fileContent), "\n") + uniqueURLs := make(map[string]bool) var fileArray []string for _, line := range lines { trimmed := strings.TrimSpace(line) - if trimmed != "" && !strings.HasPrefix(trimmed, "#") { + if trimmed != "" && !strings.HasPrefix(trimmed, "#") && !uniqueURLs[trimmed] { fileArray = append(fileArray, trimmed) + uniqueURLs[trimmed] = true } } + selector = NewURLSelector(fileArray) + mu.Lock() - csvCache[path] = fileArray + csvCache[path] = selector mu.Unlock() - return fileArray, nil + return selector, nil } func handleAPIRequest(w http.ResponseWriter, r *http.Request) { @@ -172,19 +221,19 @@ func handleAPIRequest(w http.ResponseWriter, r *http.Request) { return } - fileArray, err := getCSVContent(csvPath) + selector, err := getCSVContent(csvPath) if err != nil { http.Error(w, "Failed to fetch CSV content", http.StatusInternalServerError) log.Println("Error fetching CSV content:", err) return } - if len(fileArray) == 0 { + if len(selector.URLs) == 0 { http.Error(w, "No content available", http.StatusNotFound) return } - randomURL := fileArray[rng.Intn(len(fileArray))] + randomURL := selector.GetRandomURL() duration := time.Since(start) log.Printf("Request: %s %s from %s - Duration: %v - Redirecting to: %s\n",