`choise/index.js` 的代码简单调整一下。

pull/311/head
王良 2024-04-30 15:53:17 +08:00
parent 166ccd13a1
commit 17bf7c4e36
4 changed files with 58 additions and 51 deletions

View File

@ -25,40 +25,44 @@ class ChoiceCache {
class DynamicChoice { class DynamicChoice {
constructor (key) { constructor (key) {
this.key = key this.key = key
this.count = {} this.countMap = {} /* ip -> count { value, total, error, keepErrorCount, successRate } */
this.value = null // 当前使用的host
this.backupList = [] // 备选host列表
this.createTime = new Date() this.createTime = new Date()
} }
doRank () { doRank () {
// 将count里面根据权重排序 // 将count里面根据成功率排序
const list = [] const countList = []
for (const key in this.count) { for (const key in this.countMap) {
list.push(this.count[key]) countList.push(this.countMap[key])
} }
list.sort((a, b) => {
// 将countList根据成功率排序
countList.sort((a, b) => {
return b.successRate - a.successRate return b.successRate - a.successRate
}) })
log.info('do rank', JSON.stringify(list))
const backup = list.map(item => item.value)
this.setBackupList(backup) log.info('Do rank:', JSON.stringify(countList))
const newBackupList = countList.map(item => item.value)
this.setBackupList(newBackupList)
} }
/** /**
* 设置新的backup列表 * 设置新的backup列表
* @param backupList * @param newBackupList 新的backupList
*/ */
setBackupList (backupList) { setBackupList (newBackupList) {
this.backup = backupList this.backupList = newBackupList
let defaultTotal = backupList.length let defaultTotal = newBackupList.length
for (const item of backupList) { for (const ip of newBackupList) {
if (this.count[item]) { if (!this.countMap[ip]) {
continue this.countMap[ip] = { value: ip, total: defaultTotal, error: 0, keepErrorCount: 0, successRate: 0.5 }
defaultTotal--
} }
this.count[item] = { value: item, total: defaultTotal, error: 0, keepErrorCount: 0, successRate: 0.5 }
defaultTotal--
} }
this.value = backupList.shift() this.value = newBackupList.shift()
this.doCount(this.value, false) this.doCount(this.value, false)
} }
@ -67,45 +71,52 @@ class DynamicChoice {
} }
/** /**
* 换下一个 * 换下一个
* @param count * @param count 计数器
*/ */
changeNext (count) { changeNext (count) {
log.info('切换backup', count, this.backup) log.info('切换backup', count, this.backupList)
count.keepErrorCount = 0 // 清空连续失败 count.keepErrorCount = 0 // 清空连续失败
count.total = 0 count.total = 0
count.error = 0 count.error = 0
if (this.backup.length > 0) {
this.value = this.backup.shift() const valueBackup = this.value
if (this.backupList.length > 0) {
this.value = this.backupList.shift()
log.info(`切换backup完成: ${this.key}, ip: ${valueBackup}${this.value}, this:`, this)
} else { } else {
this.value = null this.value = null
log.info(`切换backup完成: ${this.key}, backupList为空了设置this.value: from '${valueBackup}' to null. this:`, this)
} }
log.info('切换backup完成', this.value, this.backup)
} }
/** /**
* 记录使用次数或错误次数 * 记录使用次数或错误次数
* @param value * @param ip
* @param isError * @param isError
*/ */
doCount (value, isError) { doCount (ip, isError) {
let count = this.count[value] let count = this.countMap[ip]
if (count == null) { if (count == null) {
count = this.count[value] = { value: value, total: 5, error: 0, keepErrorCount: 0, successRate: 1 } count = this.countMap[ip] = { value: ip, total: 5, error: 0, keepErrorCount: 0, successRate: 1 }
} }
if (isError) { if (isError) {
// 失败次数+1累计连续失败次数+1
count.error++ count.error++
count.keepErrorCount++ count.keepErrorCount++
} else { } else {
count.total += 1 // 总次数+1
count.total++
} }
// 计算成功率
count.successRate = 1.0 - (count.error / count.total) count.successRate = 1.0 - (count.error / count.total)
if (isError && this.value === value) { if (isError && this.value === ip) {
// 连续错误3次切换下一个 // 连续错误3次切换下一个
if (count.keepErrorCount >= 3) { if (count.keepErrorCount >= 3) {
this.changeNext(count) this.changeNext(count)
} }
// 成功率小于50%,切换下一个 // 成功率小于40%,切换下一个
if (count.successRate < 0.4) { if (count.successRate < 0.4) {
this.changeNext(count) this.changeNext(count)
} }

View File

@ -50,7 +50,7 @@ module.exports = function createConnectHandler (sslConnectInterceptor, middlewar
function connect (req, cltSocket, head, hostname, port, dnsConfig/* , sniRegexpMap */) { function connect (req, cltSocket, head, hostname, port, dnsConfig/* , sniRegexpMap */) {
// tunneling https // tunneling https
// log.info('connect:', hostname, port) // log.info('connect:', hostname, port)
const start = new Date().getTime() const start = new Date()
let isDnsIntercept = null let isDnsIntercept = null
const hostport = `${hostname}:${port}` const hostport = `${hostname}:${port}`
// const replaceSni = matchUtil.matchHostname(sniRegexpMap, hostname, 'sni') // const replaceSni = matchUtil.matchHostname(sniRegexpMap, hostname, 'sni')
@ -102,13 +102,13 @@ function connect (req, cltSocket, head, hostname, port, dnsConfig/* , sniRegexpM
log.error(`cltSocket error: ${hostport}, errorMsg: ${e.message}`) log.error(`cltSocket error: ${hostport}, errorMsg: ${e.message}`)
}) })
proxySocket.on('timeout', () => { proxySocket.on('timeout', () => {
const end = new Date().getTime() const cost = new Date() - start
log.info('代理socket timeout', hostname, port, (end - start) + 'ms') log.info('代理socket timeout', hostname, port, cost + 'ms')
}) })
proxySocket.on('error', (e) => { proxySocket.on('error', (e) => {
// 连接失败可能被GFW拦截或者服务端拥挤 // 连接失败可能被GFW拦截或者服务端拥挤
const end = new Date().getTime() const cost = new Date() - start
log.error('代理连接失败:', e.message, hostname, port, (end - start) + 'ms') log.error('代理连接失败:', e.message, hostname, port, cost + 'ms')
cltSocket.destroy() cltSocket.destroy()
if (isDnsIntercept) { if (isDnsIntercept) {
const { dns, ip, hostname } = isDnsIntercept const { dns, ip, hostname } = isDnsIntercept

View File

@ -109,7 +109,7 @@ module.exports = function createRequestHandler (createIntercepts, middlewares, e
function onFree () { function onFree () {
const url = `${rOptions.protocol}//${rOptions.hostname}:${rOptions.port}${rOptions.path}` const url = `${rOptions.protocol}//${rOptions.hostname}:${rOptions.port}${rOptions.path}`
const start = new Date().getTime() const start = new Date()
log.info('代理请求:', url, rOptions.method, rOptions.servername ? ', sni: ' + rOptions.servername : '') log.info('代理请求:', url, rOptions.method, rOptions.servername ? ', sni: ' + rOptions.servername : '')
let isDnsIntercept let isDnsIntercept
if (dnsConfig) { if (dnsConfig) {
@ -147,8 +147,7 @@ module.exports = function createRequestHandler (createIntercepts, middlewares, e
// log.debug('agent:', rOptions.agent) // log.debug('agent:', rOptions.agent)
// log.debug('agent.options:', rOptions.agent.options) // log.debug('agent.options:', rOptions.agent.options)
proxyReq = (rOptions.protocol === 'https:' ? https : http).request(rOptions, (proxyRes) => { proxyReq = (rOptions.protocol === 'https:' ? https : http).request(rOptions, (proxyRes) => {
const end = new Date().getTime() const cost = new Date() - start
const cost = end - start
if (rOptions.protocol === 'https:') { if (rOptions.protocol === 'https:') {
log.info('代理请求返回:', url, cost + 'ms') log.info('代理请求返回:', url, cost + 'ms')
} }
@ -161,8 +160,7 @@ module.exports = function createRequestHandler (createIntercepts, middlewares, e
// 代理请求的事件监听 // 代理请求的事件监听
proxyReq.on('timeout', () => { proxyReq.on('timeout', () => {
const end = new Date().getTime() const cost = new Date() - start
const cost = end - start
log.error('代理请求超时', rOptions.protocol, rOptions.hostname, rOptions.path, cost + 'ms') log.error('代理请求超时', rOptions.protocol, rOptions.hostname, rOptions.path, cost + 'ms')
countSlow(isDnsIntercept, 'to slow ' + cost + 'ms') countSlow(isDnsIntercept, 'to slow ' + cost + 'ms')
proxyReq.end() proxyReq.end()
@ -172,15 +170,13 @@ module.exports = function createRequestHandler (createIntercepts, middlewares, e
reject(error) reject(error)
}) })
proxyReq.on('error', (e) => { proxyReq.on('error', (e) => {
const end = new Date().getTime() const cost = new Date() - start
const cost = end - start
log.error('代理请求错误', e.code, e.message, rOptions.hostname, rOptions.path, cost + 'ms') log.error('代理请求错误', e.code, e.message, rOptions.hostname, rOptions.path, cost + 'ms')
countSlow(isDnsIntercept, 'error:' + e.message) countSlow(isDnsIntercept, 'error:' + e.message)
reject(e) reject(e)
}) })
proxyReq.on('aborted', () => { proxyReq.on('aborted', () => {
const end = new Date().getTime() const cost = new Date() - start
const cost = end - start
log.error('代理请求被取消', rOptions.hostname, rOptions.path, cost + 'ms') log.error('代理请求被取消', rOptions.hostname, rOptions.path, cost + 'ms')
if (cost > MAX_SLOW_TIME) { if (cost > MAX_SLOW_TIME) {

View File

@ -24,7 +24,7 @@ utils.createCA = function (CN) {
const cert = pki.createCertificate() const cert = pki.createCertificate()
cert.publicKey = keys.publicKey cert.publicKey = keys.publicKey
cert.serialNumber = (new Date()).getTime() + '' cert.serialNumber = (new Date()).getTime() + ''
cert.validity.notBefore = new Date(new Date().getTime() - (60 * 60 * 1000)) cert.validity.notBefore = new Date(new Date() - (60 * 60 * 1000))
cert.validity.notAfter = new Date() cert.validity.notAfter = new Date()
cert.validity.notAfter.setFullYear(cert.validity.notAfter.getFullYear() + 20) cert.validity.notAfter.setFullYear(cert.validity.notAfter.getFullYear() + 20)
const attrs = [{ const attrs = [{