java简单的多url负载均衡

简单的多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;
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值