• 升级Spring Cloud最新版后,有个重要的组件被弃用了


    前不久,我把Mall微服务版本全面升级了 ,在通过Gateway网关调用其他服务的时候,出现了Service Unavailable的问题。排查原因时发现作为负载均衡组件的Ribbon被弃用了,作为Netflix开源的一个组件,Ribbon早已进入维护状态。现在推荐使用的是Loadbalancer,今天我们就来聊聊Loadbalancer的使用!

    SpringCloud实战电商项目mall-swarm(8.8k+star)地址:github.com/macrozheng/…

    LoadBalancer简介

    LoadBalancer是Spring Cloud官方提供的负载均衡组件,可用于替代Ribbon。其使用方式与Ribbon基本兼容,可以从Ribbon进行平滑过渡。

    使用

    下面介绍下LoadBalancer的基本使用,我们将使用Nacos作为注册中心,通过nacos-loadbalancer-servicenacos-user-service两个服务间的相互调用来进行演示。

    负载均衡

    我们将使用RestTemplate来演示下LoadBalancer的负载均衡功能。

    • 首先在nacos-loadbalancer-service模块的pom.xml文件中添加LoadBalancer相关依赖;
    1. <dependency>
    2. <groupId>org.springframework.cloudgroupId>
    3. <artifactId>spring-cloud-starter-loadbalancerartifactId>
    4. dependency>
    5. 复制代码
    • 然后创建Java配置类,用于配置RestTemplate,同时使用@LoadBalanced注解赋予其负载均衡能力;
    1. /**
    2. * RestTemplate相关配置
    3. * Created by macro on 2019/8/29.
    4. */
    5. @Configuration
    6. public class RestTemplateConfig {
    7. @Bean
    8. @ConfigurationProperties(prefix = "rest.template.config")
    9. public HttpComponentsClientHttpRequestFactory customHttpRequestFactory() {
    10. return new HttpComponentsClientHttpRequestFactory();
    11. }
    12. @Bean
    13. @LoadBalanced
    14. public RestTemplate restTemplate() {
    15. return new RestTemplate(customHttpRequestFactory());
    16. }
    17. }
    18. 复制代码
    • application.yml中可以使用自定义配置对RestTemplate的调用超时进行配置;
    1. rest:
    2. template:
    3. config: # RestTemplate调用超时配置
    4. connectTimeout: 5000
    5. readTimeout: 5000
    6. 复制代码
    • 然后在Controller中使用RestTemplate进行远程调用;
    1. /**
    2. * Created by macro on 2019/8/29.
    3. */
    4. @RestController
    5. @RequestMapping("/user")
    6. public class UserLoadBalancerController {
    7. @Autowired
    8. private RestTemplate restTemplate;
    9. @Value("${service-url.nacos-user-service}")
    10. private String userServiceUrl;
    11. @GetMapping("/{id}")
    12. public CommonResult getUser(@PathVariable Long id) {
    13. return restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id);
    14. }
    15. @GetMapping("/getByUsername")
    16. public CommonResult getByUsername(@RequestParam String username) {
    17. return restTemplate.getForObject(userServiceUrl + "/user/getByUsername?username={1}", CommonResult.class, username);
    18. }
    19. @GetMapping("/getEntityByUsername")
    20. public CommonResult getEntityByUsername(@RequestParam String username) {
    21. ResponseEntity entity = restTemplate.getForEntity(userServiceUrl + "/user/getByUsername?username={1}", CommonResult.class, username);
    22. if (entity.getStatusCode().is2xxSuccessful()) {
    23. return entity.getBody();
    24. } else {
    25. return new CommonResult("操作失败", 500);
    26. }
    27. }
    28. @PostMapping("/create")
    29. public CommonResult create(@RequestBody User user) {
    30. return restTemplate.postForObject(userServiceUrl + "/user/create", user, CommonResult.class);
    31. }
    32. @PostMapping("/update")
    33. public CommonResult update(@RequestBody User user) {
    34. return restTemplate.postForObject(userServiceUrl + "/user/update", user, CommonResult.class);
    35. }
    36. @PostMapping("/delete/{id}")
    37. public CommonResult delete(@PathVariable Long id) {
    38. return restTemplate.postForObject(userServiceUrl + "/user/delete/{1}", null, CommonResult.class, id);
    39. }
    40. }
    41. 复制代码
    • 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的相关依赖;
    1. <dependency>
    2. <groupId>org.springframework.cloudgroupId>
    3. <artifactId>spring-cloud-starter-openfeignartifactId>
    4. dependency>
    5. 复制代码
    • 然后在OpenFeign的客户端接口中声明好需要调用的服务接口以及调用方式;
    1. /**
    2. * Created by macro on 2019/9/5.
    3. */
    4. @FeignClient(value = "nacos-user-service")
    5. public interface UserService {
    6. @PostMapping("/user/create")
    7. CommonResult create(@RequestBody User user);
    8. @GetMapping("/user/{id}")
    9. CommonResult getUser(@PathVariable Long id);
    10. @GetMapping("/user/getByUsername")
    11. CommonResult getByUsername(@RequestParam String username);
    12. @PostMapping("/user/update")
    13. CommonResult update(@RequestBody User user);
    14. @PostMapping("/user/delete/{id}")
    15. CommonResult delete(@PathVariable Long id);
    16. }
    17. 复制代码
    • 再在Controller中使用OpenFeign的客户端接口来调用远程服务;
    1. /**
    2. * Created by macro on 2019/8/29.
    3. */
    4. @RestController
    5. @RequestMapping("/userFeign")
    6. public class UserFeignController {
    7. @Autowired
    8. private UserService userService;
    9. @GetMapping("/{id}")
    10. public CommonResult getUser(@PathVariable Long id) {
    11. return userService.getUser(id);
    12. }
    13. @GetMapping("/getByUsername")
    14. public CommonResult getByUsername(@RequestParam String username) {
    15. return userService.getByUsername(username);
    16. }
    17. @PostMapping("/create")
    18. public CommonResult create(@RequestBody User user) {
    19. return userService.create(user);
    20. }
    21. @PostMapping("/update")
    22. public CommonResult update(@RequestBody User user) {
    23. return userService.update(user);
    24. }
    25. @PostMapping("/delete/{id}")
    26. public CommonResult delete(@PathVariable Long id) {
    27. return userService.delete(id);
    28. }
    29. }
    30. 复制代码
    • 如果你想设置下OpenFeign的超时配置的话,可以在application.yml中添加如下内容;
    1. feign:
    2. client:
    3. config:
    4. default: # Feign调用超时配置
    5. connectTimeout: 5000
    6. readTimeout: 5000
    7. 复制代码

    服务实例缓存

    LoadBalancer为了提高性能,不会在每次请求时去获取实例列表,而是将服务实例列表进行了本地缓存。

    默认的缓存时间为35s,为了减少服务不可用还会被选择的可能性,我们可以进行如下配置。

    1. spring:
    2. cloud:
    3. loadbalancer:
    4. cache: # 负载均衡缓存配置
    5. enabled: true # 开启缓存
    6. ttl: 5s # 设置缓存时间
    7. capacity: 256 # 设置缓存大小
    8. 复制代码

    HTTP请求转换

    如果你想在每次远程调用中传入自定义的请求头的话,可以试试LoadBalancerRequestTransformer,通过它可以对原始请求进行一定的转换。

    • 首先我们需要配置好LoadBalancerRequestTransformer的Bean实例,这里我们将ServiceInstanceinstanceId放入到请求头X-InstanceId中;
    1. /**
    2. * LoadBalancer相关配置
    3. * Created by macro on 2022/7/26.
    4. */
    5. @Configuration
    6. public class LoadBalancerConfig {
    7. @Bean
    8. public LoadBalancerRequestTransformer transformer() {
    9. return new LoadBalancerRequestTransformer() {
    10. @Override
    11. public HttpRequest transformRequest(HttpRequest request, ServiceInstance instance) {
    12. return new HttpRequestWrapper(request) {
    13. @Override
    14. public HttpHeaders getHeaders() {
    15. HttpHeaders headers = new HttpHeaders();
    16. headers.putAll(super.getHeaders());
    17. headers.add("X-InstanceId", instance.getInstanceId());
    18. return headers;
    19. }
    20. };
    21. }
    22. };
    23. }
    24. }
    25. 复制代码
    • 然后修改nacos-user-service中的代码,打印获取到的请求头X-InstanceId的信息;
    1. /**
    2. * Created by macro on 2019/8/29.
    3. */
    4. @RestController
    5. @RequestMapping("/user")
    6. public class UserController {
    7. @GetMapping("/{id}")
    8. public CommonResult getUser(@PathVariable Long id) {
    9. User user = userService.getUser(id);
    10. LOGGER.info("根据id获取用户信息,用户名称为:{}", user.getUsername());
    11. ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    12. HttpServletRequest request = servletRequestAttributes.getRequest();
    13. String instanceId = request.getHeader("X-InstanceId");
    14. if (StrUtil.isNotEmpty(instanceId)) {
    15. LOGGER.info("获取到自定义请求头:X-InstanceId={}", instanceId);
    16. }
    17. return new CommonResult<>(user);
    18. }
    19. }
    20. 复制代码
    • 接下来访问接口进行测试,nacos-user-service控制台将打印如下日志,发现自定义请求头已经成功传递了,访问地址:http://localhost:8308/user/1
    1. 2022-07-26 15:05:19.920 INFO 14344 --- [nio-8206-exec-5] c.macro.cloud.controller.UserController : 根据id获取用户信息,用户名称为:macro
    2. 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
    3. 复制代码

    总结

    今天通过对LoadBalancer的一波实践我们可以发现,使用LoadBalancer和Ribbon的区别其实并不大,主要是一些配置方式的相同。如果你之前使用过Ribbon的话,基本上可以无缝切换到LoadBalancer。

    最近面试的小伙伴很多,对此我整理了一份Java面试题手册:基础知识、JavaOOP、Java集合/泛型面试题、
    Java异常面试题、Java中的IO与NIO面试题、Java反射、Java序列化、Java注解、多线程&并发、JVM、Mysql、Redis、
    Memcached、MongoDB、Spring、SpringBoot、SpringCloud、RabbitMQ、Dubbo、MyBatis、ZooKeeper、数据结构、算法、
    Elasticsearch、Kafka、微服务、Linux等等。可以分享给大家学习。【持续更新中】领取方式【999】就可以领取资料了

  • 相关阅读:
    python螺旋数字矩阵
    产学研合作生态硬核来袭,共探数据库技术发展与应用
    【面试题精讲】Java 和 C++ 的区别?
    这篇数据库设计规范建议,我必须分享给你
    JavaScript公共组件父子依赖调用及子校验父条件问题解决
    超级实用网站+公众号合集
    Mac安装JDK
    用 pytorch 训练端对端验证码识别神经网络并进行 C++ 移植
    C++语言之组合、聚合类间关系
    一年拿两证?初级会计和中级会计可以同时报考吗?
  • 原文地址:https://blog.csdn.net/m0_67322837/article/details/126273604