Load Balancer Pool Distribution Analyzer
.calc-header {
margin-bottom: 30px;
padding: 25px;
background: linear-gradient(135deg, #FF6B6B 0%, #C92A2A 100%);
border-radius: 12px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
color: white;
}
.calc-header h2 {
margin-top: 0;
color: white;
font-size: 28px;
margin-bottom: 10px;
}
.calc-header p {
margin: 10px 0 20px 0;
opacity: 0.95;
font-size: 15px;
}
.form-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 15px;
margin-top: 20px;
}
.form-grid-3 {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 15px;
margin-top: 15px;
}
.form-group {
display: flex;
flex-direction: column;
gap: 8px;
}
.form-group label {
font-weight: 600;
font-size: 14px;
color: white;
}
.form-group input,
.form-group select {
padding: 12px;
font-size: 15px;
border: 2px solid rgba(255,255,255,0.3);
border-radius: 6px;
background: rgba(255,255,255,0.95);
transition: all 0.3s ease;
font-family: 'Courier New', monospace;
}
.form-group input:focus,
.form-group select:focus {
outline: none;
border-color: #4CAF50;
background: white;
box-shadow: 0 0 0 3px rgba(76, 175, 80, 0.15);
}
.form-group select {
cursor: pointer;
font-family: inherit;
}
.submit-btn {
padding: 14px 35px;
font-size: 16px;
background: #4CAF50;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-weight: bold;
transition: all 0.3s ease;
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
margin-top: 15px;
width: 100%;
}
.submit-btn:hover {
background: #45a049;
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(0,0,0,0.3);
}
.submit-btn:active {
background: #3d8b40;
transform: translateY(0);
}
.secondary-btn {
padding: 10px 20px;
font-size: 14px;
background: rgba(255,255,255,0.2);
color: white;
border: 2px solid rgba(255,255,255,0.5);
border-radius: 6px;
cursor: pointer;
font-weight: bold;
transition: all 0.3s ease;
}
.secondary-btn:hover {
background: rgba(255,255,255,0.35);
border-color: white;
}
.danger-btn {
padding: 8px 14px;
font-size: 13px;
background: #e53935;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-weight: bold;
transition: all 0.2s ease;
}
.danger-btn:hover {
background: #c62828;
}
.result-section {
background: white;
border: 1px solid #e0e0e0;
border-radius: 8px;
margin-top: 20px;
overflow: hidden;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
transition: all 0.3s ease;
display: none;
}
.result-section.show {
display: block;
}
.result-section:hover {
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
.section-header {
background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%);
color: white;
padding: 15px 20px;
}
.section-header h3 {
margin: 0;
font-size: 18px;
display: flex;
align-items: center;
gap: 10px;
}
.section-content {
padding: 20px;
background: #fafafa;
}
.data-row {
display: flex;
margin: 12px 0;
padding: 10px;
background: white;
border-radius: 4px;
border-left: 3px solid #4CAF50;
align-items: center;
}
.data-label {
font-weight: bold;
color: #555;
min-width: 220px;
flex-shrink: 0;
font-size: 14px;
}
.data-value {
color: #333;
font-family: 'Courier New', monospace;
word-break: break-word;
font-size: 18px;
}
.data-value.highlight {
color: #4CAF50;
font-weight: bold;
font-size: 22px;
}
.data-value.warning {
color: #FF6B6B;
font-weight: bold;
font-size: 22px;
}
.help-text {
margin-top: 15px;
font-size: 14px;
color: rgba(255,255,255,0.9);
background: rgba(0,0,0,0.1);
padding: 10px 15px;
border-radius: 4px;
}
.help-text strong {
color: white;
}
/* Member list table */
.member-table {
width: 100%;
border-collapse: collapse;
margin-top: 10px;
font-size: 14px;
}
.member-table th {
background: #f0f0f0;
padding: 10px 12px;
text-align: left;
font-weight: 600;
color: #444;
border-bottom: 2px solid #ddd;
}
.member-table td {
padding: 10px 12px;
border-bottom: 1px solid #eee;
vertical-align: middle;
}
.member-table tr:last-child td {
border-bottom: none;
}
.member-table tr:hover td {
background: #f9f9f9;
}
/* Status badge */
.status-badge {
display: inline-block;
padding: 3px 10px;
border-radius: 12px;
font-size: 12px;
font-weight: bold;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.status-badge.up {
background: #e8f5e9;
color: #2e7d32;
border: 1px solid #a5d6a7;
}
.status-badge.down {
background: #ffebee;
color: #c62828;
border: 1px solid #ef9a9a;
}
/* Balance score ring */
.balance-score-container {
display: flex;
align-items: center;
gap: 20px;
padding: 15px;
background: white;
border-radius: 8px;
border: 1px solid #e0e0e0;
margin-bottom: 15px;
}
.balance-score-ring {
width: 80px;
height: 80px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 22px;
font-weight: bold;
color: white;
flex-shrink: 0;
box-shadow: 0 2px 8px rgba(0,0,0,0.2);
}
.balance-score-ring.excellent {
background: linear-gradient(135deg, #43a047, #1b5e20);
}
.balance-score-ring.good {
background: linear-gradient(135deg, #7cb342, #558b2f);
}
.balance-score-ring.fair {
background: linear-gradient(135deg, #fb8c00, #e65100);
}
.balance-score-ring.poor {
background: linear-gradient(135deg, #e53935, #b71c1c);
}
.balance-score-info {
flex: 1;
}
.balance-score-info h4 {
margin: 0 0 5px 0;
font-size: 18px;
color: #333;
}
.balance-score-info p {
margin: 0;
font-size: 13px;
color: #666;
line-height: 1.5;
}
/* Metrics grid */
.metrics-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 12px;
margin-bottom: 20px;
}
.metric-card {
background: white;
border: 1px solid #e0e0e0;
border-radius: 8px;
padding: 15px;
text-align: center;
border-top: 3px solid #4CAF50;
}
.metric-card .metric-value {
font-size: 22px;
font-weight: bold;
color: #333;
font-family: 'Courier New', monospace;
}
.metric-card .metric-label {
font-size: 12px;
color: #888;
margin-top: 4px;
}
/* Chart container */
.chart-wrapper {
background: white;
border: 1px solid #e0e0e0;
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
}
.chart-wrapper h4 {
margin: 0 0 15px 0;
color: #444;
font-size: 15px;
font-weight: 600;
}
/* Algorithm explanation */
.algo-explanation {
background: #fff8e1;
border: 1px solid #ffe082;
border-radius: 6px;
padding: 12px 15px;
font-size: 13px;
color: #5d4037;
line-height: 1.6;
margin-top: 10px;
}
.algo-explanation strong {
color: #3e2723;
}
/* Hash ring visualization */
.ring-wrapper {
display: flex;
align-items: flex-start;
gap: 20px;
}
#hashRingCanvas {
border: 1px solid #e0e0e0;
border-radius: 50%;
display: block;
flex-shrink: 0;
}
.ring-legend {
flex: 1;
}
.ring-legend-item {
display: flex;
align-items: center;
gap: 10px;
padding: 6px 0;
font-size: 13px;
border-bottom: 1px solid #f0f0f0;
}
.ring-legend-dot {
width: 14px;
height: 14px;
border-radius: 50%;
flex-shrink: 0;
}
/* Redistribution info */
.redistribution-row {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 12px;
background: white;
border-radius: 4px;
border-left: 3px solid #FF6B6B;
margin-bottom: 6px;
font-size: 14px;
}
.redistribution-row .arrow {
color: #FF6B6B;
font-size: 18px;
flex-shrink: 0;
}
.redistribution-row .move-label {
color: #555;
flex: 1;
}
.redistribution-row .move-count {
font-family: 'Courier New', monospace;
font-weight: bold;
color: #333;
}
/* Slider styling */
.slider-group {
display: flex;
flex-direction: column;
gap: 8px;
margin-top: 5px;
}
.slider-group label {
font-weight: 600;
font-size: 14px;
color: white;
display: flex;
justify-content: space-between;
}
.slider-group label span {
font-family: 'Courier New', monospace;
background: rgba(0,0,0,0.2);
padding: 2px 8px;
border-radius: 4px;
font-size: 13px;
}
input[type="range"] {
width: 100%;
accent-color: #4CAF50;
height: 6px;
cursor: pointer;
}
/* Controls area inside header */
.header-controls {
margin-top: 20px;
}
.control-group-label {
font-weight: 600;
font-size: 13px;
color: rgba(255,255,255,0.8);
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 8px;
margin-top: 18px;
}
.add-member-row {
display: flex;
gap: 8px;
align-items: flex-end;
flex-wrap: wrap;
}
.add-member-row .form-group {
flex: 1;
min-width: 120px;
}
/* Failure simulation section */
.failure-section {
background: rgba(0,0,0,0.1);
border-radius: 8px;
padding: 15px;
margin-top: 18px;
}
.failure-section .control-group-label {
margin-top: 0;
}
.failure-row {
display: flex;
gap: 10px;
align-items: flex-end;
flex-wrap: wrap;
}
.failure-row .form-group {
flex: 1;
min-width: 140px;
}
.failure-btn-group {
display: flex;
gap: 8px;
}
/* Responsive design */
@media (max-width: 768px) {
.form-grid,
.form-grid-3 {
grid-template-columns: 1fr;
}
.metrics-grid {
grid-template-columns: 1fr 1fr;
}
.data-row {
flex-direction: column;
align-items: flex-start;
}
.data-label {
margin-bottom: 4px;
min-width: auto;
}
.ring-wrapper {
flex-direction: column;
}
#hashRingCanvas {
width: 220px !important;
height: 220px !important;
align-self: center;
}
.balance-score-container {
flex-direction: column;
text-align: center;
}
.add-member-row {
flex-direction: column;
}
.add-member-row .form-group {
min-width: auto;
width: 100%;
}
.failure-row {
flex-direction: column;
}
}
@media (max-width: 480px) {
.calc-header h2 {
font-size: 22px;
}
.metrics-grid {
grid-template-columns: 1fr;
}
}
Chart.js CDN
========== 标题/控件 ==========
========== 会员表 ==========
========== 分配结果 ==========
========== 失败重新分配 ==========
========== 哈希环可视化 ==========
========== 帮助/概念 ==========
负载均衡器池分配分析器
模拟不同的负载平衡算法如何在池成员之间分配流量。对故障场景进行建模并比较重新分配效率。
算法和会话计数
会话计数滑块
故障模拟
交通量
池成员管理
人才库成员
故障场景模拟
工作原理:客户端 IP 是从 /16 子网 (10.0.0.0/16) 采样的。
对于基于源的哈希,每个唯一的客户端 IP 都确定性地映射到一个成员。
循环法忽略客户端 IP 并按顺序分发。加权使用比例
随机分配。一致的散列将客户端和成员都置于虚拟环上。
人才库成员
| ID | IP:端口 | 重量 | 地位 | 行动 |
|---|
分配结果
平衡分+算法解释
指标网格
条形图
会员详细信息行
每个成员的会话分布
故障重新分布分析
一致性哈希环
读环:每个成员被放置在环周围的多个点上
(虚拟节点)。客户端 IP 被散列到环上的某个位置,然后发送请求
顺时针到最近的成员。添加或删除成员仅影响弧线
环其拥有——最大限度地减少会话中断。
算法参考
循环赛
会话按顺序分配:成员 1, 2, 3, ..., N, 1, 2, ... 每个成员
正好收到所有请求的 1/N。忽略服务器容量和客户端关联性。
简单且可预测,但在异构硬件上很难失败。
源IP哈希
客户端 IP 的哈希值以成员计数为模来选择后端。同一个客户
总是到达同一个成员——对于有状态的应用程序很有用。添加或删除
成员改组全部客户端映射(N 更改为所有会话的 (N-1)/N)。
加权
每个成员收到的份额与其权重相对于总权重成正比
池。权重 = 4 的成员获得的会话数是权重 = 1 的成员的 4 倍。用于建模
异构后端容量(例如,虚拟机与裸机服务器)。
一致性哈希
成员和客户端都通过哈希映射到一个圆环上。每个客户都去
到环上顺时针方向最近的成员。虚拟节点(每个成员的副本)
提高分布均匀性。当某个成员发生故障时,只有其会话会转移到
环上的下一个成员 — 1/N 个会话受到干扰,而模哈希则为 100%。