yungame 2020-02-23
目前已经实现让A总能找到B,如何实现负载均衡
==Netflix开源的客户端侧负载均衡器==
// 在spring容器中,创建一个对象,类型RestTemplate;名称/ID是:restTemplate // <bean id="restTemplate" class="xxx.RestTemplate"/> @Bean @LoadBalanced public RestTemplate restTemplate() { RestTemplate template = new RestTemplate(); return template; }
@Autowired private RestTemplate restTemplate; @GetMapping("/test-rest-template-sentinel/{userId}") public UserDTO test(@PathVariable Integer userId) { return this.restTemplate .getForObject( "http://user-center/users/{userId}", UserDTO.class, userId); }
@Configuration @RibbonClient(name = "user-center",configuration = RibbonConfiguration.class) public class UserCenterRibbonConfiguration { }
@Configuration public class RibbonConfiguration { @Bean public IRule ribbonRule() { return new NacosSameClusterWeightedRule(); } }
user-center: ribbon: NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
@Configuration @RibbonClients(defaultConfiguration = RibbonConfiguration.class) public class UserCenterRibbonConfiguration { } @Configuration public class RibbonConfiguration { @Bean public IRule ribbonRule() { return new NacosSameClusterWeightedRule(); } }
ribbon: eager-load: enabled: true clients: user-center
import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.NamingService; import com.alibaba.nacos.api.naming.pojo.Instance; import com.netflix.client.config.IClientConfig; import com.netflix.loadbalancer.*; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.alibaba.nacos.NacosDiscoveryProperties; import org.springframework.cloud.alibaba.nacos.ribbon.NacosServer; @Slf4j public class NacosWeightedRule extends AbstractLoadBalancerRule { @Autowired private NacosDiscoveryProperties nacosDiscoveryProperties; @Override public void initWithNiwsConfig(IClientConfig clientConfig) { // 读取配置文件,并初始化NacosWeightedRule } @Override public Server choose(Object key) { try { BaseLoadBalancer loadBalancer = (BaseLoadBalancer) this.getLoadBalancer(); // log.info("lb = {}", loadBalancer); // 想要请求的微服务的名称 String name = loadBalancer.getName(); // 拿到服务发现的相关API NamingService namingService = nacosDiscoveryProperties.namingServiceInstance(); // nacos client自动通过基于权重的负载均衡算法,给我们选择一个实例。 Instance instance = namingService.selectOneHealthyInstance(name); log.info("选择的实例是:port = {}, instance = {}", instance.getPort(), instance); return new NacosServer(instance); } catch (NacosException e) { return null; } } } // spring cloud commons --> 定义了标准 // spring cloud loadbalancer --> 没有权重
import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.NamingService; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.client.naming.core.Balancer; import com.netflix.client.config.IClientConfig; import com.netflix.loadbalancer.AbstractLoadBalancerRule; import com.netflix.loadbalancer.BaseLoadBalancer; import com.netflix.loadbalancer.Server; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.alibaba.nacos.NacosDiscoveryProperties; import org.springframework.cloud.alibaba.nacos.ribbon.NacosServer; import org.springframework.util.CollectionUtils; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; @Slf4j public class NacosSameClusterWeightedRule extends AbstractLoadBalancerRule { @Autowired private NacosDiscoveryProperties nacosDiscoveryProperties; @Override public void initWithNiwsConfig(IClientConfig clientConfig) { } @Override public Server choose(Object key) { try { // 拿到配置文件中的集群名称 BJ String clusterName = nacosDiscoveryProperties.getClusterName(); BaseLoadBalancer loadBalancer = (BaseLoadBalancer) this.getLoadBalancer(); // 想要请求的微服务的名称 String name = loadBalancer.getName(); // 拿到服务发现的相关API NamingService namingService = nacosDiscoveryProperties.namingServiceInstance(); // 1. 找到指定服务的所有实例 A List<Instance> instances = namingService.selectInstances(name, true); // 2. 过滤出相同集群下的所有实例 B List<Instance> sameClusterInstances = instances.stream() .filter(instance -> Objects.equals(instance.getClusterName(), clusterName)) .collect(Collectors.toList()); // 3. 如果B是空,就用A List<Instance> instancesToBeChosen = new ArrayList<>(); if (CollectionUtils.isEmpty(sameClusterInstances)) { instancesToBeChosen = instances; log.warn("发生跨集群的调用, name = {}, clusterName = {}, instances = {}", name, clusterName, instances ); } else { instancesToBeChosen = sameClusterInstances; } // 4. 基于权重的负载均衡算法,返回1个实例 Instance instance = ExtendBalancer.getHostByRandomWeight2(instancesToBeChosen); log.info("选择的实例是 port = {}, instance = {}", instance.getPort(), instance); return new NacosServer(instance); } catch (NacosException e) { log.error("发生异常了", e); return null; } } } class ExtendBalancer extends Balancer { public static Instance getHostByRandomWeight2(List<Instance> hosts) { return getHostByRandomWeight(hosts); } }
元数据就是一堆的描述信息,以map存储。举个例子:
spring: cloud: nacos: metadata: # 自己这个实例的版本 version: v1 # 允许调用的提供者版本 target-version: v1
@Slf4j public class NacosFinalRule extends AbstractLoadBalancerRule { @Autowired private NacosDiscoveryProperties nacosDiscoveryProperties; @Override public Server choose(Object key) { // 负载均衡规则:优先选择同集群下,符合metadata的实例 // 如果没有,就选择所有集群下,符合metadata的实例 // 1. 查询所有实例 A // 2. 筛选元数据匹配的实例 B // 3. 筛选出同cluster下元数据匹配的实例 C // 4. 如果C为空,就用B // 5. 随机选择实例 try { String clusterName = this.nacosDiscoveryProperties.getClusterName(); String targetVersion = this.nacosDiscoveryProperties.getMetadata().get("target-version"); DynamicServerListLoadBalancer loadBalancer = (DynamicServerListLoadBalancer) getLoadBalancer(); String name = loadBalancer.getName(); NamingService namingService = this.nacosDiscoveryProperties.namingServiceInstance(); // 所有实例 List<Instance> instances = namingService.selectInstances(name, true); List<Instance> metadataMatchInstances = instances; // 如果配置了版本映射,那么只调用元数据匹配的实例 if (StringUtils.isNotBlank(targetVersion)) { metadataMatchInstances = instances.stream() .filter(instance -> Objects.equals(targetVersion, instance.getMetadata().get("version"))) .collect(Collectors.toList()); if (CollectionUtils.isEmpty(metadataMatchInstances)) { log.warn("未找到元数据匹配的目标实例!请检查配置。targetVersion = {}, instance = {}", targetVersion, instances); return null; } } List<Instance> clusterMetadataMatchInstances = metadataMatchInstances; // 如果配置了集群名称,需筛选同集群下元数据匹配的实例 if (StringUtils.isNotBlank(clusterName)) { clusterMetadataMatchInstances = metadataMatchInstances.stream() .filter(instance -> Objects.equals(clusterName, instance.getClusterName())) .collect(Collectors.toList()); if (CollectionUtils.isEmpty(clusterMetadataMatchInstances)) { clusterMetadataMatchInstances = metadataMatchInstances; log.warn("发生跨集群调用。clusterName = {}, targetVersion = {}, clusterMetadataMatchInstances = {}", clusterName, targetVersion, clusterMetadataMatchInstances); } } Instance instance = ExtendBalancer.getHostByRandomWeight2(clusterMetadataMatchInstances); return new NacosServer(instance); } catch (Exception e) { log.warn("发生异常", e); return null; } } @Override public void initWithNiwsConfig(IClientConfig iClientConfig) { } }
public class ExtendBalancer extends Balancer { /** * 根据权重,随机选择实例 * * @param instances 实例列表 * @return 选择的实例 */ public static Instance getHostByRandomWeight2(List<Instance> instances) { return getHostByRandomWeight(instances); } }
==服务调用不能跨namespace==