学习 spring-cloud-aibaba第三篇,负载均衡Ribbon


特别声明:文章的内容学习自慕课网大目老师的微服务视频,视频链接: https://coding.imooc.com/learn/list/358.html,大目老师还写了很多微服务有关的手记,都很实用!

前情提要:上一篇博文已经介绍了服务注册nacos的实现。现在我注册了content-centeruser-center两个服务,content-center使用RestTemplate调用user-center服务,使用ribbon做负载均衡

1.ribbon的组成

在这里插入图片描述

  • IRule 核心接口,负载均衡的规则,默认是ZoneAvoidanceRule
  • ServerListUpdater,更新ServerList的策略,当nacos上server实例个数发生变化时,ribbon里的ServerList什么时候去更新呢?默认值PollingServerListerUpdate定时更新

2.项目使用Ribbon

2.1 引入依赖

Ribbon已经包含在spring-cloud-starter-alibaba-nacos-discovery里了,所以不用再重复添加依赖了
在这里插入图片描述

2.2 加注解

启动类那边不用加注解,注解加载RestTemplate上面@LoadBalanced

@Configuration
public class RestTemplateConfig {

    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}

2.3 写配置

暂时不用写配置

3.负载均衡规则 IRule

在这里插入图片描述

3.1 测试 ZoneAvoidanceRule 默认规则

这里的zone是什么,我也不知道,我看源码是个String。我测试的环境应该是没有zone的,看启动当然日志是 UNKNOWN ,所以它相当于RoundRobinRule轮询。
在这里插入图片描述
启动三个user-center和一个content-center
在这里插入图片描述

  • content-center 代码:
@RestController
@RequestMapping("poem")
public class PoemController {

    private static final Logger log = LoggerFactory.getLogger(PoemController.class);

    @Autowired
    private PoemService poemService;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private DiscoveryClient discoveryClient;

    @GetMapping(value = "testRibbon")
    public Object testRibbon() {
//        List<ServiceInstance> instances = discoveryClient.getInstances("user-center");
//        String targetUrl = instances.stream()
//                .map(instance -> instance.getUri().toString() + "/reciteHis/testAno")
//                .findFirst()
//                .orElseThrow(() -> new IllegalArgumentException("当前没有实例!"));
        String targetUrl= "http://user-center/reciteHis/testAno";
        log.info("targetUrl={}",targetUrl);
        Page forObject = restTemplate.getForObject(targetUrl, Page.class);
        return ResponseVO.success(forObject);
    }

String targetUrl= “http://user-center/reciteHis/testAno 里的user-center就是nacosuser-center的服务名

  • user-center 代码;
@RestController
@RequestMapping("/reciteHis")
public class ReciteHisController {

    private static final Logger log = LoggerFactory.getLogger(ReciteHisController.class);

    @Autowired
    private ReciteHisService reciteHisService;

    @Autowired
    private MemberService memberService;

    @GetMapping(value = "testAno")
    public Object memberRctHisAno(HttpServletRequest request){
        Page<ReciteHisOT> page = new Page<>(1, 2);
        page.setRecords(reciteHisService.selectUserListPageAno(page,"3"));
        log.info("我是{},我被调用了",request.getRequestURL());
        return page;
    }

随便提供一个测试接口就行,返回的内容不重要

  • 访问content-center的 http://localhost:8081/poem/testRibbon 接口测试,刷新6次
    看user-center的日志,三个console窗口各显示被调用了2次,是平均的,看来是轮询规则没错
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

3.2 测试 RandomRule 随机规则

  • 写配置
    application.yml。没有代码提示,纯手敲,user-center代表针对的是user-center服务写的配置 (这里用的是属性配置方式,还有一种java代码的配置方式)
user-center:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
  • 测试
    请求content-center的 http://localhost:8081/poem/testRibbon 接口测试,请求10次
    user-center服务,我关掉了一个,三个太多了,现在2个服务实例,一个被调用6次,一个4次,简单证明是随机
    在这里插入图片描述
    在这里插入图片描述

3.3 其它的内置规则没那么方便测试,我不测试了

3.4 ribbon的全局配置

上面提到了ribbon的配置有两种方式

  • 属性配置
    这种方式暂时无法实现全局配置,刚才配置的RandomRule也只能对user-center服务起作用,如果调用其它服务,则还是默认的ZoneAvoidanceRule
  • java代码配置
    step1:新建一个和com包平行的configuration包,新建RibbonConfiguration类
    新建一个平行的包的目的是为了避免这个类被启动类扫描到,如果被启动类扫描到,会有父子上下文问题,如果父子上下文了,轻则配置不灵光了,重则服务启动不来,对于RibbonConfiguration这个来说,如果父子上下文了,那么它就自动变成全局的ribbon配置了,无法用于针对单个服务的特别配置
    在这里插入图片描述
package configuration;

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RoundRobinRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RibbonConfiguration {

    @Bean
    public IRule ribbonRule(){
        return new RoundRobinRule();
    }
}

这里配置的是轮询规则

step2:新建个RibbonClientConfig类,类名不是固定的,大家随意
在这里插入图片描述

package com.zengchen.content.config;

import configuration.RibbonConfiguration;
import org.springframework.cloud.netflix.ribbon.RibbonClients;
import org.springframework.context.annotation.Configuration;

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

step3: 全局配置了轮询规则,测试一下
http://localhost:8081/poem/testRibbon 接口测试,请求6次
user-center 两个实例个被调用3次,基本证明全局配置生效。
在这里插入图片描述在这里插入图片描述
而此时,application.yml里配置的是随机规则 ,并没有注释,所以java代码方式的配置优先级应该比属性配置的方式高一些。但是这个意义不大,最好不要两种方式同时配置!

user-center:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

3.5 自定义nacos权重负载均衡规则

