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 servicesnacos-loadbalancer-service
andnacos-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
modulepom.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 tonacos-loadbalancer-service
service for remote invocation;
- Then start one
nacos-loadbalancer-service
, and twonacos-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 twonacos-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
modulepom.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
- Next, call the remote service through the test interface and find that it can be called normally. Access address: http://localhost:8308/userFeign/1
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 putServiceInstance
instanceId
into the request headerX-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 headerX-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
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。