HTTP Topic
HTTP/2 and Performance Optimization
What You'll Learn:
-
HTTP/2 features and benefits over HTTP/1.1
-
Performance optimization techniques
-
Implementing HTTP/2 in Spring Boot
Detailed Explanation:
HTTP/2 Key Features
1. Binary Protocol
-
HTTP/1.1: Text-based protocol
-
HTTP/2: Binary protocol (more efficient parsing)
2. Multiplexing
-
Multiple requests/responses over single connection
-
Eliminates head-of-line blocking
3. Header Compression (HPACK)
-
Compresses headers to reduce overhead
-
Uses static and dynamic tables
4. Server Push
-
Server can push resources before client requests them
5. Stream Prioritization
-
Client can specify priority for requests
HTTP/2 vs HTTP/1.1 Comparison
HTTP/1.1 Limitations:
1. Multiple TCP connections for parallelism 2. Head-of-line blocking 3. Uncompressed headers 4. No server push capability
HTTP/2 Advantages:
1. Single connection for all requests 2. True multiplexing 3. Header compression 4. Built-in server push 5. Better resource prioritization
Enabling HTTP/2 in Spring Boot
Application Properties:
# Enable HTTP/2
server.http2.enabled=true
# SSL configuration (HTTP/2 requires HTTPS)
server.ssl.key-store=classpath:keystore.p12
server.ssl.key-store-password=password
server.ssl.key-store-type=PKCS12
server.ssl.key-alias=tomcat
# Server configuration
server.port=8443
server.servlet.context-path=/api
Programmatic Configuration:
@Bean
public ServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
tomcat.addConnectorCustomizers(connector -> {
if (connector.getProtocolHandler() instanceof AbstractHttp11Protocol) {
((AbstractHttp11Protocol<?>) connector.getProtocolHandler())
.setMaxSwallowSize(-1);
}
});
return tomcat;
}
Performance Optimization Techniques
1. Resource Compression:
@Configuration
public class CompressionConfig {
@Bean
public FilterRegistrationBean<GzipFilter> gzipFilter() {
FilterRegistrationBean<GzipFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new GzipFilter());
registration.addUrlPatterns("/*");
return registration;
}
}
2. Connection Keep-Alive:
# Tomcat connection settings
server.tomcat.max-connections=10000
server.tomcat.max-threads=200
server.tomcat.connection-timeout=5s
3. Static Resource Caching:
@Configuration
public class StaticResourceConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/")
.setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS));
}
}
HTTP/2 Server Push
Servlet 4.0 Implementation:
@WebServlet("/index.html")
public class PushServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// Get push builder from request
PushBuilder pushBuilder = request.newPushBuilder();
if (pushBuilder != null) {
// Push CSS file
pushBuilder.path("/static/css/styles.css")
.addHeader("content-type", "text/css")
.push();
// Push JavaScript file
pushBuilder.path("/static/js/app.js")
.addHeader("content-type", "application/javascript")
.push();
}
// Serve main content
response.getWriter().write("<html>...</html>");
}
}
Monitoring HTTP/2 Performance
Metrics Configuration:
@Configuration
public class MetricsConfig {
@Bean
public MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() {
return registry -> registry.config().commonTags(
"application", "my-app",
"protocol", "HTTP/2"
);
}
@Bean
public TimedAspect timedAspect(MeterRegistry registry) {
return new TimedAspect(registry);
}
}
Performance Monitoring:
@RestController
public class UserController {
@GetMapping("/api/users/{id}")
@Timed(value = "user.get", description = "Time taken to get user")
@Counted(value = "user.get.counter", description = "Number of user get requests")
public User getUser(@PathVariable Long id) {
return userService.findById(id);
}
}
HTTP/2 Client with WebClient
WebClient Configuration:
@Configuration
public class WebClientConfig {
@Bean
public WebClient webClient() {
HttpClient httpClient = HttpClient.create()
.protocol(HttpProtocol.HTTP2)
.compress(true)
.followRedirect(true);
return WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.baseUrl("https://api.example.com")
.defaultHeader(HttpHeaders.USER_AGENT, "MyApp/1.0")
.build();
}
}
Using WebClient with HTTP/2:
@Service
public class ApiClient {
private final WebClient webClient;
public Mono<User> getUser(Long id) {
return webClient.get()
.uri("/users/{id}", id)
.header("Accept", "application/json")
.retrieve()
.bodyToMono(User.class)
.timeout(Duration.ofSeconds(5));
}
public Flux<User> getUsers() {
return webClient.get()
.uri("/users")
.retrieve()
.bodyToFlux(User.class);
}
}
Performance Testing
Load Testing with HTTP/2:
@SpringBootTest
@TestPropertySource(properties = {
"server.http2.enabled=true",
"server.ssl.enabled=true"
})
public class Http2PerformanceTest {
@Autowired
private TestRestTemplate restTemplate;
@Test
public void testHttp2Performance() {
// Warm up
IntStream.range(0, 100).forEach(i ->
restTemplate.getForObject("/api/users/1", User.class));
// Measure performance
long startTime = System.currentTimeMillis();
IntStream.range(0, 1000).parallel().forEach(i ->
restTemplate.getForObject("/api/users/1", User.class));
long endTime = System.currentTimeMillis();
double averageTime = (endTime - startTime) / 1000.0;
assertThat(averageTime).isLessThan(50.0); // Less than 50ms per request
}
}
Comments (0)
No comments yet. Be the first to comment!