Повторное использование Processor и кэш
Это руководство объясняет различия между функциями уровня пакета и экземплярами Processor, помогая сделать правильный выбор и добиться оптимальной производительности в различных сценариях.
Два режима вызова
Функции уровня пакета (однократный вызов)
result, err := html.Extract(data)Внутренне использует sync.Pool для управления временными Processor: при каждом вызове Processor извлекается из пула, а после использования возвращается обратно.
Применимые сценарии: редкие вызовы (например, CLI-утилиты, одноразовые скрипты)
Жизненный цикл:
Вызов Extract()
→ Получение Processor из sync.Pool (или создание нового)
→ Выполнение извлечения
→ Возврат в sync.PoolЭкземпляр Processor (режим повторного использования)
p, _ := html.New()
defer p.Close()
for _, page := range pages {
result, _ := p.Extract(page)
}Создаётся независимый экземпляр Processor с ручным управлением жизненным циклом.
Применимые сценарии: высокочастотные вызовы (например, веб-сервисы, сканеры)
Жизненный цикл:
html.New()
→ Создание Processor (кэш, аудит, статистика)
→ Циклический вызов p.Extract() (повторное использование кэша)
→ defer p.Close()Как выбрать
| Сценарий | Рекомендуемый способ | Причина |
|---|---|---|
| CLI-утилиты, однократная обработка | Функции пакета | Просто и прямолинейно, не требует управления |
| Веб-сервисы, API-бэкенд | Экземпляр Processor | Ускорение через кэш, мониторинг статистики |
| Пакетные сканеры | Экземпляр Processor | Дедупликация через кэш, контролируемые ресурсы |
| Тестовый код | Функции пакета | Без сохранения состояния, изоляция тестов |
Механизм кэширования
Экземпляр Processor имеет встроенный кэш на основе контента. Одинаковый HTML-ввод не обрабатывается повторно.
Настройка кэша
cfg := html.DefaultConfig()
cfg.MaxCacheEntries = 2000 // Максимальное количество записей в кэше (0=отключить)
cfg.CacheTTL = time.Hour // Время жизни записей кэша
cfg.CacheCleanup = 5 * time.Minute // Интервал фоновой очистки| Параметр | По умолчанию | Описание |
|---|---|---|
MaxCacheEntries | 2000 | Максимальная ёмкость кэша, установка в 0 отключает кэш |
CacheTTL | 1 час | Время жизни записей |
CacheCleanup | 5 минут | Интервал фоновой очистки устаревших записей |
Генерация ключа кэша
Ключ кэша генерируется на основе контента после конвертации в UTF-8:
- Для контента менее 64 КБ: хеш вычисляется от полного содержимого
- Для контента более 64 КБ: используется алгоритм 5-точечной выборки (начало + конец + равномерная выборка)
Одинаковый HTML-контент при повторных вызовах попадает в кэш напрямую, минуя этапы парсинга и извлечения.
Мониторинг попаданий в кэш
p, _ := html.New()
defer p.Close()
// Обработка пакета страниц
for _, page := range pages {
p.Extract(page)
}
// Получение статистики
stats := p.GetStatistics()
fmt.Printf("Всего обработано: %d\n", stats.TotalProcessed)
fmt.Printf("Попаданий в кэш: %d\n", stats.CacheHits)
fmt.Printf("Промахов кэша: %d\n", stats.CacheMisses)
hitRate := float64(stats.CacheHits) / float64(stats.TotalProcessed) * 100
fmt.Printf("Процент попаданий: %.1f%%\n", hitRate)Рекомендуемые паттерны
Singleton веб-сервиса
В веб-сервисах рекомендуется использовать Singleton Processor:
var processor *html.Processor
func init() {
cfg := html.DefaultConfig()
cfg.MaxCacheEntries = 5000
cfg.CacheTTL = 30 * time.Minute
cfg.ProcessingTimeout = 10 * time.Second
var err error
processor, err = html.New(cfg)
if err != nil {
log.Fatal(err)
}
}
func handleExtract(w http.ResponseWriter, r *http.Request) {
data, _ := io.ReadAll(r.Body)
result, err := processor.Extract(data)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
json.NewEncoder(w).Encode(result)
}Пакетная обработка сканера
p, _ := html.New(html.DefaultConfig())
defer p.Close()
urls := crawlURLs()
pages := fetchPages(urls) // [][]byte
batch := p.ExtractBatch(pages)
fmt.Printf("Успешно: %d, Неудачно: %d\n", batch.Success, batch.Failed)Регулярное обслуживание
Долго работающий Processor требует регулярного обслуживания:
// Регулярная очистка кэша (предотвращение роста памяти)
go func() {
ticker := time.NewTicker(10 * time.Minute)
for range ticker.C {
p.ClearCache()
}
}()
// Регулярный сброс статистики (с сохранением кэша)
go func() {
ticker := time.NewTicker(time.Hour)
for range ticker.C {
stats := p.GetStatistics()
log.Printf("Обработано %d раз, ошибок %d",
stats.TotalProcessed, stats.ErrorCount)
p.ResetStatistics()
}
}()Сравнение производительности
Повторная обработка одного и того же HTML 1000 раз (для справки):
| Режим | Первая обработка | Попадание в кэш |
|---|---|---|
| Функции пакета | Базовый | Нет кэша |
| Processor (без кэша) | ≈Базовый | ≈Базовый |
| Processor (с кэшем) | ≈Базовый | ≈1/10 от базового |
Условия действия кэша
Кэш работает только на экземпляре Processor. Функции уровня пакета переиспользуют Processor через sync.Pool, но конфигурация пула отключает кэш (MaxCacheEntries = 0) и очищает кэш при возврате, поэтому кэширование недоступно.
Частые заблуждения
| Заблуждение | Правильный подход |
|---|---|
Создание Processor через html.New() при каждом вызове | Повторное использование одного экземпляра |
Забыли вызвать p.Close() | Используйте defer p.Close() |
| Ожидание кэша от функций уровня пакета | Кэш работает только на экземплярах Processor |
| Использование Processor после закрытия | Проверяйте ошибку ErrProcessorClosed |
Следующие шаги
- Оптимизация производительности - Дополнительные советы по оптимизации
- Справочник API: Processor - Полный список методов
- Справочник API: Конфигурация - Подробности настройки кэша