Просмотр исходного кода

feat(淘宝账号池): 增加账号级 QPS 限制

为淘宝账号配置新增 qps_limit,并支持后台修改。
账号选择时使用滑动窗口限流,超限后回退其他候选账号。
dodo hold 1 неделя назад
Родитель
Сommit
0c45b173a1

+ 4 - 2
molilian.api/Controllers/admin/TaobaoController.cs

@@ -101,8 +101,10 @@ namespace molilian.api.Controllers
                            .Update();
                     log.SaveAsync();
                     break;
-                case "time_range":
-                    int.TryParse(val, out int iVal);
+                case "time_range":
+                case "qps_limit":
+                    int.TryParse(val, out int iVal);
+                    if ("qps_limit".Equals(name) && iVal < 0) iVal = 0;
                     result = new DBContext.Table("tk_pool")
                            .Add(name, iVal)
                            .Add("last_time", DateTime.Now)

+ 59 - 49
molilian.core/Core/taoke/TkPoolCore.cs

@@ -1,6 +1,6 @@
 using dodohold.core;
-using Org.BouncyCastle.Bcpg.OpenPgp;
-using YunhuiKit;
+using Org.BouncyCastle.Bcpg.OpenPgp;
+using YunhuiKit;
 
 
 namespace molilian.core
@@ -22,7 +22,7 @@ namespace molilian.core
             _end_point = Environment.GetEnvironmentVariable("EndPoint");
         }
 
-        private static SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
+        private static SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
 
         private static IEnumerable<TkPoolDTO> _cached;
         private static IEnumerable<TkPoolDTO> _all_cached;
@@ -61,12 +61,12 @@ namespace molilian.core
             }
             return null;
         }
-        public static async Task<TkPoolDTO> GetOneAsync(TkAction action, bool isTaobaoUrl, string riskStrategy, int launchScene, string parse_type = "", string strategy_id = "")
+        public static async Task<TkPoolDTO> GetOneAsync(TkAction action, bool isTaobaoUrl, string riskStrategy, int launchScene, string parse_type = "", string strategy_id = "")
         {
             var list = await ListAsync().ConfigureAwait(false);
-            if (!list.Any())
-                return null;
-
+            if (!list.Any())
+                return null;
+
             // 获取所有账号(包括离线账号)
             var allAccounts = await AllListAsync().ConfigureAwait(false);
             if (allAccounts == null)
@@ -127,9 +127,9 @@ namespace molilian.core
 
             if (filteredList.Count == 0) return null;
 
-            // 1. 筛选出有可用端点的账号池,并计算权重
-            var weightedAccounts = new List<(TkPoolDTO account, int weight)>();
-            var random = new Random(Guid.NewGuid().GetHashCode()); // 避免重复种子问题
+            // 1. 筛选出有可用端点的账号池,并计算权重
+            var weightedAccounts = new List<(TkPoolDTO account, int weight)>();
+            var random = new Random(Guid.NewGuid().GetHashCode()); // 避免重复种子问题
 
             foreach (var item in filteredList)
             {
@@ -146,45 +146,55 @@ namespace molilian.core
                     .Where(e => !TkEndpointManager.IsEndpointSuspended(item.id, e.endpoint)) // 直接检查端点对象
                     .ToList();
 
-                if (availableEndpoints.Count == 0) continue;
-
-                // 计算账号权重(基于剩余调用量)
-                int weight = availableEndpoints.Sum(e =>
-                    e.hourly_calls_limit <= 0 ? 3600 : e.hourly_calls_limit - e.current_hourly_calls);
-
-                weightedAccounts.Add((item, weight));
-            }
-
-            if (weightedAccounts.Count == 0) return null;
-
-            // 2. 加权随机选择
-            int totalWeight = weightedAccounts.Sum(x => x.weight);
-            int randomNumber = random.Next(0, totalWeight);
-
-            foreach (var (account, weight) in weightedAccounts)
-            {
-                if (randomNumber < weight)
-                {
-                    if (await FilterNodesAsync(account, action).ConfigureAwait(false))
-                        return account;
-                    break; // 如果过滤失败,跳出当前循环
-                }
-                randomNumber -= weight;
-            }
-
-            // 3. 如果加权选择失败,回退到简单随机选择
-            var fallbackCandidates = weightedAccounts
-                .OrderBy(_ => random.Next())
-                .Select(x => x.account);
-
-            foreach (var account in fallbackCandidates)
-            {
-                if (await FilterNodesAsync(account, action).ConfigureAwait(false))
-                    return account;
-            }
-            return null;
-        }
-
+                if (availableEndpoints.Count == 0) continue;
+
+                // 计算账号权重(基于剩余调用量)
+                int weight = availableEndpoints.Sum(e =>
+                    e.hourly_calls_limit <= 0 ? 3600 : e.hourly_calls_limit - e.current_hourly_calls);
+
+                weightedAccounts.Add((item, weight));
+            }
+
+            if (weightedAccounts.Count == 0) return null;
+
+            // 2. 保留原有加权随机选择,选中账号超出 QPS 时继续尝试其他账号
+            int totalWeight = weightedAccounts.Sum(x => x.weight);
+            int randomNumber = random.Next(0, totalWeight);
+
+            foreach (var (account, weight) in weightedAccounts)
+            {
+                if (randomNumber < weight)
+                {
+                    if (await FilterNodesAsync(account, action).ConfigureAwait(false) && TryAcquireQps(account))
+                        return account;
+                    break;
+                }
+                randomNumber -= weight;
+            }
+
+            // 3. 选中账号不可用或超过 QPS 时,回退尝试其他候选账号
+            var fallbackCandidates = weightedAccounts
+                .OrderBy(_ => random.Next())
+                .Select(x => x.account);
+
+            foreach (var account in fallbackCandidates)
+            {
+                if (await FilterNodesAsync(account, action).ConfigureAwait(false) && TryAcquireQps(account))
+                    return account;
+            }
+            return null;
+        }
+
+        private static bool TryAcquireQps(TkPoolDTO account)
+        {
+            if (account == null) return false;
+
+            return SpecialBusinessRateLimiter.TryAcquire(
+                $"tk_pool_qps:{account.id}",
+                account.qps_limit,
+                TimeSpan.FromSeconds(1));
+        }
+
         private static async Task<bool> FilterNodesAsync(TkPoolDTO item, TkAction action)
         {
             if (!PassesFastAccountChecks(item, action)) return false;

+ 4 - 3
molilian.core/DTO/alimama/TkPoolDTO.cs

@@ -33,9 +33,10 @@
 
         public int daily_calls_limit { get; set; } = 0;
         public int current_hourly_calls { get; set; } = 0;
-        public int hourly_calls_limit { get; set; } = 0;
-        public int current_daily_calls { get; set; } = 0;
-        public int time_range { get; set; } = 0;
+        public int hourly_calls_limit { get; set; } = 0;
+        public int current_daily_calls { get; set; } = 0;
+        public int time_range { get; set; } = 0;
+        public int qps_limit { get; set; } = 0;
 
 
         public decimal draw_balance { get; set; } = 0;