SpringCloud之ribbon+feign實現負載均衡


ribbon是什么?

Spring Cloud Ribbon是基於Netflix Ribbon實現的一套客戶端負載均衡的工具。

簡單的說,Ribbon是Netflix發布的開源項目,主要功能是提供客戶端的軟件負載均衡算法,將Netflix的中間層服務連接在一起。Ribbon客戶端組件提供一系列完善的配置項如連接超時,重試等。簡單的說,就是在配置文件中列出Load Balancer(簡稱LB)后面所有的機器,Ribbon會自動的幫助你基於某種規則(如簡單輪詢,隨機連接等)去連接這些機器。我們也很容易使用Ribbon實現自定義的負載均衡算法。

服務端的負載均衡和客戶端的負載均衡區別?

 

服務端的負載均衡是一個url先經過一個代理服務器(這里是nginx),然后通過這個代理服務器通過算法(輪詢,隨機,權重等等..)反向代理你的服務,來完成負載均衡。

客戶端的負載均衡則是一個請求在客戶端的時候已經聲明了要調用哪個服務,然后通過具體的負載均衡算法來完成負載均衡。

如何使用?

引入依賴,但是eureka已經把ribbon集成到他的依賴里面去了,所以這里不需要再引用ribbon的依賴,如圖:

 

其實要使用ribbon,只需要一個注解:@LoadBalanced

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

在RestTemplate上面加入@LoadBalanced注解,這樣子就已經有了負載均衡, 怎么來證明?、

們這里現在啟動了eureka集群(3個eureka) 和Power集群(2個power) 和一個服務調用者(User)

但是我們的User僅僅只需要調用服務,不需要注冊服務信息,所以需要改一下配置文件:

server:
  port: 5000
eureka:
  client:
   registerWithEureka: false
   serviceUrl:
     defaultZone:http://localhost:3000/eureka/,http://eureka3001.com:3001/eureka,http://eureka3002.com:3002/eureka

 啟動服務即可。

 

微服務名:SERVER-POWER 下面有2個微服務(power-1,power2),現在我們來通過微服務名調用這個服務

這是user項目的調用代碼 :

 

private static final String URL="http://SERVER-POWER";
@Autowired
private RestTemplate restTemplate;
@RequestMapping("/power.do")
public Object power(){
  return  restTemplate.getForObject(URL+"/power.do",Object.class);
}

 這里已經完成了ribbon的負載均衡,他默認的負載均衡是輪詢策略。

核心組件:IRule

IRule是什么? 它是Ribbon對於負載均衡策略實現的接口, 怎么理解這句話? 說白了就是你實現這個接口,就能自定義負載均衡策略, 自定義我們待會兒來講, 我們先來看看他有哪些默認的實現

 

 

自定義負載均衡策略:

 

 我們剛剛講過,只要實現了IRule就可以完成自定義負載均衡,至於具體怎么來,我們先看看他默認的實現:

/*
*
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.netflix.loadbalancer;
import java.util.List;
import java.util.Random;
import com.netflix.client.config.IClientConfig;
/**
* A loadbalacing strategy that randomly distributes traffic amongst existing
* servers.
*
* @author stonse
*
*/
public class RandomRule extends AbstractLoadBalancerRule {
 
  Random rand;
  public RandomRule() {
    rand = new Random();
 }
  /**
  * Randomly choose from all living servers
  */
  @edu.umd.cs.findbugs.annotations.SuppressWarnings(value =
"RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE")
  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 = rand.nextInt(serverCount);
      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;
 }
 @Override
 public Server choose(Object key) {
   return choose(getLoadBalancer(), key);
 }
 @Override
 public void initWithNiwsConfig(IClientConfig clientConfig) {
   // TODO Auto-generated method stub
  
 }
}

  我們來看看這個類AbstractLoadBalancerRule

public abstract class AbstractLoadBalancerRule implements IRule, IClientConfigAware {
  private ILoadBalancer lb;
   
  @Override
  public void setLoadBalancer(ILoadBalancer lb){
    this.lb = lb;
 }
 
  @Override
  public ILoadBalancer getLoadBalancer(){
    return lb;
 }   
}

這里我們能發現,還是我們上面所說過的 實現了IRule就能夠自定義負載均衡即使是他默認的策略也實現了IRule

我們可以直接把代碼copy過來改動一點:

package com.luban.rule;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import java.util.List;
import java.util.Random;

