负载均衡
📖 概述
负载均衡(Load Balancing)是一种在多个服务器之间分配网络流量的技术,确保没有单一服务器承担过多负载。它是构建高可用、高性能分布式系统的核心技术。
🎯 学习目标
- 理解负载均衡的核心概念和算法
- 掌握不同层级的负载均衡技术
- 学习健康检查和故障转移机制
- 实现智能的负载均衡管理系统
⚖️ 负载均衡架构
1. 负载均衡器框架
javascript
// 负载均衡器核心框架
class LoadBalancerFramework {
constructor(config = {}) {
this.config = config
this.algorithms = new Map()
this.backends = new Map()
this.healthCheckers = new Map()
this.trafficRouter = new TrafficRouter()
this.sessionManager = new SessionManager()
this.statisticsCollector = new StatisticsCollector()
this.circuitBreaker = new CircuitBreaker()
// 注册内置算法
this.registerBuiltinAlgorithms()
}
// 后端服务器管理
class Backend {
constructor(config) {
this.id = config.id || this.generateId()
this.name = config.name
this.host = config.host
this.port = config.port
this.weight = config.weight || 1
this.maxConnections = config.maxConnections || 1000
this.protocol = config.protocol || 'http'
this.ssl = config.ssl || false
this.backup = config.backup || false
// 状态信息
this.status = 'unknown' // 'healthy', 'unhealthy', 'unknown', 'maintenance'
this.currentConnections = 0
this.totalRequests = 0
this.totalErrors = 0
this.lastHealthCheck = null
this.responseTime = 0
this.lastError = null
// 统计信息
this.statistics = {
requestsPerSecond: 0,
averageResponseTime: 0,
errorRate: 0,
uptime: 0,
lastRestart: new Date()
}
this.created = new Date()
}
getEndpoint() {
const protocol = this.ssl ? 'https' : this.protocol
return `${protocol}://${this.host}:${this.port}`
}
isAvailable() {
return this.status === 'healthy' &&
this.currentConnections < this.maxConnections
}
incrementConnections() {
this.currentConnections++
}
decrementConnections() {
this.currentConnections = Math.max(0, this.currentConnections - 1)
}
recordRequest(responseTime, isError = false) {
this.totalRequests++
if (isError) {
this.totalErrors++
}
// 更新平均响应时间
this.responseTime = (this.responseTime + responseTime) / 2
// 更新统计信息
this.updateStatistics()
}
updateStatistics() {
const now = Date.now()
const uptime = now - this.statistics.lastRestart.getTime()
this.statistics.uptime = uptime
this.statistics.averageResponseTime = this.responseTime
this.statistics.errorRate = this.totalRequests > 0 ?
(this.totalErrors / this.totalRequests) * 100 : 0
}
generateId() {
return `backend_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`
}
getInfo() {
return {
id: this.id,
name: this.name,
endpoint: this.getEndpoint(),
status: this.status,
weight: this.weight,
currentConnections: this.currentConnections,
maxConnections: this.maxConnections,
totalRequests: this.totalRequests,
totalErrors: this.totalErrors,
responseTime: this.responseTime,
statistics: this.statistics,
backup: this.backup,
lastHealthCheck: this.lastHealthCheck
}
}
}
// 负载均衡算法
class LoadBalancingAlgorithm {
constructor(name) {
this.name = name
}
select(backends, request = null) {
throw new Error('select方法必须被实现')
}
filterAvailableBackends(backends) {
return backends.filter(backend => backend.isAvailable())
}
}
// 轮询算法
class RoundRobinAlgorithm extends LoadBalancingAlgorithm {
constructor() {
super('round_robin')
this.currentIndex = 0
}
select(backends, request) {
const available = this.filterAvailableBackends(backends)
if (available.length === 0) {
return null
}
const selected = available[this.currentIndex % available.length]
this.currentIndex = (this.currentIndex + 1) % available.length
return selected
}
}
// 加权轮询算法
class WeightedRoundRobinAlgorithm extends LoadBalancingAlgorithm {
constructor() {
super('weighted_round_robin')
this.weightedList = []
this.currentIndex = 0
}
select(backends, request) {
const available = this.filterAvailableBackends(backends)
if (available.length === 0) {
return null
}
// 重建加权列表
this.buildWeightedList(available)
if (this.weightedList.length === 0) {
return available[0]
}
const selected = this.weightedList[this.currentIndex % this.weightedList.length]
this.currentIndex = (this.currentIndex + 1) % this.weightedList.length
return selected
}
buildWeightedList(backends) {
this.weightedList = []
backends.forEach(backend => {
for (let i = 0; i < backend.weight; i++) {
this.weightedList.push(backend)
}
})
}
}
// 最少连接算法
class LeastConnectionsAlgorithm extends LoadBalancingAlgorithm {
constructor() {
super('least_connections')
}
select(backends, request) {
const available = this.filterAvailableBackends(backends)
if (available.length === 0) {
return null
}
return available.reduce((min, backend) => {
if (!min) return backend
const minRatio = min.currentConnections / min.weight
const backendRatio = backend.currentConnections / backend.weight
return backendRatio < minRatio ? backend : min
}, null)
}
}
// 加权响应时间算法
class WeightedResponseTimeAlgorithm extends LoadBalancingAlgorithm {
constructor() {
super('weighted_response_time')
}
select(backends, request) {
const available = this.filterAvailableBackends(backends)
if (available.length === 0) {
return null
}
// 计算每个后端的得分(权重/响应时间)
const scored = available.map(backend => ({
backend,
score: backend.weight / Math.max(backend.responseTime, 1)
}))
// 选择得分最高的后端
return scored.reduce((best, current) =>
current.score > best.score ? current : best
).backend
}
}
// IP哈希算法
class IPHashAlgorithm extends LoadBalancingAlgorithm {
constructor() {
super('ip_hash')
}
select(backends, request) {
const available = this.filterAvailableBackends(backends)
if (available.length === 0) {
return null
}
if (!request || !request.clientIP) {
// 如果没有客户端IP,退回到轮询
return available[0]
}
const hash = this.hashIP(request.clientIP)
const index = hash % available.length
return available[index]
}
hashIP(ip) {
let hash = 0
for (let i = 0; i < ip.length; i++) {
const char = ip.charCodeAt(i)
hash = ((hash << 5) - hash) + char
hash = hash & hash // 转换为32位整数
}
return Math.abs(hash)
}
}
// 一致性哈希算法
class ConsistentHashAlgorithm extends LoadBalancingAlgorithm {
constructor() {
super('consistent_hash')
this.ring = new Map()
this.virtualNodes = 150 // 每个后端的虚拟节点数
this.sortedKeys = []
}
select(backends, request) {
const available = this.filterAvailableBackends(backends)
if (available.length === 0) {
return null
}
// 重建哈希环
this.buildHashRing(available)
if (!request || !request.sessionId) {
return available[0]
}
const hash = this.hashString(request.sessionId)
return this.findBackend(hash)
}
buildHashRing(backends) {
this.ring.clear()
this.sortedKeys = []
backends.forEach(backend => {
for (let i = 0; i < this.virtualNodes; i++) {
const virtualKey = `${backend.id}:${i}`
const hash = this.hashString(virtualKey)
this.ring.set(hash, backend)
this.sortedKeys.push(hash)
}
})
this.sortedKeys.sort((a, b) => a - b)
}
findBackend(hash) {
if (this.sortedKeys.length === 0) {
return null
}
// 找到第一个大于等于hash的key
let index = this.sortedKeys.findIndex(key => key >= hash)
// 如果没有找到,使用第一个key(环形结构)
if (index === -1) {
index = 0
}
const key = this.sortedKeys[index]
return this.ring.get(key)
}
hashString(str) {
let hash = 0
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i)
hash = ((hash << 5) - hash) + char
hash = hash & hash
}
return Math.abs(hash)
}
}
// 健康检查器
class HealthChecker {
constructor(config = {}) {
this.interval = config.interval || 30000 // 30秒
this.timeout = config.timeout || 5000 // 5秒
this.retries = config.retries || 3
this.checkPath = config.checkPath || '/health'
this.expectedStatus = config.expectedStatus || 200
this.isRunning = false
this.checkTimer = null
}
start(backends) {
if (this.isRunning) {
return
}
this.isRunning = true
this.backends = backends
console.log('开始健康检查')
// 立即执行一次检查
this.performHealthCheck()
// 设置定期检查
this.checkTimer = setInterval(() => {
this.performHealthCheck()
}, this.interval)
}
stop() {
if (!this.isRunning) {
return
}
this.isRunning = false
if (this.checkTimer) {
clearInterval(this.checkTimer)
this.checkTimer = null
}
console.log('停止健康检查')
}
async performHealthCheck() {
if (!this.backends) {
return
}
const checkPromises = Array.from(this.backends.values()).map(backend =>
this.checkBackend(backend)
)
await Promise.allSettled(checkPromises)
}
async checkBackend(backend) {
const startTime = Date.now()
try {
const isHealthy = await this.executeHealthCheck(backend)
const responseTime = Date.now() - startTime
const previousStatus = backend.status
backend.status = isHealthy ? 'healthy' : 'unhealthy'
backend.lastHealthCheck = new Date()
backend.responseTime = responseTime
if (previousStatus !== backend.status) {
console.log(`后端状态变化: ${backend.name} ${previousStatus} -> ${backend.status}`)
}
} catch (error) {
backend.status = 'unhealthy'
backend.lastHealthCheck = new Date()
backend.lastError = error.message
console.error(`健康检查失败: ${backend.name}`, error.message)
}
}
async executeHealthCheck(backend) {
const axios = require('axios')
const url = `${backend.getEndpoint()}${this.checkPath}`
try {
const response = await axios.get(url, {
timeout: this.timeout,
validateStatus: (status) => status === this.expectedStatus
})
return response.status === this.expectedStatus
} catch (error) {
if (error.code === 'ECONNRESET' || error.code === 'ECONNREFUSED') {
return false
}
throw error
}
}
}
// 流量路由器
class TrafficRouter {
constructor() {
this.rules = []
this.defaultAlgorithm = 'round_robin'
}
addRoutingRule(rule) {
this.rules.push({
id: this.generateRuleId(),
...rule,
created: new Date()
})
// 按优先级排序
this.rules.sort((a, b) => (b.priority || 0) - (a.priority || 0))
console.log(`添加路由规则: ${rule.name}`)
}
route(request, backends, algorithms) {
// 查找匹配的路由规则
const matchedRule = this.findMatchingRule(request)
if (matchedRule) {
console.log(`应用路由规则: ${matchedRule.name}`)
// 过滤后端
const filteredBackends = this.filterBackends(backends, matchedRule.backendFilter)
// 选择算法
const algorithmName = matchedRule.algorithm || this.defaultAlgorithm
const algorithm = algorithms.get(algorithmName)
if (algorithm && filteredBackends.length > 0) {
return algorithm.select(filteredBackends, request)
}
}
// 使用默认算法
const defaultAlgorithm = algorithms.get(this.defaultAlgorithm)
return defaultAlgorithm ? defaultAlgorithm.select(Array.from(backends.values()), request) : null
}
findMatchingRule(request) {
for (const rule of this.rules) {
if (this.evaluateConditions(request, rule.conditions)) {
return rule
}
}
return null
}
evaluateConditions(request, conditions) {
if (!conditions || conditions.length === 0) {
return true
}
return conditions.every(condition => this.evaluateCondition(request, condition))
}
evaluateCondition(request, condition) {
switch (condition.type) {
case 'path':
return this.matchPath(request.path, condition.pattern)
case 'header':
return this.matchHeader(request.headers, condition.header, condition.value)
case 'query':
return this.matchQuery(request.query, condition.parameter, condition.value)
case 'client_ip':
return this.matchClientIP(request.clientIP, condition.cidr)
default:
return false
}
}
matchPath(path, pattern) {
if (pattern.includes('*')) {
const regex = new RegExp(pattern.replace(/\*/g, '.*'))
return regex.test(path)
}
return path === pattern
}
matchHeader(headers, headerName, expectedValue) {
const actualValue = headers[headerName.toLowerCase()]
return actualValue === expectedValue
}
matchQuery(query, parameter, expectedValue) {
return query[parameter] === expectedValue
}
matchClientIP(clientIP, cidr) {
// 简化的CIDR匹配
if (!cidr.includes('/')) {
return clientIP === cidr
}
// 这里需要实现更复杂的CIDR匹配逻辑
return false
}
filterBackends(backends, filter) {
if (!filter) {
return Array.from(backends.values())
}
return Array.from(backends.values()).filter(backend => {
if (filter.tags) {
return filter.tags.every(tag => backend.tags && backend.tags.includes(tag))
}
if (filter.region) {
return backend.region === filter.region
}
if (filter.exclude_backup !== undefined) {
return backend.backup !== filter.exclude_backup
}
return true
})
}
generateRuleId() {
return `rule_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`
}
}
// 注册内置算法
registerBuiltinAlgorithms() {
this.algorithms.set('round_robin', new RoundRobinAlgorithm())
this.algorithms.set('weighted_round_robin', new WeightedRoundRobinAlgorithm())
this.algorithms.set('least_connections', new LeastConnectionsAlgorithm())
this.algorithms.set('weighted_response_time', new WeightedResponseTimeAlgorithm())
this.algorithms.set('ip_hash', new IPHashAlgorithm())
this.algorithms.set('consistent_hash', new ConsistentHashAlgorithm())
}
// 添加后端服务器
addBackend(config) {
const backend = new this.Backend(config)
this.backends.set(backend.id, backend)
console.log(`添加后端服务器: ${backend.name} (${backend.getEndpoint()})`)
return backend
}
// 移除后端服务器
removeBackend(backendId) {
const backend = this.backends.get(backendId)
if (backend) {
this.backends.delete(backendId)
console.log(`移除后端服务器: ${backend.name}`)
return true
}
return false
}
// 选择后端服务器
selectBackend(request, algorithmName = 'round_robin') {
const algorithm = this.algorithms.get(algorithmName)
if (!algorithm) {
throw new Error(`负载均衡算法不存在: ${algorithmName}`)
}
// 通过流量路由器选择后端
const selected = this.trafficRouter.route(request, this.backends, this.algorithms)
if (selected) {
selected.incrementConnections()
console.log(`选择后端: ${selected.name} (当前连接: ${selected.currentConnections})`)
}
return selected
}
// 处理请求完成
handleRequestComplete(backend, responseTime, isError = false) {
if (backend) {
backend.decrementConnections()
backend.recordRequest(responseTime, isError)
// 更新统计信息
this.statisticsCollector.recordRequest(backend.id, responseTime, isError)
}
}
// 启动负载均衡器
start() {
console.log('启动负载均衡器')
// 启动健康检查
const healthChecker = new this.HealthChecker()
healthChecker.start(this.backends)
this.healthCheckers.set('default', healthChecker)
// 启动统计收集
this.statisticsCollector.start()
}
// 停止负载均衡器
stop() {
console.log('停止负载均衡器')
// 停止健康检查
this.healthCheckers.forEach(checker => checker.stop())
this.healthCheckers.clear()
// 停止统计收集
this.statisticsCollector.stop()
}
// 获取负载均衡器状态
getStatus() {
const backends = Array.from(this.backends.values()).map(backend => backend.getInfo())
const healthyBackends = backends.filter(b => b.status === 'healthy').length
const totalBackends = backends.length
return {
status: healthyBackends > 0 ? 'healthy' : 'unhealthy',
totalBackends,
healthyBackends,
algorithms: Array.from(this.algorithms.keys()),
routingRules: this.trafficRouter.rules.length,
backends,
statistics: this.statisticsCollector.getStatistics()
}
}
}
// 统计收集器
class StatisticsCollector {
constructor() {
this.stats = new Map()
this.globalStats = {
totalRequests: 0,
totalErrors: 0,
averageResponseTime: 0,
requestsPerSecond: 0
}
this.isRunning = false
}
start() {
this.isRunning = true
// 定期更新统计信息
this.statsTimer = setInterval(() => {
this.updateGlobalStats()
}, 10000) // 每10秒更新一次
}
stop() {
this.isRunning = false
if (this.statsTimer) {
clearInterval(this.statsTimer)
this.statsTimer = null
}
}
recordRequest(backendId, responseTime, isError) {
if (!this.stats.has(backendId)) {
this.stats.set(backendId, {
requests: 0,
errors: 0,
totalResponseTime: 0,
averageResponseTime: 0,
lastRequest: null
})
}
const backendStats = this.stats.get(backendId)
backendStats.requests++
backendStats.totalResponseTime += responseTime
backendStats.averageResponseTime = backendStats.totalResponseTime / backendStats.requests
backendStats.lastRequest = new Date()
if (isError) {
backendStats.errors++
}
// 更新全局统计
this.globalStats.totalRequests++
if (isError) {
this.globalStats.totalErrors++
}
}
updateGlobalStats() {
const now = Date.now()
const windowSize = 60000 // 1分钟窗口
// 这里可以实现更复杂的时间窗口统计
console.log('更新全局统计信息')
}
getStatistics() {
return {
global: this.globalStats,
backends: Object.fromEntries(this.stats)
}
}
}
// 使用示例
async function demonstrateLoadBalancing() {
console.log('=== 负载均衡演示 ===')
const loadBalancer = new LoadBalancerFramework()
// 添加后端服务器
const backend1 = loadBalancer.addBackend({
name: 'web-server-1',
host: '192.168.1.10',
port: 3000,
weight: 3,
tags: ['web', 'primary']
})
const backend2 = loadBalancer.addBackend({
name: 'web-server-2',
host: '192.168.1.11',
port: 3000,
weight: 2,
tags: ['web', 'secondary']
})
const backend3 = loadBalancer.addBackend({
name: 'web-server-3',
host: '192.168.1.12',
port: 3000,
weight: 1,
backup: true,
tags: ['web', 'backup']
})
// 添加路由规则
loadBalancer.trafficRouter.addRoutingRule({
name: 'API路由',
priority: 10,
conditions: [
{ type: 'path', pattern: '/api/*' }
],
algorithm: 'least_connections',
backendFilter: {
exclude_backup: true
}
})
loadBalancer.trafficRouter.addRoutingRule({
name: '静态资源路由',
priority: 5,
conditions: [
{ type: 'path', pattern: '/static/*' }
],
algorithm: 'ip_hash'
})
// 启动负载均衡器
loadBalancer.start()
// 模拟请求
console.log('模拟请求处理...')
const requests = [
{ path: '/api/users', clientIP: '192.168.1.100', headers: {} },
{ path: '/api/orders', clientIP: '192.168.1.101', headers: {} },
{ path: '/static/css/main.css', clientIP: '192.168.1.100', headers: {} },
{ path: '/api/products', clientIP: '192.168.1.102', headers: {} },
{ path: '/static/js/app.js', clientIP: '192.168.1.101', headers: {} }
]
for (const request of requests) {
const selected = loadBalancer.selectBackend(request)
if (selected) {
console.log(`请求 ${request.path} -> ${selected.name}`)
// 模拟请求处理时间
const responseTime = Math.random() * 200 + 50
const isError = Math.random() < 0.1 // 10%错误率
// 模拟请求完成
setTimeout(() => {
loadBalancer.handleRequestComplete(selected, responseTime, isError)
}, responseTime)
} else {
console.log(`请求 ${request.path} -> 无可用后端`)
}
}
// 等待一段时间后检查状态
setTimeout(() => {
const status = loadBalancer.getStatus()
console.log('负载均衡器状态:', status)
}, 3000)
return loadBalancer
}
module.exports = {
LoadBalancerFramework
}
📚 最佳实践总结
- 算法选择:根据应用特性选择合适的负载均衡算法
- 健康检查:实施全面的健康检查和故障检测机制
- 会话保持:处理有状态应用的会话一致性需求
- 故障转移:快速检测和处理后端服务器故障
- 性能监控:持续监控负载分布和性能指标
- 动态配置:支持运行时动态添加和移除后端服务器
- 流量控制:实施请求限流和熔断机制
- 多层负载均衡:结合DNS、硬件和软件负载均衡
通过掌握负载均衡技术,您将能够构建高可用和高性能的分布式系统架构。