关于ribbon:聊聊如何独立使用ribbon实现业务客户端负载均衡
前言ribbon是Netflix开源的客户端负载平衡工具,ribbon实现一系列的负载平衡算法,通过这些负载平衡算法去查找相应的服务。ribbon被大家所熟知,可能是来源于spring cloud,明天就来聊聊如何独自应用ribbon来实现业务客户端负载平衡 实现要害springcloud ribbon获取服务列表是通过注册核心,而独自应用ribbon,因为没有注册核心加持,就得独自配置服务列表 示例1、在业务我的项目中pom引入ribbon GAV<dependency> <groupId>com.netflix.ribbon</groupId> <artifactId>ribbon</artifactId> <version>2.2.2</version></dependency>不过引进去,发现如果引入netfiix的相干的类,比方IPing,会发现引不进去,起因是因为这个GAV外面依赖的jar的生命周期是runtime,即在运行期或者测试阶段才失效,在编译阶段是不失效的。如果咱们为了不便,能够间接独自引入spring cloud ribbon <dependency> <groupId>org.springframework.cloud</groupId>--> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>--> <version>2.2.2.RELEASE</version> </dependency>本文咱们是想脱离springcloud,间接应用ribbon,因而咱们能够间接 引入如下GAV <!-- 外围的通用性代码--> <dependency> <groupId>com.netflix.ribbon</groupId> <artifactId>ribbon-core</artifactId> <version>${ribbon.version}</version> </dependency> <!-- 基于apache httpClient封装的rest客户端,集成了负载平衡模块,内嵌http心跳检测--> <dependency> <groupId>com.netflix.ribbon</groupId> <artifactId>ribbon-httpclient</artifactId> <version>${ribbon.version}</version> </dependency> <!-- 负载平衡模块--> <dependency> <groupId>com.netflix.ribbon</groupId> <artifactId>ribbon-loadbalancer</artifactId> <version>${ribbon.version}</version> </dependency> <!-- IClientConfig配置相干--> <dependency> <groupId>com.netflix.archaius</groupId> <artifactId>archaius-core</artifactId> <version>0.7.6</version> </dependency> <!-- IClientConfig配置相干--> <dependency> <groupId>commons-configuration</groupId> <artifactId>commons-configuration</artifactId> <version>1.8</version> </dependency>2、创立ribbon元数据配置类@Data@AllArgsConstructor@NoArgsConstructor@Builderpublic class RuleDefinition { /** * 服务名称 */ private String serviceName; /** * 命名空间,当服务名雷同时,能够通过namesapce来进行隔离辨别 * 未指定默认为public */ @Builder.Default private String namespace = DEFAULT_NAMESPACE; /** * 自定义负载平衡策略,未指定默认为轮询 */ @Builder.Default private String loadBalancerRuleClassName = RoundRobin; /** * 自定义心跳检测,未指定不检测 */ @Builder.Default private String loadBalancerPingClassName = DummyPing; /** * 服务列表,多个用英文逗号隔开 */ private String listOfServers; /** * 该优先级大于loadBalancerPingClassName */ private IPing ping; /** * 心跳距离,不配置默认是10秒,单位秒 */ private int pingIntervalSeconds; /** * 该优先级大于loadBalancerRuleClassName */ private IRule rule;}@Data@AllArgsConstructor@NoArgsConstructor@ConfigurationProperties(prefix = PREFIX)public class LoadBalanceProperty { public static final String PREFIX = "lybgeek.loadbalance"; private List<RuleDefinition> rules; public Map<String,RuleDefinition> getRuleMap(){ if(CollectionUtils.isEmpty(rules)){ return Collections.emptyMap(); } Map<String,RuleDefinition> ruleDefinitionMap = new LinkedHashMap<>(); for (RuleDefinition rule : rules) { String key = rule.getServiceName() + RULE_JOIN + rule.getNamespace(); ruleDefinitionMap.put(key,rule); } return Collections.unmodifiableMap(ruleDefinitionMap); }}3、创立负载平衡工厂【外围实现】 private final LoadBalanceProperty loadBalanceProperty; // key:serviceName + nameSpace private static final Map<String, ILoadBalancer> loadBalancerMap = new ConcurrentHashMap<>(); public ILoadBalancer getLoadBalancer(String serviceName,String namespace){ String key = serviceName + RULE_JOIN + namespace; if(loadBalancerMap.containsKey(key)){ return loadBalancerMap.get(key); } RuleDefinition ruleDefinition = getAvailableRuleDefinition(serviceName,namespace); IPing ping = ruleDefinition.getPing(); if(ObjectUtils.isEmpty(ping)){ // 无奈通过ConfigurationManager.getConfigInstance().setProperty(serviceName + DOT + namespace + DOT + PING_CLASS_NAME, ruleDefinition.getLoadBalancerPingClassName()); //LoadBalancerBuilder没提供通过ClientConfig配置ping办法,只能通过withPing批改 ping = getPing(serviceName,namespace); } IRule rule = ruleDefinition.getRule(); if(ObjectUtils.isEmpty(rule)){ // 也能够通过ConfigurationManager.getConfigInstance().setProperty(serviceName + DOT + namespace + DOT + RULE_CLASS_NAME, ruleDefinition.getLoadBalancerRuleClassName()); rule = getRule(serviceName,namespace); } // 配置服务列表 ConfigurationManager.getConfigInstance().setProperty(serviceName + DOT + namespace + DOT + SERVER_LIST, ruleDefinition.getListOfServers()); // 因为服务列表目前是配置写死,因而敞开列表更新,否则当触发定时更新时,会从新将服务列表状态复原原样,这样会导致server的isLive状态不精确 // 不设置默认采纳com.netflix.loadbalancer.PollingServerListUpdater ConfigurationManager.getConfigInstance().setProperty(serviceName + DOT + namespace + DOT + SERVERLIST_UPDATER_CLASS_NAME, EmptyServerListUpdater.class.getName()); IClientConfig config = new DefaultClientConfigImpl(namespace); config.loadProperties(serviceName); ZoneAwareLoadBalancer<Server> loadBalancer = getLoadBalancer(config, ping, rule); loadBalancerMap.put(key,loadBalancer); if(ruleDefinition.getPingIntervalSeconds() > 0){ // 默认每隔10秒进行心跳检测 loadBalancer.setPingInterval(ruleDefinition.getPingIntervalSeconds()); } return loadBalancer; } public ZoneAwareLoadBalancer<Server> getLoadBalancer(IClientConfig config, IPing ping, IRule rule){ ZoneAwareLoadBalancer<Server> serverZoneAwareLoadBalancer = LoadBalancerBuilder.newBuilder() .withClientConfig(config) .withPing(ping) .withRule(rule) .buildDynamicServerListLoadBalancerWithUpdater(); return serverZoneAwareLoadBalancer; } /** * 获取 iping * @param serviceName * @param namespace * @return */ @SneakyThrows public IPing getPing(String serviceName, String namespace){ RuleDefinition ruleDefinition = getAvailableRuleDefinition(serviceName,namespace); Class<?> loadBalancerPingClass = ClassUtils.forName(ruleDefinition.getLoadBalancerPingClassName(), Thread.currentThread().getContextClassLoader()); Assert.isTrue(IPing.class.isAssignableFrom(loadBalancerPingClass),String.format("loadBalancerPingClassName : [%s] is not Iping class type",ruleDefinition.getLoadBalancerPingClassName())); return (IPing) BeanUtils.instantiateClass(loadBalancerPingClass); } /** * 获取 loadbalanceRule * @param serviceName * @param namespace * @return */ @SneakyThrows public IRule getRule(String serviceName, String namespace){ RuleDefinition ruleDefinition = getAvailableRuleDefinition(serviceName,namespace); Class<?> loadBalancerRuleClass = ClassUtils.forName(ruleDefinition.getLoadBalancerRuleClassName(), Thread.currentThread().getContextClassLoader()); Assert.isTrue(IRule.class.isAssignableFrom(loadBalancerRuleClass),String.format("loadBalancerRuleClassName : [%s] is not Irule class type",ruleDefinition.getLoadBalancerRuleClassName())); return (IRule) BeanUtils.instantiateClass(loadBalancerRuleClass); } private RuleDefinition getAvailableRuleDefinition(String serviceName,String namespace){ Map<String, RuleDefinition> ruleMap = loadBalanceProperty.getRuleMap(); Assert.notEmpty(ruleMap,"ruleDefinition is empty"); String key = serviceName + RULE_JOIN + namespace; RuleDefinition ruleDefinition = ruleMap.get(key); Assert.notNull(ruleDefinition,String.format("NOT FOUND AvailableRuleDefinition with serviceName : [{}] in namespace:[{}]",serviceName,namespace)); return ruleDefinition; }外围实现类:com.netflix.loadbalancer.LoadBalancerBuilder 利用该类创立相应的负载平衡 ...