Ribbon詳解與實例


Ribbon是一個為客戶端提供負載均衡功能的服務,它內部提供了一個叫做ILoadBalance的接口代表負載均衡器的操作,比如有添加服務器操作、選擇服務器操作、獲取所有的服務器列表、獲取可用的服務器列表等等。

需要解決的問題:

① 如何在配置Eureka Client注冊中心時不去硬編碼Eureka Server的地址?
② 在微服務不同模塊間進行通信時,如何不去硬編碼服務提供者的地址?
③ 當部署多個相同微服務時,如何實現請求時的負載均衡? 
Ribbon是什么?
        Ribbon是Netflix發布的雲中間層服務開源項目,其主要功能是提供客戶端實現負載均衡算法。Ribbon客戶端組件提供一系列完善的配置項如連接超時,重試等。簡單的說,Ribbon是一個客戶端負載均衡器,我們可以在配置文件中Load Balancer后面的所有機器,Ribbon會自動的幫助你基於某種規則(如簡單輪詢,隨機連接等)去連接這些機器,我們也很容易使用Ribbon實現自定義的負載均衡算法。

下圖展示了Eureka使用Ribbon時的大致架構:

 

 

SpringCloud之Ribbon入門案例

① 首先引入Ribbon依賴,Ribbon的使用依賴Eureka:

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

② 如何使用Ribbon    

       使用RestTemplate進行Eureka Client(包括服務提供者以及服務消費者,在這里其實是服務消費者使用RestTemplate)之間的通信,為RestTemplate配置類添加@LoadBalanced注解即可,如下所示: 

@Bean
@LoadBalanced
  public RestTemplate restTemplate() {
  return new RestTemplate();
}

主程序:

@SpringBootApplication
@EnableEurekaClient
//在啟動該微服務的時候就能去加載我們的自定義Ribbon配置類,從而使配置生效
@RibbonClient(name="MICROSERVICECLOUD-DEPT")
public class DeptConsumer80_App{
    public static void main(String[] args){
        SpringApplication.run(DeptConsumer80_App.class, args);
    }
}

③ 如何解決硬編碼

       使用添加@LoadBalanced注解后的RestTemplate調用服務提供者的接口時,可以使用虛擬IP替代真實IP地址。所謂的虛擬IP就是服務提供者在application.properties或yml文件中配置的spring.application.name屬性的值。示例如下:

以前:

@RestController
public class DeptController_Consumer
{
    private static final String REST_URL_PREFIX = "http://localhost:8001"; //需要ip+端口
 
    @Autowired
    private RestTemplate restTemplate;
 
    @RequestMapping(value = "/consumer/dept/add")
    public boolean add(Dept dept)
    {
        return restTemplate.postForObject(REST_URL_PREFIX + "/dept/add", dept, Boolean.class);
    }
}

使用Ribbon后:

@RestController
public class DeptController_Consumer
{
    private static final String REST_URL_PREFIX = "http://MICROSERVICECLOUD-DEPT"; //微服務的虛擬id
 
    @Autowired
    private RestTemplate restTemplate;
 
    @RequestMapping(value = "/consumer/dept/add")
    public boolean add(Dept dept)
    {
        return restTemplate.postForObject(REST_URL_PREFIX + "/dept/add", dept, Boolean.class);
    }
}

小總結:Ribbon和Eureka整合后Consumer可以直接調用服務而不用再關心ip地址和端口號

微服務(服務提供者)集群搭建:

機器1
server:
  port: 8001
 
spring:
   application:
    name: microservicecloud-dept 
   datasource:
    type: com.alibaba.druid.pool.DruidDataSource          
    driver-class-name: org.gjt.mm.mysql.Driver            
    url: jdbc:mysql://localhost:3306/cloudDB01            
    username: root
    password: 123456
 
機器2
server:
  port: 8002
 
