一个线程设置代理属性、发出请求,然后恢复旧值。另一个线程用另一个代理做同样的事。从上往下读,代码看起来整洁,但两个线程都在改动共享的 JVM 状态。

System.setProperty() 改动的是进程级的系统属性,它不会把路由挂到调用它的线程上。并发请求可能看到为别人的工作写入的值,而已有的客户端可能早已捕获了自己的配置。

把路由做成客户端的一部分

对 Java 的 HTTP 客户端,应使用有意指定的代理选择器来创建客户端。当工作负载需要不同的路由策略时,就保留彼此独立的客户端:

HttpClient client = HttpClient.newBuilder()
    .proxy(ProxySelector.of(
        new InetSocketAddress("203.0.113.10", 8080)))
    .connectTimeout(Duration.ofSeconds(5))
    .build();

导入的是 java.net、java.net.http 和 java.time。该地址仅作示例;对于获准的公网路由测试,HTTP 代理列表提供了候选端点。请按应用需要单独配置请求超时和代理认证。

builder 的 proxy 方法记录的是客户端级的选择。相比把一串全局写入包在异步操作外面,这让策略更容易检查。

全局设置仍有用途

当每个参与的客户端都打算遵循同一策略时,全 JVM 统一的代理策略可能是合适的。在应用启动时设定该策略,并确认哪些库会遵循它。

问题出现在把全局属性当作单个请求的临时开关时。在服务器里,即使你自己的方法看起来是同步的,后台工作线程和库调用也可能并发执行。把路由状态留在拥有该连接的客户端或连接器上,这样另一个请求就无法在任务进行到一半时改掉它。

相关文章