  • content-center里新写个类NacosWeightedRule
@Slf4j
public class NacosWeightedRule extends AbstractLoadBalancerRule {

    @Autowired
    private NacosDiscoveryProperties discoveryProperties;

    private Server choose(DynamicServerListLoadBalancer lb, Object key) {
        if (lb == null) {
            return null;
        }
        String name = lb.getName();
        try {
            Instance instance = discoveryProperties.namingServiceInstance()
                    .selectOneHealthyInstance(name);
            return new NacosServer(instance);
        } catch (NacosException e) {
            log.error("权重规则发生异常",e);
        }
        return null;
    }

    @Override
    public Server choose(Object key) {
        return choose((DynamicServerListLoadBalancer) getLoadBalancer(), key);
    }

    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {

    }
}
  • 修改RibbonConfiguration返回新的NacosWeightedRule
@Configuration
public class RibbonConfiguration {

    @Bean
    public IRule ribbonRule(){
//        return new RoundRobinRule();
        return new NacosWeightedRule();
    }
}
  • 打开nacos控制台修改两个user-center服务实例的权重,8082端口的设置为1,8083端口的设置为4,这样8083的服务实例会接收更多的请求访问
    在这里插入图片描述
  • 重启content-center服务,请求http://localhost:8081/poem/testRibbon ,请求20次
    结果:8082访问了7次,8083访问了13次
    第二次测试结果:8082被访问了3次,8083被访问了17次
    (如果把权重设置为0,就不会有请求访问了,可以做到优雅下线)
    在这里插入图片描述
    在这里插入图片描述

3.6 自定义同集群优先访问的负载均衡规则

  • 新建类NacosSameClusterWeightedRule
@Slf4j
public class NacosSameClusterWeightedRule extends AbstractLoadBalancerRule {

    @Autowired
    private NacosDiscoveryProperties discoveryProperties;

    private Server choose(DynamicServerListLoadBalancer lb, Object key) {
        if (lb == null) {
            return null;
        }
        try {
            // 本服务所属集群
            String clusterName = discoveryProperties.getClusterName();
            // 要请求的服务的名称
            String targetInstanceName = lb.getName();
            // 服务相关Api
            NamingService namingService = discoveryProperties.namingServiceInstance();
            // 获取目标服务所有实例(健康的实例)
            List<Instance> instances = namingService.selectInstances(targetInstanceName, true);
            // 获得同集群实例
            List<Instance> sameClusterInstances = instances.stream()
                    .filter(instance -> {
                        return Objects.equals(instance.getClusterName(), clusterName);
                    }).collect(Collectors.toList());
            // 等待被选择的实例
            List<Instance> toBeChosenInstances = sameClusterInstances;
            // 如果没有同集群的实例,那就用所有实例
            if(CollectionUtils.isEmpty(sameClusterInstances)){
                toBeChosenInstances = instances;
                log.warn("发生跨集群调用,集群名称={},目标服务名称={}",clusterName,targetInstanceName);
            }
            // 根据权重从toBeChosenInstances选择一个实例
            return new NacosServer(ExtendsBalancer.getInstanceByWeight(toBeChosenInstances));
        } catch (NacosException e) {
            log.error("同集群负载规则发生异常", e);
        }
        return null;
    }

    @Override
    public Server choose(Object key) {
        return choose((DynamicServerListLoadBalancer) getLoadBalancer(), key);
    }

    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {

    }
}

/**
 * <p>
 * 利用子类使用Balance的protected getHostByRandomWeight方法
 * </p>
 *
 * @author zengchen
 * @since 2019-08-11
 */
class ExtendsBalancer extends Balancer {
    public static Instance getInstanceByWeight(List<Instance> instances){
        return getHostByRandomWeight(instances);
    }

}
  • 修改RibbonConfiguration返回新的NacosSameClusterWeightedRule
  • 测试前置准备
    启动三个user-center实例,两个SH集群的,一个BJ集群的
    8082:SH,权重 1
    8083:SH,权重 4
    8084:BJ, 权重 1
spring:
  cloud:
    nacos:
      discovery:
        server-addr: 118.31.11.178:8848
          # 所属集群 上海
          cluster-name: SH

在这里插入图片描述
content-center 启动一个属于SH
在这里插入图片描述

