• OpenFeign、RestTemplate支持自定义服务地址列表


    一、背景

    最近在支援项目时,项目组需要将后端服务部署为多个实例,且没有集成服务注册中心、反向代理、K8S等,如此提出需要客户端OpenFeign、RestTemplate等支持手动配置服务端地址列表,并支持在服务端实例间负载均衡,经过调研后给出如下基于Spring Cloud LoadBalancer的集成方案。

    二、解决思路

    OpenFeign、RestTemplate均是通过url对服务发起调用,具体url的负载均衡则是由Spring Cloud LoadBalancer负责,所以需要在LoadBalancer上做文章,查询相关文档后给出如下思路:

    • 通过@LoadBalancerClient(name, configuration)注解为不同服务标识指定相应的配置类
      • 服务标识 对应@FeignClient.name,或 RestTemplate调用url http://serviceId/path中的serviceId
    • 然后在相应的配置类中注册自定义的ServiceInstanceListSupplier
    • 最后在自定义的ServiceInstanceListSupplier中返回该服务标识手动配置的服务端地址列表

    接下来给出核心实现代码。

    三、定义通用配置

    首先,自定义配置属性如下:

    import org.springframework.boot.context.properties.ConfigurationProperties;
    
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    /**
     * 负载均衡静态配置
     *
     * @author luohq
     * @date 2024-04-25 9:35
     */
    @ConfigurationProperties(prefix = LoadBalancerStaticProps.PREFIX)
    public class LoadBalancerStaticProps {
    
        public static final String PREFIX = "spring.cloud.loadbalancer.clients";
        /**
         * 客户端ServiceId到静态地址列表的映射
         */
        private Map<String, List<String>> staticUris = new HashMap<>();
    
        public Map<String, List<String>> getStaticUris() {
            return staticUris;
        }
    
        public void setStaticUris(Map<String, List<String>> staticUris) {
            this.staticUris = staticUris;
        }
    
        @Override
        public String toString() {
            return "LoadBalancerStaticProps{" +
                    "staticUris=" + staticUris +
                    '}';
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    后续可通过如下示例配置,手动指定不同服务标识对应的服务端地址列表:

    spring:
      cloud:
        loadbalancer:
          clients:
            # 配置负载均衡静态地址
            static-uris:
              # app-rbac服务的静态地址列表
              app-rbac:
                - http://localhost:8081
                - http://localhost:8082
              # app-atom服务的静态地址列表
              app-atom:
                - http://localhost:9081
                - http://localhost:9082
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    四、定义通用StaticServiceInstanceListSuppler

    StaticServiceInstanceListSuppler实现了ServiceInstanceListSupplier接口,
    这个接口是Spring Cloud LoadBalancer的一部分,是一个通用的客户端负载均衡器。
    StaticServiceInstanceListSuppler支持自定义serviceId对应的服务地址列表serviceStaticUriList,
    同时提供create方法,支持从前文定义的配置属性LoadBalancerStaticProps 中(即从配置文件中)获取serviceId对应的服务地址列表。

    import org.springframework.cloud.client.DefaultServiceInstance;
    import org.springframework.cloud.client.ServiceInstance;
    import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
    import org.springframework.util.Assert;
    import reactor.core.publisher.Flux;
    
    import java.net.URI;
    import java.util.List;
    import java.util.stream.Collectors;
    import java.util.stream.IntStream;
    
    /**
     * 静态服务地址提供器
     *
     * @author luohq
     * @date 2024-04-25
     */
    public class StaticServiceInstanceListSuppler implements ServiceInstanceListSupplier {
    
        /**
         * 服务标识
         */
        private final String serviceId;
    
        /**
         * 服务地址列表
         */
        private final List<String> serviceStaticUriList;
    
        public StaticServiceInstanceListSuppler(String serviceId, List<String> serviceStaticUriList) {
            Assert.notNull(serviceId, "serviceId may not be null");
            Assert.notEmpty(serviceStaticUriList, "serviceStaticUriList may not be null");
            this.serviceId = serviceId;
            this.serviceStaticUriList = serviceStaticUriList;
        }
    
        @Override
        public String getServiceId() {
            return serviceId;
        }
    
        @Override
        public Flux<List<ServiceInstance>> get() {
            List<ServiceInstance> defaultServiceInstanceList = IntStream.range(0, serviceStaticUriList.size())
                    .mapToObj(i -> {
                        DefaultServiceInstance defaultServiceInstance = new DefaultServiceInstance();
                        defaultServiceInstance.setServiceId(this.serviceId);
                        defaultServiceInstance.setInstanceId(this.serviceId + i);
                        defaultServiceInstance.setUri(URI.create(serviceStaticUriList.get(i)));
                        return defaultServiceInstance;
                    })
                    .collect(Collectors.toList());
            return Flux.just(defaultServiceInstanceList);
        }
    
        public static StaticServiceInstanceListSuppler create(String serviceId, LoadBalancerStaticProps loadBalancerStaticProps) {
            return new StaticServiceInstanceListSuppler(serviceId, loadBalancerStaticProps.getStaticUris().get(serviceId));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    五、为指定serviceId定义专属的配置类

    如下实现即对应serviceId=app-rbac服务的专属配置类,
    不同的serviceId可再单独定义相应的配置类。

    import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Primary;
    /**
     * RBAC客户端负载均衡静态配置
     *
     * @author luohq
     * @date 2024-04-25 9:35
     */
    public class RbacClientLoadBalancerStaticConfiguration {
    
        public static final String SERVICE_ID = "app-rbac";
    
        @Bean
        @Primary
        public ServiceInstanceListSupplier serviceInstanceListSupplier(LoadBalancerStaticProps loadBalancerStaticProps) {
            return StaticServiceInstanceListSuppler.create(SERVICE_ID, loadBalancerStaticProps);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    六、通过@LoadBalancerClient为指定serviceId设置配置类

    6.1 OpenFeign

    如果项目中使用的是OpenFeign,以下配置已足够:

    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
    import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;
    import org.springframework.context.annotation.Configuration;
    
    /**
     * RBAC客户端负载均衡静态配置
     *
     * @author luohq
     * @date 2024-04-25 9:35
     */
    @Configuration
    @LoadBalancerClients({
            //自定义RBAC服务配置
            @LoadBalancerClient(
                    name = RbacClientLoadBalancerStaticConfiguration.SERVICE_ID,
                    configuration = RbacClientLoadBalancerStaticConfiguration.class
            )
    })
    @EnableConfigurationProperties({LoadBalancerStaticProps.class})
    public class LoadBalancerStaticConfiguration2 {
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    6.2 RestTemplate

    如果项目中使用的是RestTemplate,那么还需要通过@LoadBalanced注解设置RestTemplate支持负载均衡,
    具体配置代码如下:

    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.cloud.client.loadbalancer.LoadBalanced;
    import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
    import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.client.RestTemplate;
    
    /**
     * RBAC客户端负载均衡静态配置
     *
     * @author luohq
     * @date 2024-04-25 9:35
     */
    @Configuration
    @LoadBalancerClients({
            //自定义RBAC服务配置
            @LoadBalancerClient(
                    name = RbacClientLoadBalancerStaticConfiguration.SERVICE_ID,
                    configuration = RbacClientLoadBalancerStaticConfiguration.class
            )
    })
    @EnableConfigurationProperties({LoadBalancerStaticProps.class})
    public class LoadBalancerStaticConfiguration2 {
    
        /**
         * 通过@LoadBalanced注解开启RestTemplate负载均衡能力
         */
        @LoadBalanced
        @Bean
        public RestTemplate restTemplate() {
            return new RestTemplate();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    七、Debug调试

    Spring Cloud LoadBalancer默认使用的是RoundRobinLoadBalancer,可重点关注RoundRobinLoadBalancer中获取服务实例的代码。
    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述


    参考:
    https://spring.io/guides/gs/spring-cloud-loadbalancer
    https://juejin.cn/post/7266315019294490661
    https://docs.spring.io/spring-cloud-commons/docs/3.1.8/reference/html/#spring-cloud-loadbalancer

  • 相关阅读:
    三、C++反作弊对抗实战 (实战篇 —— 3.如何获取游戏中角色人物角色的名称坐标、血量、武器信息(非CE扫描))
    nginx进程间同步机制-互斥锁
    netty面试题及答案
    springmvc异常处理解析#ExceptionHandlerExceptionResolver
    速腾聚创发布全固态补盲激光雷达E1,成立合资公司,备战百万产能
    LeetCode50天刷题计划第二季(Day 5 — 交错字符串(20.00-22.00)
    容器镜像安全:安全漏洞扫描神器Trivy
    【nlp】文本处理的基本方法
    Gorm源码学习-数据库连接
    el-table-v2 element plus 表格虚拟滚动渲染选择器
  • 原文地址:https://blog.csdn.net/luo15242208310/article/details/138178849