SpringCloud(六)Ribbon的负载均衡(一)
Ribbon
硬件负载均衡的设备或是软件负载均衡的软件模块都会维护一个下挂可用的服务端清单,通过心跳检测来剔除故障的服务端节点以保证清单中都是可以正常访问的服务端节点。当客户端发送请求到负载均衡设备的时候,该设备按某种算法(比如线性轮询、按权重负载、按流量负载等)从维护的可用服务端清单中取出一台服务端端地址,然后进行转发。
而客户端负载均衡和服务端负载均衡最大的不同点在于上面所提到服务清单所存储的位置。在客户端负载均衡中,所有客户端节点都维护着自己要访问的服务端清单,而这些服务端端清单来自于服务注册中心,比如上一章我们介绍的Eureka服务端。同服务端负载均衡的架构类似,在客户端负载均衡中也需要心跳去维护服务端清单的健康性,默认会创建针对各个服务治理框架的Ribbon自动化整合配置,比如Eureka中的org.springframework.cloud.netflix.ribbon.eureka.RibbonEurekaAutoConfiguration,Consul中的org.springframework.cloud.consul.discovery.RibbonConsulAutoConfiguration。在实际使用的时候,我们可以通过查看这两个类的实现,以找到它们的配置详情来帮助我们更好地使用它。
什么是Ribbon
ribbon是netflixfa发布的一个负载均衡器,有助于控制Http和TCP客户端行为,在SprIngCloud中,Eureka一般配合Ribbon进行使用,Ribbon提供了客户端负载均衡功能,Ribbon利用从Eureka中读取到的服务信息,在调用服务节点提供的服务时,会合理的进行负载
在SpringCloud中可以将注册中心和Ribbon配合使用,Ribbon会自动的从注册中心中获取服务提供者的列表信息,并基于内置的负载均衡算法,请求服务。
Ribbon的主要作用
1 服务调用
基于Ribbon实现服务调用,是通过拉取到的所有服务列表组成(服务名-请求路径的)映射关系,借助RestTemplate最终进行调用
2 负载均衡
当有多个服务提供者时,Ribbon可以根据负载均衡算法自动的选择需要调用的服务地址
使用
1 在创建RestTemplate的时候,声明@LoadBalanced(rbbion提供的负载均衡的注解)注解
2 使用RestTemplate调用远程微服务,不需要拼接微服务的URL,以待请求的服务名替代IP地址
服务端负载均衡 nignx F5
客户端负载均衡
Ribbon是典型的客户端负载均衡服务器,Ribbon会获取服务的所有地址,根据内部的负载均衡的算法获取本次请求的有效地址
实例:
准备两个商品微服务(9001,9011)
在订单系统中远程以负载均衡的形式调用商品服务
``
server: port: 9001 spring: application: name: service-product #服务名称 datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/shops?useUnicode=true&characterEncoding=utf8 username: root password: 15192734369qwe jpa: database: mysql show-sql: true open-in-view: true #配置Eureka eureka: client: service-url: defaultZone: http://localhost:9000/eureka/,http://localhost:8000/eureka/ #多个EurekaServer之间用逗号分隔 instance: prefer-ip-address: true #使用IP地址进行注册 instance-id: ${spring.cloud.client.ip-address}:${server.port} #向注册中心注册服务ID lease-renewal-interval-in-seconds: 5 #发送心跳的间隔默认30s(单位为s) lease-expiration-duration-in-seconds: 60 #续约到期时间,默认90s(单位为s)
``
server: port: 9011 spring: application: name: service-product #服务名称 datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/shops?useUnicode=true&characterEncoding=utf8 username: root password: 15192734369qwe jpa: database: mysql show-sql: true open-in-view: true #配置Eureka eureka: client: service-url: defaultZone: http://localhost:9000/eureka/,http://localhost:8000/eureka/ #多个EurekaServer之间用逗号分隔 instance: prefer-ip-address: true #使用IP地址进行注册 instance-id: ${spring.cloud.client.ip-address}:${server.port} #向注册中心注册服务ID lease-renewal-interval-in-seconds: 5 #发送心跳的间隔默认30s(单位为s) lease-expiration-duration-in-seconds: 60 #续约到期时间,默认90s(单位为s)
在服务消费者里添加@LoadBalanced注解开启Ribbon自带的负载均衡功能
``
@SpringBootApplication @EntityScan("com.qqq.entity") public class OrderApplication { public static void main(String[] args) { SpringApplication.run(OrderApplication.class,args); } /** * 使用spring提供的RestTemplate发送http请求到商品服务 * 1 将RestTemplate对象交给容器管理 * 2 使用其方法完成操作 */ @LoadBalanced //Ribbon自带的负载均衡的注解 @Bean public RestTemplate restTemplate() { return new RestTemplate(); } ? ? }
``
package com.qqq.controller; ? import com.qqq.entity.Product; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; ? import java.util.List; ? @RestController @RequestMapping("/order") public class OrderController { //注入RestTemplate对象 @Autowired private RestTemplate restTemplate; /** * 注入DiscoveryClient * SpringCloud提供的获取元数据的工具类 * 调用方法获取服务的元数据信息 */ @Autowired private DiscoveryClient discoveryClient; /** * 参数:商品ID * 通过订单系统,调用商品服务根据id查询商品信息 * 1 需要配置一个商品对象 * 2 需要调用商品服务 * 使用java中的urlConnection完成,或使用httpClient,okHttp * RestTemplate */ // @RequestMapping(value = "/buy/{id}",method = RequestMethod.GET) // public Product findById(@PathVariable("id") Long id) // { // //调用DiscoveryClient的getInstances方法根据服务名获取元数据 // List<ServiceInstance> instances = discoveryClient.getInstances("SERVICE-PRODUCT"); // //获取唯一的元数据 // ServiceInstance instance = instances.get(0); // Product product=null; // //如何调用商品服务 // //根据元数据和端口号拼接请求的url // product=restTemplate.getForObject("http://"+instance.getHost()+":"+instance.getPort()+"/product/"+id,Product.class); // return product; // } ? /** * 基于Ribbon的形式调用远程微服务 * @param id * @return */ @RequestMapping(value = "/buy/{id}",method = RequestMethod.GET) public Product findById(@PathVariable("id") Long id) { Product product=null; product=restTemplate.getForObject("http://service-product/product/"+id,Product.class); return product; } }
Ribbon默认的负载均衡就是轮询策略
Ribbon内置了多种负载均衡策略
可在服务消费者的application.yml配置文件中修改负载均衡策略
``
#修改ribbon的负载均衡策略 服务名 - ribbon - NFLoadBalancedRuleClassName:策略 shop-service-product: ribbon: NFLoadBalancedRuleClassName: com.netflix.loadbalancer.RandomRule