ribbon 默认负载均衡 是什么_你要的负载均衡Ribbon,来了

大家好,我是阿七。

在上一篇文章中,我们已经实现了内容中心总能够调用用户中心,那如何实现负载均衡呢?请听阿七为你娓娓道来。(没看到上篇文章的同学请戳这里:实战(一)nacos注册与发现)

一、负载均衡的两种方式

众所周知,在负载均衡领域一般有两种方式去实现,分别是:

1、服务器端负载均衡;

2、客户端侧负载均衡;

在单体架构时代,我们一般会部署多个实例在服务器上,然后使用nginx做负载均衡(nginx也是部署在服务器上的)。请求全部打在nginx上,nginx根据负载均衡策略将请求转发到不同的实例上。如下面这幅图所示:

ribbon 默认负载均衡 是什么_你要的负载均衡Ribbon,来了_第1张图片

有同学会疑问了,为什么nginx可以抗住这么多的请求呢?因为它异步,非阻塞,使用了epoll 和大量的底层代码优化,哈哈跑题了,阿七后面再专门写文章说说nginx。

说完了服务器端负载均衡,那么什么是客户端负载均衡呢?且看下图:

ribbon 默认负载均衡 是什么_你要的负载均衡Ribbon,来了_第2张图片

在这幅图中,内容中心是要调用用户中心的,那么内容中心相对于用户中心就是客户端,对吧,这个应该可以理解。现在我们已经可以通过DiscoveryClient来获取用户中心的多个实例,如果我们在内容中心自己写一个负载均衡规则,然后交给RestTemplate来请求一个用户中心实例,这样就实现了客户端负载均衡了。说干就干,咱这就来写一个负载均衡策略。

二、手写一个客户端负载均衡器

第一步,修改代码:之前是调用findFirst()默认返回第一个用户中心实例,现在注释掉了,改成随机获取一个用户中心实例。

