热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

SpringCloud笔记Ribbon负载均衡服务调用(八)

1.概述SpringCloudRibbon是基于NetflixRibbon实现的一套客户端负载均衡工具。Ribbon客户端组件提供了一系列完善的配置项,如连接超时&

1.概述

Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡工具。Ribbon客户端组件提供了一系列完善的配置项,如连接超时,重试等。简单地说,就是在配置文件中列出Load Balancer后面所有的机器,Ribbon会自动帮我们基于某种规则(如轮询,随机连接等)去连接这些机器,我们很容易使用Ribbon实现自定义的负载均衡算法。

Ribbon官网:https://github.com/Netflix/ribbon,根据官网信息,可以看到Ribbon现在进入了维护模式,替换方案是Loadbalancer。

负载均衡的表现就是,将用户的请求分摊到多个服务器上,从而达到高可用的目的。常见的负载均衡软件有:Nginx、LVS、硬件F5等。

Nginx是服务器端负载均衡,客户端的请求都发给Nginx,Nginx实现分发,将请求发送到不同的服务器上。

Ribbon是客户端负载均衡,在调用微服务接口的时候,会在注册中心拿到注册信息服务列表缓存到本地JVM,在客户端通过某种规则,确定请求的链接,发送请求进行调用。

集中式LoadBalancer:在服务的消费方和提供方之间使用的独立LoadBalancer设备(可以是硬件,如F5,也可以是软件,如Nginx),由该设施负责把请求通过某种策略转发至服务的提供方。

进程内LoadBalancer:将LoadBalancer集成到消费方,消费者从服务注册中心获取到可用地址,自己再从这些地址中,选择一个作为要访问的服务器。Ribbon就属于进程内LoadBalancer,它只是一个类库,集成消费者进程,消费者通过它获取服务提供方的地址。


2.Ribbon负载均衡演示

之前,我们好像并没有加入Ribbon的依赖,也实现了负载均衡,其实在spring-cloud-starter-netflix-eureka-client坐标下,是引入了spring-cloud-starter-netflix-ribbon的,所以,我们仅仅只需要添加一个@LoadBalanced就可以实现负载均衡。

RestTemplate常见的方法有getForObject()、getForEntity()、postForObject()、postForEntity()方法。其中*ForObject()方法返回对象为响应体中数据转换成的对象,基本理解为JSON。*ForEntity()方法返回对象是ResponseEntity对象,包含了响应中的信息,比如响应头,响应状态码,响应体等。

将cloud-eureka-server7001、cloud-eureka-server7002的配置文件改成集群模式,让7001和7002互相注册,将cloud-provider-payment8001、cloud-provider-payment8002的配置文件改成集群模式,将cloud-consumer-order80的配置文件改成集群模式。在cloud-consumer-order80模块中的OrderController里,添加两个方法,分别调用getForEntity()和postForEntity()方法。

@GetMapping("/consumer/payment/getForEntity/{id}")
public CommonResult getPaymentById2(@PathVariable("id") Long id) {ResponseEntity entity = restTemplate.getForEntity(PAYMENT_URL + "/payment/get/" + id, CommonResult.class);System.out.println("status code=" + entity.getStatusCode());System.out.println("headers=" + entity.getHeaders());if (entity.getStatusCode().is2xxSuccessful()) {return entity.getBody();} else {return new CommonResult(404, "查找失败");}
}
@GetMapping("/consumer/payment/create2")
public CommonResult create2(Payment payment) {return restTemplate.postForEntity(PAYMENT_URL + "/payment/create", payment, CommonResult.class).getBody();
}

先启动Eureka注册中心,再启动两个生产者,最后启动消费者,浏览器发送请求来调用*ForEntity()方法进行测试。在浏览器端,可以看到port的值,不断在8001和8002之前进行切换。


3.Ribbon核心组件IRule

IRule是一个接口,它有多个实现类,分别代表不同的负载均衡策略。使用IDEA生成一下它的子类的关系图(点击IRule,按下Ctrl+Alt+B获取所有子类,按下Ctrl+A全选子类,按下Ctrl+Alt+U,选择Java Class Diagrams)。


  • com.netflix.loadbalancer.RoundRobinRule:轮询
  • com.netflix.loadbalancer.RandomRule:随机
  • com.netflix.loadbalancer.RetryRule:先按照RoundRobinRule策略获取服务,如果获取服务失败,在指定时间内重试,获取可用服务
  • com.netflix.loadbalancer.WeigthResponseTimeRule:对RoundRobinRule扩展,根据响应速度进行选择,响应速度越快,优先级越高
  • com.netflix.loadbalancer.BestAvailableRule:先过滤掉多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
  • com.netflix.loadbalancer.AvailabilityFilterRule:先过滤掉故障实例,再选择并发最小的实例
  • com.netflix.loadbalancer.ZoneAvoidanceRule:默认规则,符合判断server所在区域的性能和server可用性选择服务器

