您的位置:首页技术文章
文章详情页

Spring Cloud调用Ribbon的步骤

浏览:32日期:2023-07-14 11:44:24
目录一、简介1. 是什么2. 负载均衡2.1 集中式LB2.2 进程内LB二、实验1. RestTemplate1.1 加入到IOC容器1.2 RestTemplate 远程调用1.3 配置文件2.1 修改负载均衡算法3.1 IRule3.2 AbstractLoadBalancerRule3.3 RoundRobinRule2. LoadBalancer3. 负载均衡算法源码一、简介1. 是什么 Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。 简单的说,Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法和服务调用。 官方文档 目前已进入维护状态,以后可以通过Open Feign作为替代方案 负载均衡+RestTemplate,实现负载均衡调用2. 负载均衡 负载均衡(Load Balance,LB),即将用户的请求平摊到多个服务上,从而达到系统的高可用(HA) 负载均衡分为两种方案:集中式LB、进程内LB 2.1 集中式LB 即服务方和消费方之间使用独立的LB设施,由该设备负责把访问请求通过某种策略转发至服务提供方。 比如说Nginx、Gateway、zuul等 2.2 进程内LB 负载均衡的算法集成到消费方,消费方在注册中心中获取可用地址,然后通过LB算法选择出一个合适的服务器。 Ribbon就属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务方提供的地址。二、实验

Ribbon集成在spring-cloud-starter-netflix-eureka-client中,可以参考eureka的使用。在此基础上简单修改一下,就可以完成服务调用及负载均衡

1. RestTemplate 官网 通过RestTemplate,可以实现HttpClient的功能,只需要给它提供一个url及返回类型,即可实现远程方法调用。 1.1 加入到IOC容器

首先,将其加入到IOC容器中。@LoadBalanced表示开启负载均衡。

@Configurationpublic class ApplicationContextConfig { @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); }}1.2 RestTemplate 远程调用

