<kbd id="5sdj3"></kbd>
<th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>

    微服務SpringCloud中的負載均衡,你都會么?

    共 11979字,需瀏覽 24分鐘

     ·

    2021-06-24 01:47

    1.什么是負載均衡

    首先我們來看看維基百科對負載均衡的說明:

    負載平衡(Load balancing)是一種計算機技術,用來在多個計算機(計算機集群)、網(wǎng)絡連接、CPU、磁盤驅(qū)動器或其他資源中分配負載,以達到最優(yōu)化資源使用、最大化吞吐率、最小化響應時間、同時避免過載的目的。使用帶有負載平衡的多個服務器組件,取代單一的組件,可以通過冗余提高可靠性。負載平衡服務通常是由專用軟件和硬件來完成。主要作用是將大量作業(yè)合理地分攤到多個操作單元上進行執(zhí)行,用于解決互聯(lián)網(wǎng)架構中的高并發(fā)和高可用的問題。

    舉個例子來解釋下負載均衡

    下圖中,一群人在銀行排隊辦理業(yè)務,假設只有一個服務窗口,那么一個服務窗口來處理所有人員業(yè)務辦理,人少的時候,肯定是能夠辦理完的,如果人特別多的時候恩?一個服務窗口肯定是沒有辦法處理完這么多業(yè)務辦理的。于是就出現(xiàn)如下圖這樣,一大堆人排隊。

    這時候銀行就會多開幾個窗口來辦理業(yè)務,將原本堆積在一個窗口上處理的業(yè)務,分配到幾個窗口上,這樣業(yè)務辦理就會加快了,但是僅僅是多開幾個窗口是不夠的。

    為什么?因為沒辦法保證讓某個人在某個窗口辦理業(yè)務。大家可能還是會亂套(舉個極端例子:假設A窗口的小姐姐特別的漂亮,會不會她那里會有很多的男客戶排隊辦理業(yè)務恩?),因此銀行除了多開幾個窗口以為,還會設置一個排號,你拿到排號在哪個窗口,你就在哪個窗口進行辦理業(yè)務。

    在我們負載均衡中 ,客戶端(PC/移動/第三方)發(fā)起請求,就好比辦理業(yè)務的人員。負載均衡器(load balancer)就好比取號機,當他接收到一個請求的時候,負載均衡器就會給這個請求,分配一個服務來處理請求。就像取號機一樣,負載均衡器按照一定規(guī)則分配一個窗口來辦理業(yè)務。

    因此,在Spring cloud中,如果我們只提供一個服務,那么很可能會出現(xiàn)我們上面所說的一個窗口處理業(yè)務,處理不完的情況,導致大量人員排隊等候。

    2.注冊多個服務

    在微服務Spring Cloud快速入門中,如果我們的商品服務一個不夠用的話,根據(jù)負載均衡的理論,那我們就可以多注冊幾個服務。防止一個服務無法承載高并發(fā)時的情況。

    啟動注冊中心、商品服務,訂單服務,登錄注冊中心,查看我們當前的服務

    此時,我們的商品服務和訂單服務都只是一個服務,那么如何注冊多個商品服務恩?

    2.1向注冊中心注冊多個服務

    以同樣的項目代碼,設定端口為8091,其余配置均和之前的商品服務保持一致,然后啟動商品服務。

    server:
      port: 8091 #服務端口
    spring: 
      application:  
        name: hutao-microservice-item #指定服務名

    eureka:
      client:
        registerWithEureka: true #是否將自己注冊到Eureka服務中,默認為true
        fetchRegistry: true #是否從Eureka中獲取注冊信息,默認為true
        serviceUrl: #Eureka客戶端與Eureka服務端進行交互的地址
          defaultZone: http://127.0.0.1:9090/eureka/
      instance: 
        prefer-ip-address: true #將自己的ip地址注冊到Eureka服務中

    點擊如下所示,可以看到我們啟動了1個注冊中心,1個訂單服務,2個商品服務

    這時我們可以訪問一下注冊中心,我們可以發(fā)現(xiàn),注冊中心里面,商品服務有兩個,一個是8081端口,一個是8091端口,并且他們的服務ID都是:HUTAO-MICROSERVICE-ITEM,這個服務ID和我們在配置文件中,配置的服務ID(spring.application.name)是保持一致的。

    也就是說,我們通過服務ID:hutao-microservice-item在注冊中心里面,能夠找到兩個服務

    2.2通過服務ID找到服務

    在微服務Spring Cloud快速入門中我們定義了如下Feign接口,在這個接口上面,我們添加了一個注解@FeignClient,指定了服務ID:hutao-microservice-item。這個Feign接口會給我發(fā)起Http調(diào)用。當然在這里我們可能無法看到負載均衡的效果,因此我們需要稍微深入下底層代碼。

    @FeignClient(value = "hutao-microservice-item")
    @RequestMapping("/itemservice")
    public interface FeignOrderService {
     
     /**
      * @Description:使用聲明式HTTP客戶端發(fā)起請求:根據(jù)ID查詢訂單 
      * @author hutao
      * @mail [email protected]
      * @date 2020年8月30日
      */

     @GetMapping(value = "item/{itemId}")
     Items queryItem(@PathVariable("itemId")String itemId);
    }

    大家應該還記得,我們的商品服務和訂單服務,注冊到注冊中心的時候,我們在啟動類上面添加了一個注解:@EnableDiscoveryClient。

    現(xiàn)在我們來看下DiscoveryClient這個接口

    2.2.1.DiscoveryClient 解讀

    DiscoveryClient接口源碼如下,他是用來發(fā)現(xiàn)服務的,比如發(fā)現(xiàn)Netflix服務,其中有個方法List getInstances(String serviceId)是根據(jù)服務ID獲取服務實例集合。

    /**
     * Represents read operations commonly available to discovery services such as Netflix
     * Eureka or consul.io.
     *
     * @author Spencer Gibb
     * @author Olga Maciaszek-Sharma
     */

    public interface DiscoveryClient extends Ordered {

     /**
      * Default order of the discovery client.
      */

     int DEFAULT_ORDER = 0;

     /**
      * A human-readable description of the implementation, used in HealthIndicator.
      * @return The description.
      */

     String description();

     /**
      * Gets all ServiceInstances associated with a particular serviceId.
      * @param serviceId The serviceId to query.
      * @return A List of ServiceInstance.
      */

     List<ServiceInstance> getInstances(String serviceId);

     /**
      * @return All known service IDs.
      */

     List<String> getServices();

     /**
      * Default implementation for getting order of discovery clients.
      * @return order
      */

     @Override
     default int getOrder() {
      return DEFAULT_ORDER;
     }
    }

    我們可以看到DiscoveryClient 有4個實現(xiàn),當然這里我們用的肯定是Eureka。

    2.2.2.使用DiscoveryClient獲取服務實例

    現(xiàn)在我們來使用下DiscoveryClient 這個接口

    首先把這個接口依賴注入到我們的Controller中,我們看下能獲取到什么

    @Autowired
    private DiscoveryClient discoveryClient;

    @GetMapping(value = "order/{orderId}")
    public Order queryOrderById(@PathVariable("orderId") String orderId) {
     String serviceId = "hutao-microservice-item";
     List<ServiceInstance> instances = discoveryClient.getInstances(serviceId);
     System.out.println(instances);
     return null;
    }

    通過調(diào)試代碼,我們發(fā)現(xiàn)如我們之前所說,我們可以通過服務ID:hutao-microservice-item,找到兩個服務實例。

    查看每個實例,我們發(fā)現(xiàn)我們能夠看到每個服務的地址,端口等信息

    如果不使用Feign這種聲明式的調(diào)用Http請求,那我們來如何調(diào)用?

    2.2.3.從服務實例中獲取服務信息,發(fā)起Http請求

    那么這個時候,我們怎么去發(fā)起請求恩?很簡單,就是從服務實例中,獲取到服務信息后,將接口的請求地址拼接出來。然后使用restTemplate發(fā)起Http請求

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping(value = "order/{orderId}")
    public Order queryOrderById(@PathVariable("orderId") String orderId) {
     String serviceId = "hutao-microservice-item";
     List<ServiceInstance> instances = discoveryClient.getInstances(serviceId);
     ServiceInstance serviceInstance = instances.get(0);
     String url = serviceInstance.getHost() + ":" + serviceInstance.getPort();
     Items items = restTemplate.getForObject("http://" + url + "/itemservice/item/1" , Items.class);
     System.out.println(items);
     return null;
    }

    可以看到我們的請求是能正常訪問的。當然也有問題存在,那就是我們拿到的是多個服務,程序怎么知道我要調(diào)用的是哪一個服務呢?

    2.2.4.多個服務實例,如何負載均衡

    上述案例中,我們獲取到的實例是兩個,那么每次調(diào)用的時候,我們怎么來確定,我要調(diào)用的是哪一個服務?,因為這時候我們拿到的兩個服務,也就是兩個不同的IP地址,這時候就需要一個負載均衡器來幫我們選擇一個IP進行訪問。

    在Spring Cloud中,netfix提供一個負載均衡器Ribbon,該負載均衡器是聲明式的,其用法如下所示,在我們注入到Spring容器中的RestTemplate添加注解@LoadBalanced,這時候我們的RestTemplate就具備了負載均衡的功能。

    注意:RestTemplate底層默認使用的jdk的標準實現(xiàn),如果我們想讓RestTemplate的底層使用okhttp,可以替換實現(xiàn)的。如下源碼所示,RestTemplate提供了三個構造方法。

    我們可以使用上述的第二個構造方法,使用Okhttp。

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

    在上面的一個案例中,我們通過從服務實例中,獲取到服務,然后在從服務中心獲取具體的IP地址信息,發(fā)起請求。

    String url = serviceInstance.getHost() + ":" + serviceInstance.getPort();
    Items items = restTemplate.getForObject("http://" + url + "/itemservice/item/1" ,

    但是,現(xiàn)在我們不需要這么做了,因為我們對restTemplate聲明了是需要負載均衡的,因此我們發(fā)起請求的時候,就不需要指定IP地址了,我們可以用服務ID來代替IP地址,然后由restTemplate來幫我選擇需要調(diào)用的IP。因此上述代碼會被簡化為如下所示,被注釋掉的代碼就是被優(yōu)化的代碼。

    @GetMapping(value = "order/{orderId}")
     public Order queryOrderById(@PathVariable("orderId") String orderId) {
      String serviceId = "hutao-microservice-item";
      //List<ServiceInstance> instances = discoveryClient.getInstances(serviceId);
      //ServiceInstance serviceInstance = instances.get(0);
      //String url = serviceInstance.getHost() + ":" + serviceInstance.getPort();
      //Items items = restTemplate.getForObject("http://" + url + "/itemservice/item/1" , Items.class);
      Items items = restTemplate.getForObject("http://" + serviceId + "/itemservice/item/1" , Items.class);
      System.out.println(items);
      return null;
     }

    重啟服務后,商品服務仍然可用,那么這時候怎么來驗證我們的負載均衡成功了?

    2.2.5.簡單驗證負載均衡

    其實最簡單的驗證方式就是,在商品服務中,添加日志,看看哪個服務記錄了日志或者debug調(diào)試,看走哪一個服務的代碼。即可驗證我們的負載均衡是否成功。

    當然這里我們就不做上述方式的演示,來做一點高難度的代碼分析。

    3.分析@LoadBalanced實現(xiàn)負載均衡源碼解析

    3.1.RestTemplate源碼解析

    1、首先看org.springframework.web.client.RestTemplate類。

    當我們執(zhí)行如下代碼時

    restTemplate.getForObject("http://" + serviceId + "/itemservice/item/1" , Items.class);

    最終會執(zhí)行到如下方法

    doExecute(URI, HttpMethod, RequestCallback, ResponseExtractor<T>)

    如上代碼根據(jù)我們的提供的信息,創(chuàng)建了ClientHttpRequest請求, 我們發(fā)現(xiàn)doExecute有如下幾個實現(xiàn),這里由于我們使用的是默認的RestTemplate,因此我們查看org.springframework.http.client.AbstractClientHttpRequest這個類的org.springframework.http.client.AbstractClientHttpRequest.execute()方法。

    3.2.LoadBalancerInterceptor源碼解析

    逐步深入代碼,我們找到了如下的攔截器。LoadBalancerInterceptororg.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor

    3.3.RibbonLoadBalancerClient源碼解析

    繼續(xù)深入代碼,我們找到了RibbonLoadBalancerClient這個類org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient通過查看代碼,我們發(fā)現(xiàn)了getLoadBalancer(String)這個方法通過serviceId找到了兩個服務實例,

    接著執(zhí)行完畢getServer這個方法后,我們發(fā)現(xiàn),只有一個服務實例了為8090。第二次調(diào)試代碼后發(fā)現(xiàn)獲取的是8091.多次調(diào)試后發(fā)現(xiàn)是8090和8091是輪詢出現(xiàn)的,

    也就是說getServer這個方法給我們實現(xiàn)了負載均衡??丛创a知道,如果為設置負載均衡參數(shù),就使用default。否則就根據(jù)配置的參數(shù)選擇服務。

    protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
     if (loadBalancer == null) {
      return null;
     }
     // Use 'default' on a null hint, or just pass it on?
     return loadBalancer.chooseServer(hint != null ? hint : "default");
    }

    具體的實現(xiàn)的負載均衡算法,本文不做討論。

    4.負載均衡代碼驗證演示

    通過上述源碼分析,我們發(fā)現(xiàn)如果我們開啟了負載均衡,但是沒有配置負載均衡參數(shù),則會采用默認的配置,也就是輪詢算法來實現(xiàn)負載均衡。

    通過上述的debug閱讀,我們參照getServer這個方法來寫代碼測試下負載均衡

    @Autowired
    private LoadBalancerClient loadBalancerClient;

    @GetMapping(value = "order/{orderId}")
    public Order queryOrderById(@PathVariable("orderId") String orderId) {
     String serviceId = "hutao-microservice-item";
     for(int i = 0 ; i < 10 ; i++) {
      ServiceInstance choose = loadBalancerClient.choose(serviceId);
      System.out.println(choose);
     }
     //List<ServiceInstance> instances = discoveryClient.getInstances(serviceId);
     //ServiceInstance serviceInstance = instances.get(0);
     //String url = serviceInstance.getHost() + ":" + serviceInstance.getPort();
     //Items items = restTemplate.getForObject("http://" + serviceId + "/itemservice/item/1" , Items.class);
     //System.out.println(items);
     return null;
    }

    5.負載均衡配置

    在我們之前調(diào)試代碼的時候我們發(fā)現(xiàn)源碼中定義了IRule.choose(Object objet)這個接口,并且如下幾個實現(xiàn)。

    各個實現(xiàn)如下所示。

    如果我們不想要默認的輪詢機制,我們可以采用如上實現(xiàn)中的任何一種

    比如,我們設置為隨機策略。

    • hutao-microservice-item是我們需要對某個服務設定
    • NFLoadBalancerRuleClassName是我們的策略(也就是實現(xiàn)類的路徑)
    hutao-microservice-item: 
      ribbon:
        NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule  

    重啟服務后,再看我們的負載均衡,是隨機的,而不是輪詢的。

    6.總結

    1. 注冊多個服務:將項目部署在不同的服務器,或者同一個服務器不同端口號,然后注冊到注冊中心的服務ID(serviceId)保持一致即可;
    2. 負載均衡實現(xiàn):通過對RestTemplate添加注解@LoadBalanced,使用代理模式,攔截RestTemplate請求時,訪問指定的負載服務,從而實現(xiàn)負載均衡。
    3. DiscoveryClient接口他是用來發(fā)現(xiàn)服務的,比如發(fā)現(xiàn)Netflix服務,其中有個方法List getInstances(String serviceId)是根據(jù)服務ID獲取服務實例集合。
    4. LoadBalancerInterceptor進行請求攔截處理
    5. RibbonLoadBalancerClient首先通過getLoadBalancer根據(jù)服務ID獲取到負載,然后getServer會從多個負載中獲取到適合的服務。其實現(xiàn)chooseServer如果沒有設置負載均衡規(guī)則,則使用默認的輪詢算法。
    6. IRule接口定義了choose方法。

    (感謝閱讀,希望對你所有幫助)
    來源:blog.csdn.net/m0_37892044/article/details/108329893

    1. 300 秒快速了解 Java 9 - 16 新特性,助你脫離內(nèi)卷

    2. 老大讓我優(yōu)化數(shù)據(jù)庫,我上來就分庫分表,他過來就是一jio。。。

    3. Kafka 存儲選型的奧秘

    4. 推薦一個完善的停車管理系統(tǒng),物聯(lián)網(wǎng)項目springboot,有源碼

    最近面試BAT,整理一份面試資料Java面試BATJ通關手冊,覆蓋了Java核心技術、JVM、Java并發(fā)、SSM、微服務、數(shù)據(jù)庫、數(shù)據(jù)結構等等。

    獲取方式:點“在看”,關注公眾號并回復 Java 領取,更多內(nèi)容陸續(xù)奉上。

    文章有幫助的話,在看,轉(zhuǎn)發(fā)吧。

    謝謝支持喲 (*^__^*)

    瀏覽 146
    點贊
    評論
    收藏
    分享

    手機掃一掃分享

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

    手機掃一掃分享

    分享
    舉報

    <kbd id="5sdj3"></kbd>
    <th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>
    日本三级无码 | aa视频| www,操逼 | 蜜桃人妻无码AV天堂二区 | 人人很很操人人 |