SpringCloud微服務實戰——搭建企業級開發框架(十二):OpenFeign+Ribbon實現負載均衡


  Ribbon是Netflix下的負載均衡項目,它主要實現中間層應用程序的負載均衡。為Ribbon配置服務提供者地址列表后,Ribbon就會基於某種負載均衡算法,自動幫助服務調用者去請求。Ribbon默認提供的負載均衡算法有多種,例如輪詢、隨即、加權輪訓等,也可以為Ribbon實現自定義的負載均衡算法。

Ribbon有以下特性:

  • 負載均衡器,可支持插拔式的負載均衡規則
  • 對多種協議提供支持,如HTTP、TCP、UDP
  • 集成了負載均衡功能的客戶端

Feign利用Ribbon實現負載均衡的過程:

  • 通過在啟動類加@EnableFeignCleints注解開啟FeignCleint
  • 根據Feign的規則實現接口,並加在接口定義處添加@FeignCleint注解
  • 服務啟動后,掃描帶有@ FeignCleint的注解的類,並將這些信息注入到ioc容器中
  • 當接口的方法被調用,通過jdk的代理,來生成具體的RequesTemplate
  • RequesTemplate再生成Request
  • Request交給Client去處理,其中Client可以是HttpUrlConnection、HttpClient也可以是Okhttp
    最后Client被封裝到LoadBalanceClient類,這個類結合類Ribbon做到了負載均衡。

OpenFeign 中使用 Ribbon 進行負載均衡,所以 OpenFeign 直接內置了 Ribbon。在導入OpenFeign 依賴后,無需再專門導入 Ribbon 依賴。接下來,我們把gitegg-service-base作為服務的調用方,啟動兩個不同端口的gitegg-service-system作為服務的被調用方,測試Ribbon的負載均衡。

1、首先在gitegg-service-system工程中,新建被調用的controller方法,返回系統配置的端口號以區分是哪個服務被調用了。

package com.gitegg.service.system.controller;

import com.gitegg.platform.boot.common.base.Result;
import com.gitegg.service.system.dto.SystemDTO;
import com.gitegg.service.system.service.ISystemService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;

@RestController
@RequestMapping(value = "system")
@RequiredArgsConstructor(onConstructor_ = @Autowired)
@Api(tags = "gitegg-system")
@RefreshScope
public class SystemController {

    private final ISystemService systemService;

    @Value("${spring.datasource.maxActive}")
    private String nacosMaxActiveType;

    @Value("${server.port}")
    private Integer serverPort;

    @GetMapping(value = "list")
    @ApiOperation(value = "system list接口")
    public Object list() {
        return systemService.list();
    }


    @GetMapping(value = "page")
    @ApiOperation(value = "system page接口")
    public Object page() {
        return systemService.page();
    }

    @GetMapping(value = "exception")
    @ApiOperation(value = "自定義異常及返回測試接口")
    public Result<string> exception() {
        return Result.data(systemService.exception());
    }

    @PostMapping(value = "valid")
    @ApiOperation(value = "參數校驗測試接口")
    public Result<systemdto> valid(@Valid @RequestBody SystemDTO systemDTO) {
        return Result.data(systemDTO);
    }

    @PostMapping(value = "nacos")
    @ApiOperation(value = "Nacos讀取配置文件測試接口")
    public Result<string> nacos() {
        return Result.data(nacosMaxActiveType);
    }

    @GetMapping(value = "api/by/id")
    @ApiOperation(value = "Fegin Get調用測試接口")
    public Result<object> feginById(@RequestParam("id") String id) {
        return Result.data(systemService.list());
    }

    @PostMapping(value = "api/by/dto")
    @ApiOperation(value = "Fegin Post調用測試接口")
    public Result<object> feginByDto(@Valid @RequestBody SystemDTO systemDTO) {
        return Result.data(systemDTO);
    }

    @GetMapping("/api/ribbon")
    @ApiOperation(value = "Ribbon調用測試接口")
    public Result<string> testRibbon() {
        return Result.data("現在訪問的服務端口是:" + serverPort);
    }
}

2、在gitegg-service-system-api工程中,編寫使用OpenFeign調用testRibbon的公共方法

package com.gitegg.service.system.api.feign;

import com.gitegg.platform.boot.common.base.Result;
import com.gitegg.service.system.api.dto.ApiSystemDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient(name = "gitegg-service-system")
public interface ISystemFeign {

