SpringCloud microservice practice -- building an enterprise level development framework: OpenFeign+Ribbon to achieve load balancing

Keywords: Java Front-end Spring Boot Vue.js Back-end

  Ribbon is a load balancing project under Netflix, which mainly realizes the load balancing of middle tier applications. After configuring the service provider address list for the Ribbon, the Ribbon will automatically help the service caller to request based on a load balancing algorithm. The Ribbon provides a variety of load balancing algorithms by default, such as polling, random, weighted rotation training, etc. you can also implement a custom load balancing algorithm for the Ribbon.

Ribbon has the following features:

  • The load balancer can support plug-in load balancing rules
  • Support for a variety of protocols, such as HTTP, TCP and UDP
  • Integrated load balancing client

Feign uses Ribbon to realize load balancing:

  • Start FeignCleint by adding the @ EnableFeignCleints annotation to the startup class
  • Implement the interface according to Feign's rules, and add @ FeignCleint annotation at the interface definition
  • After the service is started, scan the class with @ FeignCleint annotation and inject this information into the ioc container
  • When the interface method is called, a specific requesttemplate is generated through the jdk proxy
  • Regenerate requesttemplate into Request
  • The Request is handed over to the Client for processing. The Client can be HttpUrlConnection, HttpClient or Okhttp
    Finally, the Client is encapsulated into the LoadBalanceClient class, which combines the Ribbon class to achieve load balancing.

   Ribbon is used for load balancing in OpenFeign, so Ribbon is directly built in OpenFeign. After importing OpenFeign dependencies, there is no need to import Ribbon dependencies. Next, we take gitegg service base as the caller of the service, start gitegg service system on two different ports as the callee of the service, and test the load balancing of Ribbon.
1. First, in the gitegg service system project, create a new called controller method and return the port number configured by the system to distinguish which service was called.

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 Interface")
    public Object list() {
        return systemService.list();
    }


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

    @GetMapping(value = "exception")
    @ApiOperation(value = "Custom exception and return test interface")
    public Result<String> exception() {
        return Result.data(systemService.exception());
    }

    @PostMapping(value = "valid")
    @ApiOperation(value = "Parameter verification test interface")
    public Result<SystemDTO> valid(@Valid @RequestBody SystemDTO systemDTO) {
        return Result.data(systemDTO);
    }

    @PostMapping(value = "nacos")
    @ApiOperation(value = "Nacos Read configuration file test interface")
    public Result<String> nacos() {
        return Result.data(nacosMaxActiveType);
    }

    @GetMapping(value = "api/by/id")
    @ApiOperation(value = "Fegin Get Call test interface")
    public Result<Object> feginById(@RequestParam("id") String id) {
        return Result.data(systemService.list());
    }

    @PostMapping(value = "api/by/dto")
    @ApiOperation(value = "Fegin Post Call test interface")
    public Result<Object> feginByDto(@Valid @RequestBody SystemDTO systemDTO) {
        return Result.data(systemDTO);
    }

    @GetMapping("/api/ribbon")
    @ApiOperation(value = "Ribbon Call test interface")
    public Result<String> testRibbon() {
        return Result.data("The service port accessed now is:" + serverPort);
    }
}

2. In the gitegg service system API project, write a public method to call testRibbon using OpenFeign

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 Test Get
     *
     * @param id
     * @return
     */
    @GetMapping("/system/api/by/id")
    Result<Object> querySystemById(@RequestParam("id") Long id);

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

    /**
     * OpenFeign Test Ribbon load balancing function
     * @return
     */
    @GetMapping("/system/api/ribbon")
    Result<String> testRibbon();

}

3. Add Feign calling method to test Ribbon load balancing in gitegg service base

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 Call test interface")
    public Result<Object> feginById(@RequestParam("id") Long id) {
        return Result.data(systemFeign.querySystemById(id));
    }

    @PostMapping(value = "api/by/dto")
    @ApiOperation(value = "Fegin Post Call test interface")
    public Result<Object> feginByDto(@Valid @RequestBody ApiSystemDTO systemDTO) {
        return Result.data(systemFeign.querySystemByDto(systemDTO));
    }

    @PostMapping(value = "api/ribbon")
    @ApiOperation(value = "Ribbon Call test interface")
    public Result<Object> testRibbon() {
        return Result.data(systemFeign.testRibbon());
    }
}

4. Start the gitegg service base service first, and then the gitegg service system service. After the service is started successfully, change the server.port in bootstrap.yml under gitegg service system to 8011, and then click start. In this way, two gitegg service system services are started (if you are prompted when running two services: gitegg service system is not allowed to run in parallel. Would you like to stop the running one?, then click Run edit configurations - check Allow parallel run in IDEA). After all services are started, you can see the Console of the three services in the Console window

Three services:

5. Open browser access: http://127.0.0.1:8001/doc.html Click the Ribbon to call the test interface
Menu, test and click request. We can see that the returned ports change every time, one is 8001 and the other is 8011, because Ribbon load balancing uses the polling strategy by default

6. If we need to modify the load balancing policy or customize the load balancing policy, according to our architecture design, we set a public load balancing policy in gitegg platform cloud, a sub project of gitegg platform, and then each micro service needs a different policy, we can add a configuration file in our own project. Next, we will create a new policy in gitegg platform cloud Create Ribbon configuration class

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 Common load balancing policy configuration
 */
@Configuration
public class RibbonConfig {

    /**
     * Load balancing policy configuration
     * @return
     */
    @Bean
    public IRule rule(){
        //Random policy randomly selects one of all available providers
        return new RandomRule();
    }

}

7. After the modification is completed, the GitEgg_Platform project re executes install, and the GitEgg_Cloud refreshes the imported package, and then performs the test according to step 5. At this time, we find that the port returned by the microservice is no longer a regular switching, but a random and uncertain occurrence.

be careful:

Here, RibbonConfig is only used to test load balancing strategy. Please do not use it in production environment. Otherwise, there will be problems: call micro service B and micro service C in microservice A, then call micro service B, which is RibbonLoadBalancerClient when getting micro service, serviceId will be null, which will get the last micro service, resulting in 404 errors. By default, the load balancing policy provided by Ribbon is used by eign. In practical application, we can select the NacosRule policy provided by Nacos and use the Nacos weight for load balancing:
  #Load balancing strategy
  NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule

The source code of this article is in https://gitee.com/wmz1930/GitEgg chapter-12 branch of.

Posted by gthri on Tue, 02 Nov 2021 14:51:33 -0700