public class TestRule extends AbstractLoadBalancerRule {
  //原來是純隨機策略 我們現在改為。 如果一個下標已經被隨機到了2次了,第三次還是同樣的下標的話,那就再隨機一次
  Random rand;
  private int lastIndex = -1;
  private int nowIndex = -1;
  private int skipIndex = -1;
  public LubanRule() {
    rand = new Random();
 }
  /**
  * Randomly choose from all living servers
  */
  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 = rand.nextInt(serverCount);
      System.out.println("當前下標為:"+index);
      if (skipIndex>=0&&index == skipIndex) {
        System.out.println("跳過");
        index = rand.nextInt(serverCount);
        System.out.println("跳過后的下標:"+index);
     }
      skipIndex=-1;
      nowIndex = index;
      if (nowIndex == lastIndex) {
        System.out.println("下一次需要跳過的下標"+nowIndex);
        skipIndex = nowIndex;
     }
      lastIndex = nowIndex;
      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;
 }
  @Override
  public Server choose(Object key) {
    return choose(getLoadBalancer(), key);
 }
  @Override
  public void initWithNiwsConfig(IClientConfig clientConfig) {
    // TODO Auto-generated method stub
 }
}

  這里我們就把自己寫的Rule給new出來交給spring 就好了

@Bean
public IRule iRule(){
  return new TestRule ();
}

  自定義的負載均衡就這樣完成了。

Feign負載均衡

Feign是什么 :

Feign是一個聲明式WebService客戶端。使用Feign能讓編寫Web Service客戶端更加簡單, 它的使用方法是定義一個接口,然后在上面添加注解,同時也支持JAX-RS標准的注解。Feign也支持可拔插式的編碼器和解碼器。SpringCloud對Feign進行了封裝,使其支持了Spring MVC標准注解和HttpMessageConverters。Feign可以與Eureka和Ribbon組合使用以支持負載均衡。

Feign 能干什么:

Feign旨在使編寫Java Http客戶端變得更容易。 前面在使用Ribbon+RestTemplate時,利用RestTemplate對http請求的封裝處理,形成了一套模版化的調用方法。但是在實際開發中,由於對服務依賴的調用可能不止一處,往往一個接口會被多處調用,所以通常都會針對每個微服務自行封裝一些客戶端類來包裝這些依賴服務的調用。所以,Feign在此基礎上做了進一步封裝,由他來幫助我們定義和實現依賴服務接口的定義。在Feign的實現下,我們只需創建一個接口並使用注解的方式來配置它(以前是Dao接口上面標注Mapper注解,現在是一個微服務接口上面標注一個Feign注解即可),即可完成對服務提供方的接口綁定,簡化了使用Spring cloud Ribbon時,自動封裝服務調用客戶端的開發量。

如何使用?

在客戶端(User)引入依賴:

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

在啟動類上面加上注解: @EnableFeignClients
然后編寫一個service類加上@FeignClient(“xxx”)注解 參數就是你的微服務名字

@FeignClient("SERVER-POWER")
public interface PowerServiceClient {
  @RequestMapping("/power.do")
  public Object power();
}

下面是調用代碼:

@RestController
public class UserController {
  private static final String URL="http://SERVER-POWER";
  @Autowired
  private RestTemplate restTemplate;
  @Autowired
  PowerServiceClient powerServiceClient;
  @RequestMapping("/power.do")   public Object power(){     return  restTemplate.getForObject(URL+"/power.do",Object.class);  }
  @RequestMapping("/feignPower.do")   public Object feignPower(){     return powerServiceClient.power();  } }

Feign集成了Ribbon利用Ribbon維護了服務列表信息,並且融合了Ribbon的負載均衡配置,也就是說之前自定義的負載均衡也有效,這里需要多測試幾遍多理解一下。

而與Ribbon不同的是,通過feign只需要定義服務綁定接口且以聲明式的方法,優雅而簡單的實現了服務調用。

Ribbon和Feign的區別

Ribbon:

是一個基於 HTTP 和 TCP 客戶端 的負載均衡的工具。
它可以 在客戶端 配置 RibbonServerList(服務端列表),使用 HttpClient 或 RestTemplate 模擬http請求,步驟相當繁瑣。

Feign:

Feign 是在 Ribbon的基礎上進行了一次改進,是一個使用起來更加方便的 HTTP 客戶端。

采用接口的方式, 只需要創建一個接口,然后在上面添加注解即可 ,將需要調用的其他服務的方法定義成抽象方法即可, 不需要自己構建http請求。

然后就像是調用自身工程的方法調用,而感覺不到是調用遠程方法,使得編寫 客戶端變得非常容易。

代碼方面的用法區別:

Ribbon:用 REST_URL_PREFIX 指定請求地址 , 使用 restTemplate 模擬 http 請求。也就是說需要自己來構建發起HTTP請求

Feign:

a) 新建一個接口,添加@FeignClient 注解,指定微服務名稱 MICROSERVICECLOUD-DEPT
b) 指定請求地址 @RequestMapping


免責聲明!

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



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