Ribbon


1 负载均衡的两种方式

 

@Autowired
private DiscoveryClient discoveryClient;
//-------------------------------------------手写负载均衡----------------------------//
@GetMapping("/hello")
public String hello() {
    // 1.拿到用户中心所有的实例信息
    List<ServiceInstance> userInstances = discoveryClient.getInstances("user-center");
    // 2.所有用户中心实例的请求地址
    List<String> targetUrls = userInstances.stream()
            .map((instance) -> instance.getUri().toString() + "/user/hello")
            .collect(Collectors.toList());
    //随机请求
    int i = ThreadLocalRandom.current().nextInt(targetUrls.size());
    log.info("请求的目标地址:{}", targetUrls.get(i));
    return new RestTemplate().getForObject(targetUrls.get(i), String.class);
}

2 整合Ribbon

2.1 加依赖

spring-cloud-starter-alibaba-nacos-discovery 已经依赖ribbon了所以无需单独引入

使用ribbon无需写注解,无需写配置(需要修改另说)

2.2 hello world

https://cloud.spring.io/spring-cloud-static/Hoxton.SR3/reference/htmlsingle/#rest-template-loadbalancer-client

@Configuration
public class RestTemplageConfig {
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}
@Autowired
private RestTemplate restTemplate;
@GetMapping("/hello")
public String hello() {
    return restTemplate.getForObject("http://user-center/user/hello", String.class);
}

3 Ribbon组成


4 Ribbon内置的负载均衡规则


5 Ribbon细粒度配置

5.1 Java代码配置

Spring中的父子容器:https://yuanyu.blog.csdn.net/article/details/105294435

https://cloud.spring.io/spring-cloud-static/Greenwich.SR1/single/spring-cloud.html#_customizing_the_ribbon_client

16.2 Customizing the Ribbon Client

The CustomConfiguration clas must be a @Configuration class, but take care that it is not in a @ComponentScan for the main application context. Otherwise, it is shared by all the @RibbonClients. If you use @ComponentScan (or @SpringBootApplication), you need to take steps to avoid it being included (for instance, you can put it in a separate, non-overlapping package or specify the packages to scan explicitly in the @ComponentScan).

注意: UserCenterRibbonConfiguration需要被扫描到,RibbonConfiguration不能被扫描到,否则会成为全局配置。@SpringBootApplication会扫描当前包和子包

@Configuration
@RibbonClient(name = "user-center",configuration = RibbonConfiguration.class)
public class UserCenterRibbonConfiguration {}
@Configuration
public class RibbonConfiguration {
    @Bean
    public IRule ribbonRule() {
        return new RandomRule();
    }
}

5.2 用配置属性配置

<clientName>.ribbon.NFLoadBalancerRuleClassName
  • com.netflix.loadbalancer.RandomRule
  • com.nobug.client.config.NacosSameClusterWeightedRule
user-center:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

5.3 最佳实践总结

代码配置方式 vs 属性配置方式
配置方式优点缺点
代码配置基于代码,更加灵活有小坑(父子上下文) 
线上修改得重新打包、发布
属性配置【优先级更高】易上手
配置更加直观
线上修改无需重新打包、发布
优先级更高
极端场景下没有代码配置方式灵活
  • 尽量使用属性配置,属性方式实现不了的情况下再考虑用代码配置
  • 在同一个微服务内尽量保持单一性,比如统一使用属性配置,不要两种方式混用,增加定位代码的复杂性

6 Ribbon全局配置 

  • 方式一 : 让ComponentScan上下文重叠(强烈不建议使用)
  • 方式二【唯一正确的途径】 : @RibbonClients(defaultConfiguration=xxx.class)

 RibbonConfiguration需要被@SpringBootApplication扫描

@Configuration
@RibbonClients(defaultConfiguration = RibbonConfiguration.class) 
public class RibbonConfiguration {
}

7 Ribbon支持的配置项

@Configuration
public class RibbonConfiguration {
    @Bean
    public IRule ribbonRule() {
        return new RandomRule();
    }
    @Bean
    public IPing ping(){
        return new PingUrl();
    }
    //...
}

https://cloud.spring.io/spring-cloud-static/Greenwich.SR1/single/spring-cloud.html#_customizing_the_ribbon_client_by_setting_properties

