zoukankan      html  css  js  c++  java
  • Spring Cloud:第三章:Ribbon客服端负载均衡

    负载均衡是对系统的高可用、网络压力的缓解和处理能力扩容的重要手段。理解Ribbon对于我们使用Spring Cloud来讲非常的重要。它是一个基于Http和TCP的客户端负载均衡工具。它不像服务注册中心、配置中心、API网关那样独立部署,但是它几乎存在于每个微服务的基础设施中。

    基于Ribbon+RestTemplate的用法

    1、引入依赖

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</artifactId>
    </dependency>

    注意:Eureka默认集成了Ribbon,只需引入Eureka JAR即可。

    2、在启动类中注入配置

    package com.mimaxueyuan.consumer.robbin;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import org.springframework.cloud.client.loadbalancer.LoadBalanced;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Primary;
    import org.springframework.web.client.RestTemplate;
    
    @SpringBootApplication
    @EnableDiscoveryClient
    public class RibbonConsumerApplication {
    
        @Bean
        @LoadBalanced // 需要使用负载均衡,必须与Bean一同使用
        public RestTemplate balanceRestTemplate() {
            return new RestTemplate();
        }
        
        @Primary //自动装配时当出现多个Bean候选者时,被注解为@Primary的Bean将作为首选者,否则将抛出异常 
        @Bean //需要多个RestTemplate, 有的RestTemplate使用负载均衡,有的不使用,不使用的不增加@LoadBalanced注解
        public RestTemplate noBalanceRestTemplate() {
            return new RestTemplate();
        }
        
        public static void main(String[] args) {
            SpringApplication.run(RibbonConsumerApplication.class, args);
        }
    
    }

    3、编写 Controller——演示使用负载均衡和不使用负载均衡的用法及区别

    package com.mimaxueyuan.consumer.robbin.controller;
    import java.net.URI;
    import java.net.URISyntaxException;
    import java.util.List;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.cloud.client.ServiceInstance;
    import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
    import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.client.RestTemplate;
    import com.mimaxueyuan.consumer.entity.User;
    
    @RestController
    public class RibbonController {
    
        // 注入restTemplate, 这个类已经在RibbonConsumerApplication中初始化,不使用负载均衡
        @Autowired
        private RestTemplate noBalanceRestTemplate;
    
        // 注入restTemplate, 这个类已经在RibbonConsumerApplication中初始化,并且使用负载均衡
        @Autowired // 默认按照类型注入,如果需要按照名字注入需要使用@Qualifier注解
        //@LoadBalanced //使用带有负载均衡的RestTemplate
        @Qualifier("balanceRestTemplate")
        private RestTemplate balanceRestTemplate;
    
        // 以下注入负载均衡客户端LoadBalancerClient是一个接口,下面只有一个RibbonLoadBalancerClient实现类
        @Autowired
        private LoadBalancerClient loadBalancerClient;
        @Autowired
        private RibbonLoadBalancerClient ribbonLoadBalancerClient;
    
        /**
         * 不使用ribbon的旧调用方式
         *
         * @author Kevin
         * @Title: old
         * @return
         * @return: String
         */
        @GetMapping("/ribbon/old/get/{id}")
        public String old(@PathVariable("id") String id) {
            
            // 使用noBalanceRestTemplate是非负载均衡的,所以没问题
            String result = noBalanceRestTemplate.getForObject("http://localhost:9907/get/"+id, String.class);
            System.out.println("[hardcode1]" + result);
            
            // 由于balanceRestTemplate已经使用了Ribbon做负载均衡,所以使用硬编码方式就不允许了,会提示:No instances available for localhost
            result = balanceRestTemplate.getForObject("http://localhost:9907/get/"+id, String.class);
            System.out.println("[hardcode2]" + result);
            
            return "result";
        }
    
        /**
         * ribbon使用
         *
         * @author Kevin
         * @Title: ribbon 
         * @param id
         * @return
         * @return: String
         */
        @GetMapping("/ribbon/get/{id}")
        public String ribbon(@PathVariable("id") String id) {
            // -----------------以下代码使用ribbon做客户端负载均衡
            // 使用provider的instanceName替代ip和端口的硬编码
            String result = balanceRestTemplate.getForObject("http://mima-cloud-producer/get/"+id, String.class);
            System.out.println("[ribbon]" + result);
    
            System.out.println("[loadBalancerClient]choose的结果,代表负载均衡之后要选择的服务实例");
            ServiceInstance instance = loadBalancerClient.choose("mima-cloud-producer");
            System.out.println("host:" + instance.getHost() + ",port:" + instance.getPort() + ",serviceId=" + instance.getServiceId() + ",uri=" + instance.getUri());
    
            System.out.println("[ribbonLoadBalancerClient]choose的结果,代表负载均衡之后要选择的服务实例");
            instance = ribbonLoadBalancerClient.choose("mima-cloud-producer");
            System.out.println("host:" + instance.getHost() + ",port:" + instance.getPort() + ",serviceId=" + instance.getServiceId() + ",uri=" + instance.getUri());
            
            System.out.println("[ribbonLoadBalancerClient]choose的结果,代表负载均衡之后要选择的服务实例");
            instance = ribbonLoadBalancerClient.choose("mima-cloud-producer");
            System.out.println("host:" + instance.getHost() + ",port:" + instance.getPort() + ",serviceId=" + instance.getServiceId() + ",uri=" + instance.getUri());
            try {
                // 根据负载均衡后的服务,构建一个访问url
                // 第二个参数不能为null
                System.out.println("根据负载均衡后的服务,构建一个访问url");
                URI reconstructURI = ribbonLoadBalancerClient.reconstructURI(instance, new URI(""));
                System.out.println("reconstructURI1-yes:" + reconstructURI);
                // 拼写在请求地址后边,需要注意是否需要添加/
                reconstructURI = ribbonLoadBalancerClient.reconstructURI(instance, new URI("/ribbon/get"));
                System.out.println("reconstructURI2-yes:" + reconstructURI);
                reconstructURI = ribbonLoadBalancerClient.reconstructURI(instance, new URI("http"));
                System.out.println("reconstructURI3-no:" + reconstructURI);
                reconstructURI = ribbonLoadBalancerClient.reconstructURI(instance, new URI("https"));
                System.out.println("reconstructURI4-no:" + reconstructURI);
                reconstructURI = ribbonLoadBalancerClient.reconstructURI(instance, new URI("test"));
                System.out.println("reconstructURI5-no:" + reconstructURI);
                // 使用http:/xxx、https:/xxx可以用于切换http协议还是https协议
                reconstructURI = ribbonLoadBalancerClient.reconstructURI(instance, new URI("http:/ribbin/get"));
                System.out.println("reconstructURI6-yes:" + reconstructURI);
                reconstructURI = ribbonLoadBalancerClient.reconstructURI(instance, new URI("https:/ribbin/get"));
                System.out.println("reconstructURI7-yes:" + reconstructURI);
            } catch (URISyntaxException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    
            return "ribbon's demo,please to see console output";
        }
    
        @GetMapping("/nobalance/get/{id}")
        public String nobalance(@PathVariable("id") String id) {
            // -----------------以下代码使用硬编码方式调用服务
            // 如果restTemplate已经使用了Ribbon做负载均衡,也就是使用了@LoadBaleced注解,依然使用硬编码方式就不允许了,会提示:No instances available for localhost
    
            String result = noBalanceRestTemplate.getForObject("http://localhost:9907/get/"+id, String.class);
            System.out.println("[noBalanceRestTemplate-hardcode1]" + result); //正常访问
            result = noBalanceRestTemplate.getForObject("http://localhost:9908/get/"+id, String.class);
            System.out.println("[noBalanceRestTemplate-hardcode2]" + result); //正常访问
    
            try {
                //异常访问,Ribbon负载均衡只能通过服务名调用
                result = balanceRestTemplate.getForObject("http://localhost:9907/get/"+id, String.class);
                System.out.println("[balanceRestTemplate-hardcode1]" + result); 
                //异常访问,Ribbon负载均衡只能通过服务名调用
                result = balanceRestTemplate.getForObject("http://localhost:9908/get/"+id, String.class);
                System.out.println("[balanceRestTemplate-hardcode2]" + result); 
            } catch (Exception e) {
                System.out.println("使用balanceRestTemplate同时使用地址硬编码错误:" + e.getMessage());
            }
            return "ribbon's demo,please to see console output";
        }
    
        @SuppressWarnings("unchecked")
        @GetMapping("listAll")
        public List<User> listAll() {
            // restTemplate怎样返回一个List对象
            List<User> list = balanceRestTemplate.getForObject("http://mima-cloud-producer/listAll", List.class);
            return list;
        }
    
    }

    其中 mima-cloud-producer 为服务名,启动两个服务节点如下:
    http://localhost:9907/
    http://localhost:9908/

  • 相关阅读:
    Mysql登录错误:ERROR 1045 (28000): Plugin caching_sha2_password could not be loaded
    Docker配置LNMP环境
    Docker安装mysqli扩展和gd扩展
    Docker常用命令
    Ubuntu常用命令
    单例模式的优缺点和使用场景
    ABP 多租户数据共享
    ABP Core 后台Angular+Ng-Zorro 图片上传
    ERROR Error: If ngModel is used within a form tag, either the name attribute must be set or the form control must be defined as 'standalone' in ngModelOptions.
    AbpCore 执行迁移文件生成数据库报错 Could not find root folder of the web project!
  • 原文地址:https://www.cnblogs.com/javawxid/p/12811941.html
Copyright © 2011-2022 走看看