用 common-httpclient 封装一个 HttpUtil 工具类,可以发送 post 和 get 请求,同时支持表单和 json 参数。带有一个简单的熔断机制,如果一个 url 请求不成功,那么下次请求这个 url 必须和这一次有一个间隔,小于这个间隔的请求直接丢弃掉,这个间隔随着失败次数逐渐变大(比如第一次失败需要等 10 秒,第二次 20 秒,第三次 30 秒,最多间隔 10 分钟)。
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
public class HttpUtil {
private static final long MAX_WAIT_TIME = 600_000; // 最大等待时间(毫秒),10 分钟
private static volatile boolean circuitBreakerEnabled = true; // 全局开关,默认开启
private static final ConcurrentHashMap<String, FailureInfo> failureMap = new ConcurrentHashMap<>();
public static String sendGet(String url) throws IOException {return execute(new GetMethod(url), url);
}
public static String sendPostForm(String url, Map<String, String> params) throws IOException {PostMethod postMethod = new PostMethod(url);
for (Map.Entry<String, String> entry : params.entrySet()) {postMethod.addParameter(entry.getKey(), entry.getValue());
}
return execute(postMethod, url);
}
public static String sendPostJson(String url, String json) throws IOException {PostMethod postMethod = new PostMethod(url);
postMethod.setRequestEntity(new StringRequestEntity(json, "application/json", "UTF-8"));
return execute(postMethod, url);
}
private static String execute(org.apache.commons.httpclient.methods.HttpMethod method, String url) throws IOException {HttpClient client = new HttpClient();
// 检查是否应该丢弃请求
if (circuitBreakerEnabled && shouldDiscardRequest(url)) {throw new IOException("Request discarded due to circuit breaker");
}
try {int statusCode = client.executeMethod(method);
if (statusCode == HttpStatus.SC_OK) {resetFailureCount(url); // 请求成功,重置失败计数
return method.getResponseBodyAsString();} else {handleFailure(url); // 处理失败情况
throw new IOException("HTTP error: " + statusCode);
}
} catch (IOException e) {handleFailure(url); // 处理失败情况
throw e;
} finally {method.releaseConnection();
}
}
private static boolean shouldDiscardRequest(String url) {FailureInfo info = failureMap.get(url);
if (info == null || !circuitBreakerEnabled) {return false;}
long currentTime = System.currentTimeMillis();
long nextAllowedTime = info.getLastFailedTime() + calculateWaitTime(info.getFailureCount());
return currentTime < nextAllowedTime;
}
private static void handleFailure(String url) {failureMap.compute(url, (key, value) -> {if (value == null) {return new FailureInfo(1, System.currentTimeMillis());
} else {value.setLastFailedTime(System.currentTimeMillis());
value.incrementFailureCount();
return value;
}
});
}
private static void resetFailureCount(String url) {failureMap.remove(url);
}
private static long calculateWaitTime(int failureCount) {
long waitTime = failureCount * 10_000L; // 每次失败增加 10 秒
return Math.min(waitTime, MAX_WAIT_TIME); // 不超过最大等待时间
}
private static class FailureInfo {
private int failureCount;
private long lastFailedTime;
public FailureInfo(int failureCount, long lastFailedTime) {
this.failureCount = failureCount;
this.lastFailedTime = lastFailedTime;
}
public int getFailureCount() {return failureCount;}
public void incrementFailureCount() {this.failureCount++;}
public long getLastFailedTime() {return lastFailedTime;}
public void setLastFailedTime(long lastFailedTime) {this.lastFailedTime = lastFailedTime;}
}
public static void setCircuitBreakerEnabled(boolean enabled) {circuitBreakerEnabled = enabled;}
}
功能说明
- 全局开关:通过
setCircuitBreakerEnabled(boolean enabled)
方法可以启用或禁用熔断功能。默认情况下,熔断功能是启用的。 - 熔断机制:如果某个 URL 的请求失败,会在 failureMap 中记录失败次数和最后一次失败的时间。下次请求该 URL 时,会检查当前时间与上一次失败时间之间的间隔,如果小于计算出的等待时间且熔断功能启用,则直接抛出异常,丢弃请求。
- 等待时间计算:每次失败后,等待时间逐渐增加,最多不超过 10 分钟。
- 重置失败计数:如果请求成功,会从
failureMap
中移除该 URL 的记录,以便下一次请求不受影响。
如何使用
- GET 请求:调用
sendGet(String url)
方法。 - POST 表单请求:调用
sendPostForm(String url, Map<String, String> params)
方法。 - POST JSON 请求:调用
sendPostJson(String url, String json)
方法