|
20 | 20 | import java.io.IOException; |
21 | 21 | import java.util.concurrent.TimeUnit; |
22 | 22 | import java.util.concurrent.atomic.AtomicInteger; |
| 23 | +import org.apache.http.config.SocketConfig; |
23 | 24 | import org.apache.http.impl.client.CloseableHttpClient; |
24 | 25 | import org.apache.http.impl.client.HttpClients; |
25 | 26 | import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; |
|
47 | 48 | * which beats most NAT idle timers (typical 5–15min);</li> |
48 | 49 | * <li>{@code connectionTimeToLive}: hard ceiling — every connection is forcibly recycled |
49 | 50 | * after {@value #CONNECTION_TTL_MINUTES}min regardless of activity, so backend IP rotation |
50 | | - * (K8s pod drift, blue/green) is recovered from in bounded time.</li> |
| 51 | + * (K8s pod drift, blue/green) is recovered from in bounded time;</li> |
| 52 | + * <li>{@code SO_KEEPALIVE=true} on every pooled socket: a last-resort path so the OS |
| 53 | + * eventually surfaces dead peers in the worst-case "host pulled the plug / kernel |
| 54 | + * panic" black-hole scenario where no FIN/RST is ever sent. Linux defaults are |
| 55 | + * conservative (~2h11min) — the application-layer paths above will normally fire |
| 56 | + * long before this kicks in.</li> |
51 | 57 | * </ul> |
52 | 58 | * |
53 | 59 | * <p><b>Health signalling to the cluster manager</b>. The cluster manager's periodic health |
@@ -140,6 +146,19 @@ protected void doOpen() { |
140 | 146 | // Re-validate idle pooled connections before reuse so we do not send a request through a |
141 | 147 | // socket the server has already half-closed. |
142 | 148 | cm.setValidateAfterInactivity(VALIDATE_AFTER_INACTIVITY_MS); |
| 149 | + // Enable TCP-level keep-alive (SO_KEEPALIVE) on every pooled socket. This gives the OS a |
| 150 | + // last-resort path for surfacing dead peers in the worst case where neither the |
| 151 | + // application-layer Keep-Alive timer (5min) nor connectionTimeToLive (10min) fires — |
| 152 | + // typically the "host pulled the plug / kernel panic" black-hole scenario where no FIN |
| 153 | + // or RST is ever sent. Linux defaults are conservative (~2h11min total: 7200s idle + |
| 154 | + // 9 × 75s probes) so this isn't a fast path, but it's a pure win — the alternative is |
| 155 | + // socket lingering until {@code tcp_retries2} (~15min) catches up. Operators who care |
| 156 | + // about faster recovery should tune sysctl {@code net.ipv4.tcp_keepalive_time/intvl/probes} |
| 157 | + // host-wide; Apache HttpClient 4.x's blocking IO does not expose per-socket |
| 158 | + // configuration of those values via SocketOptions, hence the OS default applies. |
| 159 | + cm.setDefaultSocketConfig(SocketConfig.custom() |
| 160 | + .setSoKeepAlive(true) |
| 161 | + .build()); |
143 | 162 | httpClient = HttpClients.custom() |
144 | 163 | .setConnectionManager(cm) |
145 | 164 | // Background eviction of stale & long-idle connections; keeps the pool tidy in |
|
0 commit comments