如何为Vert.x HTTP Web Client配置缓存?附Apache HttpClient示例
如何为Vert.x HTTP Web Client配置连接池与缓存
我懂你——习惯了Apache HttpClient那套直观的连接池配置,现在想在Vert.x HTTP Web Client里实现类似的功能对吧?别慌,Vert.x这边也有对应的配置方式,甚至能轻松扩展出HTTP缓存能力,咱们一步步来拆解:
一、先搞定连接池配置(对应Apache的PoolingHttpClientConnectionManager)
Vert.x的Web Client是基于底层的HttpClient实现的,所以连接池的配置其实是在HttpClientOptions里完成的,Web Client会自动复用这个配置。话不多说,直接上代码:
// 初始化HttpClient配置项,对应你之前的连接池参数 HttpClientOptions clientOptions = new HttpClientOptions() // 对应Apache的setMaxTotal:设置连接池的全局最大连接数 .setMaxPoolSize(configuration.getMaxTotalConnections()) // 对应Apache的setDefaultMaxPerRoute:设置每个目标路由的默认最大连接数 .setMaxPerRoute(configuration.getDefaultMaxConnectionsPerRoute()) // 如果你需要给特定主机+端口单独设置连接数上限,就用这个方法,和你之前的setPerRoute逻辑一致 .setHostMaxConnections( configuration.getHost(), configuration.getPort(), configuration.getHostSpecificMaxConnections() ); // 创建配置好的HttpClient HttpClient httpClient = vertx.createHttpClient(clientOptions); // 基于这个HttpClient创建Web Client,确保复用同一连接池 WebClient webClient = WebClient.create(vertx, new WebClientOptions().setHttpClientOptions(clientOptions));
这里几个关键参数的对应关系给你理清楚:
setMaxPoolSize→ Apache的setMaxTotal:控制整个连接池的总连接上限setMaxPerRoute→ Apache的setDefaultMaxPerRoute:给每个目标域名/端口设置默认的连接数上限setHostMaxConnections→ Apache的connectionManager.setMaxPerRoute(httpHost, ...):针对特定主机+端口单独配置连接数
二、实现HTTP响应缓存(类似Apache HttpClient Cache的功能)
Vert.x Web Client本身没有内置HTTP缓存模块,但咱们可以通过自定义拦截器来实现符合RFC 7234规范的缓存逻辑。下面给你一个实用的示例:
1. 先定义缓存存储(生产环境建议换成Redis或Caffeine,这里用本地Map做演示)
// 用ConcurrentHashMap做线程安全的本地缓存 private final ConcurrentMap<String, CacheEntry> responseCache = new ConcurrentHashMap<>(); // 缓存条目类,存储响应内容、头信息和过期时间 static class CacheEntry { Buffer body; MultiMap headers; long expirationTime; CacheEntry(Buffer body, MultiMap headers, long expirationTime) { this.body = body; this.headers = headers; this.expirationTime = expirationTime; } // 判断缓存是否过期 boolean isExpired() { return System.currentTimeMillis() > expirationTime; } }
2. 给Web Client添加缓存拦截器
webClient = WebClient.create(vertx, new WebClientOptions().setHttpClientOptions(clientOptions)) .addInterceptor(requestContext -> { // 只缓存GET请求,POST/PUT这类写请求跳过缓存 if (requestContext.request().method() != HttpMethod.GET) { requestContext.next(); return; } // 用请求的绝对URI作为缓存Key String cacheKey = requestContext.request().absoluteURI(); CacheEntry cachedEntry = responseCache.get(cacheKey); // 如果缓存存在且未过期,直接返回缓存的响应,不用发真实请求 if (cachedEntry != null && !cachedEntry.isExpired()) { requestContext.response(new HttpResponseImpl( requestContext.request(), 200, "OK", cachedEntry.headers, cachedEntry.body, requestContext.request().version() )); return; } // 缓存无效或不存在,继续发送请求,然后缓存响应结果 requestContext.send().onSuccess(response -> { // 检查响应是否允许缓存(参考Cache-Control头) String cacheControl = response.getHeader("Cache-Control"); if (cacheControl != null && !cacheControl.contains("no-cache") && !cacheControl.contains("no-store")) { // 解析max-age计算过期时间,默认10分钟 long maxAgeMillis = 600_000; Matcher maxAgeMatcher = Pattern.compile("max-age=(\\d+)").matcher(cacheControl); if (maxAgeMatcher.find()) { maxAgeMillis = Long.parseLong(maxAgeMatcher.group(1)) * 1000; } long expirationTime = System.currentTimeMillis() + maxAgeMillis; // 将响应存入缓存 responseCache.put(cacheKey, new CacheEntry(response.body(), response.headers(), expirationTime)); } // 将真实响应返回给调用方 requestContext.response(response); }).onFailure(requestContext::fail); });
这个拦截器的逻辑很清晰:
- 只处理GET请求,避免缓存写操作的响应
- 先检查缓存是否有效,有效就直接返回,减少网络请求
- 缓存失效时发送真实请求,然后根据响应的
Cache-Control头决定是否缓存结果
三、一些实用提醒
- Vert.x的连接池是异步非阻塞设计,和Apache HttpClient的同步连接池语义略有不同,但核心的连接数控制逻辑是一致的
- 生产环境别用本地Map做缓存,建议用Caffeine(本地缓存)或Redis(分布式缓存)来保证可靠性和扩展性
- 如果需要更复杂的缓存策略(比如缓存验证、过期刷新等),可以参考RFC 7234的规范来完善拦截器逻辑
内容的提问来源于stack exchange,提问作者Philip K. Adetiloye
相关产品推荐
相关产品推荐