@Slf4j@RestController@RequestMapping('/order')public class OrderController { @Autowired RestTemplate restTemplate; // 在ioc容器中获取 @Value('${payment.url}') String paymentUrl; // 远程调用的URL,保存在配置文件中,解耦 @GetMapping('/payment/get/{id}') public CommonResult<Payment> getPaymentById(@PathVariable('id') Long id) { CommonResult<Payment> result = restTemplate.getForObject(paymentUrl + '/payment/get/' + id, CommonResult.class); // get方法调用,并且返回封装成 CommonResult 类型 log.info('Order 查询 Payment,id:' + id); return result; }}

也可以使用getForEntity()方法,获取整个响应,自己在响应中获取想要的内容。

@GetMapping('/payment/getEntity/{id}') public CommonResult<Payment> getPaymentEntityById(@PathVariable('id') Long id) { ResponseEntity<CommonResult> entity = restTemplate.getForEntity(paymentUrl + '/payment/get/' + id, CommonResult.class); log.info('获取到的信息是:' + entity.toString()); log.info('获取到的StatusCode是:' + entity.getStatusCode()); log.info('获取到的StatusCodeValue是:' + entity.getStatusCodeValue()); log.info('获取到的Headers是:' + entity.getHeaders()); if (entity.getStatusCode().is2xxSuccessful()) { log.info('查询成功:' + id); return entity.getBody(); } else { log.info('查询失败:' + id); return new CommonResult<>(CommonResult.FAIlURE, '查询失败'); } }

如果使用post方法,就将get改成post就好了。

1.3 配置文件

url,可以写具体的地址,表示直接调用该地址;也可以写在eureka的服务名,首先在eureka中获取该服务的所有地址,再通过LB选择一个。

payment: url: 'http://CLOUD-PAYMENT-SERVICE'2. LoadBalancer

上面通过@LoadBalanced开启了负载均衡。默认使用轮询算法,也可以修改成其他算法。

Class 算法 com.netflix.loadbalancer.RoundRobinRule 轮询,默认算法 com.netflix.loadbalancer.RandomRule 随机算法,通过产生随机数选择服务器 com.netflix.loadbalancer.RetryRule 先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内会进行重试,获取可用的服务 WeightedResponseTimeRule 对RoundRobinRule的扩展,响应速度越快的实例选择权重越大,越容易被选择 BestAvailableRule 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务 AvailabilityFilteringRule 先过滤掉故障实例,再选择并发较小的实例 ZoneAvoidanceRule 默认规则,复合判断server所在区域的性能和server的可用性选择服务器

2.1 修改负载均衡算法

如果想让该算法只针对某个服务,则不能将其放在ComponentScan够得到的地方,否则会修改所有服务的负载均衡算法。因此,最好在外面再新建一个package,用来放这个LB

@Configurationpublic class MyRule { @Bean public IRule rule() { return new RandomRule(); }}

在主启动类上,标识一下服务与算法直接的映射关系

@SpringBootApplication@EnableEurekaClient@RibbonClient(name = 'CLOUD-PAYMENT-SERVICE', configuration = MyRule.class)public class OrderApplication80 { public static void main(String[] args) { SpringApplication.run(OrderApplication80.class, args); }}

如果嫌这种方法麻烦,也可以使用配置文件的方法

CLOUD-PAYMENT-SERVICE: # 服务名称 ribbon: NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 算法选择3. 负载均衡算法源码

以默认的RoundRobinRule作为阅读的源码,其他的源码基本上很类似,只是修改的选择服务器的代码。

Spring Cloud调用Ribbon的步骤

RoundRobinRule父类为AbstractLoadBalancerRule,AbstractLoadBalancerRule实现了接口IRule 3.1 IRule

public interface IRule { Server choose(Object var1); // 选择服务器,最重要的方法 void setLoadBalancer(ILoadBalancer var1); ILoadBalancer getLoadBalancer();}3.2 AbstractLoadBalancerRule

基本没什么作用,只是将公共的部分提取了出来进行实现。

public abstract class AbstractLoadBalancerRule implements IRule, IClientConfigAware { private ILoadBalancer lb; // ILoadBalancer接口,主要的功能就是获取当前服务器的状态、数量等,为负载均衡算法提供计算的参数 public AbstractLoadBalancerRule() { } public void setLoadBalancer(ILoadBalancer lb) { this.lb = lb; } public ILoadBalancer getLoadBalancer() { return this.lb; }}3.3 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() { this.nextServerCyclicCounter = new AtomicInteger(0); // 初始化 } public RoundRobinRule(ILoadBalancer lb) { // 设置LoadBalancer this(); this.setLoadBalancer(lb); } public Server choose(ILoadBalancer lb, Object key) { // 最重要的方法,选择服务器并返回 // 下面贴出来 } private int incrementAndGetModulo(int modulo) { // 对计数器进行修改,并返回一个选择值,是轮询算法的实现 // 下面贴出来 } public Server choose(Object key) { // 接口的方法,在该类中调用了另一个方法实现 return this.choose(this.getLoadBalancer(), key); } public void initWithNiwsConfig(IClientConfig clientConfig) {}}

简单来说,该方法就是根据目前的状态,选择一个服务器返回。

public Server choose(ILoadBalancer lb, Object key) { if (lb == null) { // 如果没有LoadBalancer,那就不白费功夫了 log.warn('no load balancer'); return null; } else { Server server = null; int count = 0; while(true) { if (server == null && count++ < 10) { // 尝试十次,如果还找不到server就放弃了 List<Server> reachableServers = lb.getReachableServers(); // 通过LB获取目前所有可获取的服务器 List<Server> allServers = lb.getAllServers(); // 获取实际上的所有服务器 int upCount = reachableServers.size(); // 获取目前可获得的服务器数量 int serverCount = allServers.size(); // 所有服务器的数量,这是取余的除数 if (upCount != 0 && serverCount != 0) { // 如果目前有服务器且服务器可用 int nextServerIndex = this.incrementAndGetModulo(serverCount); // 最关键的选择算法,将目前的的服务器数量放进去,返回一个选择的号码 server = (Server)allServers.get(nextServerIndex); // 根据下标将服务器取出来 if (server == null) { // 如果取出来为空,表示目前不可用,则进入下一个循环 Thread.yield(); } else { if (server.isAlive() && server.isReadyToServe()) { // 如果该服务器活着且可以被使用,则直接将其返回return server; } server = null; } continue; } log.warn('No up servers available from load balancer: ' + lb); return null;}if (count >= 10) { log.warn('No available alive servers after 10 tries from load balancer: ' + lb);}return server; } } }

简单来说,就是将目前的计数器+1取余,获取一个下标,并返回。为了避免高并发的危险,采用CAS的方法进行设置。

private int incrementAndGetModulo(int modulo) { int current; int next; do { current = this.nextServerCyclicCounter.get(); // 获取当前值 next = (current + 1) % modulo; // +1取余 } while(!this.nextServerCyclicCounter.compareAndSet(current, next)); // CAS,如果成功就返回,失败就再来 return next; }

以上就是Spring Cloud调用Ribbon的步骤的详细内容,更多关于Spring Cloud调用Ribbon的资料请关注好吧啦网其它相关文章!

标签: Spring
相关文章: