1
头图
Not long ago, I fully upgraded the Mall microservice version . When calling other services through the Gateway gateway, there was a problem of Service Unavailable . When investigating the cause, it was found that Ribbon, which is a load balancing component, was deprecated. As a component of Netflix's open source, Ribbon has already entered a maintenance state. Now the recommended use is Loadbalancer, today we will talk about the use of Loadbalancer!

SpringCloud actual e-commerce project mall-swarm (8.8k+star) address: https://github.com/macrozheng/mall-swarm

Introduction to LoadBalancer

LoadBalancer is an official load balancing component provided by Spring Cloud and can be used to replace Ribbon. Its usage is basically compatible with Ribbon, and it can make a smooth transition from Ribbon.

use

The basic use of LoadBalancer is described below. We will use Nacos as the registration center to demonstrate the mutual calls between the two services nacos-loadbalancer-service and nacos-user-service .

load balancing

We will use RestTemplate to demonstrate the load balancing function of LoadBalancer.
  • First add LoadBalancer related dependencies in the nacos-loadbalancer-service module pom.xml file;
 <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
  • Then create a Java configuration class to configure RestTemplate , and use the @LoadBalanced annotation to give it load balancing capabilities;
 /**
 * RestTemplate相关配置
 * Created by macro on 2019/8/29.
 */
@Configuration
public class RestTemplateConfig {

    @Bean
    @ConfigurationProperties(prefix = "rest.template.config")
    public HttpComponentsClientHttpRequestFactory customHttpRequestFactory() {
        return new HttpComponentsClientHttpRequestFactory();
    }

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate(customHttpRequestFactory());
    }
}
  • In application.yml you can use custom configuration to configure RestTemplate call timeout;
 rest:
  template:
    config: # RestTemplate调用超时配置
      connectTimeout: 5000
      readTimeout: 5000
  • Then use RestTemplate in Controller to make remote calls;
 /**
 * Created by macro on 2019/8/29.
 */
@RestController
@RequestMapping("/user")
public class UserLoadBalancerController {
    @Autowired
    private RestTemplate restTemplate;
    @Value("${service-url.nacos-user-service}")
    private String userServiceUrl;

    @GetMapping("/{id}")
    public CommonResult getUser(@PathVariable Long id) {
        return restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id);
    }

    @GetMapping("/getByUsername")
    public CommonResult getByUsername(@RequestParam String username) {
        return restTemplate.getForObject(userServiceUrl + "/user/getByUsername?username={1}", CommonResult.class, username);
    }

    @GetMapping("/getEntityByUsername")
    public CommonResult getEntityByUsername(@RequestParam String username) {
        ResponseEntity<CommonResult> entity = restTemplate.getForEntity(userServiceUrl + "/user/getByUsername?username={1}", CommonResult.class, username);
        if (entity.getStatusCode().is2xxSuccessful()) {
            return entity.getBody();
        } else {
            return new CommonResult("操作失败", 500);
        }
    }

    @PostMapping("/create")
    public CommonResult create(@RequestBody User user) {
        return restTemplate.postForObject(userServiceUrl + "/user/create", user, CommonResult.class);
    }

    @PostMapping("/update")
    public CommonResult update(@RequestBody User user) {
        return restTemplate.postForObject(userServiceUrl + "/user/update", user, CommonResult.class);
    }

    @PostMapping("/delete/{id}")
    public CommonResult delete(@PathVariable Long id) {
        return restTemplate.postForObject(userServiceUrl + "/user/delete/{1}", null, CommonResult.class, id);
    }
}
  • In nacos-user-service we have implemented these interfaces, which can be provided to nacos-loadbalancer-service service for remote invocation;

  • Then start one nacos-loadbalancer-service , and two nacos-user-service , the following services will be displayed in Nacos at this time;

  • At this time, through the nacos-loadbalancer-service calling interface to test, you will find two nacos-user-service alternately print log information, using the polling strategy, access address: http://localhost:8308/user /1

Declarative service invocation

Of course, in addition to using RestTemplate to make remote calls, LoadBalancer can also use OpenFeign to make declarative service calls, which we will introduce below.
  • First nacos-loadbalancer-service module pom.xml Add OpenFeign related dependencies to the file;
 <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
  • Then declare the service interface and calling method to be called in the client interface of OpenFeign;
 /**
 * Created by macro on 2019/9/5.
 */
@FeignClient(value = "nacos-user-service")
public interface UserService {
    @PostMapping("/user/create")
    CommonResult create(@RequestBody User user);

    @GetMapping("/user/{id}")
    CommonResult<User> getUser(@PathVariable Long id);

    @GetMapping("/user/getByUsername")
    CommonResult<User> getByUsername(@RequestParam String username);

