DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Related

  • How To Build Web Service Using Spring Boot 2.x
  • Mutual TLS With gRPC Between Python and Go Services
  • Beginners Guide for Web Scraping Using Selenium
  • Introduction to the Circuit Breaker Pattern

Trending

  • How to Perform Custom Error Handling With ANTLR
  • How to Ensure Cross-Time Zone Data Integrity and Consistency in Global Data Pipelines
  • Secure by Design: Modernizing Authentication With Centralized Access and Adaptive Signals
  • Operational Principles, Architecture, Benefits, and Limitations of Artificial Intelligence Large Language Models
  1. DZone
  2. Data Engineering
  3. IoT
  4. Retry-After HTTP Header in Practice

Retry-After HTTP Header in Practice

Retry-After is a lesser known HTTP response header.

By 
Tomasz Nurkiewicz user avatar
Tomasz Nurkiewicz
DZone Core CORE ·
Feb. 20, 15 · Tutorial
Likes (0)
Comment
Save
Tweet
Share
15.8K Views

Join the DZone community and get the full member experience.

Join For Free

Retry-After is a lesser known HTTP response header. Let me quote relevant part of RFC 2616 (HTTP 1.1 spec):

14.37 Retry-After

The Retry-After response-header field can be used with a 503 (Service Unavailable) response to indicate how long the service is expected to be unavailable to the requesting client. This field MAY also be used with any 3xx (Redirection) response to indicate the minimum time the user-agent is asked wait before issuing the redirected request. The value of this field can be either an HTTP-date or an integer number of seconds (in decimal) after the time of the response.

Retry-After  = "Retry-After" ":" ( HTTP-date | delta-seconds )


Two examples of its use are

Retry-After: Fri, 31 Dec 1999 23:59:59 GMT
Retry-After: 120
In the latter example, the delay is 2 minutes.

Although the use case with 3xx response is interesting, especially in eventually consistent systems ("your resource will be available under this link within 2 seconds), we will focus on error handling. By adding Retry-After to response server can give a hint to the client when it will become available again. One might argue that the server hardly ever knows when it will be back on-line, but there are several valid use cases when such knowledge can be somehow inferred:

  • Planned maintenance - this one is obvious, if your server is down within scheduled maintenance window, you can sendRetry-After from proxy with precise information when to call back. Clients won't bother retrying earlier, of course IF they understand and honour this header
  • Queue/thread pool full - if your request must be handled by a thread pool and it's full, you can estimate when next request can be handled. This requires bound queue (see: ExecutorService - 10 tips and tricks, point 6.) and rough estimate how long it takes for one task to be handled. Having this knowledge you can estimate when next client can be served without queueing.
  • Circuit breaker open - in Hystrix you can query
  • Next available token/resource/whatever

Let's focus on one non-trivial use case. Imagine your web service is backed by Hystrix command:

private static final HystrixCommand.Setter CMD_KEY = HystrixCommand.Setter
    .withGroupKey(HystrixCommandGroupKey.Factory.asKey("REST"))
    .andCommandKey(HystrixCommandKey.Factory.asKey("fetch"));
 
@RequestMapping(value = "/", method = GET)
public String fetch() {
    return fetchCommand().execute();
}
 
private HystrixCommand<String> fetchCommand() {
    return new HystrixCommand<String>(CMD_KEY) {
        @Override
        protected String run() throws Exception {
            //...
        }
    };
}

This works as expected, if command fails, times out or circuit breaker is open, client will receive 503. However in case of circuit breaker we can at least estimate how long would it take for circuit to close again. Unfortunately there is no public API telling for how long exactly circuit will remain open in case of catastrophic failures. But we know for how long by default circuit breaker remains open, which is a good max estimate. Of course circuit may remain open if underlying command keeps failing. But Retry-After doesn't guarantee that a server will operate upon given time, it's just a hint for the client to stop trying beforehand. The following implementation is simple, but broken:

@RequestMapping(value = "/", method = GET)
public ResponseEntity<String> fetch() {
    final HystrixCommand<String> command = fetchCommand();
    if (command.isCircuitBreakerOpen()) {
        return handleOpenCircuit(command);
    }
    return new ResponseEntity<>(command.execute(), HttpStatus.OK);
}
 
private ResponseEntity<String> handleOpenCircuit(HystrixCommand<String> command) {
    final HttpHeaders headers = new HttpHeaders();
    final Integer retryAfterMillis = command.getProperties()
            .circuitBreakerSleepWindowInMilliseconds().get();
    headers.set(HttpHeaders.RETRY_AFTER, Integer.toString(retryAfterMillis / 1000));
    return new ResponseEntity<>(headers, HttpStatus.SERVICE_UNAVAILABLE);
}

This one works well. If command throws an exception and associated circuit is open, we set appropriate header. In all examples we take milliseconds and normalize to seconds. I wouldn't recommend it, but if for some reason you prefer absolute dates rather than relative timeouts in Retry-After header, HTTP date formatting is finally part of Java (since JDK 8):

import java.time.format.DateTimeFormatter;
 
//...
 
final ZonedDateTime after5seconds = ZonedDateTime.now().plusSeconds(5);
final String httpDate = DateTimeFormatter.RFC_1123_DATE_TIME.format(after5seconds);

A note about auto-DDoS

You have to be careful with Retry-After header if you send the same timestamp to a lot of unique clients. Imagine it's 15:30 and you send Retry-After: Thu, 10 Feb 2015 15:40:00 GMT to everyone around - just because you somehow estimated that service will be up at 15:40. The longer you keep sending the same timestamp, the bigger DDoS "attack" you can expect from clients respecting Retry-After. Basically everyone will schedule retry precisely at 15:40 (obviously clocks are not perfectly aligned and network latency varies, but still), flooding your system with requests. If your system is properly designed, you might survive it. However chances are you will mitigate this "attack" by sending another fixed Retry-After header, essentially re-scheduling attack later.

That being said avoid fixed, absolute timestamps sent to multiple unique clients. Even if you know precisely when your system will become available, spread Retry-After values along some time period. Actually you should gradually let in more and more clients, so experiment with different probability distributions.

Summary

Retry-After HTTP response header is neither universally known nor often applicable. But in rather rare cases when downtime can be anticipated, consider implementing it on the server side. If clients are aware of it as well, you can significantly reduce network traffic while improving system throughput and response times


Circuit Breaker Pattern Web Service Requests Command (computing) Thread pool Use case IT Network Java (programming language)

Published at DZone with permission of Tomasz Nurkiewicz, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • How To Build Web Service Using Spring Boot 2.x
  • Mutual TLS With gRPC Between Python and Go Services
  • Beginners Guide for Web Scraping Using Selenium
  • Introduction to the Circuit Breaker Pattern

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends: