数组+原子计数器实现线程安全轮询:用String[]存服务器地址,AtomicInteger取模获取下标,避免锁竞争;需判空、支持服务发现刷新、URL编码参数并统一配置超时。

用数组实现基于计数的负载均衡轮询逻辑,核心是把服务器列表存为数组,再用一个线程安全的计数器循环取下标。它不依赖外部状态感知,适合配置一致、无显著性能差异的集群场景,也便于嵌入变量请求分发流程中。
用数组存服务地址,配合原子计数器轮询
服务器列表本质是一维结构,数组最直接。例如:
String[] servers = {"10.0.1.10:8080", "10.0.1.11:8080", "10.0.1.12:8080"};
轮询不是靠遍历数组本身,而是靠一个全局递增的整数(如 AtomicInteger index)对数组长度取模,得到当前应选的下标:
int idx = index.getAndIncrement() % servers.length;
这样每次调用都拿到下一个位置,自动回绕,无需手动重置或判断边界。
确保多线程安全:避免本地变量或锁竞争
并发环境下,计数器必须是线程安全的。推荐方式有:
- 用 AtomicInteger 或 AtomicLong —— 基于 CAS,无锁且高效
- 避免用普通 int + synchronized 方法 —— 锁粒度大,高并发时成为瓶颈
- 不要在方法内定义局部计数器(如 for 循环里的 i++),那只是单次调用有效,无法跨请求轮询
示例关键代码:
private final AtomicInteger currentIndex = new AtomicInteger(0);
public String selectServer() {
int idx = currentIndex.getAndIncrement() % servers.length;
return servers[idx];
}
与变量网络请求联动:动态拼接 URL 并分发
轮询选出服务器后,需将变量请求(如用户 ID、订单号、参数化路径)发往该节点。关键点在于:
- URL 构建要编码参数:URLEncoder.encode(userId, "UTF-8"),防止特殊字符破坏路径
- 主机和端口从轮询结果中提取,而非硬编码:String targetUrl = "http://" + selectedServer + "/api/order?uid=" + encodedId;
- URLConnection 配置需统一超时、User-Agent,并根据请求类型(GET/POST)设置 setDoOutput(true) 等
例如分发一个带用户 ID 的 GET 请求:
String server = selectServer();
String urlStr = String.format("http://%s/user/profile?uid=%s",
server, URLEncoder.encode(userId, "UTF-8"));
URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(3000); conn.setReadTimeout(5000);
注意边界与健壮性处理
实际部署中容易忽略但影响稳定性的问题:
- 数组为空时直接报 ArrayIndexOutOfBoundsException,应在 selectServer() 中加判空并抛明确异常或返回默认兜底地址
- 服务器下线导致数组长度变化时,旧计数器可能越界——建议结合服务发现机制定期刷新数组,而非长期静态持有
- 若需支持权重,不能只靠数组+计数器,得升级为加权轮询(如维护每个节点的 weight 和 currentWeight 双字段)
这种数组+原子计数的组合,轻量、可控、易测,适合中小规模服务治理或 SDK 内部负载逻辑封装。

















