fast-proxy-balancer
Version:
Smart and flexible proxy balancer for Node.js
131 lines • 4.88 kB
JavaScript
import { testProxy } from './ProxyTester.js';
import { sortProxies } from './ProxySelector.js';
import { loadProxiesFromFile } from './utils/loadProxy.js';
import { log } from './utils/logger.js';
import { createAgent } from './utils/createAgent.js';
class ProxyBalancer {
proxies = [];
proxyListOrFile;
maxFailures;
testTimeout;
refreshInterval;
concurrentTests;
testUrl;
constructor(proxyListOrFile, options = {}) {
this.proxyListOrFile = proxyListOrFile;
this.maxFailures = options.maxFailures ?? 3;
this.testTimeout = options.testTimeout ?? 5000;
this.refreshInterval = options.refreshInterval ?? 60000;
this.concurrentTests = options.concurrentTests ?? 5;
this.testUrl = options.testUrl ?? 'https://example.com';
}
async init() {
if (typeof this.proxyListOrFile === 'string') {
log(`Loading proxies from file: ${this.proxyListOrFile}`);
this.proxies = (await loadProxiesFromFile(this.proxyListOrFile)).map(url => ({
url,
latency: Infinity,
failures: 0,
lastUsed: 0
}));
}
else if (Array.isArray(this.proxyListOrFile)) {
log('Loading proxies from array');
this.proxies = this.proxyListOrFile.map(url => ({
url,
latency: Infinity,
failures: 0,
lastUsed: 0
}));
}
else {
throw new Error('Invalid proxy source: must be a file path or an array');
}
}
async refreshProxies() {
log('Refreshing proxies...');
const promises = this.proxies.map((proxy, index) => new Promise(resolve => setTimeout(async () => {
try {
await testProxy(proxy, this.testUrl, this.testTimeout);
}
catch (error) {
log(`Error testing proxy ${proxy.url}: ${error.message}`, 'error');
}
resolve();
}, (index % this.concurrentTests) * 500)));
await Promise.all(promises);
log('Proxy refresh completed');
}
async getBestProxy() {
if (this.proxies.length === 0) {
throw new Error('No proxies available');
}
this.proxies = sortProxies(this.proxies);
const availableProxies = this.proxies.filter(p => p.failures < this.maxFailures);
if (availableProxies.length === 0) {
throw new Error('All proxies have exceeded the max failure threshold');
}
const bestProxy = availableProxies[0];
bestProxy.lastUsed = Date.now();
const agent = await createAgent(bestProxy.url);
log(`Best proxy selected: ${bestProxy.url}`);
return {
url: bestProxy.url,
agent
};
}
startAutoRefresh() {
setInterval(() => this.refreshProxies(), this.refreshInterval);
}
getProxyStats() {
return this.proxies.map(proxy => ({
url: proxy.url,
latency: isFinite(proxy.latency) ? `${proxy.latency} ms` : 'N/A',
failures: proxy.failures,
lastUsed: proxy.lastUsed
? new Date(proxy.lastUsed).toLocaleString()
: 'Never used'
}));
}
sortByLatency() {
const sorted = this.proxies
.filter(p => p.failures < this.maxFailures && isFinite(p.latency))
.sort((a, b) => a.latency - b.latency);
return sorted.map(proxy => ({
url: proxy.url,
latency: `${proxy.latency} ms`,
failures: proxy.failures,
lastUsed: proxy.lastUsed
? new Date(proxy.lastUsed).toLocaleString()
: 'Never used'
}));
}
getFailedProxies() {
const failed = this.proxies.filter(p => p.failures >= this.maxFailures);
return failed.map(proxy => ({
url: proxy.url,
latency: isFinite(proxy.latency) ? `${proxy.latency} ms` : 'N/A',
failures: proxy.failures,
lastUsed: proxy.lastUsed
? new Date(proxy.lastUsed).toLocaleString()
: 'Never used'
}));
}
getStatsSummary() {
const total = this.proxies.length;
const active = this.proxies.filter(p => p.failures < this.maxFailures);
const failed = total - active.length;
const avgLatency = active.length > 0
? Math.round(active.reduce((sum, p) => sum + (isFinite(p.latency) ? p.latency : 0), 0) /
active.length)
: null;
return {
total,
active: active.length,
failed,
avgLatency: avgLatency !== null ? `${avgLatency} ms` : 'N/A'
};
}
}
export { ProxyBalancer };
//# sourceMappingURL=ProxyBalancer.js.map