|
@@ -1,6 +1,6 @@
|
|
|
using dodohold.core;
|
|
using dodohold.core;
|
|
|
-using Org.BouncyCastle.Bcpg.OpenPgp;
|
|
|
|
|
-using YunhuiKit;
|
|
|
|
|
|
|
+using Org.BouncyCastle.Bcpg.OpenPgp;
|
|
|
|
|
+using YunhuiKit;
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace molilian.core
|
|
namespace molilian.core
|
|
@@ -22,7 +22,7 @@ namespace molilian.core
|
|
|
_end_point = Environment.GetEnvironmentVariable("EndPoint");
|
|
_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> _cached;
|
|
|
private static IEnumerable<TkPoolDTO> _all_cached;
|
|
private static IEnumerable<TkPoolDTO> _all_cached;
|
|
@@ -61,12 +61,12 @@ namespace molilian.core
|
|
|
}
|
|
}
|
|
|
return null;
|
|
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);
|
|
var list = await ListAsync().ConfigureAwait(false);
|
|
|
- if (!list.Any())
|
|
|
|
|
- return null;
|
|
|
|
|
-
|
|
|
|
|
|
|
+ if (!list.Any())
|
|
|
|
|
+ return null;
|
|
|
|
|
+
|
|
|
// 获取所有账号(包括离线账号)
|
|
// 获取所有账号(包括离线账号)
|
|
|
var allAccounts = await AllListAsync().ConfigureAwait(false);
|
|
var allAccounts = await AllListAsync().ConfigureAwait(false);
|
|
|
if (allAccounts == null)
|
|
if (allAccounts == null)
|
|
@@ -127,9 +127,9 @@ namespace molilian.core
|
|
|
|
|
|
|
|
if (filteredList.Count == 0) return null;
|
|
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)
|
|
foreach (var item in filteredList)
|
|
|
{
|
|
{
|
|
@@ -146,45 +146,55 @@ namespace molilian.core
|
|
|
.Where(e => !TkEndpointManager.IsEndpointSuspended(item.id, e.endpoint)) // 直接检查端点对象
|
|
.Where(e => !TkEndpointManager.IsEndpointSuspended(item.id, e.endpoint)) // 直接检查端点对象
|
|
|
.ToList();
|
|
.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)
|
|
private static async Task<bool> FilterNodesAsync(TkPoolDTO item, TkAction action)
|
|
|
{
|
|
{
|
|
|
if (!PassesFastAccountChecks(item, action)) return false;
|
|
if (!PassesFastAccountChecks(item, action)) return false;
|