← Назад к вопросам

Как ресурс проверяет новую версию копии данных

2.0 Middle🔥 131 комментариев
#Многопоточность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Проверка версии кэшированных данных в HTTP

Ресурс проверяет новую версию копии данных через механизм валидации кэша, который работает на основе HTTP заголовков ETag и условных запросов.

Основные механизмы валидации

1. ETag (Entity Tag) Это уникальный идентификатор версии ресурса:

// Сервер отправляет:
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"

// Клиент сохраняет и отправляет обратно:
If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"

Если версия совпадает — сервер возвращает 304 Not Modified, данные не передаются.

2. Last-Modified / If-Modified-Since Проверка по временной метке:

// Сервер отправляет:
Last-Modified: Wed, 21 Oct 2025 07:28:00 GMT

// Клиент проверяет:
If-Modified-Since: Wed, 21 Oct 2025 07:28:00 GMT

Если данные не изменены — 304 Not Modified.

Практическая реализация на Java

Серверная часть (Spring)

@GetMapping("/api/users/{id}")
public ResponseEntity<User> getUser(@PathVariable String id,
                                     @RequestHeader(value = "If-None-Match", required = false) String ifNoneMatch) {
    User user = userService.findById(id);
    String etag = "\"" + Integer.toHexString(user.hashCode()) + "\"";
    
    if (ifNoneMatch != null && ifNoneMatch.equals(etag)) {
        return ResponseEntity.status(HttpStatus.NOT_MODIFIED).build();
    }
    
    return ResponseEntity.ok()
        .header("ETag", etag)
        .header("Cache-Control", "max-age=3600")
        .body(user);
}

Клиентская часть

private Map<String, CacheEntry> cache = new HashMap<>();

public User getUser(String id) throws IOException {
    CacheEntry cached = cache.get(id);
    
    HttpRequest.Builder builder = HttpRequest.newBuilder()
        .uri(URI.create("http://api.example.com/users/" + id));
    
    if (cached != null) {
        builder.header("If-None-Match", cached.etag);
    }
    
    HttpRequest request = builder.GET().build();
    HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
    
    if (response.statusCode() == 304) {
        return cached.data;
    }
    
    User user = new ObjectMapper().readValue(response.body(), User.class);
    String etag = response.headers().firstValue("ETag").orElse("");
    cache.put(id, new CacheEntry(user, etag));
    return user;
}

Как это работает

  1. Первый запрос: клиент запрашивает ресурс, сервер возвращает 200 OK с данными и ETag
  2. Кэширование: клиент сохраняет данные и ETag
  3. Следующий запрос: клиент отправляет условный запрос с If-None-Match
  4. Проверка на сервере: если версия совпадает → 304 Not Modified
  5. Обновление: если данные изменились → 200 OK с новыми данными и новым ETag

Преимущества

  • Экономия трафика — при 304 тело ответа не передаётся
  • Точность — ETag отражает содержимое, не привязана к времени
  • Надёжность — работает через кэширующие прокси
  • Встроенная конкурентность — используется в REST API

Это стандартная практика в REST API и веб-сервисах.