<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          SpringCloud系列之客戶端負載均衡Netflix Ribbon

          共 10194字,需瀏覽 21分鐘

           ·

          2020-12-10 02:44

          走過路過不要錯過

          點擊藍字關(guān)注我們


          1. 什么是負載均衡?

          負載均衡是一種基礎(chǔ)的網(wǎng)絡(luò)服務(wù),它的核心原理是按照指定的負載均衡算法,將請求分配到后端服務(wù)集群上,從而為系統(tǒng)提供并行處理和高可用的能力。提到負載均衡,你可能想到nginx。對于負載均衡,一般分為服務(wù)端負載均衡和客戶端負載均衡

          • 服務(wù)端負載均衡:在消費者和服務(wù)提供方中間使用獨立的代理方式進行負載,有硬件的負載均衡器,比如 F5,也有軟件,比如 Nginx。

          • 客戶端負載均衡:所謂客戶端負載均衡,就是客戶端根據(jù)自己的請求情況做負載,本文介紹的Netflix Ribbon就是客戶端負載均衡的組件

          2. 什么是Netflix Ribbon?

          在上一章的學(xué)習(xí)中,我們知道了微服務(wù)的基本概念,知道怎么基于Ribbon+restTemplate的方式實現(xiàn)服務(wù)調(diào)用,接著上篇博客,我們再比較詳細學(xué)習(xí)客戶端負載均衡Netflix Ribbon,學(xué)習(xí)本博客之前請先學(xué)習(xí)上篇博客,然后再學(xué)習(xí)本篇博客

          Ribbon 是由 Netflix 發(fā)布的負載均衡器,它有助于控制 HTTP 和 TCP 的客戶端的行為。Ribbon 屬于客戶端負載均衡。

          3. Netflix Ribbon實驗環(huán)境準(zhǔn)備

          環(huán)境準(zhǔn)備:

          • JDK 1.8

          • SpringBoot2.2.3

          • SpringCloud(Hoxton.SR6)

          • Maven 3.2+

          • 開發(fā)工具

            • IntelliJ IDEA

            • smartGit

          創(chuàng)建一個SpringBoot Initialize項目,詳情可以參考我之前博客:SpringBoot系列之快速創(chuàng)建項目教程

          可以引入Eureka Discovery Client,也可以單獨添加Ribbon

          Spring Cloud Hoxton.SR6版本不需要引入spring-cloud-starter-netflix-ribbon,已經(jīng)默認集成

          也可以單獨添加Ribbon依賴:

          本博客的是基于spring-cloud-starter-netflix-eureka-client進行試驗,試驗前要運行eureka服務(wù)端,eureka服務(wù)提供者,代碼請參考上一章博客

          補充:IDEA中多實例運行方法

          step1:如圖,不要加上勾選

          step2:指定不同的server端口和實例id,如圖:

          啟動成功后,是可以看到多個實例的

          4. Netflix Ribbon API使用

          使用LoadBalancerClient

           @Autowired
          LoadBalancerClient loadBalancerClient;

          @Test
          void contextLoads() {
          ServiceInstance serviceInstance = loadBalancerClient.choose("EUREKA-SERVICE-PROVIDER");
          URI uri = URI.create(String.format("http://%s:%s", serviceInstance.getHost() , serviceInstance.getPort()));
          System.out.println(uri.toString());
          }

          構(gòu)建BaseLoadBalancer實例例子:

           @Test
          void testLoadBalancer(){
          // 服務(wù)列表
          List serverList = Arrays.asList(new Server("localhost", 8083), new Server("localhost", 8084));
          // 構(gòu)建負載實例
          BaseLoadBalancer loadBalancer = LoadBalancerBuilder.newBuilder().buildFixedServerListLoadBalancer(serverList);
          loadBalancer.setRule(new RandomRule());
          for (int i = 0; i < 5; i++) {
          String result = LoadBalancerCommand.builder().withLoadBalancer(loadBalancer).build()
          .submit(new ServerOperation() {
          public Observable call(Server server) {
          try {
          String address = "http://" + server.getHost() + ":" + server.getPort()+"/EUREKA-SERVICE-PROVIDER/api/users/mojombo";
          System.out.println("調(diào)用地址:" + address);
          return Observable.just("");
          } catch (Exception e) {
          return Observable.error(e);
          }
          }
          }).toBlocking().first();
          System.out.println("result:" + result);
          }
          }

          5. 負載均衡@LoadBalanced

          Ribbon負載均衡實現(xiàn),RestTemplate 要加上@LoadBalanced

          package com.example.springcloud.ribbon.configuration;

          import org.springframework.cloud.client.loadbalancer.LoadBalanced;
          import org.springframework.context.annotation.Bean;
          import org.springframework.context.annotation.Configuration;
          import org.springframework.web.client.RestTemplate;

          /**
          *

          * RestConfiguration
          *

          *
          *

          * @author mazq
          * 修改記錄
          * 修改后版本: 修改人:修改日期: 2020/07/31 09:43 修改內(nèi)容:
          *

          */

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

          yaml配置:

          server:
          port: 8082
          spring:
          application:
          name: eureka-service-consumer
          eureka:
          client:
          service-url:
          defaultZone: http://localhost:8761/eureka/
          fetch-registry: true
          register-with-eureka: false
          healthcheck:
          enabled: false
          instance:
          status-page-url-path: http://localhost:8761/actuator/info
          health-check-url-path: http://localhost:8761/actuator//health
          prefer-ip-address: true
          instance-id: eureka-service-consumer8082


          關(guān)鍵點,使用SpringCloud的@LoadBalanced,才能調(diào)http://EUREKA-SERVICE-PROVIDER/api/users/? 接口的數(shù)據(jù),瀏覽器是不能直接調(diào)的


          import com.example.springcloud.ribbon.bean.User;
          import lombok.extern.slf4j.Slf4j;
          import org.springframework.beans.factory.annotation.Autowired;
          import org.springframework.boot.SpringApplication;
          import org.springframework.boot.autoconfigure.SpringBootApplication;
          import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
          import org.springframework.web.bind.annotation.*;
          import org.springframework.web.client.RestTemplate;

          import java.net.URI;

          @SpringBootApplication
          @EnableEurekaClient
          @RestController
          @Slf4j
          public class SpringcloudRibbonApplication {


          @Autowired
          RestTemplate restTemplate;

          public static void main(String[] args) {
          SpringApplication.run(SpringcloudRibbonApplication.class, args);
          }

          @GetMapping("/findUser/{username}")
          public User index(@PathVariable("username")String username){
          return restTemplate.getForObject("http://EUREKA-SERVICE-PROVIDER/api/users/"+username,User.class);
          }

          }

          6. 定制Netflix Ribbon client

          具體怎么定制?可以參考官網(wǎng),@RibbonClient指定定制的配置類既可

          package com.example.springcloud.ribbon.configuration;

          import com.example.springcloud.ribbon.component.MyRule;
          import com.netflix.loadbalancer.IPing;
          import com.netflix.loadbalancer.IRule;
          import com.netflix.loadbalancer.PingUrl;
          import org.springframework.cloud.netflix.ribbon.ZonePreferenceServerListFilter;
          import org.springframework.context.annotation.Bean;
          import org.springframework.context.annotation.Configuration;

          /**
          *

          * Ribbon Clients configuration
          *

          *
          *

          * @author mazq
          * 修改記錄
          * 修改后版本: 修改人:修改日期: 2020/07/29 14:22 修改內(nèi)容:
          *

          */

          //@Configuration(proxyBeanMethods = false)
          //@IgnoreComponentScan
          public class RibbonClientConfiguration {

          // @Autowired
          // IClientConfig config;

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

          @Bean
          public ZonePreferenceServerListFilter serverListFilter() {
          ZonePreferenceServerListFilter filter = new ZonePreferenceServerListFilter();
          filter.setZone("myTestZone");
          return filter;
          }

          @Bean
          public IPing ribbonPing() {
          return new PingUrl();
          }

          }

          在Application類加上@RibbonClient,name是為服務(wù)名稱,跟bootstrap.yml配置的一樣既可

          @RibbonClient(name = "eureka-service-provider",configuration = RibbonClientConfiguration.class)

          特別注意:官網(wǎng)這里特意提醒,這里的意思是說@RibbonClient指定的配置類必須加@Configuration(不過在Hoxton.SR6版本經(jīng)過我的驗證,其實是可以不加的,加了反而可能報錯),@ComponentScan掃描要排除自定義的配置類,否則,它由所有@RibbonClients共享。如果你使用@ComponentScan(或@SpringBootApplication)

          其實就是想讓我們排除這個配置的全局掃描,所以我們可以進行編碼,寫個注解類@IgnoreComponentScan,作用于類,指定@Target(ElementType.TYPE)

          package com.example.springcloud.ribbon.configuration;

          import java.lang.annotation.*;

          @Target(ElementType.TYPE)
          @Retention(RetentionPolicy.RUNTIME)
          @Documented
          public @interface IgnoreComponentScan {

          }

          加上自定義的注解類

          任何在Application加上代碼,避免全局掃描:

          @ComponentScan(excludeFilters={@ComponentScan.Filter(type= FilterType.ANNOTATION,value= IgnoreComponentScan.class)})

          7. Netflix Ribbon常用組件

          ps:介紹Netflix Ribbon的負載策略之前,先介紹Netflix Ribbon常用組件及其作用:

          組件作用
          ILoadBalancer定義一系列的操作接口,比如選擇服務(wù)實例。
          IRule負載算法策略,內(nèi)置算法策略來為服務(wù)實例的選擇提供服務(wù)。
          ServerList負責(zé)服務(wù)實例信息的獲?。梢垣@取配置文件中的,也可以從注冊中心獲取。)
          ServerListFilter過濾掉某些不想要的服務(wù)實例信息。
          ServerListUpdater更新本地緩存的服務(wù)實例信息。
          IPing對已有的服務(wù)實例進行可用性檢查,保證選擇的服務(wù)都是可用的。

          8. 定制Netflix Ribbon策略

          因為服務(wù)提供者是多實例的,所以再寫個接口測試,調(diào)用了哪個實例,來看看Netflix Ribbon的負載策略

          @Autowired
          LoadBalancerClient loadBalancerClient;

          @GetMapping(value = {"/test"})
          public String test(){
          ServiceInstance serviceInstance = loadBalancerClient.choose("EUREKA-SERVICE-PROVIDER");
          URI uri = URI.create(String.format("http://%s:%s", serviceInstance.getHost() , serviceInstance.getPort()));
          System.out.println(uri.toString());
          return uri.toString();
          }

          部署成功,多次調(diào)用,可以看到每次調(diào)用的服務(wù)實例都不一樣?其實Netflix Ribbon默認是按照輪詢的方式調(diào)用的

          要定制Netflix Ribbon的負載均衡策略,需要實現(xiàn)AbstractLoadBalancerRule抽象類,下面給出類圖:

          Netflix Ribbon內(nèi)置了如下的負載均衡策略,引用https://juejin.im/post/6854573215587500045的歸納:

          ok,接著我們可以在配置類,修改規(guī)則

          @Bean
          public IRule roundRobinRule() {
          return new BestAvailableRule();
          }

          測試,基本都是調(diào)8083這個實例,因為這個實例性能比較好

          顯然,也可以自己寫個策略類,代碼參考com.netflix.loadbalancer.RandomRule,網(wǎng)上也有很多例子,思路是修改RandomRule原來的策略,之前隨機調(diào)服務(wù)實例一次,現(xiàn)在改成每調(diào)5次后,再調(diào)其它的服務(wù)實例

          package com.example.springcloud.ribbon.component;

          import com.netflix.client.config.IClientConfig;
          import com.netflix.loadbalancer.AbstractLoadBalancerRule;
          import com.netflix.loadbalancer.ILoadBalancer;
          import com.netflix.loadbalancer.Server;

          import java.util.ArrayList;
          import java.util.List;
          import java.util.concurrent.ThreadLocalRandom;


          public class MyRule extends AbstractLoadBalancerRule
          {
          // 總共被調(diào)用的次數(shù),目前要求每臺被調(diào)用5次
          private int total = 0;
          // 當(dāng)前提供服務(wù)的機器號
          private int index = 0;

          public Server choose(ILoadBalancer lb, Object key)
          {
          if (lb == null) {
          return null;
          }
          Server server = null;

          while (server == null) {
          if (Thread.interrupted()) {
          return null;
          }
          // 獲取可用的服務(wù)列表
          List upList = lb.getReachableServers();
          // 獲取所有服務(wù)列表
          List allList = lb.getAllServers();

          int serverCount = allList.size();
          if (serverCount == 0) {
          // 沒有獲取到服務(wù)
          return null;
          }

          //int index = chooseRandomInt(serverCount);
          //server = upList.get(index);
          if(total < 5)
          {
          server = upList.get(index);
          total++;
          }else {
          total = 0;
          index++;
          if(index >= upList.size())
          {
          index = 0;
          }
          }

          if (server == null) {
          // 釋放線程
          Thread.yield();
          continue;
          }

          if (server.isAlive()) {
          return (server);
          }

          server = null;
          Thread.yield();
          }

          return server;
          }

          protected int chooseRandomInt(int serverCount) {
          return ThreadLocalRandom.current().nextInt(serverCount);
          }

          @Override
          public Server choose(Object key) {
          return choose(getLoadBalancer(), key);
          }

          @Override
          public void initWithNiwsConfig(IClientConfig clientConfig) {
          }
          }

          修改IRule ,返回MyRule

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




          往期精彩推薦



          騰訊、阿里、滴滴后臺面試題匯總總結(jié) — (含答案)

          面試:史上最全多線程面試題 !

          最新阿里內(nèi)推Java后端面試題

          JVM難學(xué)?那是因為你沒認真看完這篇文章


          END


          關(guān)注作者微信公眾號 —《JAVA爛豬皮》


          了解更多java后端架構(gòu)知識以及最新面試寶典


          你點的每個好看,我都認真當(dāng)成了


          看完本文記得給作者點贊+在看哦~~~大家的支持,是作者源源不斷出文的動力


          作者:smileNicky

          出處:https://www.cnblogs.com/mzq123/p/13411111.html

          瀏覽 51
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  性生活片无码在线 | 成人 免费视频A片视频88p | 亚洲三级电影在线 | 美女免费操B视频 | 99在线视频在线 |