    /**
     * OpenFeign測試Get
     *
     * @param id
     * @return
     */
    @GetMapping("/system/api/by/id")
    Result<object> querySystemById(@RequestParam("id") Long id);

    /**
     * OpenFeign測試Post
     *
     * @param apiSystemDTO
     * @return ApiSystemDTO
     */
    @PostMapping("/system/api/by/dto")
    Result<apisystemdto> querySystemByDto(@RequestBody ApiSystemDTO apiSystemDTO);

    /**
     * OpenFeign測試Ribbon負載均衡功能
     * @return
     */
    @GetMapping("/system/api/ribbon")
    Result<string> testRibbon();

}

3、在gitegg-service-base中添加測試Ribbon負載均衡的Feign調用方法

package com.gitegg.service.base.controller;

import com.gitegg.platform.boot.common.base.Result;
import com.gitegg.service.system.api.dto.ApiSystemDTO;
import com.gitegg.service.system.api.feign.ISystemFeign;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;

@RestController
@RequestMapping(value = "base")
@RequiredArgsConstructor(onConstructor_ = @Autowired)
@Api(tags = "gitegg-base")
@RefreshScope
public class BaseController {

    private final ISystemFeign systemFeign;

    @GetMapping(value = "api/by/id")
    @ApiOperation(value = "Fegin Get調用測試接口")
    public Result<object> feginById(@RequestParam("id") Long id) {
        return Result.data(systemFeign.querySystemById(id));
    }

    @PostMapping(value = "api/by/dto")
    @ApiOperation(value = "Fegin Post調用測試接口")
    public Result<object> feginByDto(@Valid @RequestBody ApiSystemDTO systemDTO) {
        return Result.data(systemFeign.querySystemByDto(systemDTO));
    }

    @PostMapping(value = "api/ribbon")
    @ApiOperation(value = "Ribbon調用測試接口")
    public Result<object> testRibbon() {
        return Result.data(systemFeign.testRibbon());
    }
}

4、先啟動gitegg-service-base服務,再啟動gitegg-service-system服務,服務啟動成功之后,將gitegg-service-system下bootstrap.yml里面server.port改為8011,然后再點擊啟動,這樣就啟動了兩個gitegg-service-system服務(如果運行兩個服務時提示:gitegg-service-system is not allowed to run in parallel. Would you like to stop the running one? 這時,在IDEA中點擊Run-Edit configurations-勾選Allow parallel run即可),服務全部啟動完畢之后,可以在Console窗口里面看到三個服務的Console

image.png

三個服務:

image.png

5、打開瀏覽器訪問:http://127.0.0.1:8001/doc.html,點擊Ribbon調用測試接口
菜單,進行測試,點擊請求,我們可以看到每次返回的端口都是變化的,一會兒是8001一會兒是8011,因為Ribbon負載均衡默認是使用的輪詢策略

image.png
image.png

6、如果我們需要修改負載均衡策略或者自定義負載均衡策略,根據我們的架構設計,我們在GitEgg-Platform的子工程gitegg-platform-cloud中設置公共的負載均衡策略,然后每個微服務需要不同的策略的話,可以在自己的工程中添加配置文件。接下來,在gitegg-platform-cloud中新建Ribbon配置類

package com.gitegg.platform.cloud.ribbon.config;

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @Description Ribbon公共負載均衡策略配置
 */
@Configuration
public class RibbonConfig {

    /**
     * 負載均衡策略配置
     * @return
     */
    @Bean
    public IRule rule(){
        //隨機策略  從所有可用的提供者中隨機選擇一個
        return new RandomRule();
    }

}

7、修改完成之后,GitEgg_Platform工程重新執行install,GitEgg_Cloud刷新導入的包,參照步驟5再執行測試,這時我們發現微服務返回的端口,不再是有規律的切換,而是隨機不確定的出現。

注意:

這里RibbonConfig只用於測試負載均衡策略,請不要在生產環境中這樣使用,否則會出現問題:在微服務A中調用微服務B和微服務C,然后再調用微服務B,這是RibbonLoadBalancerClient在獲取微服務時,渠到的serviceId為null,就會獲取到上次的微服務,進而導致404錯誤。因為OpenFeign默認使用的是Ribbon提供的負載均衡策略,我們在實際應用中可以選擇Nacos提供的NacosRule策略,利用Nacos權重進行負載均衡:
  #負載均衡策略
  NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule

本文源碼在https://gitee.com/wmz1930/GitEgg 的chapter-12分支。


免責聲明!

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



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