美文网首页
SpringCloud之客户端实现负载均衡组件---------

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

作者: 你弄啥来 | 来源:发表于2019-11-18 22: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;
                }
            }
        }
    

    相关文章

      网友评论

          本文标题:SpringCloud之客户端实现负载均衡组件---------

          本文链接:https://www.haomeiwen.com/subject/ajmmictx.html