在这里提到,自定义Ribbon Client的时候,我们不能将自定义的配置类放在@ComponentScan所扫描的包及其子包下,否则我们自定义的配置类会被所有Ribbon Client所共享,达不到特殊化定制的目的了。

新建MyRule配置类,注意包名,这里我放在com.atguigu.rule包下,假设我改成了RandomRule。

package com.atguigu.rule;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 MyRule {@Beanpublic IRule iRule() {return new RandomRule();}
}

告诉主启动类,使用的哪一个Ribbon Client。

package com.atguigu.springcloud;import com.atguigu.rule.MyRule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;@SpringBootApplication
@EnableEurekaClient
// 自定义Ribbon规则适用于哪个服务(服务名要大写),自定义Ribbon规则对应的类
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE", cOnfiguration= MyRule.class)
public class OrderMain80 {public static void main(String[] args) {SpringApplication.run(OrderMain80.class, args);}
}

通过浏览器进行访问,查看浏览器给出的端口信息,发现自定义的随机规则生效了。


4.Ribbon负载均衡算法

负载均衡算法:REST接口第几次请求数字%服务器集群总数量=实际调用服务器位置下标,每次重启服务器REST接口计数从1开始。找一个IRule接口的实现类,这里选择RoundRobinRule类,查看它的实现。