public ShareDTO findById(Integer id) {        Share share = this.shareMapper.selectByPrimaryKey(id);        Integer userId = share.getUserId();        //用户中心的所有实例信息        List instances = discoveryClient.getInstances("user-center");//        String targetUrl = instances//                .stream()//                .map(instance -> instance.getUri().toString() + "/users/{id}")//                .findFirst()//                .orElseThrow(() -> new IllegalArgumentException("当前没有实例对象"));        List targetUrls = instances                .stream()                .map(instance -> instance.getUri().toString() + "/users/{id}")                .collect(Collectors.toList());        //随机算法        int i = ThreadLocalRandom.current().nextInt(targetUrls.size());        String targetUrl = targetUrls.get(i);        log.info("请求的目标地址:{}",targetUrl);        //根据userId查询用户信息        UserDTO userDTO = this.restTemplate.getForObject(targetUrl, UserDTO.class, userId);        ShareDTO shareDTO = new ShareDTO();        BeanUtils.copyProperties(share, shareDTO);        shareDTO.setWxNickname(userDTO.getWxNickname());        return shareDTO;    }

第二步,测试:

1、我们先启动一个用户中心实例,端口为8888;然后修改端口号为8887,点击Edit Configurations,勾选Allow parallel run,保存,再启动一次项目,这样就可以启动两个用户中心实例。如下图:

ribbon 默认负载均衡 是什么_你要的负载均衡Ribbon,来了_第3张图片

2、启动内容中心实例,打开nacos看看

ribbon 默认负载均衡 是什么_你要的负载均衡Ribbon,来了_第4张图片

3、接口测试,访问http://localhost:8889/shares/1

69b868d5eba7c12f65f790096a362b3f.png

这时,我们去控制台看看:

ribbon 默认负载均衡 是什么_你要的负载均衡Ribbon,来了_第5张图片

哎,我们会发现,内容中心在随机调用用户中心的实例。说白了,负载均衡器就是给你一个list,你随机从中获取一个实例来调用。 但是我们这个代码还是比较简单的,只是模拟一下。如果我们每次进行服务调用都写这么一堆代码肯定也是不现实的。

那么下面我们就要ribbon进行重构咯,同学们继续往下看。

三、整合ribbon

在使用ribbon进行重构之前,我们肯定得了解什么是ribbon对吧。一句话阐述ribbon是Netflix开源的客户端侧负载均衡器。 其实,ribbon就是我们刚刚写的代码的一个组件,但是它封装的更好,提供了更多的负载均衡策略。

下面我们就来整合ribbon到我们的项目中。还记得三步骤吗?

第一步加依赖:因为nacos-discovery中已经集成了netflix-ribbon,所以这里就不要在单独集成了。

ribbon 默认负载均衡 是什么_你要的负载均衡Ribbon,来了_第6张图片

第二步加注解:在RestTemplate上加注解@LoadBalanced

@MapperScan("com.seven")@SpringBootApplicationpublic class ContentCenterApplication {    public static void main(String[] args) {        SpringApplication.run(ContentCenterApplication.class, args);    }    @Bean    @LoadBalanced    public RestTemplate restTemplate(){        return new RestTemplate();    }}

第三步写配置:在yml文件中写配置,这里不需要写。

第四步修改代码:ribbon会根据user-center去nacos中找到请求的真是路径。

public ShareDTO findById(Integer id) {        Share share = this.shareMapper.selectByPrimaryKey(id);        Integer userId = share.getUserId();        //根据userId查询用户信息        UserDTO userDTO = this.restTemplate                .getForObject("http://user-center/users/{userId}", UserDTO.class, userId);        ShareDTO shareDTO = new ShareDTO();        BeanUtils.copyProperties(share, shareDTO);        shareDTO.setWxNickname(userDTO.getWxNickname());        return shareDTO;    }

ok,到这里ribbon就整合完了。那有人会问,到这就结束了吗?那显然不是,阿七不是浮于表面的人,咱学习一个东西就要刨根问底。

四、ribbon组成

ribbon组件虽小,但是五脏俱全,它的组成都有哪些呢?贴心的我已经为大家准备好了。

ribbon 默认负载均衡 是什么_你要的负载均衡Ribbon,来了_第7张图片

同时,ribbon支持8种负载均衡策略,如下所示:

ribbon 默认负载均衡 是什么_你要的负载均衡Ribbon,来了_第8张图片

这里的Zone本意为地区、地带的意思,这里作机房的架子,也就是放服务器的机架。那我们实际上没有Zone的,也就是说默认采用的负载均衡策略是RoundRibonRule(轮训策略)。我们看一下源码,找到RoundRobinRule.java类,其中最主要的就是这一段,实际上就是取余得到访问的server的index。

private int incrementAndGetModulo(int modulo) {        int current;        int next;        do {            current = this.nextServerCyclicCounter.get();            next = (current + 1) % modulo;        } while(!this.nextServerCyclicCounter.compareAndSet(current, next));        return next;}

compareAndSet是AtomicInteger的方法,其实类似参数列表compareAndSwapInt(var1, var2, var5, var4),作为base的var1加上偏移量var2之后和var5比较是不是值相同,相同就update为var4. valueOffset是native的C的方法指针找到的地址。

public final boolean compareAndSet(int expect, int update) {    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);}

五、细粒度配置ribbon

那我们如何细粒度、自定义配置负载均衡策略呢?比如,内容中心想指定一种负载均衡策略来获取用户中心的实例。很简单,我们只需要加一下yml配置文件即可。

#指定服务名user-center:  ribbon:    #负载均衡策略的全路径    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

这样我们就修改ribbon负载均衡策略为RandomRule,也就是随机获取一个实例。

六、ribbon解饿加载

在实际代码运行中发现,当我们第一次请求http://localhost:8889/shares/1这个接口时,运行结果是很慢的,为什么呢?因为ribbon默认是懒加载。只有在下面代码第一次执行的时候,才会创建一个名叫user-center的ribbon client。

//根据userId查询用户信息        UserDTO userDTO = this.restTemplate                .getForObject("http://user-center/users/{userId}", UserDTO.class, userId);

我们可以通过修改yml配置文件来解决这个问题。

ribbon:  eager-load:    enabled: true    #多个用逗号隔开 user-center,xxx,yyy    clients: user-center

这样就可以第一次请求也变得很快啦。

好了ribbon就学习到这里了。但是,我们的代码还是存在很多问题的,那么下一篇我们将一起学习声明式http客户端--feign,一起将代码优化的更好吧。

喜欢的朋友记得点个关注吧,一起探讨,共同进步。

你可能感兴趣的:(ribbon,默认负载均衡,是什么,经过负载均衡图片加载不出来)