    @PostMapping("/user/update")
    CommonResult update(@RequestBody User user);

    @PostMapping("/user/delete/{id}")
    CommonResult delete(@PathVariable Long id);
}
  • Then use OpenFeign's client interface in the Controller to call the remote service;
 /**
 * Created by macro on 2019/8/29.
 */
@RestController
@RequestMapping("/userFeign")
public class UserFeignController {
    @Autowired
    private UserService userService;

    @GetMapping("/{id}")
    public CommonResult getUser(@PathVariable Long id) {
        return userService.getUser(id);
    }

    @GetMapping("/getByUsername")
    public CommonResult getByUsername(@RequestParam String username) {
        return userService.getByUsername(username);
    }

    @PostMapping("/create")
    public CommonResult create(@RequestBody User user) {
        return userService.create(user);
    }

    @PostMapping("/update")
    public CommonResult update(@RequestBody User user) {
        return userService.update(user);
    }

    @PostMapping("/delete/{id}")
    public CommonResult delete(@PathVariable Long id) {
        return userService.delete(id);
    }
}
  • If you want to set the timeout configuration of OpenFeign, you can add the following content in application.yml ;
 feign:
  client:
    config:
      default: # Feign调用超时配置
        connectTimeout: 5000
        readTimeout: 5000

service instance cache

In order to improve performance, LoadBalancer does not obtain the instance list for each request, but caches the service instance list locally.

The default cache time is 35s . In order to reduce the possibility that the service will be selected when unavailable, we can configure it as follows.

 spring:
  cloud:
    loadbalancer:
      cache: # 负载均衡缓存配置
        enabled: true # 开启缓存
        ttl: 5s # 设置缓存时间
        capacity: 256 # 设置缓存大小

HTTP request conversion

If you want to pass in a custom request header in each remote call, you can try LoadBalancerRequestTransformer , which can convert the original request to a certain extent.
  • First we need to configure the Bean instance of LoadBalancerRequestTransformer , here we put ServiceInstance instanceId into the request header X-InstanceId
 /**
 * LoadBalancer相关配置
 * Created by macro on 2022/7/26.
 */
@Configuration
public class LoadBalancerConfig {
    @Bean
    public LoadBalancerRequestTransformer transformer() {
        return new LoadBalancerRequestTransformer() {
            @Override
            public HttpRequest transformRequest(HttpRequest request, ServiceInstance instance) {
                return new HttpRequestWrapper(request) {
                    @Override
                    public HttpHeaders getHeaders() {
                        HttpHeaders headers = new HttpHeaders();
                        headers.putAll(super.getHeaders());
                        headers.add("X-InstanceId", instance.getInstanceId());
                        return headers;
                    }
                };
            }
        };
    }
}
  • Then modify the code in nacos-user-service , and print the information of the obtained request header X-InstanceId ;
 /**
 * Created by macro on 2019/8/29.
 */
@RestController
@RequestMapping("/user")
public class UserController {
    @GetMapping("/{id}")
    public CommonResult<User> getUser(@PathVariable Long id) {
        User user = userService.getUser(id);
        LOGGER.info("根据id获取用户信息,用户名称为:{}", user.getUsername());
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = servletRequestAttributes.getRequest();
        String instanceId = request.getHeader("X-InstanceId");
        if (StrUtil.isNotEmpty(instanceId)) {
            LOGGER.info("获取到自定义请求头:X-InstanceId={}", instanceId);
        }
        return new CommonResult<>(user);
    }
}
  • Next, access the interface for testing, nacos-user-service the console will print the following log, and find that the custom request header has been successfully passed, and the access address: http://localhost:8308/user/1
 2022-07-26 15:05:19.920  INFO 14344 --- [nio-8206-exec-5] c.macro.cloud.controller.UserController  : 根据id获取用户信息,用户名称为:macro
2022-07-26 15:05:19.921  INFO 14344 --- [nio-8206-exec-5] c.macro.cloud.controller.UserController  : 获取到自定义请求头:X-InstanceId=192.168.3.227#8206#DEFAULT#DEFAULT_GROUP@@nacos-user-service

Summarize

Today, through a wave of practice on LoadBalancer, we can find that the difference between using LoadBalancer and Ribbon is not big, mainly because some configuration methods are the same. If you've used Ribbon before, you can basically switch to LoadBalancer seamlessly.

References

Official documentation: https://docs.spring.io/spring-cloud-commons/docs/current/reference/html/#spring-cloud-loadbalancer

Project source code address

https://github.com/macrozheng/springcloud-learning/tree/master/nacos-loadbalancer-service


macrozheng
1.1k 声望1.3k 粉丝