Files
XC_VM/docs/ru/development/caching-and-redis.md
T

297 lines
16 KiB
Markdown
Raw Normal View History

# Стратегия кэширования и Redis
XC_VM использует двухуровневую стратегию кэширования:
- **Файловый кэш (igbinary)** — основной уровень, используемый как потоковыми, так и административными путями
- **Redis/KeyDB** — дополнительный высокопроизводительный уровень для определения состояния соединения и расширенных операций
Путь потоковой передачи считывается исключительно из файлового кэша (никаких запросов к базе данных).
Путь администратора считывается из базы данных с дополнительным кратковременным кэшем.
---
## Интерфейс кэширования
Файл: `src/Core/Cache/CacheInterface.php`
```php
get($key, $maxAge = null)
set($key, $data, $ttl = 0)
delete($key)
has($key, $maxAge = null)
flush()
```
- `$ttl = 0` означает кэширование навсегда (до ручного удаления или промывки).
- `$maxAge` проверяет время изменения файла на свежесть (только для файлового кэша).
---
## Файловый кэш
Файл: `src/Core/Cache/FileCache.php`
Реализация кэша по умолчанию. Данные, сериализованные в двоичном формате, хранятся в виде плоских файлов.
```php
$cache = new FileCache(CACHE_TMP_PATH);
$cache->set('my_key', $data, 3600);
$data = $cache->get('my_key', 120); // only if < 2 min old
```
Статический удобный API (обратная совместимость):
```php
FileCache::setCache($key, $data)
FileCache::getCache($key, $maxAge = null)
```
Характеристики:
- Сериализация: igbinary (если доступно) или PHP выполнить резервную сериализацию.
- Блокировка: `LOCK_EX` при записи для предотвращения повреждения.
- Расположение файла: `{basePath}/{key}` (нет подкаталогов для основных ключей).
- Восстановление поврежденных файлов: обнаруживает поврежденные данные, автоматически удаляет поврежденные файлы.
---
## Заново спрятать
Файл: `src/Core/Cache/RedisCache.php`
Дополнительная высокопроизводительная реализация.
```php
$redis = new RedisCache('127.0.0.1', 6379, $password, 'prefix:');
$redis->set($key, $data, 600); // 10-minute TTL via SETEX
$redis->getConnection(); // raw phpredis for sorted sets, pipelines
```
- Отложенное подключение: подключается при первой операции.
- Встроенная поддержка TTL через Redis `SETEX`.
- Используется в основном для `ConnectionTracker` (отсортированных наборов для текущего состояния соединения).
---
## Redis Управление подключениями
Файл: `src/Infrastructure/Redis/RedisManager.php`
Жизненный цикл синглтона:
```php
RedisManager::instance() // get active Redis or null
RedisManager::ensureConnected() // connect if not already
RedisManager::isConnected() // health check
RedisManager::closeInstance() // disconnect
```
Проверка работоспособности выдает сигнал Redis каждые 30 секунд (отменено). При сбое автоматически восстанавливается соединение. При сбое соединения возвращается значение null (постепенное ухудшение).
Конфигурация:
|Установка|Источник|По умолчанию|
| --- | --- | --- |
| `hostname` | `config.ini` |—|
| `port` |жестко запрограммированный| `6379` |
| `password` | `settings.redis_password` |—|
| `read_timeout` |жестко запрограммированный| `2.0s` |
| `tcp_keepalive` |жестко запрограммированный| `60s` |
### Сохраняющиеся при отключении в режиме ожидания (долгоживущие демоны)
Кратковременные запросы (PHP-FPM stream/admin) открывают новое соединение для каждого процесса
и на них не влияют тайм-ауты простоя. Демоны-долгожители — цикл `watchdog`,
`fanout_sync` — вместо этого удерживайте соединение **один** через синглтон для их
весь срок службы, при котором возможны два режима сбоя на загруженном сервере или на межсерверном сервере
(LB → ГЛАВНАЯ) ссылка:
- **Сервер простаивает - закрывается.** Redis закрывает любой клиент, который простаивает дольше своего `timeout` (`300s`
в комплекте `bin/redis/redis.conf`). затем phpredis прозрачно откроется снова.
сокет в следующей команде **без повторного воспроизведения аутентификации**, поэтому более поздняя команда
отвечает `NOAUTH` — или просто возвращает `false`.
- **Устранен пробел в проверке работоспособности.** `instance()` пингуется только каждые 30 секунд, так что между
пингует, что сброшенное соединение еще не замечено.
Охранники на месте:
- `instance()` обрабатывает любой ответ, не связанный с`PONG` пингом (беззвучное повторное подключение / `NOAUTH`
состояние) как отключенное соединение и принудительно выполняет полное, **повторная аутентификация** повторное подключение
через `\XC_VM::redis_connect()` — это не просто повторная попытка на уровне сокета.
- Вызывайте сайты, которые командами конвейера проверяют объект конвейера. Например
`ConnectionTracker::getCapacity()` проверяет, что `$redis->multi()` вернул
`\Redis` (сломанный сокет возвращает `false` и вызывает `zCard()` для этого bool
был бы фатальным вне пути повторного подключения) и выдает, чтобы его цикл повторных попыток снова подключился.
The server-side alternative (`timeout 0`) is deliberately **not** used — the
вместо этого клиент становится устойчивым, и `tcp-keepalive` по-прежнему получает доступ к мертвым одноранговым узлам.
---
## Заполнение кэша
Файлы кэша генерируются двумя заданиями cron:
### Облегченный кэш (CacheCronJob)
Запускает каждый цикл cron. Восстанавливает быстро меняющиеся данные (~1 секунда):
- `settings` — настройки панели
- `servers` — список серверов
- `bouquets` — пакеты каналов
- `categories` — категории потоков
- Блокирующие списки: `blocked_isp`, `blocked_ua`, `blocked_ips`, `blocked_servers`
- `allowed_ips`, `output_formats`, `hmac_keys`, `rtmp_ips`
### Большой объем кэша (CacheEngineCronJob)
Перестраивает потоковые, линейные и последовательные данные. Регулируется до одного раза в 5 минут с помощью маркера `heavy_cache_built`:
- `STREAMS_TMP_PATH/stream_{id}` — метаданные отдельного потока
- `LINES_TMP_PATH/line_i_{user_id}` — данные учетной записи пользователя
- `LINES_TMP_PATH/line_c_{username_password}` — имя пользователя → поиск идентификатора пользователя
- `LINES_TMP_PATH/line_t_{access_token}` — токен → поиск идентификатора пользователя
- `SERIES_TMP_PATH/series_{id}` — метаданные серии
Режим обнаружения изменений (если включено `cache_changes`): сравнивает временную метку базы данных `updated` с файлом `mtime`, восстанавливает только измененные элементы.
Режим полной перестройки: восстанавливает все записи. Регулируется параметром `cache_thread_count`.
### Готовность кэша
После каждой полной сборки кэша записывается файл `cache_complete`. Путь потоковой передачи проверяет наличие этого файла и завершает работу с ошибкой, если он отсутствует.
### Безопасность холодного кэширования
Потоковый загрузчик (`LegacyInitializer::initStreaming()`) считывает `servers`,
блок-листы и `proxy_servers` из файлового кэша. Перед первой сборкой
(fresh boot, cleared tmp) those files do not exist and `CacheReader::get()`
возвращает `null`, поэтому для каждого такого глобального массива по умолчанию используется пустой массив. Холодный кэш
следовательно, **не удается закрыть** — запрос не находит серверов и показывает "нет в эфире". —
вместо предупреждения `foreach(null)` или `in_array($x, null)` со смертельным исходом (PHP 8)
вниз по течению. Действительно поврежденный кэш *сборка* все еще отображается отдельно с помощью
`FileCache` предупреждение о сбое записи, поэтому это значение по умолчанию маскирует только временный
окно холодного пуска, а не настоящий сбой.
---
## Соглашения о ключах кэширования
### Системные ключи (CACHE_TMP_PATH)
|Ключ|Содержание|
| --- | --- |
| `settings` |массив настроек панели|
| `servers` |`array[server_id]` → конфигурация сервера|
| `bouquets` |`array[bouquet_id]` → bouquet определение|
| `categories` |`array[category_id]` → данные категории|
| `bouquet_map` |`array[stream_id]` → `array[bouquet_id]`|
| `category_map` |`array[bouquet_id]` → `array[category_id]`|
| `permissions_{group_id}` |набор групповых разрешений|
| `cache_complete` |`time()` временная метка последней полной сборки|
### Ключи потока (STREAMS_TMP_PATH)
|Ключ|Содержание|
| --- | --- |
| `stream_{id}` |информация о потоке + букеты + состояние каждого сервера|
| `channels_categories` |`array[stream_id]` → `array[category_id]`|
### Линейные ключи (LINES_TMP_PATH)
|Ключ|Содержание|
| --- | --- |
| `line_i_{user_id}` |полная запись о пользователе|
| `line_c_{username_password}` |user_id (поиск учетных данных)|
| `line_t_{access_token}` |user_id (поиск токена)|
### Ключи серии (SERIES_TMP_PATH)
|Ключ|Содержание|
| --- | --- |
| `series_{id}` |метаданные серии|
| `series_map` |`array[stream_id]` → идентификатор серии|
| `episodes_{series_id}` |`array[season_num]` → список эпизодов|
---
## Шаблоны аннулирования
|Спусковой крючок|Затронутые ключи|Механизм|
| --- | --- | --- |
|Администратор редактирует поток|`stream_{id}`, `bouquet_map`|сигнал → следующий `cron:cache_engine`|
|Администратор редактирует строку|`line_i_*`, `line_c_*`, `line_t_*`|следующий `cron:cache_engine`|
|Настройки изменены|`settings`, категории, блок-листы|`SettingsManager::clearCache()` + хрон|
|Обновлен список серверов|`servers`, `bouquet_map`|крон|
|Начало потока (FFprobe)| `{md5(source)}` |5-минутный TTL с помощью проверки mtime файла|
|Кнопка сброса администратора|все файлы в `CACHE_TMP_PATH`| `rm -rf` |
---
## Потоковая передача против пути администратора
### Путь потоковой передачи (`www/stream/*`)
- `cached: true` по умолчанию.
- Считывает данные исключительно из файлового кэша (никаких запросов к базе данных).
- Raw igbinary deserialization: `igbinary_unserialize(file_get_contents(...))`.
- Если `cache_complete` отсутствует: завершите работу с ошибкой.
### Путь администратора (`Public/Controllers/Admin/*`)
- `cached: false` по умолчанию.
- Считывает данные из базы данных непосредственно через доменные службы.
- Необязательный кратковременный кэш (пример из `BouquetService::getAll()`):
```php
$rCache = FileCache::getCache('bouquets', 60); // only if < 60s old
if (!empty($rCache)) {
return $rCache;
}
// miss: query database and write cache
FileCache::setCache('bouquets', $rOutput);
```
---
## Расположение файла кэша
```text
/home/xc_vm/tmp/cache/
├── settings
├── servers
├── bouquets
├── categories
├── bouquet_map
├── category_map
├── cache_complete
├── heavy_cache_built
├── streams/
│ ├── stream_{id}
│ └── channels_categories
├── lines/
│ ├── line_i_{user_id}
│ ├── line_c_{username_password}
│ └── line_t_{access_token}
└── series/
├── series_{id}
├── series_map
└── episodes_{series_id}
```
---
## Связанные файлы
|Файл|Цель|
| --- | --- |
| `src/Core/Cache/CacheInterface.php` |контракт на кэширование|
| `src/Core/Cache/FileCache.php` |реализация кэша на основе файлов|
| `src/Core/Cache/RedisCache.php` |Redis реализация кэширования|
| `src/Infrastructure/Redis/RedisManager.php` |Redis одноэлементное соединение|
| `src/Infrastructure/Cache/CacheReader.php` |устаревший мост для чтения кэша|
| `src/Cli/CronJobs/CacheCronJob.php` |облегченная генерация кэша|
| `src/Cli/CronJobs/CacheEngineCronJob.php` |генерация большого объема кэша (потоки, строки, серии)|
| `src/Domain/Bouquet/BouquetService.php` |пример кэширования пути администратора|
| `src/Domain/Stream/ConnectionTracker.php` |Redis отсортированные наборы для определения состояния соединения|