Ribbon之负载均衡

服务端的负载均衡(Nginx)

 我们用户服务发送请求首先打到Ng上,然后Ng根据负载均衡算法进行选择一个服务调用,而我们的Ng部署在服务器上的,所以Ng又称为服务端的负载均衡

客户端负载均衡(ribbon)

spring cloud ribbon是 基于NetFilix ribbon 实现的一套客户端的负载均衡工具,Ribbon客户端组件提供一系列的完善的配置,如超时,重试等。通过Load Balancer(LB)获取到服务提供的所有机器实例,Ribbon 会自动基于某种规则(轮询,随机)去调用这些服务。

Ribbon也可以实现我们自己的负载均衡算法。

内置的负载均衡算法

ZoneAvoidanceRule  默认规则,复合判断server所在区域的性能和server的可用性选择服务器

RandomRule(随机选择一个Server)

RoundRobinRule 轮询选择, 轮询index,选择index对应位置的Server

RetryRule 先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内会进行重试,获取可用的服务

AvailabilityFilteringRule 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务, 还有并发的连接数量超过阈值的服务,然后对剩余的服务列表按照轮询策略进行访问

BestAvailableRule  会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务

WeightedResponseTimeRule 根据平均响应时间计算所有服务的权重,响应时间越快服务权重越大被选中的概率越高。

刚启动时如果统计信息不足,则使用RoundRobinRule策略,等统计信息足够, 会切换到WeightedResponseTimeRule

自定义的负载均衡算法(随机)

写一个TestRestTemplate类继承RestTemplate,从写doExucute()方法。

/**
 * 根据RestTemplate特性自己改造
 */
@Slf4j
public class TestRestTemplate extends RestTemplate {

    private DiscoveryClient discoveryClient;

    public TestRestTemplate (DiscoveryClient discoveryClient) {
        this.discoveryClient = discoveryClient;
    }

    protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
                              @Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {

        Assert.notNull(url, "URI is required");
        Assert.notNull(method, "HttpMethod is required");
        ClientHttpResponse response = null;
        try {

            log.info("请求的url路径为:{}",url);
            //把服务名 替换成我们的IP
            url = replaceUrl(url);

            log.info("替换后的路径:{}",url);

            ClientHttpRequest request = createRequest(url, method);
            if (requestCallback != null) {
                requestCallback.doWithRequest(request);
            }
            response = request.execute();
            handleResponse(url, method, response);
            return (responseExtractor != null ? responseExtractor.extractData(response) : null);
        }
        catch (IOException ex) {
            String resource = url.toString();
            String query = url.getRawQuery();
            resource = (query != null ? resource.substring(0, resource.indexOf('?')) : resource);
            throw new ResourceAccessException("I/O error on " + method.name() +
                    " request for \"" + resource + "\": " + ex.getMessage(), ex);
        } finally {
            if (response != null) {
                response.close();
            }
        }
    }


    /**
     * 方法实现说明:把微服务名称  去注册中心拉取对应IP进行调用
     * http://product-center/selectProductInfoById/1
     */
    private URI replaceUrl(URI url){

        //1:从URI中解析调用的调用的serviceName=product-center
        String serviceName = url.getHost();
        log.info("调用微服务的名称:{}",serviceName);

        //2:解析我们的请求路径 reqPath= /selectProductInfoById/1
        String reqPath = url.getPath();
        log.info("请求path:{}",reqPath);


        //通过微服务的名称去nacos服务端获取 对应的实例列表
        List<ServiceInstance> serviceInstanceList = discoveryClient.getInstances(serviceName);
        if(serviceInstanceList.isEmpty()) {
            throw new RuntimeException("没有可用的微服务实例列表:"+serviceName);
        }

        String serviceIp = chooseTargetIp(serviceInstanceList);

        String source = serviceIp+reqPath;
        try {
            return new URI(source);
        } catch (URISyntaxException e) {
            log.error("根据source:{}构建URI异常",source);
        }
        return url;
    }

    /**
     * 方法实现说明:从服务列表中 随机选举一个ip
     */
    private String chooseTargetIp(List<ServiceInstance> serviceInstanceList) {
        //采取随机的获取一个
        Random random = new Random();
        Integer randomIndex = random.nextInt(serviceInstanceList.size());
        String serviceIp = serviceInstanceList.get(randomIndex).getUri().toString();
        log.info("随机选举的服务IP:{}",serviceIp);
        return serviceIp;
    }
}

个人博客:https://www.upheart.top/ 

个人公众号,日常分享一个知识点,每天进步一点点,面试不慌:

原创文章 35 获赞 231 访问量 16万+

猜你喜欢

转载自blog.csdn.net/qq_35508033/article/details/106176219
今日推荐