步子百科步子百科

ribbon什么意思(你要的负载均衡Ribbon)

大家好,什意思我是负载阿七。

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

一、负载均衡的什意思两种方式

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

1、服务器端负载均衡;

2、什意思客户端侧负载均衡;

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

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

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

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

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

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

public ShareDTO findById(Integer id) { Share share = this.shareMapper.selectByPrimaryKey(id); Integer userId = share.getUserId(); //用户中心的所有实例信息 List<ServiceInstance> instances = discoveryClient.getInstances("user-center");// String targetUrl = instances// .stream()// .map(instance -> instance.getUri().toString() + "/users/{ id}")// .findFirst()// .orElseThrow(() -> new IllegalArgumentException("当前没有实例对象")); List<String> 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,保存,再启动一次项目,这样就可以启动两个用户中心实例。如下图:

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

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

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

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

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

三、整合ribbon

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

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

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

第二步加注解:在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支持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,一起将代码优化的更好吧。

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