文章目录
简单的多URL负载均衡和熔断
gitee源码
这个是简单的url负载均衡实现,在开发过程中,很多时候可能会遇到有好多个url可以达到相同的目的,我们就可以通过一个url列表来随机选择一个进行请求,获取返回结果,如果请求失败了,可以把这个url从url列表中移除,等待一段时间在重新使用。
简单使用
- 创建自己的实例 FileHttpUtilsInstance
package cn.yan.study.test;
import cn.yan.study.utils.YanObjectUtils;
import cn.yan.study.utils.http.BaseResult;
import cn.yan.study.utils.http.HttpUtilsInstance;
import cn.yan.study.utils.http.UniRestUtils;
public class FileHttpUtilsInstance extends HttpUtilsInstance {
private UniRestUtils uniRestUtils;
public FileHttpUtilsInstance(String name, Long interval, UniRestUtils uniRestUtils) {
super(name, interval);
this.uniRestUtils = uniRestUtils;
}
@Override
public BaseResult<String> doHttpOp(String url, String param) {
BaseResult<String> result = new BaseResult<>();
String dataStr = uniRestUtils.filPost(url, "", param);
if (YanObjectUtils.isEmpty(dataStr)) {
return result.makeFailedResult();
}
return result.makeSuccessResult(dataStr);
}
}
- 使用 HttpTest
package cn.yan.study.test;
import cn.yan.study.utils.http.BaseResult;
import cn.yan.study.utils.http.UniRestUtils;
import cn.yan.study.utils.http.RibbonHttpUtils;
import com.alibaba.fastjson.JSON;
public class HttpTest {
public static void main(String[] args) throws Exception {
UniRestUtils uniRestUtils = new UniRestUtils();
FileHttpUtilsInstance fileHttpUtilsInstance = new FileHttpUtilsInstance("FilPost", 20 * 1000L, uniRestUtils);
/**
* #rpcUrl: https://api.filscan.io:8700/rpc/v1
* rpcUrl: https://filfox.info/rpc/v0
* #broadCastUrl: https://api.node.glif.io/rpc/v0
*
*/
String[] urlList = {"https://api.filscan.io:8700/rpc/v1","https://filfox.info/rpc/v0","https://filfox.info/rpc/v2","https://api.node.glif.io/rpc/v0"};
for (String urlStr : urlList) {
fileHttpUtilsInstance.addUrl(urlStr);
}
RibbonHttpUtils.addInstance(fileHttpUtilsInstance.getName(), fileHttpUtilsInstance);
String param = "{ \"jsonrpc\": \"2.0\", \"method\": \"Filecoin.ChainHead\", \"params\": [], \"id\": 1 }";
for (int i = 0; i < 100; i++) {
Thread.sleep(2000L);
System.out.println("request count : " + i);
long start = System.currentTimeMillis();
BaseResult<String> result = RibbonHttpUtils.httpOp("FilPost",param);
long end = System.currentTimeMillis();
// System.out.println("spendTime : " + (end - start));
System.out.println("result : " + JSON.toJSONString(result).length());
}
}
}
使用到的类
- BaseResult
- CleanNoActiveThread
- HttpUtilsInstance
- NodeStatus
- RibbonHttpUtils
- UniRestUtils
BaseResult
package cn.yan.study.utils.http;
import lombok.Builder;
import lombok.Data;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
/**
* Created with IDEA
*
* @author: gentlemen_k
* @emali: test@qq.com
**/
@Data
@Builder
public class BaseResult<T> implements Serializable {
private T data;
private Integer code;
private String message;
private Map<String,Object> extendObj;
private boolean success = false;
public BaseResult(T data, Integer code, String message, Map<String, Object> extendObj, boolean success) {
this.data = data;
this.code = code;
this.message = message;
this.extendObj = extendObj;
this.success = success;
}
public BaseResult() {
}
public BaseResult(T data) {
this.data = data;
code = 200;
message = "";
success = true;
}
public BaseResult(T data, String message) {
this.data = data;
code = 200;
success = true;
this.message = message;
}
public BaseResult makeSuccessResult() {
code = 200;
message = "success";
success = true;
return this;
}
public BaseResult makeSuccessResult(T data) {
this.data = data;
code = 200;
message = "success";
success = true;
return this;
}
public BaseResult makeFailedResult() {
code = -1;
message = "failed";
success = false;
return this;
}
public BaseResult makeFailedResult(String message) {
code = -1;
this.message = message;
success = false;
return this;
}
public BaseResult makeFailedResult(Integer code, String message) {
this.code = code;
this.message = message;
success = false;
return this;
}
public void setExtendObj(String key, Object value) {
if (this.extendObj == null) {
this.extendObj = new HashMap<>();
}
this.extendObj.put(key, value);
}
}
CleanNoActiveThread
package cn.yan.study.utils.http;
/**
* 每一分钟清理一次过期缓存
*/
public class CleanNoActiveThread implements Runnable{
@Override
public void run() {
while (true) {
RibbonHttpUtils.processNoActiveUrl();
try {
Thread.sleep(5 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
HttpUtilsInstance
package cn.yan.study.utils.http;
import lombok.Data;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Data
public abstract class HttpUtilsInstance {
private String name;
private List<String> activeUrlList;
private List<String> noActiveUrlList;
private volatile Long count;
private Long interval;
private Map<String, NodeStatus> statusMap;
List<String> willCleanUrlList;
public void processNoActiveUrl() {
willCleanUrlList = new ArrayList<>();
Long now = System.currentTimeMillis();
for (String url : noActiveUrlList) {
NodeStatus nodeStatus = statusMap.get(url);
if (nodeStatus.getLastTimeStamp() + interval < now) {
nodeStatus.setErrorCount(0);
nodeStatus.setLastTimeStamp(now);
willCleanUrlList.add(url);
}
}
for (String url : willCleanUrlList) {
moveToActive(url);
}
}
public abstract BaseResult<String> doHttpOp(String url, String param);
public synchronized void addCount() {
count++;
}
public synchronized void moveToNoActive(String url) {
activeUrlList.remove(url);
noActiveUrlList.add(url);
}
public synchronized void moveToActive(String url) {
noActiveUrlList.remove(url);
activeUrlList.add(url);
}
public HttpUtilsInstance(String name, Long interval) {
this.name = name;
this.activeUrlList = new ArrayList<>();
this.noActiveUrlList = new ArrayList<>();
this.count = -1L;
this.interval = interval;
statusMap = new HashMap<>();
}
public void addUrl(String url) { //, int weight
activeUrlList.add(url);
statusMap.put(url, NodeStatus.builder()
.lastTimeStamp(System.currentTimeMillis())
.errorCount(0)
.build());
}
}
NodeStatus
package cn.yan.study.utils.http;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class NodeStatus {
/**
* 上次失败时间
*/
private Long lastTimeStamp;
private int errorCount;
}
RibbonHttpUtils
package cn.yan.study.utils.http;
import lombok.extern.slf4j.Slf4j;
import java.util.HashMap;
import java.util.Map;
@Slf4j
public class RibbonHttpUtils {
/**
* 这个类主要是为了实现rpc的负载均衡和熔断
*/
private static Map<String, HttpUtilsInstance> instanceMap = new HashMap<>();
private static Boolean isFirst = true;
public static void addInstance(String name, HttpUtilsInstance instance) {
instanceMap.put(name, instance);
if (isFirst) {
startCleanNoActiveUrl();
isFirst = false;
}
}
public static void processNoActiveUrl() {
for (Map.Entry<String, HttpUtilsInstance> entry : instanceMap.entrySet()) {
entry.getValue().processNoActiveUrl();
}
}
/**
* 通过服务名称,调用rpc服务
*/
public static BaseResult<String> httpOp(String name, String param) {
BaseResult<String> result = new BaseResult<>();
HttpUtilsInstance httpUtilsInstance = instanceMap.get(name);
httpUtilsInstance.addCount();
if (httpUtilsInstance.getActiveUrlList().size() == 0) {
return result.makeFailedResult("没有可以使用的url");
}
String url = httpUtilsInstance.getActiveUrlList().get((int)(httpUtilsInstance.getCount() % httpUtilsInstance.getActiveUrlList().size()));
NodeStatus nodeStatus = httpUtilsInstance.getStatusMap().get(url);
long now = System.currentTimeMillis();
result = httpUtilsInstance.doHttpOp(url, param);
if (result.isSuccess()) {
return result;
} else {
if (nodeStatus.getErrorCount() > 0 && now < nodeStatus.getLastTimeStamp() + httpUtilsInstance.getInterval()) {
nodeStatus.setErrorCount(nodeStatus.getErrorCount() + 1);
nodeStatus.setLastTimeStamp(now);
if (nodeStatus.getErrorCount() >= 2) {
httpUtilsInstance.moveToNoActive(url);
}
} else {
nodeStatus.setLastTimeStamp(now);
nodeStatus.setErrorCount(1);
}
return httpOp(name, param);
}
}
public static void startCleanNoActiveUrl() {
CleanNoActiveThread cleanTimeOutThread = new CleanNoActiveThread();
Thread thread = new Thread(cleanTimeOutThread);
thread.setDaemon(true);
thread.start();
}
}
UniRestUtils
package cn.yan.study.utils.http;
import cn.yan.study.utils.YanObjectUtils;
import com.alibaba.fastjson.JSON;
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;
import com.mashape.unirest.http.exceptions.UnirestException;
import com.mashape.unirest.request.HttpRequestWithBody;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.List;
/**
* @Author benxiaohai
* @date 2020/6/22 18:31
*/
@Slf4j
public class UniRestUtils {
private Boolean showLog;
public String doGet(String url) {
if (showLog) {
log.info("UniRestUtils -> doGet url : {}", url);
}
try {
HttpResponse<String> response = Unirest.get(url).asString();
String dataStr = response.getBody();
if (showLog) {
log.info("dataStr : {}", dataStr);
}
if(response.getStatus()!=200){
log.error("url:"+url);
log.error("response:"+dataStr);
return "";
}
return dataStr;
} catch (UnirestException e) {
log.error("UniRestUtils DoGet, url : {}", url);
e.printStackTrace();
return null;
}
}
public static List<Header> baseHeader;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public static class Header {
private String key;
private String value;
}
static {
baseHeader = new ArrayList<>();
baseHeader.add(Header.builder().key("Content-Type").value("application/json").build());
Unirest.setTimeouts(10000, 10000);
}
public String doPost(String url, String body, List<Header> header) {
if (showLog) {
log.info("UniRestUtils -> doPost url : {}, body : {}", url, body);
}
try {
HttpRequestWithBody uniRest = Unirest.post(url);
if (YanObjectUtils.notEmpty(body)) {
uniRest.body(body);
}
processHeader(uniRest, header);
HttpResponse<String> response = uniRest.asString();
String dataStr = response.getBody();
if (showLog) {
log.info("dataStr : {}", dataStr);
}
if(response.getStatus()!=200){
log.error("url:"+url+" body:"+body);
log.error("response:"+dataStr);
return "";
}
return dataStr;
} catch (UnirestException e) {
log.error("UniRestUtils doPost, url : {}, body : {}, header : {}", url, body, JSON.toJSONString(header));
log.error("UniRestUtils doPost, err : {}", e);
return null;
}
}
private void processHeader(HttpRequestWithBody uniRest, List<Header> header) {
if (YanObjectUtils.notEmpty(header)) {
for (Header entry : header) {
uniRest.header(entry.getKey(), entry.getValue());
}
}
}
public String doPostWithAuth(String url, String user, String pwd, String body, List<Header> header) {
if (showLog) {
log.info("UniRestUtils -> doPost url : {}, body : {}", url, body);
}
try {
HttpRequestWithBody uniRest = Unirest.post(url);
processHeader(uniRest, header);
uniRest.basicAuth(user, pwd);
HttpResponse<String> response = uniRest.body(body).asString();
String dataStr = response.getBody();
if (showLog) {
log.info("dataStr : {}", dataStr);
}
return dataStr;
} catch (UnirestException e) {
log.error("UniRestUtils doPostWithAuth, url : {}, user : {}, pwd : {}, body : {}, header : {}", url, user, pwd, body, JSON.toJSONString(header));
return null;
}
}
public String filPost(String url, String bearerToken, String body) {
log.info("url : {}, body : {}", url, body);
System.out.println("url : " + url + " , body : " + body);
try {
HttpResponse<String> response = Unirest.post(url)
// .header("Authorization", "Bearer " + bearerToken)
.header("Content-Type", "application/json")
.body(body)
.asString();
return response.getBody();
} catch (UnirestException e) {
e.printStackTrace();
}
return null;
}
}