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%。