spring:
   application:
    name: microservicecloud-dept 
   datasource:
    type: com.alibaba.druid.pool.DruidDataSource   
    driver-class-name: org.gjt.mm.mysql.Driver          
    url: jdbc:mysql://localhost:3306/cloudDB02        
    username: root
    password: 123456
 
機器3
server:
  port: 8003
 
spring:
   application:
    name: microservicecloud-dept 
   datasource:
    type: com.alibaba.druid.pool.DruidDataSource         
    driver-class-name: org.gjt.mm.mysql.Driver            
    url: jdbc:mysql://localhost:3306/cloudDB03              
    username: root
    password: 123456

其中{Spring.application.name}都是一樣的,不可以變。

 

 

Ribbon組件IRule

默認的是RoundBobinRule(輪詢)

 

RetryRule

  • 1、先按照RoundRobinRule(輪詢)的策略獲取服務,如果獲取的服務失敗側在指定的時間會進行重試,進行獲取可用的服務
  • 2、如多次獲取某個服務失敗,這不會再再次獲取該服務如(高德地圖上某條道路堵車,司機不會走那條道路)= 

使用:

@Configuration
public class ConfigBean //boot -->spring   applicationContext.xml --- @Configuration配置   ConfigBean = applicationContext.xml
{ 
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
    
    @Bean
    public IRule myRule(){
        //return new RoundRobinRule();
        //return new RandomRule();//達到的目的,用我們重新選擇的隨機算法替代默認的輪詢。
        return new RetryRule();  //在這里選擇負載均衡算法
    }
}

自定義負載均衡算法:
       所謂的自定義Ribbon Client的主要作用就是使用自定義配置替代Ribbon默認的負載均衡策略,注意:自定義的Ribbon Client是有針對性的,一般一個自定義的Ribbon Client是對一個服務提供者(包括服務名相同的一系列副本)而言的。自定義了一個Ribbon Client 它所設定的負載均衡策略只對某一特定服務名的服務提供者有效,但不能影響服務消費者與別的服務提供者通信所使用的策略。根據官方文檔的意思,推薦在 springboot 主程序掃描的包范圍之外進行自定義配置類。其實純代碼自定義RibbonClient的話有兩種方式:

方式一:在springboot主程序掃描的包外定義配置類,然后為springboot主程序添加 @RibbonClient 注解引入配置類。

配置類不應該在SpringBoot的包路徑下,通過@RibbonClient 注解加載:

@Configuration
public class MySelfRule
{
    @Bean
    public IRule myRule()
    {    
        return new RandomRule_ZY();  // 我自定義為每台機器5次,5次之后在輪詢到下一個
    }
}

springboot主程序:

@SpringBootApplication
@EnableEurekaClient
//在啟動該微服務的時候就能去加載我們的自定義Ribbon配置類,從而使配置生效
@RibbonClient(name="MICROSERVICECLOUD-DEPT",configuration=MySelfRule.class)
public class DeptConsumer80_App
{
    public static void main(String[] args)
    {
        SpringApplication.run(DeptConsumer80_App.class, args);
    }
}

自定義LoadBalance:

public class RandomRule_ZY extends AbstractLoadBalancerRule{
 
    // total = 0 // 當total==5以后,我們指針才能往下走,
    // index = 0 // 當前對外提供服務的服務器地址,
    // total需要重新置為零,但是已經達到過一個5次,我們的index = 1
    // 分析:我們5次,但是微服務只有8001 8002 8003 三台,OK?
    
    private int total = 0;         // 總共被調用的次數,目前要求每台被調用5次
    private int currentIndex = 0;        // 當前提供服務的機器號
 
    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) {
                return null;
            }
        
                        if(total < 5){
                        server = upList.get(currentIndex);
                        total++;
                        }else {
                        total = 0;
                        currentIndex++;
                        if(currentIndex >= upList.size()){
                          currentIndex = 0;
                        }
                        }                            
            if (server == null) {
                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;
    }
    @Override
    public Server choose(Object key){
        return choose(getLoadBalancer(), key);
    }
 
    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig){
    }
}

 


免責聲明!

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



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