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進行測試。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM