• 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/

  • 相关阅读:
    POJ2407:Relatives(欧拉函数) java程序员
    POJ1664:放苹果(搜索) java程序员
    关于android中数据库的创建以及基础的增删改查的相应操作
    家庭版记账本app开发进度。开发到现在整个app只剩下关于图表的设计了,具体功能如下
    在tap的碎片上与活动进行绑定实现点击事件(日期时间选择以及按钮跳转时间)
    使用tap、Fragment等相关相关知识点。实现类似微信的界面
    android学习相关intent和fragment的先关知识点
    家庭记账本app进度之关于tap的相关操作1
    家庭版记账本app进度之关于listview显示账单,并为其添加点击事件
    家庭版记账本app进度之编辑框组件
  • 原文地址:https://www.cnblogs.com/javawxid/p/15644616.html
Copyright © 2020-2023  润新知