前不久,我把 Mall 微服务版本全面降级了,在通过 Gateway 网关调用其余服务的时候,呈现了
Service Unavailable
的问题。排查起因时发现作为负载平衡组件的 Ribbon 被弃用了,作为 Netflix 开源的一个组件,Ribbon 早已进入保护状态。当初举荐应用的是 Loadbalancer,明天咱们就来聊聊 Loadbalancer 的应用!
SpringCloud 实战电商我的项目mall-swarm
(8.8k+star)地址:https://github.com/macrozheng/mall-swarm
LoadBalancer 简介
LoadBalancer 是 Spring Cloud 官网提供的负载平衡组件,可用于代替 Ribbon。其应用形式与 Ribbon 根本兼容,能够从 Ribbon 进行平滑过渡。
应用
上面介绍下 LoadBalancer 的根本应用,咱们将应用 Nacos 作为注册核心,通过
nacos-loadbalancer-service
和nacos-user-service
两个服务间的互相调用来进行演示。
负载平衡
咱们将应用 RestTemplate 来演示下 LoadBalancer 的负载平衡性能。
- 首先在
nacos-loadbalancer-service
模块的pom.xml
文件中增加 LoadBalancer 相干依赖;
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
- 而后创立 Java 配置类,用于配置
RestTemplate
,同时应用@LoadBalanced
注解赋予其负载平衡能力;
/**
* 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());
}
}
- 在
application.yml
中能够应用自定义配置对 RestTemplate 的调用超时进行配置;
rest:
template:
config: # RestTemplate 调用超时配置
connectTimeout: 5000
readTimeout: 5000
- 而后在 Controller 中应用 RestTemplate 进行近程调用;
/**
* 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);
}
}
- 在
nacos-user-service
中咱们曾经实现了这些接口,能够提供给nacos-loadbalancer-service
服务进行近程调用;
- 而后启动一个
nacos-loadbalancer-service
,和两个nacos-user-service
,此时 Nacos 中会显示如下服务;
- 此时通过
nacos-loadbalancer-service
调用接口进行测试,会发现两个nacos-user-service
交替打印日志信息,应用的是轮询策略,拜访地址:http://localhost:8308/user/1
申明式服务调用
当然 LoadBalancer 除了应用 RestTemplate 来进行近程调用,还能够应用 OpenFeign 来进行申明式服务调用,上面咱们就来介绍下。
- 首先
nacos-loadbalancer-service
模块的pom.xml
文件中增加 OpenFeign 的相干依赖;
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
- 而后在 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);
}
- 再在 Controller 中应用 OpenFeign 的客户端接口来调用近程服务;
/**
* 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);
}
}
- 如果你想设置下 OpenFeign 的超时配置的话,能够在
application.yml
中增加如下内容;
feign:
client:
config:
default: # Feign 调用超时配置
connectTimeout: 5000
readTimeout: 5000
- 接下来通过测试接口调用近程服务,发现能够失常调用,拜访地址:http://localhost:8308/userFei…
服务实例缓存
LoadBalancer 为了进步性能,不会在每次申请时去获取实例列表,而是将服务实例列表进行了本地缓存。
默认的缓存工夫为35s
,为了缩小服务不可用还会被抉择的可能性,咱们能够进行如下配置。
spring:
cloud:
loadbalancer:
cache: # 负载平衡缓存配置
enabled: true # 开启缓存
ttl: 5s # 设置缓存工夫
capacity: 256 # 设置缓存大小
HTTP 申请转换
如果你想在每次近程调用中传入自定义的申请头的话,能够试试
LoadBalancerRequestTransformer
,通过它能够对原始申请进行肯定的转换。
- 首先咱们须要配置好
LoadBalancerRequestTransformer
的 Bean 实例,这里咱们将ServiceInstance
的instanceId
放入到申请头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;
}
};
}
};
}
}
- 而后批改
nacos-user-service
中的代码,打印获取到的申请头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);
}
}
- 接下来拜访接口进行测试,
nacos-user-service
控制台将打印如下日志,发现自定义申请头曾经胜利传递了,拜访地址: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
总结
明天通过对 LoadBalancer 的一波实际咱们能够发现,应用 LoadBalancer 和 Ribbon 的区别其实并不大,次要是一些配置形式的雷同。如果你之前应用过 Ribbon 的话,基本上能够无缝切换到 LoadBalancer。
参考资料
官网文档:https://docs.spring.io/spring…
我的项目源码地址
https://github.com/macrozheng…