• SpringCloud学习笔记(2):使用Ribbon负载均衡


    简介

    Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡工具,在注册中心对Ribbon客户端进行注册后,Ribbon可以基于某种负载均衡算法,如轮询(默认)、随机、加权轮询、加权随机等自动帮助服务消费者调用接口。

    项目介绍

    1. sc-parent,父模块(请参照SpringCloud学习笔记(1):Eureka注册中心)
    2. sc-eureka,注册中心(请参照SpringCloud学习笔记(1):Eureka注册中心)
    3. sc-provider-random,随机端口的提供者
    4. sc-consumer,使用Ribbon负载均衡的消费者

    随机端口的提供者

    1.在父模块下创建子模块项目sc-provider-random,pom.xml:

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
      <parent>
        <groupId>com.cf</groupId>
        <artifactId>sc-parent</artifactId>
        <version>0.0.1-SNAPSHOT</version>
      </parent>
      <artifactId>sc-provider-random</artifactId>
      
      <dependencies>
      	<dependency>
    	    <groupId>org.springframework.cloud</groupId>
    	    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    	</dependency>
      </dependencies>
    </project>
    

    2.创建启动类provider.ProviderApplication:

    package provider;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class ProviderApplication {
    
    	public static void main(String[] args) {
    		SpringApplication.run(ProviderApplication.class, args);
    	}
    }
    

    3.创建Controller:provider.controller.BookController

    package provider.controller;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RequestMapping("/book")
    @RestController
    public class BookController {
    	
    	@GetMapping("/list")
    	public String getBookList(){
    		System.out.println("------------我被访问了-----------");
    		return "["Java入门到放弃","C++入门到放弃","Python入门到放弃","C入门到放弃"]";
    	}
    }
    

    4.创建application.yml:

    server:
      port: 0 #默认8080,配置随机端口需要设置为0
    
    spring:
      application:
        name: sc-provider-random
    eureka:
      client:
        serviceUrl: 
          defaultZone: http://localhost:8080/eureka/
      instance:
        instance-id: ${spring.application.name}:${random.value} #实例名随机生成
    

    5.依次启动注册中心sc-eureka以及两个提供者sc-provider-random,提供者sc-provider-random每次启动端口都不一样,可以看到sc-provider-random的两个实例都在注册中心注册:

    使用Ribbon负载均衡的消费者

    1.在父模块下创建子模块项目sc-consumer,pom.xml:

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
      <parent>
        <groupId>com.cf</groupId>
        <artifactId>sc-parent</artifactId>
        <version>0.0.1-SNAPSHOT</version>
      </parent>
      <artifactId>sc-consumer</artifactId>
      
      <dependencies>
      	<dependency>
    	    <groupId>org.springframework.cloud</groupId>
    	    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    	</dependency>
    	
    	<!-- spring-cloud-starter-netflix-eureka-client依赖中已经包含ribbon -->
    	<!-- <dependency>
    		<groupId>org.springframework.cloud</groupId>
    		<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
    	</dependency> -->
      </dependencies>
    </project>
    

    2.创建启动类consumer.ConsumerApplication:

    package consumer;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.loadbalancer.LoadBalanced;
    import org.springframework.context.annotation.Bean;
    import org.springframework.web.client.RestTemplate;
    
    @SpringBootApplication
    public class ConsumerApplication {
    
    	public static void main(String[] args) {
    		SpringApplication.run(ConsumerApplication.class, args);
    	}
    	
    	//为RestTemplate整合Ribbon,使其具备负载均衡的能力
    	@LoadBalanced
    	@Bean
    	public RestTemplate restTemplate(){
    		return new RestTemplate();
    	}
    }
    

    3.创建调用提供者服务的Controller:consumer.controller.ConsumerController

    package consumer.controller;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.client.RestTemplate;
    
    @RestController
    public class ConsumerController {
    	@Autowired
    	private RestTemplate restTemplate;
    	
    	@GetMapping("/getBookList")
    	public String getBookList(){
    		//sc-provider-random 为提供者服务名
    		return restTemplate.getForObject("http://sc-provider-random/book/list", String.class);
    	}
    }
    

    注意:在使用Ribbon负载均衡时,服务名称不能有下划线,否则会出现valid hostname异常

    4.创建application.yml:

    server:
      port: 8083
    
    spring:
      application:
        name: sc-consumer
        
    eureka:
      client:
        registerWithEureka: false
        serviceUrl:
          defaultZone: http://localhost:8080/eureka/    
    

    5.依次启动注册中心sc-eureka、两个提供者sc-provider-random、消费者sc-consumer,并访问http://localhost:8083/getBookList:

    多次访问后,通过控制台的日志打印可以发现消费者时通过轮询的方式访问两个提供者实例。

    更改负载均衡策略

    Ribbon默认使用RoundRobinRule(轮询)来做为负载均衡策略,我们可以实现IRule接口或者使用Ribbon提供的现成的负载均衡策略来替换默认的轮询策略。如下,使用随机访问的策略来替代默认的轮询,在消费者启动类中添加:

    	@Bean
    	public IRule randomRule(){
    		return new RandomRule();//使用随机访问的策略来替代默认的轮询
    	}
    

    自定义负载均衡策略

    一个自定义负载均衡策略小例子,依旧是轮询访问策略,只是每个服务实例访问5次后才会访问下一个服务实例。

    1.创建类consumer.rule.MyRule:

    package consumer.rule;
    
    import java.util.List;
    
    import com.netflix.client.config.IClientConfig;
    import com.netflix.loadbalancer.AbstractLoadBalancerRule;
    import com.netflix.loadbalancer.ILoadBalancer;
    import com.netflix.loadbalancer.Server;
    
    public class MyRule extends AbstractLoadBalancerRule {
    	private int currIndex = 0;//当前服务实例索引
    	
    	private int currCount = 0;//当前服务实例被访问次数
    	
    	private static final int MAX_COUNT = 5;//每个服务实例最大访问次数为5
    
        public Server choose(ILoadBalancer lb, Object key) {
            if (lb == null) {
                return null;
            }
            Server server = null;
    
            while (server == null) {
                if (Thread.interrupted()) {
                    return null;
                }
                List<Server> upList = lb.getReachableServers();
                List<Server> allList = lb.getAllServers();
    
                int serverCount = allList.size();
                if (serverCount == 0) {
                    /*
                     * No servers. End regardless of pass, because subsequent passes
                     * only get more restrictive.
                     */
                    return null;
                }
    
                int index = chooseRandomInt(upList.size());
                server = upList.get(index);
    
                if (server == null) {
                    /*
                     * The only time this should happen is if the server list were
                     * somehow trimmed. This is a transient condition. Retry after
                     * yielding.
                     */
                    Thread.yield();
                    continue;
                }
    
                if (server.isAlive()) {
                    return (server);
                }
    
                // Shouldn't actually happen.. but must be transient or a bug.
                server = null;
                Thread.yield();
            }
    
            return server;
    
        }
    
        protected synchronized int chooseRandomInt(int serverCount) {
        	currCount++;
        	if(currCount > MAX_COUNT){
        		currIndex++;
        		if(currIndex >= serverCount){
        			currIndex = 0;
        		}
        		currCount = 1;
        	}
        	return currIndex;
        }
    
    	@Override
    	public Server choose(Object key) {
    		return choose(getLoadBalancer(), key);
    	}
    
    	@Override
    	public void initWithNiwsConfig(IClientConfig clientConfig) {
    		// TODO Auto-generated method stub
    		
    	}
    }
    

    2.修改配置:

    	@Bean
    	public IRule randomRule(){
    		return new MyRule();
    	}
    

    依次启动注册中心sc-eureka、两个提供者sc-provider-random、消费者sc-consumer,并访问http://localhost:8083/getBookList进行测试。

  • 相关阅读:
    基础004_V7-DSP Slice
    基础003_V7-Memory Resources
    基础001_Xilinx V7资源
    基础002_V7-CLB
    FIR调用DSP48E_05
    FIR仿真module_04
    FIR基本型仿真_03
    FIR定点提高精度的trick_02
    〖Android〗快速部署SSHD和Bash Shell(程序:DroidSSHD和BetterTerminalEmulatorPro)
    〖Android〗Nexus 7 flo (razor) 刷入Recovery/CM-11.0后卡在开机动画的解决方法
  • 原文地址:https://www.cnblogs.com/seve/p/11502669.html
Copyright © 2020-2023  润新知