-
2022-04-06 18:41:44
1:首先呢,先上流程图
2:如何改变它得负载均衡规则(有两种方式)
(1):在配置类中注入一个新的IRule(举例为随机均衡策略,当然这种为全局进行配置,也就是该服务中得分所有规则都将采用配置得负载均衡规则。)
@Bean public IRule randomRule(){ return new RandomRule(); }
(2):在yml文件中进行配置(这样得配置可以针对某一个服务改变他得负载均衡策略,以下图中就只针对了userservice服务及逆行配置,也就是说在本服务下,只有user service采取随机负载均衡策略,其他得服务还是轮询负载均衡策略。)
userservice: ribbon: NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #负载均衡规则
3:饥饿加载
Ribbon默认是采用懒加载,即第一次访问时才会去创建LoadBalanceClient,请求时间会很长。 而饥饿加载则会在项目启动时创建,降低第一次访问的耗时,通过下面配置开启饥饿加载:
ribbon: eager-load: enabled: true # 开启饥饿加载 clients: # 指定饥饿加载得服务名称。(因为他是java.util.List<String> ,所以他不是一个,可以采用下面这种方式) - userservice
更多相关内容 -
springcloud【五】Ribbon 负载均衡策略切换与重写轮询算法
2020-09-27 17:56:14Ribbon 负载均衡的策略包括: 轮询(默认) 随机 加权 … Ribbon 简单地说就是负载均衡 + RestTemplate 调用,最终可以实现RPC的远程调用。 新版的eureka中已经集成了ribbon,所以就另外添加依赖。 一、...简介
Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。
Ribbon 负载均衡的策略包括:- 轮询(默认)
- 随机
- 加权
等 …
Ribbon 简单地说就是负载均衡 + RestTemplate 调用,实现RPC的远程调用。
新版的eureka中已经集成了ribbon,所以就另外添加依赖。
一、RestTemplate调用
主要分为get,post两类。
Entity比Object多了一些响应头信息。- getForObject() / getForEntity()
- postForObject() / postForEntity()
1 getForObject()
@GetMapping("/get/{id}") public CommonResult<Payment> getPayment(@PathVariable("id") Long id){ return restTemplate.getForObject(PAYMENT_URL+"/payment/get/"+id,CommonResult.class); }
2 getForEntity()
@GetMapping("/getEntity/{id}") public CommonResult<Payment> getEntity(@PathVariable("id") Long id){ ResponseEntity<CommonResult> entity= restTemplate.getForEntity(PAYMENT_URL+"/payment/get/"+id,CommonResult.class); if(entity.getStatusCode().is2xxSuccessful()){ return entity.getBody(); }else{ return new CommonResult<>(444,"存在异常!"); } }
3 postForObject()
@PostMapping("/create") public CommonResult<Payment> create(@RequestBody Payment payment){ log.info("*******消费者启动创建订单*******"); return restTemplate.postForObject(PAYMENT_URL+"/payment/create",payment, CommonResult.class); }
二、修改Ribbon的负载均衡策略
轮询是默认的负载均衡策略,8001,8002两个支付服务会轮流切换。现在我们将轮询改为随机。
1 目录结构
2 创建随机策略类
我们自定义的策略类不能放在@ComponentScan所扫描的当前包及其子包中,不然的话,相当于整个项目都采用了我们定制的策略,就达不到特殊定制的要求了。也就是说我们不能将自定义的策略类放在主启动类的同一级。
MySelfRule类放在新建的 org.example.myrule 目录下。
package org.example.myrule; import com.netflix.loadbalancer.IRule; import com.netflix.loadbalancer.RandomRule; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MySelfRule { @Bean public IRule muRule(){ return new RandomRule(); } }
3 主启动类中添加注解
name指定服务名称,configuration 指定负载均衡的策略。
@RibbonClient(name="CLOUD-PROVIDER-SERVICE",configuration = MySelfRule.class)
4 测试
重新调用 http://localhost/consumer/payment/get/3。
应该可以看到8001与8002不再依次出现了。
三、重写轮询算法
1 Ribbon源码
1)IRule接口
//IRule接口 public interface IRule{ /* * choose one alive server from lb.allServers or * lb.upServers according to key * * @return choosen Server object. NULL is returned if none * server is available */ //选择哪个服务实例 public Server choose(Object key); public void setLoadBalancer(ILoadBalancer lb); public ILoadBalancer getLoadBalancer(); }
2)RoundRobinRule 轮询源码
public class RoundRobinRule extends AbstractLoadBalancerRule { private AtomicInteger nextServerCyclicCounter; private static final boolean AVAILABLE_ONLY_SERVERS = true; private static final boolean ALL_SERVERS = false; private static Logger log = LoggerFactory.getLogger(RoundRobinRule.class); public RoundRobinRule() { nextServerCyclicCounter = new AtomicInteger(0); } public RoundRobinRule(ILoadBalancer lb) { this(); setLoadBalancer(lb); } public Server choose(ILoadBalancer lb, Object key) { if (lb == null) { log.warn("no load balancer"); return null; } Server server = null; int count = 0; while (server == null && count++ < 10) { List<Server> reachableServers = lb.getReachableServers(); List<Server> allServers = lb.getAllServers(); int upCount = reachableServers.size(); int serverCount = allServers.size(); if ((upCount == 0) || (serverCount == 0)) { log.warn("No up servers available from load balancer: " + lb); return null; } int nextServerIndex = incrementAndGetModulo(serverCount); server = allServers.get(nextServerIndex); if (server == null) { /* Transient. */ Thread.yield(); continue; } if (server.isAlive() && (server.isReadyToServe())) { return (server); } // Next. server = null; } if (count >= 10) { log.warn("No available alive servers after 10 tries from load balancer: " + lb); } return server; } /** * Inspired by the implementation of {@link AtomicInteger#incrementAndGet()}. * * @param modulo The modulo to bound the value of the counter. * @return The next value. */ private int incrementAndGetModulo(int modulo) { for (;;) { int current = nextServerCyclicCounter.get(); int next = (current + 1) % modulo; if (nextServerCyclicCounter.compareAndSet(current, next)) return next; } } @Override public Server choose(Object key) { return choose(getLoadBalancer(), key); } @Override public void initWithNiwsConfig(IClientConfig clientConfig) { } }
2、重写轮询算法
思路:
- rest 接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标
- 每次服务器重启后rest接口数从1开始
- 接口请求数达到整数最大值时变成0
目录结构如下:
1、在8001和8002支付模块中添加返回端口号的接口
/** * 返回当前模块的端口号信息 */ @GetMapping(value = "/lb") public String getPaymentLB(){ return serverPort; }
2 注释掉ribbon自带的负载均衡注解@LoadBalanced
3 添加接口LoadBalancer 返回调用的服务信息
package org.example.springcloud.lb; import org.springframework.cloud.client.ServiceInstance; import java.util.List; public interface LoadBalancer { ServiceInstance instances(List<ServiceInstance> serviceInstances); }
4 MyLB 实现 LoadBalancer 接口
package org.example.springcloud.lb; import org.springframework.cloud.client.ServiceInstance; import org.springframework.stereotype.Component; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; @Component public class MyLB implements LoadBalancer{ /**记录当前接口请求的次数*/ private AtomicInteger atomicInteger=new AtomicInteger(0); /** * 1、获取当前接口请求次数 * 2、使用 CAS(比较和替换) 自选锁的方式 ,判断当前值(this.atomicInteger.get())与期望值(current) * 是否一致。 * 若一致:代表当前没有其他用户请求该接口,可以修改 请求接口次数的值 atomicInteger + 1。 * 若不一致:存在其他用户操作该值,自旋等待直到当前值与期望值一致。 * @return 修改后的请求接口次数 */ private final int getAndIncrement(){ int current; int next; do{ current=this.atomicInteger.get(); next = current >= Integer.MAX_VALUE ? 0 : current+1; }while (!this.atomicInteger.compareAndSet(current,next)); System.out.println("*****第几次访问,次数next*****:"+next); return next; } /** * 实际调用服务器位置下标 = rest 接口第几次请求数 % 服务器集群总数量 * @param serviceInstances * @return 下次服务请求的端口号 */ @Override public ServiceInstance instances(List<ServiceInstance> serviceInstances) { int index = getAndIncrement() % serviceInstances.size(); return serviceInstances.get(index); } }
5 修改 OrderController
package org.example.springcloud.controller; import lombok.extern.slf4j.Slf4j; import org.example.springcloud.entities.CommonResult; import org.example.springcloud.entities.Payment; import org.example.springcloud.lb.LoadBalancer; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import org.springframework.web.client.RestTemplate; import javax.annotation.Resource; import java.net.URI; import java.util.List; @RestController @Slf4j @RequestMapping("/consumer/payment") public class OrderController { //public static final String PAYMENT_URL= "http://localhost:8001"; public static final String PAYMENT_URL= "http://CLOUD-PROVIDER-SERVICE"; @Resource private RestTemplate restTemplate; /**引入我们自己写的轮询接口*/ @Resource private LoadBalancer loadBalancer; /**用来获取服务信息*/ @Resource private DiscoveryClient discoveryClient; @PostMapping("/create") public CommonResult<Payment> create(@RequestBody Payment payment){ log.info("*******消费者启动创建订单*******"); return restTemplate.postForObject(PAYMENT_URL+"/payment/create",payment, CommonResult.class); } @GetMapping("/get/{id}") public CommonResult<Payment> getPayment(@PathVariable("id") Long id){ return restTemplate.getForObject(PAYMENT_URL+"/payment/get/"+id,CommonResult.class); } @GetMapping("/getEntity/{id}") public CommonResult<Payment> getEntity(@PathVariable("id") Long id){ ResponseEntity<CommonResult> entity= restTemplate.getForEntity(PAYMENT_URL+"/payment/get/"+id,CommonResult.class); if(entity.getStatusCode().is2xxSuccessful()){ return entity.getBody(); }else{ return new CommonResult<>(444,"存在异常!"); } } @GetMapping("/lb") public String getPaymentLB(){ /**返回服务列表信息*/ List<ServiceInstance> instances=discoveryClient.getInstances("CLOUD-PROVIDER-SERVICE"); /**判断服务是否存在*/ if(instances == null || instances.size()<=0){ return null; } /**获取负载均衡(轮询)后的服务信息*/ ServiceInstance serviceInstance = loadBalancer.instances(instances); /**获取服务地址*/ URI uri = serviceInstance.getUri(); /**接口调用*/ return restTemplate.getForObject(uri+"/payment/lb",String.class); } }
6 测试
接口测试:http://localhost/consumer/payment/lb
可以看到8001与8002交替出现。控制台打印的请求次数信息。
-
Ribbon修改默认负载均衡策略
2022-07-17 10:27:44如果要使用自定义的负载均算法,一般就自己创建一个类,然后继承AbstractLoadBalancerRule抽象类,然后写相应的负载均衡逻辑。最后再使用上面的两种方法,要么在配置类中创建该对象并注册进容器,要么在yml文件中...第一种方式 使用配置类
如果这个配置类使用
@Configuration
注解并写在主启动类能够进行包扫描的范围中,那么调用其他微服务,一律使用这一种负载均衡算法。当然也可以写在主启动所在包的外面配置类的内容如下:
package com.hs.rule; import com.netflix.loadbalancer.IRule; import com.netflix.loadbalancer.RandomRule; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @Description: 将ribbon默认的负载均衡方式改为随机的 * @Author 胡尚 * @Date: 2022/6/9 11:10 * @Version 1.0 */ @Configuration public class MyRuleConfig { @Bean public IRule iRule(){ return new RandomRule(); } }
然后在主启动类中指定调用某些服务的时候使用我们配置的负载均衡算法
@SpringBootApplication // 如果只有一个服务就可以直接使用@RibbonClient注解,而不需要@RibbonClients。 // name是写服务提供方的服务名 configuration是指定我们上面创建的配置类 @RibbonClients(value = { @RibbonClient(name = "stock-service", configuration = MyRuleConfig.class) }) public class OrderApplication { public static void main(String[] args) { SpringApplication.run(OrderApplication.class, args); } }
第二种方式 使用yaml配置
修改的这种负载均衡算法是使用nacos设置的权重方式
# 使用配置文件的方式修改ribbon默认的负载均衡算法 stock-service: ribbon: NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule
接下来进行测试,首先在nacos管理平台上面修改订单服务stock的权重
然后再进行调用,就可以发现测试效果了。
如果要使用自定义的负载均算法,一般就自己创建一个类,然后继承AbstractLoadBalancerRule抽象类,然后写相应的负载均衡逻辑。最后再使用上面的两种方法,要么在配置类中创建该对象并注册进容器,要么在yml文件中指定全路径
-
Ribbon的负载均衡策略
2021-03-30 20:58:08一、Ribbon负载均衡 如上图所示,负载均衡就是避免单个服务的实例处理大批量请求而导致其他实例空闲,造成资源浪费。负载均衡分为客户端、服务端的负载均衡,它们最大的区别在于维护服务器的清单保存的位置不同,...一、Ribbon负载均衡
如上图所示,负载均衡就是避免单个服务的实例处理大批量请求而导致其他实例空闲,造成资源浪费。负载均衡分为客户端、服务端的负载均衡,它们最大的区别在于维护服务器的清单保存的位置不同,如:Ribbon属于客户端负载均衡,客户端根据注册中心的服务实例的状态,判定是否访问某个实例。那么判定访问哪个实例,就是负载均衡策略要做的事。Ribbon的负载均衡有三个主要接口:com.netflix.loadbalancer.IPing(服务的检测)、com.netflix.loadbalancer.IRule(定义均衡策略)、com.netflix.loadbalancer.ILoadBalancer(根据IRule请求实例)。
二、Ribbon的负载均衡策略
如上图所示,是IRule的类图。如果需要自定义均衡策略,则继承com.netflix.loadbalancer.AbstractLoadBalancerRule抽象类。如下图所示是Ribbon的7种均衡策略。默认是com.netflix.loadbalancer.ZoneAvoidanceRule(综合判定来选择服务实例)。
三、自定义负载均衡策略
注意:自定义均衡策略,则继承com.netflix.loadbalancer.AbstractLoadBalancerRule抽象类。覆写代码参考该抽象类的实现类。如下代码所示。核心代码是chooseIndex(int reachableCount)方法。
package com.common.instance.gateway.config.ribbon; import com.log.util.LogUtil; import com.netflix.client.config.IClientConfig; import com.netflix.loadbalancer.AbstractLoadBalancerRule; import com.netflix.loadbalancer.ILoadBalancer; import com.netflix.loadbalancer.Server; import org.springframework.context.annotation.Configuration; import java.util.ArrayList; import java.util.List; /** * @description 自定义Ribbon负载均衡策略 * @author TCM * @version 1.0 * @date 2021/4/1 10:58 **/ @Configuration public class MyRibbonBalancerRule extends AbstractLoadBalancerRule { // 所有服务实例 private volatile int total; // 实例的索引 private volatile int index; // 可用的所有实例 List<Server> upList = new ArrayList<>(); public Server choose(ILoadBalancer lb, Object key) { LogUtil.info(String.format("lb=%s,key=%s", lb, key.toString())); // 负载均衡器是否为null if (lb == null) { return null; } Server server = null; while (server == null) { // 当前线程是否已中断 if (Thread.interrupted()) { return null; } // 获取负载均衡器中的所有服务实例 List<Server> allList = lb.getAllServers(); int serverCount = allList.size(); if (serverCount == 0) { return null; } if (total == 0) { // 可用的所有实例 upList = lb.getReachableServers(); } // 修改当前索引 chooseIndex(lb.getReachableServers().size()); // 根据索引获取服务 server = upList.get(index); if (server == null) { Thread.yield(); continue; } if (server.isAlive()) { return (server); } server = null; Thread.yield(); } return server; } // 修改当前索引 protected void chooseIndex(int reachableCount) { if (total < 3) { if (upList.size() != reachableCount) { index = 0; } total ++; } else { total = 0; index ++; if (index >= reachableCount) { index = 0; } } } @Override public Server choose(Object key) { return choose(getLoadBalancer(), key); } @Override public void initWithNiwsConfig(IClientConfig clientConfig) { } }
gateway的yml配置内容:ribbon.NFLoadBalancerRuleClassName: com.common.instance.gateway.config.ribbon.MyRibbonBalancerRule
# hystrix开启 feign: hystrix: enabled: true hystrix: command: default: # default全局有效,service id指定应用有效 execution: timeout: # true则超时根据熔断超时,false则ribbon控制 enabled: true isolation: thread: timeoutInMilliseconds: 4000 # 断路器超时时间,默认1000ms # ribbon配置 ribbon: OkToRetryOnAllOperations: true # 所有请求重试,默认false ReadTimeout: 3000 # 负载均衡超时时间,默认值5000ms ConnectTimeout: 1000 # 请求连接的超时时间,默认值2000ms MaxAutoRetries: 0 # 当前实例的重试次数,默认0 MaxAutoRetriesNextServer: 2 # 切换实例的重试次数,默认1 NFLoadBalancerRuleClassName: com.common.instance.gateway.config.ribbon.MyRibbonBalancerRule spring: cloud: gateway: # gateway发现该eureka下的所有服务 discovery: locator: enabled: false lowerCaseServiceId: true default-filters: - name: Hystrix args: name: default fallbackUri: 'forward:/fallback/global' routes: - id: ${application.name.instance-demo} predicates: - Path=/${application.name.instance-demo}/** uri: lb://${application.name.instance-demo}
gateway路由接口代码:
package com.common.instance.demo.controller; import com.common.instance.demo.core.Response; import com.common.instance.demo.service.RibbonService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; /** * @description ribbon负载均衡策略 * @author TCM * @version 1.0 * @date 2021/3/29 20:30 **/ @RestController @RequestMapping("/ribbon") @Api(tags = "Ribbon测试") public class RibbonController { @Resource private RibbonService ribbonService; @GetMapping("/rule") @ApiOperation("Ribbon策略") public Response<String> testRibbonRule(int mills) { try { Thread.sleep(mills); } catch (InterruptedException e) { e.printStackTrace(); } String result = ribbonService.testRibbonRule(); return Response.success(result); } }
package com.common.instance.demo.service.impl; import com.common.instance.demo.service.RibbonService; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; /** * @author TCM * @version 1.0 * @description TODO * @date 2021/3/29 20:41 **/ @Service public class RibbonServiceImpl implements RibbonService { @Value("${spring.cloud.client.ip-address}") private String ipAddress; @Value("${server.port}") private String port; @Override public String testRibbonRule() { return String.format("Ribbon均衡策略测试:主机%s:%s", ipAddress, port); } }
测试结果:
连续3次:发生熔断
{ "success": false, "code": "500", "message": "网络发生异常,请稍后重试!", "tip": "操作失败", "data": null }
连续3次:9012端口
{ "success": true, "code": "200", "message": "操作成功", "tip": "操作成功", "data": "Ribbon均衡策略测试:主机192.168.1.107:9012" }
连续3次:9013端口
{ "success": true, "code": "200", "message": "操作成功", "tip": "操作成功", "data": "Ribbon均衡策略测试:主机192.168.1.107:9013" }
-
配置类和配置文件修改ribbon的负载均衡策略
2022-01-27 21:21:26配置类修改负载均衡策略 第一步:新建一个不会被@ComponentScan组件扫描到的包,如:com.ribbon 第二步:在该包下新建自己的负载均衡算法的规则类 package ribbon; import com.netflix.loadbalancer.IRule; ... -
springcloud ribbon 配置负载均衡策略以及自定义策略
2021-12-14 10:30:14Ribbon 的负载均衡策略 策略类 命名 说明 RandomRule 随机策略 随机选择 Server RoundRobinRule 轮训策略 按顺序循环选择 Server RetryRule 重试策略 在一个配置时问段内当选择 Server 不成功,则一直尝试选择一个... -
【SpringCloud】Ribbon 负载均衡策略设置以及点对点直连
2022-05-11 20:32:051、Ribbon 负载均衡策略设置 示例项目接上一篇文章:【SpringCloud】什么是 Ribbon 以及入门案例_风落_的博客-CSDN博客 1.1、全局 在启动类或配置类中注入负载均衡策略对象。所有服务请求均使用该策略。 @Bean ... -
[踩坑日寄]Ribbon负载均衡失效
2022-07-02 23:19:57今天学习Ribbon自定义负载均衡的时候按照文档写了一个自定义负载均衡配置类注册为Bean并在启动了上加入@RibbonClient注解 但是启动之后发现还是默认的轮询策略,自定义的策略失效了,后来找了半天发现@RibbonClient... -
Ribbon负载均衡的原理
2022-04-06 23:58:30Ribbon 依赖 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.12.RELEASE</version> &... -
Spring Cloud Ribbon负载均衡策略详解
2020-12-30 07:05:51一,IRule接口IRule接口定义了选择负载均衡策略的基本操作。通过调用choose()方法,就可以选择具体的负载均衡策略。// 选择目标服务节点Serverchoose(Object var1);// 设置负载均衡策略void setLoadBalancer... -
ribbon负载均衡策略详解
2021-05-14 14:25:37IRule接口定义了选择负载均衡策略的基本操作。通过调用choose()方法,就可以选择具体的负载均衡策略。 // 选择目标服务节点 Server choose(Object var1); // 设置负载均衡策略 void setLoadBalancer... -
Spring Cloud Ribbon负载均衡策略自定义配置
2018-05-26 15:08:14Spring Cloud Ribbon负载均衡策略自定义配置 上一篇文章Spring Cloud Ribbon 消费服务 实现客户端负载均衡中使用ribbon实现了客户端负载均衡,我们知道ribbon默认负载均衡算法是轮询,ribbon实际上提供了很多负载... -
Ribbon负载均衡策略以及重写
2021-08-18 22:13:09Ribbon 负载均衡策略 Ribbon 的负载均衡策略是由 IRule 接口定义, 该接口由如下实现: 在jar包:...如果要切换负载均衡策略: @Bean public IRule iRule(){ return new RoundRobinRule() -
spring cloud ribbon 更换负载均衡的算法
2021-12-13 17:17:05Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现。通过Spring Cloud的封装,可以让我们轻松地将面向服务的REST模版请求自动转换成客户端负载均衡的服务调用。Spring Cloud ... -
Ribbon负载均衡策略以及使用(基于springcloud G版本)
2021-12-12 12:20:20@LoadBalanced //通过resttemplate结合eureka中的ribbon进行远程调用并实现负载均衡(封装ribbon + eureka + restTemplate) @Bean //如果不加@LoadBalanced 注解就是单独的两个模块之间进行远程调用,如果想通过... -
Spring Cloud 学习5-Ribbon负载均衡实例
2022-07-02 09:40:24在系统并发量高,单个服务器处理能力能力达到最大的情况下,一般会用多台...将用户大量的请求按照一定的均衡算法平摊分配到多个服务器上运行,就是负载均衡。使用服务器集群和负载均衡可以给我们带来的几个好处...