High-performance DASH MPD Proxy dengan Hybrid Architecture: APCu Cache + Redis Analytics
- ✅ APCu Cache - Main MPD caching (super fast, <1ms access)
- ✅ Cache Locking - Anti-stampede protection (prevent server overload)
- ✅ Redis Analytics - Real-time dashboard & viewer tracking
- ✅ KID Extraction - Auto DRM Key ID dari init segment
- ✅ Multi-channel Support - Track viewers per channel
- ✅ Responsive Dashboard - Mobile & Desktop friendly
- ✅ Auto-refresh - Live stats update setiap 5 saat
┌─────────────────────────────────────────┐
│ MAIN CACHE (MPD Data) │
│ → APCu Only (<1ms access) │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ ANALYTICS & DASHBOARD │
│ → Redis (reliable key scanning) │
└─────────────────────────────────────────┘
Why Hybrid?
- APCu: Fastest cache (in-process memory, no network)
- Redis: Better for analytics (easy key scanning, data structures)
- Best of both worlds! 🎉
# Update system
sudo apt update && sudo apt upgrade -y
# Install PHP 8.2 + FPM + Apache modules
sudo apt install -y php php-fpm php-cli php-curl php-xml php-mbstring libapache2-mod-php
# Enable Apache modules
sudo a2enmod proxy_fcgi setenvif
sudo a2enconf php8.2-fpm
# Restart services
sudo systemctl restart apache2
sudo systemctl restart php8.2-fpm# Install APCu extension
sudo apt install -y php-apcu
# Enable APCu for CLI (optional, for testing)
sudo nano /etc/php/8.2/cli/php.ini
# Add at the end:
# extension=apcu
# apc.enabled=1
# apc.enable_cli=1
# Enable APCu for Apache/FPM
sudo nano /etc/php/8.2/fpm/conf.d/20-apcu.ini
# Add:
# apc.enabled=1
# apc.enable_cli=0
# apc.shm_size=128M
# apc.ttl=3600
# apc.max_file_size=1M
# Restart PHP-FPM
sudo systemctl restart php8.2-fpm# Install Redis server
sudo apt install -y redis-server
# Configure Redis (optional - optimize for performance)
sudo nano /etc/redis/redis.conf
# Recommended settings:
# maxmemory 256mb
# maxmemory-policy allkeys-lru
# appendonly no
# Enable & start Redis
sudo systemctl enable redis-server
sudo systemctl start redis-server
# Verify Redis running
redis-cli ping
# Output: PONG# Check APCu enabled
php -m | grep apcu
# Output: apcu
# Check Redis PHP extension
php -m | grep redis
# Output: redis
# Test APCu
php -r 'apcu_store("test", "works!"); echo apcu_fetch("test");'
# Output: works!
# Test Redis connection
redis-cli ping
# Output: PONG# Create web directory
sudo mkdir -p /var/www/mpd-proxy
sudo chown -R www-data:www-data /var/www/mpd-proxy
# Copy script
sudo cp index.php /var/www/mpd-proxy/
# Create Apache config
sudo nano /etc/apache2/sites-available/mpd-proxy.confApache Config:
<VirtualHost *:80>
ServerName your-domain.com
DocumentRoot /var/www/mpd-proxy
<Directory /var/www/mpd-proxy>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
# Enable PHP-FPM
<FilesMatch \.php$>
SetHandler "proxy:unix:/run/php/php8.2-fpm.sock|fcgi://localhost/"
</FilesMatch>
</Directory>
# Logging
ErrorLog ${APACHE_LOG_DIR}/mpd-proxy-error.log
CustomLog ${APACHE_LOG_DIR}/mpd-proxy-access.log combined
</VirtualHost># Enable site
sudo a2ensite mpd-proxy.conf
sudo a2dissite 000-default.conf # Disable default if needed
# Test & reload Apache
sudo apache2ctl configtest
sudo systemctl reload apache2sudo touch /tmp/mpd-error.log
sudo chown www-data:www-data /tmp/mpd-error.log
sudo chmod 644 /tmp/mpd-error.log# Test main proxy
curl "http://your-domain.com/?mpd=TV3"
# Test dashboard
curl "http://your-domain.com/?dashboard"
# Check APCu in browser
echo '<?php print_r(apcu_cache_info()); ?>' | sudo tee /var/www/mpd-proxy/apcu-test.php
# Access: http://your-domain.com/apcu-test.phpEdit index.php bahagian configuration:
define('CACHE_TIME', 10); // Cache MPD duration (saat)
define('CACHE_KID_TIME', 360000); // Cache KID duration (saat)
define('LOCK_TIME', 5); // Lock duration (saat)
define('LOCK_WAIT_TIME', 1); // Max wait time untuk lock (saat)
define('ANALYTICS_ENABLED', true); // Enable/disable analytics| Endpoint | URL | Description |
|---|---|---|
| Main Proxy | /?mpd=TV3 |
Stream MPD untuk channel TV3 |
| Dashboard | /?dashboard |
Analytics & live viewers |
| Stats | /?stats |
Alias untuk dashboard |
# Clear APCu cache via PHP
php -r 'apcu_clearcache();'
# Or create clear script
echo '<?php apcu_clearcache(); echo "APCu cache cleared!"; ?>' | sudo tee /var/www/mpd-proxy/clear_cache.php
# Access: http://your-domain.com/clear_cache.php# Clear semua Redis keys
redis-cli FLUSHALL
# Or clear analytics keys only
redis-cli KEYS 'analytics:*' | xargs redis-cli DEL
# Clear specific date
redis-cli KEYS 'analytics:*:2026-03-07' | xargs redis-cli DELsudo nano /var/www/mpd-proxy/reset.php<?php
/**
* Reset Dashboard Stats
* Access: http://your-domain.com/reset.php?confirm=yes
*/
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
if ($_GET['confirm'] === 'yes') {
$keys = $redis->keys('analytics:*');
foreach ($keys as $key) {
$redis->del($key);
}
apcu_clearcache();
echo "✅ Dashboard stats reset to zero!";
echo "<br><a href='/?dashboard'>Back to Dashboard</a>";
} else {
echo "⚠️ Are you sure?<br>";
echo "<a href='?confirm=yes' style='color:red;'>YES, RESET EVERYTHING</a><br>";
echo "<a href='/?dashboard'>Cancel</a>";
}
?>| Feature | This Script (v4.0) | Standard Proxy | Redis-Only |
|---|---|---|---|
| Cache Speed | < 0.5ms (APCu) | ~5-10ms | 1-5ms |
| Cache Locking | ✅ Yes (Anti-stampede) | ❌ No | |
| Analytics | ✅ Real-time Dashboard | ❌ None | ✅ Yes |
| Live Viewers | ✅ Per-channel tracking | ❌ None | ✅ Yes |
| Multi-layer | ✅ Hybrid (APCu + Redis) | ❌ Single | ❌ Single |
| Dashboard | ✅ Responsive UI | ❌ None | |
| KID Extraction | ✅ Auto | ||
| Resource Usage | 🟢 Low | 🟢 Low | 🟡 Medium |
Problem: Bila cache expire, ramai user request serentak → semua fetch dari origin → server overload!
Solution: Hanya 1 process dibenarkan fetch, lain WAIT untuk lock release.
Result: 1 miss + 99 hits instead of 100 misses!
APCu (Main Cache) → Super fast (<0.5ms), no network overhead
Redis (Analytics) → Easy scanning, data structures, persistence
Best of both worlds!
- Live viewers per channel
- Cache hit/miss ratio
- Request statistics
- Recent activity log
- Auto-refresh setiap 5 saat
- Extract DRM Key ID dari init segment automatically
- Cache KID untuk 1 jam (tak perlu fetch berulang)
- Support semua channels
| Component | Technology | Purpose |
|---|---|---|
| Language | PHP 8.x | Main programming |
| Web Server | Apache + PHP-FPM | Request handling |
| Cache L1 | APCu | Main MPD storage (<0.5ms) |
| Cache L2 | Redis | Analytics & dashboard |
| Locking | APCu atomic ops | Anti-stampede protection |
| Data Structures | DOMDocument, XPath | MPD XML parsing |
| HTTP Client | cURL | Origin server requests |
| Binary Parsing | Custom binary reader | KID extraction |
┌─────────────────────────────────────────────────────────┐
│ USER REQUEST │
└────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 1. Check APCu Cache │
│ → HIT? Return immediately (<0.5ms) │
│ → MISS? Continue... │
└────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 2. Acquire Lock (APCu atomic) │
│ → Only 1 process can fetch from origin │
│ → Others wait (max 1 second) │
└────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 3. Fetch from Origin (Unifi Server) │
│ → Download MPD │
│ → Extract KID (if needed) │
│ → Add BaseURL │
└────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 4. Cache to APCu │
│ → Store for 10 seconds │
│ → Release lock │
└────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 5. Track Analytics (Redis) │
│ → Increment viewer count │
│ → Log recent activity │
│ → Update channel stats │
└────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 6. Return MPD to User │
└─────────────────────────────────────────────────────────┘
# Check if enabled
php -m | grep apcu
# Check APCu info
php -r 'print_r(apcu_cache_info());'
# Enable APCu
sudo nano /etc/php/8.2/fpm/conf.d/20-apcu.ini
# Add: apc.enabled=1# Check Redis status
sudo systemctl status redis-server
# Check Redis port
netstat -tlnp | grep 6379
# Test connection
redis-cli ping# Check if Redis running
redis-cli KEYS 'analytics:*'
# Check analytics data
redis-cli GET 'analytics:total:2026-03-07'
redis-cli LRANGE 'analytics:recent' 0 10# Check PHP-FPM status
sudo systemctl status php8.2-fpm
# Check Apache modules
sudo apache2ctl -M | grep proxy_fcgi
# Restart services
sudo systemctl restart php8.2-fpm
sudo systemctl restart apache2# APCu
php -r 'apcu_clearcache();'
# Redis
redis-cli FLUSHALL
# Both
php -r 'apcu_clearcache();' && redis-cli FLUSHALL# APCu stats
php -r 'print_r(apcu_cache_info());'
# Redis stats
redis-cli INFO stats
# Real-time monitoring
watch -n1 'redis-cli INFO stats | grep keyspace'# Count active viewers per channel
redis-cli KEYS 'analytics:viewer:*' | cut -d: -f4 | sort | uniq -c
# View recent activity
redis-cli LRANGE 'analytics:recent' 0 20# Error log
sudo tail -f /var/log/apache2/mpd-proxy-error.log
# Access log
sudo tail -f /var/log/apache2/mpd-proxy-access.log| Version | Changes |
|---|---|
| v4.0 | Hybrid: APCu cache + Redis analytics |
| v3.4 | Full Redis + APCu multi-layer |
| v3.0 | Added cache locking |
Author: CikguSuraya
License: MIT
Optimized for: Debian 12, PHP 8.2-FPM, Apache 2.4, Redis 7.x
Issues & Questions:
- Check troubleshooting section
- Verify APCu & Redis enabled
- Check error logs:
/tmp/mpd-error.log
Happy Streaming! 🎉