关于Ribbon策略负载均衡实战

突发奇想,今天学到Ribbon的一点知识,OK,let's tell about Ribbon.

Load Balance解决一个机器(进程)无法解决所有的请求而产生的一种算法;就像nginx可以使用负载均衡分配流量。

好处

1.当有服务宕机后,剩余的服务可以继续运行,不会因为大量请求导致宕机。

2.多个系统保证不会有一台系统cpu彪满,保证了服务器的良性使用。

Ribbon的策略

1.RandomRule(随机策略)

从服务器中随机选择一个服务器,简单实例如下

public Server choose(ILoadBalancer lb, Object key) {
    if (lb == null) {
        return null;
    }
    Server server = null;
 
    while (server == null) {
        if (Thread.interrupted()) {
            return null;
        }
        List upList = lb.getReachableServers();
        List allList = lb.getAllServers();
        int serverCount = allList.size();
        if (serverCount == 0) {
            return null;
        }
        int index = rand.nextInt(serverCount); // 使用jdk内部的Random类随机获取索引值index
        server = upList.get(index); // 得到服务器实例
 
        if (server == null) {
            Thread.yield();
            continue;
        }
 
        if (server.isAlive()) {
            return (server);
        }
 
        server = null;
        Thread.yield();
    }
    return server;
}

2.RoundRobinRule(轮询策略)

顾名思义,10台服务器:1,2,3,4,5,6,7,8,9,10,1.....如此往复交替

public Server choose(ILoadBalancer lb, Object key) {
        if (lb == null) {
            log.warn("no load balancer");
            return null;
        }
 
        Server server = null;
        int count = 0;
        while (server == null && count++ < 10) {
            List reachableServers = lb.getReachableServers();
            List allServers = lb.getAllServers();
            int upCount = reachableServers.size();
            int serverCount = allServers.size();
 
            if ((upCount == 0) || (serverCount == 0)) {
                log.warn("No up servers available from load balancer: " + lb);
                return null;
            }
 
            int nextServerIndex = incrementAndGetModulo(serverCount);
            server = allServers.get(nextServerIndex);
 
            if (server == null) {
                /* Transient. */
                Thread.yield();
                continue;
            }
 
            if (server.isAlive() && (server.isReadyToServe())) {
                return (server);
            }
 
            // Next.
            server = null;
        }
 
        if (count >= 10) {
            log.warn("No available alive servers after 10 tries from load balancer: "
                    + lb);
        }
        return server;
    }
 
    /**
     * Inspired by the implementation of {@link AtomicInteger#incrementAndGet()}.
     *
     * @param modulo The modulo to bound the value of the counter.
     * @return The next value.
     */
    private int incrementAndGetModulo(int modulo) {
        for (;;) {
            int current = nextServerCyclicCounter.get();
            int next = (current + 1) % modulo;
            if (nextServerCyclicCounter.compareAndSet(current, next))
                return next;
        }
    }

3.WeightedResponseTimeRule(权重策略)

根据响应时间分配权重,权重越低,选择的可能性越高。

至于使用那种策略还是根据自己项目业务的实际情况选择。择优(武当 or 少林)

通过Ribbon调用Authority服务获取Token

现在都是基于这种方法

// 在RestTemplate中我们都是使用 ip 和 port 
// 注意到 url 中的 ip 和 port 换成了服务名称
String requestUrl = String.format("http://%s/服务实例名/api资源路径",CommonConstant.AUTHORITY_CENTER_SERVICE_ID);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
// 这里的restTemplate是Ribbon底层基于的restTemplate,不要混淆了
return restTemplate.postForObject(requestUrl,new HttpEntity<>(JSON.toJSONString(json参数),header),JwtToken.class);

使用原生的Ribbon Api,看看Ribbon是如何完成:服务调用 + 负载均衡

public JwtToken thinkRibbon(请求参数) {

        String urlFormat = "http://%s/服务名/api资源路径";

        // 1. 找到服务提供方的地址和端口号
        List targetInstances = discoveryClient.getInstances(
                CommonConstant.AUTHORITY_CENTER_SERVICE_ID
        );

        // 构造 Ribbon 服务列表
        List servers = new ArrayList<>(targetInstances.size());
        targetInstances.forEach(i -> {
            servers.add(new Server(i.getHost(), i.getPort()));
            log.info("found target instance: [{}] -> [{}]", i.getHost(), i.getPort());
        });

        // 2. 使用负载均衡策略实现远端服务调用
        // 构建 Ribbon 负载实例
        BaseLoadBalancer loadBalancer = LoadBalancerBuilder.newBuilder()
                .buildFixedServerListLoadBalancer(servers);
        // 设置负载均衡策略
        loadBalancer.setRule(new RetryRule(new RandomRule(), 300));

        String result = LoadBalancerCommand.builder().withLoadBalancer(loadBalancer)
                .build().submit(server -> {

                    String targetUrl = String.format(
                            urlFormat,
                            String.format("%s:%s", server.getHost(), server.getPort())
                    );
                    log.info("target request url: [{}]", targetUrl);

                    HttpHeaders headers = new HttpHeaders();
                    headers.setContentType(MediaType.APPLICATION_JSON);

                    String tokenStr = new RestTemplate().postForObject(
                            targetUrl,
                            new HttpEntity<>(JSON.toJSONString(usernameAndPassword), headers),
                            String.class
                    );

                    return Observable.just(tokenStr);

                }).toBlocking().first().toString();

        return JSON.parseObject(result, JwtToken.class);
    }

你可能感兴趣的:(ribbon,spring,cloud,云原生)