public Server choose(ILoadBalancer lb, Object key) {if (lb == null) {log.warn("no load balancer");return null;} else {Server server = null;int count = 0;while(true) {// 获取server,如果获取失败,进行重试,如果10次后,还没获取到server,就报错if (server == null && count++ <10) {// 获取所有状态是up的serverList reachableServers = lb.getReachableServers();// 获取所有serverList allServers = lb.getAllServers();int upCount = reachableServers.size();int serverCount = allServers.size();if (upCount != 0 && serverCount != 0) {// 计算出要访问的server下标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;}}
}private int incrementAndGetModulo(int modulo) {int current;int next;// 利用cas和自旋锁,获取next的值do {current = this.nextServerCyclicCounter.get();next = (current + 1) % modulo;} while(!this.nextServerCyclicCounter.compareAndSet(current, next));return next;
}

下面手写一个本地的负载均衡。

cloud-eureka-server7001和cloud-eureka-server7002集群方式启动。

在cloud-provider-payment8001和cloud-provider-payment8002的controller里,加入如下方法,用于返回访问接口。

@GetMapping("/payment/loadbalance")
public String getLoadBalancePort() {return serverPort;
}

修改cloud-consumer-order80模块。

将配置类中的@LoadBalanced注解去掉,添加LoadBalancer.java接口和MyLoadBalancer.java实现类,同样,在MyLoadBalancer.java里用到cas和自旋锁。

package com.atguigu.springcloud.balance;import org.springframework.cloud.client.ServiceInstance;import java.util.List;public interface LoadBalancer {ServiceInstance instances(List serviceInstanceList);
}

package com.atguigu.springcloud.balance;import org.springframework.cloud.client.ServiceInstance;
import org.springframework.stereotype.Component;import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;@Component
public class MyLoadBalancer implements LoadBalancer {private AtomicInteger atomicInteger = new AtomicInteger(0);@Overridepublic ServiceInstance instances(List serviceInstanceList) {int index = getAndIncrement() % serviceInstanceList.size();return serviceInstanceList.get(index);}public final int getAndIncrement() {int current, 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;}
}

在OrderController.java里注入MyLoadBalancer实例和DiscoveryClient实例。添加一个请求方法,输出负载均衡后需要访问的端口号,测试负载均衡效果是否有效。

@GetMapping("/consumer/payment/loadbalance")
public String getPaymentLoadBalance() {// 通过discoveryClient,使用服务提供者对应的应用名称大写获取所有服务实例List instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");if (instances == null || instances.size() == 0) {return null;}// instances()方法拿到所有的服务实例,使用访问次数%服务实例数量求得目标服务的下标,返回下标对应的服务实例ServiceInstance instance = myLoadBalancer.instances(instances);URI uri = instance.getUri();// 获取这个实例的uri// /payment/loadbalance请求对应服务提供者controller中新加的映射方法,返回当前服务提供者的serverPort的值return restTemplate.getForObject(uri + "/payment/loadbalance", String.class);
}

启动Eureka注册中心集群,启动服务提供者,启动服务消费者,通过浏览器访问http://localhost/consumer/payment/loadbalance,可以看到端口号的轮询变化,此时我们自己的负载均衡轮询策略生效。


推荐阅读
  • 优化后的标题:深入探讨网关安全:将微服务升级为OAuth2资源服务器的最佳实践
    本文深入探讨了如何将微服务升级为OAuth2资源服务器,以订单服务为例,详细介绍了在POM文件中添加 `spring-cloud-starter-oauth2` 依赖,并配置Spring Security以实现对微服务的保护。通过这一过程,不仅增强了系统的安全性,还提高了资源访问的可控性和灵活性。文章还讨论了最佳实践,包括如何配置OAuth2客户端和资源服务器,以及如何处理常见的安全问题和错误。 ... [详细]
  • 网站访问全流程解析
    本文详细介绍了从用户在浏览器中输入一个域名(如www.yy.com)到页面完全展示的整个过程,包括DNS解析、TCP连接、请求响应等多个步骤。 ... [详细]
  • 本文介绍了 Go 语言中的高性能、可扩展、轻量级 Web 框架 Echo。Echo 框架简单易用,仅需几行代码即可启动一个高性能 HTTP 服务。 ... [详细]
  • 【实例简介】本文详细介绍了如何在PHP中实现微信支付的退款功能,并提供了订单创建类的完整代码及调用示例。在配置过程中,需确保正确设置相关参数,特别是证书路径应根据项目实际情况进行调整。为了保证系统的安全性,存放证书的目录需要设置为可读权限。值得注意的是,普通支付操作无需证书,但在执行退款操作时必须提供证书。此外,本文还对常见的错误处理和调试技巧进行了说明,帮助开发者快速定位和解决问题。 ... [详细]
  • 本文深入解析了Spring Cloud路由网关Zuul的核心功能及其典型应用场景。通过对方志朋老师教材的学习和实践,详细探讨了Zuul在微服务架构中的重要作用,包括请求路由、过滤器链管理以及服务动态扩展等关键特性。同时,结合实际案例,展示了Zuul在高并发和复杂业务场景下的应用优势,为读者提供了全面的技术参考。 ... [详细]
  • centos 7.0 lnmp成功安装过程(很乱)
    下载nginx[rootlocalhostsrc]#wgethttp:nginx.orgdownloadnginx-1.7.9.tar.gz--2015-01-2412:55:2 ... [详细]
  • 本文探讨了 TypeScript 中泛型的重要性和应用场景,通过多个实例详细解析了泛型如何提升代码的复用性和类型安全性。 ... [详细]
  • 小程序的授权和登陆
    小程序的授权和登陆 ... [详细]
  • 包含phppdoerrorcode的词条 ... [详细]
  • 本文详细解析了ASP.NET 2.0中的Callback机制,不仅介绍了基本的使用方法,还深入探讨了其背后的实现原理。通过对比Atlas框架,帮助读者更好地理解和应用这一机制。 ... [详细]
  • HTTP(HyperTextTransferProtocol)是超文本传输协议的缩写,它用于传送www方式的数据。HTTP协议采用了请求响应模型。客服端向服务器发送一 ... [详细]
  • Linux CentOS 7 安装PostgreSQL 9.5.17 (源码编译)
    近日需要将PostgreSQL数据库从Windows中迁移到Linux中,LinuxCentOS7安装PostgreSQL9.5.17安装过程特此记录。安装环境&#x ... [详细]
  • 本文详细介绍了 InfluxDB、collectd 和 Grafana 的安装与配置流程。首先,按照启动顺序依次安装并配置 InfluxDB、collectd 和 Grafana。InfluxDB 作为时序数据库,用于存储时间序列数据;collectd 负责数据的采集与传输;Grafana 则用于数据的可视化展示。文中提供了 collectd 的官方文档链接,便于用户参考和进一步了解其配置选项。通过本指南,读者可以轻松搭建一个高效的数据监控系统。 ... [详细]
  • 服务器部署中的安全策略实践与优化
    服务器部署中的安全策略实践与优化 ... [详细]
  • 在PHP中实现腾讯云接口签名,以完成人脸核身功能的对接与签名配置时,需要注意将文档中的POST请求改为GET请求。具体步骤包括:使用你的`secretKey`生成签名字符串`$srcStr`,格式为`GET faceid.tencentcloudapi.com?`,确保参数正确拼接,避免因请求方法错误导致的签名问题。此外,还需关注API的其他参数要求,确保请求的完整性和安全性。 ... [详细]
author-avatar
mobiledu2502858787
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有