16.4 Customizing the Ribbon Client by Setting Properties

  • <clientName>.ribbon.NFLoadBalancerClassName: Should implement ILoadBalancer
  • <clientName>.ribbon.NFLoadBalancerRuleClassName: Should implement IRule
  • <clientName>.ribbon.NFLoadBalancerPingClassName: Should implement IPing
  • <clientName>.ribbon.NIWSServerListClassName: Should implement ServerList
  • <clientName>.ribbon.NIWSServerListFilterClassName: Should implement ServerListFilter
xxx-center:
  ribbon:
    NFLoadBalancerClassName: ILoadBalancer实现类
    NFLoadBalancerRuleClassName: IRule实现类
    NFLoadBalancerPingClassName: IPing实现类
    NIWSServerListClassName: ServerList实现类
    NIWSServerListFilterClassName: ServerListFilter实现类

8  Ribbon的饥饿加载 

开启饥饿加载

ribbon:
  eager-load:
    enabled: true
    clients: user-service ,xxx # 细粒度指定哪些具体的服务【多个使用逗号分隔,不在这个名单上的依然使用懒加载】

9 nacos && Ribbon

9.1 扩展Ribbon支持Nacos权重

http://www.imooc.com/article/288660

/**
 * 扩展Ribbon支持Nacos权重
 */
//IRule
@Slf4j
public class NacosWeightedRule extends AbstractLoadBalancerRule {
    @Autowired
    private NacosDiscoveryProperties nacosDiscoveryProperties;
    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {
        //读取配置文件,并初始化NacosWeightedRule
    }
    @Override
    public Server choose(Object o) {
        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) {
            //e.printStackTrace();
            log.error(e.getErrMsg());
        }
        return null;
    }
}

9.2 扩展Ribbon同一集群优先调用

/**
 * 扩展Ribbon-同一集群优先调用
 */
@Slf4j
public class NacosSameClusterWeightedRule extends AbstractLoadBalancerRule {
    @Autowired
    private NacosDiscoveryProperties nacosDiscoveryProperties;
    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {}
    @Override
    public Server choose(Object o) {
        //1.找到指定服务的所有实例 A
        //2.过滤出相同集群下的所有实例 B
        //3.如果B是空,就用A
        //4.基于权重的负载均衡算法,返回1个实例
        try {
            //获取到配置中的集群名称 SH
            //spring.cloud.nacos.discovery.cluster-name=SH
            String clusterName = nacosDiscoveryProperties.getClusterName();
            BaseLoadBalancer loadBalancer = (BaseLoadBalancer) this.getLoadBalancer();
            //想要请求的微服务的名称
            String name = loadBalancer.getName();
            //拿到服务发现的相关API
            NamingService namingService = nacosDiscoveryProperties.namingServiceInstance();
            //1.找到指定服务的所有实例
            List<Instance> instances = namingService.selectInstances(name, true);

            //2.过滤出相同集群下的所有实例
            List<Instance> sameClusterInstances = instances.stream()
                    .filter(instance -> Objects.equals(instance.getClusterName(), clusterName))
                    .collect(Collectors.toList());
            // 3.如果上述2为空,就用1
            List<Instance> instancesToBeChosen;
            if (CollectionUtils.isEmpty(sameClusterInstances)) {
                instancesToBeChosen = instances;
                log.warn("发生了跨集群的调用,name = {}, clusterName = {}, instances = {}", name, clusterName, instances);
            } else {
                instancesToBeChosen = sameClusterInstances;
                log.info("同一集群调用,name = {}, clusterName = {}, instances = {}", name, clusterName, instances);
            }
            //4.基于权重的负载均衡算法,返回一个实例
            Instance instance = ExtendsBalancer.getHostByRandomWeightExtends(instancesToBeChosen);
            log.info("选择的实例是  instance = {}", instance);
            return new NacosServer(instance);
        } catch (Exception e) {
            //e.printStackTrace();
            log.error(e.getMessage());
            return null;
        }
    }
}
/**
 *
 */
class ExtendsBalancer extends Balancer {
    public static Instance getHostByRandomWeightExtends(List<Instance> hosts) {
        return getHostByRandomWeight(hosts);
    }
}

 

扩展Ribbon支持基于元数据的版本管理:http://www.imooc.com/article/288674

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值