SpringCloud之客户端实现负载均衡组件---------

2019-11-18  本文已影响0人  你弄啥来

看代码说事情:

没有用到注册中心时,服务端的配置如下图所示:

spring:
  application:
    name: spring-clound-ribbon-consumer
server:
  port: 9001
eureka:
  client:
    enabled: false
# 两个客户端分别是localhost:8080,localhost:8088
helloclient:
  ribbon:
    listOfServers: localhost:8080,localhost:8088

配置一个RestTemplate,并加上一个@LoadBalanced的注解

package com.chen.cloud.chenribbon.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class RestTemplateT {

    @Bean
    @LoadBalanced
    public RestTemplate template() {
        return new RestTemplate();
    }
}

controller类中实现一个简单的逻辑

package com.chen.cloud.chenribbon.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.RestTemplate;

@Controller
public class RibbonController {

    @Autowired
    private RestTemplate restTemplateT;

    @GetMapping("hello")
    @ResponseBody
    public String getObject() {
       //发送请求的地址其实是一个配置文件中的一个配置名加地址
        Object result = restTemplateT.getForObject("http://helloclient/test",Object.class);
        System.out.println(result);
        return "Hello";
    }
}

新增两个普通web项目端口号分别是8080,和 8088

8080的client1 项目

package com.chen.cloud.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;

@RestController
public class HelloController {
    @GetMapping("test")
    public Object getTest(){
        System.err.println("进入到了客户端1");
        Map<String,String> singleMap = new HashMap<>();
        singleMap.put("key","Hello");
        return singleMap;
    }
}

8088的client2 项目

package com.chen.cloud.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;

@RestController
public class HelloController {
    @GetMapping("test")
    public Object getTest(){
        System.err.println("进入到了客户端2");
        Map<String,String> singleMap = new HashMap<>();
        singleMap.put("key","Hello");
        return singleMap;
    }
}

最终效果:

当我在地址栏输入ribbon客户端的地址 :http://localhost:9001/hello
时,ribbon 在调用:http://helloclient/test,时轮训的调用着8080和8089两个端口。
追究原因:发现在@LoadBalanced 上面有一段注释:该注解是用在RestTemplate上,用来配置LoadBalancerClient用的。

package org.springframework.cloud.client.loadbalancer;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.beans.factory.annotation.Qualifier;
/**
 * Annotation to mark a RestTemplate bean to be configured to use a LoadBalancerClient.
 * @author Spencer Gibb
 */
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}

类的关系图如下所示

SptingTransation.png

LoadBalanceClient中核心方法就是根据serviceId中找到ServiceINstance,具体的实现是在RibbonLoadBalancerClient中,RibbonLoadBalancerClient先去获取了ILoadBalancer。
ILoadBalancer,他是所有服务的处理站。他可以addServer,chooserServer,makeServerDown等一系列方法。因此想要获得server,先去获取ILoadBalancer,还好ILoadBalancer获取不是特别困难,在SpringClientFactory中可以获得。

/**
 * A factory that creates client, load balancer and client configuration instances. It
 * creates a Spring ApplicationContext per client name, and extracts the beans that it
 * needs from there.
 *
 * @author Spencer Gibb
 * @author Dave Syer
 */
public class SpringClientFactory extends NamedContextFactory<RibbonClientSpecification> {
}

注解中可以了解到该Factory可以创建 IClient,ILoadBalancer,IClientConfig,并且会为每一个客户端的名称创建一个bean容器,并从该容器中取到自己需要的bean实例出来。springcloud 默认取到的是ZoneAwareLoadBalancer(springboot自动装配获得的),ZoneAwareLoadBalancer调用chooseServer时,判断是否配置了zone的相关属性,没有的话会调用BaseLoadBalancer.chooserServer(),并在此处实现了负载均衡的策略。
ZoneAwareLoadBalancer的代码:

    public Server chooseServer(Object key) {
        if (!ENABLED.get() || getLoadBalancerStats().getAvailableZones().size() <= 1) {
            logger.debug("Zone aware logic disabled or there is only one zone");
            return super.chooseServer(key);
        }
······························
}

BaseLoadBalancer的代码:

  public Server chooseServer(Object key) {
        if (counter == null) {
            counter = createCounter();
        }
        counter.increment();
        if (rule == null) {
            return null;
        } else {
            try {
                return rule.choose(key);
            } catch (Exception e) {
                logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", name, key, e);
                return null;
            }
        }
    }
上一篇下一篇

猜你喜欢

热点阅读