  • 测试
    请求http://localhost:8081/poem/testRibbon ,请求20
    8082:SH,权重 1,被访问2次
    8083:SH,权重 4,被访问18次
    8084:BJ, 权重 1,被访问0次
    符合预期!
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

3.7 自定义根据服务实例元数据的负载均衡规则

可以实现v1版本的content-center只调用v1版本user-center
可以实现v2版本的content-center只调用v2版本user-center

  • 给content-center配置元数据 self-version: v1target-version: v1
    application.yml
spring:
  cloud:
    nacos:
      discovery:
        server-addr: 118.31.11.178:8848
        # 集群名称
        cluster-name: SH
        # 元数据
        metadata:
          self-version: v1
          target-version: v1
  • 新建类 NacosClusterMetadataWeightedRule
@Slf4j
public class NacosClusterMetadataWeightedRule extends AbstractLoadBalancerRule {

    @Autowired
    private NacosDiscoveryProperties discoveryProperties;

    // 自己的版本号
    private final static String SELF_VERSION = "self-version";
    // 需要调用的版本号
    private final static String TARGET_VERSION = "target-version";

    private Server choose(DynamicServerListLoadBalancer lb, Object key) {
        if (lb == null) {
            return null;
        }
        try {
            // 本服务目标版本号
            String targetVersion = discoveryProperties.getMetadata().get(SELF_VERSION);
            // 本服务所属集群
            String clusterName = discoveryProperties.getClusterName();
            // 要请求的服务的名称
            String targetInstanceName = lb.getName();
            // 服务相关Api
            NamingService namingService = discoveryProperties.namingServiceInstance();
            // 获取目标服务所有实例(健康的实例)
            List<Instance> instances = namingService.selectInstances(targetInstanceName, true);
            List<Instance> versionMatchInstances = instances;
            // 获得targetVersion的实例
            if(StringUtils.isNotBlank(targetVersion)){
                versionMatchInstances = instances.stream()
                        .filter(instance -> {
                            return Objects.equals(instance.getMetadata().get(SELF_VERSION),TARGET_VERSION);
                        }).collect(Collectors.toList());
                if(CollectionUtils.isEmpty(versionMatchInstances)){
                    log.warn("未找到元数据匹配的目标实例!请检查配置。targetVersion = {}, instance = {}", targetVersion, instances);
                    return null;
                }
            }

            // 从元数据匹配的实例中获得同集群实例
            List<Instance> clusterMetadataMatchInstances = versionMatchInstances;
            if(StringUtils.isNotBlank(clusterName)){
                clusterMetadataMatchInstances =  versionMatchInstances.stream()
                        .filter(instance -> {
                            return Objects.equals(instance.getClusterName(), clusterName);
                        }).collect(Collectors.toList());
                if(CollectionUtils.isEmpty(clusterMetadataMatchInstances)){
                    log.warn("发生跨集群调用。clusterName = {}, targetVersion = {}, clusterMetadataMatchInstances = {}", clusterName, targetVersion, clusterMetadataMatchInstances);
                    clusterMetadataMatchInstances = versionMatchInstances;
                }
            }
            // 根据权重从toBeChosenInstances选择一个实例
            return new NacosServer(ExtendsBalancer.getInstanceByWeight(clusterMetadataMatchInstances));
        } catch (NacosException e) {
            log.error("元数据同集群负载规则发生异常", e);
        }
        return null;
    }

    @Override
    public Server choose(Object key) {
        return choose((DynamicServerListLoadBalancer) getLoadBalancer(), key);
    }

    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {

    }
}
  • 修改RibbonConfiguration返回新的NacosClusterMetadataWeightedRule
@Configuration
public class RibbonConfiguration {

    @Bean
    public IRule ribbonRule(){
//        return new RoundRobinRule();
//        return new NacosWeightedRule();
//        return new NacosSameClusterWeightedRule();
        return new NacosClusterMetadataWeightedRule();
    }
}
  • 测试前置准备
    启动四个user-center实例,三个SH集群的,一个BJ集群的
    8082:SH,权重 1,self-version v1
    8083:SH,权重 4,self-version v1
    8084:SH,权重 1,self-version v2
    8085:BJ, 权重 1,self-version v1
    在这里插入图片描述
  • 测试
    请求http://localhost:8081/poem/testRibbon ,请求20
    8082:SH,权重 1,self-version v1,被访问2
    8083:SH,权重 4,self-version v1,被访问18
    8084:SH,权重 1,self-version v2,被访问0
    8085:BJ, 权重 1,self-version v1,被访问0
    符合预期!
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

4.饥饿加载

第一次调用user-center服务,速度都很慢。因为ribbon是懒加载模式,可以通过属性配置方式改成饥饿模式,这样第一次和后面再次请求的速度就没有区别了
application.yml

ribbon:
  # 饥饿加载
  eager-load:
    # 开启
    enabled: true
    # 哪些服务,英文逗号隔开
    clients: user-center
  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值