SpringCloud学习总结(六)——服务调用和负载均衡Ribbon
用例spring_cloud_ribbon
项目地址:传送门
一、Ribbon服务调用
1、Ribbon概述
1.1 什么是Ribbon
是 Netflixfa 发布的一个负载均衡器,有助于控制 HTTP 和 TCP客户端行为。在 SpringCloud 中,Eureka一般配合Ribbon进行使用,Ribbon提供了客户端负载均衡的功能,Ribbon利用从Eureka中读取到的服务信息,在调用服务节点提供的服务时,会合理的进行负载。
在SpringCloud中可以将注册中心和Ribbon配合使用,Ribbon自动的从注册中心中获取服务提供者的列表信息,并基于内置的负载均衡算法,请求服务
1.2 Ribbon的主要作用
(1)服务调用 基于Ribbon实现服务调用, 是通过拉取到的所有服务列表组成(服务名-请求路径的)映射关系。借助RestTemplate 最终进行调用
(2)负载均衡 当有多个服务提供者时,Ribbon可以根据负载均衡的算法自动的选择需要调用的服务地址
2、Ribbon实现订单调用商品服务
不论是基于Eureka的注册中心还是基于Consul的注册中心,SpringCloudRibbon统一进行了封装,所以对于服务调用,两者的方式是一样的。
2.1 坐标依赖
在springcloud提供的服务发现的jar中以及包含了Ribbon的依赖。所以这里不需要导入任何额外的坐标,Eureka继承了Ribbon
2.2 服务消费者
修改服务消费者order_service模块中的启动类OrderApplication ,在创建RestTemplate方法上添加 @LoadBalanced 注解
@SpringBootApplication @EntityScan("cn.hzp.order.domain") public class OrderApplication { /** * 使用spring提供的RestTemplate发送http请求到商品服务 * 1.创建RestTemplate对象交给容器管理 * 2.在使用的时候,调用其方法完成操作 (getXX,postxxx) * * @LoadBalanced : 是ribbon提供的负载均衡的注解 */ @LoadBalanced @Bean public RestTemplate restTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(OrderApplication.class,args); } }
在order_service模块的OrderController 中添加下面的方法,并使用RestTemplate完成服务调用
/** * 基于ribbon的形式调用远程微服务 * 1.使用@LoadBalanced声明RestTemplate * 2.使用服务名称替换ip地址 */ @RequestMapping(value = "/buy/{id}",method = RequestMethod.GET) public Product findById(@PathVariable Long id) { //服务名称service-product替换ip地址 Product product = restTemplate.getForObject("http://service-product/product/"+id,Product.class); return product; }
2.3 代码测试
浏览器中请求 http://localhost:9002/order/buy/1 查看展示效果如下,已经可以在订单微服务中已服务名称的形式调用商品微服务获取数据。
二、Ribbon负载均衡
1、负载均衡概述
1.1 什么是负载均衡
在搭建网站时,如果单节点的 web服务性能和可靠性都无法达到要求;或者是在使用外网服务时,经常担心被人攻破,一不小心就会有打开外网端口的情况,通常这个时候加入负载均衡就能有效解决服务问题。
负载均衡是一种基础的网络服务,其原理是通过运行在前面的负载均衡服务,按照指定的负载均衡算法,将流量分配到后端服务集群上,从而为系统提供并行扩展的能力。
负载均衡的应用场景包括流量包、转发规则以及后端服务,由于该服务有内外网个例、健康检查等功能,能够有效提供系统的安全性和可用性。
1.2 客户端负载均衡与服务端负载均衡
注意:Ribbon是一个典型的客户端负载均衡器。
服务端负载均衡
先发送请求到负载均衡服务器或者软件,然后通过负载均衡算法,在多个服务器之间选择一个进行访问;即在服务器端再进行负载均衡算法分配。
客户端负载均衡
客户端会获取一个服务器地址列表,在发送请求前通过负载均衡算法选择一个服务器,然后进行访问,这是客户端负载均衡;即在客户端就进行负载均衡算法分配,获取发送的服务地址。
2、基于Ribbon实现负载均衡
2.1 搭建多个商品服务
1、修改product_service 的 application.yml 配置文件,以profiles的形式配置多个实例
server: port: 9001 #端口 spring: application: name: service-product #服务名称 datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://192.168.126.99:3306/spring_cloud_demo?useUnicode=true&characterEncoding=utf8 username: root password: root 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 #想注册中心发送心跳的续约间隔时间5s lease-expiration-duration-in-seconds: 10 #eureka client发送心跳给server端后,续约到期时间(默认90秒) #配置多个商品服务实例 --- #配置product1 9001 的配置信息 spring: profiles: product1 server: port: 9001 #端口 --- #配置product1 9011 的配置信息 spring: profiles: product2 server: port: 9011 #端口
2、修改商品模块的代码,返回服务IP以及端口信息
public class ProductController { @Autowired private ProductService productService; @Value("${server.port}") private String port; @Value("${spring.cloud.client.ip-address}") private String ip; @RequestMapping(value = "/{id}",method = RequestMethod.GET) public Product findById(@PathVariable Long id) { Product product = productService.findById(id); product.setProductName(ip+":"+port); return product; } }
3、IDEA配置两个商品服务启动配置:
在订单服务中用负载均衡调用商品服务
/** * 基于Eureka元数据方式DiscoveryClient调用服务 * @param id * @return */ @RequestMapping(value = "/buy/{id}",method = RequestMethod.GET) public Product findById(@PathVariable Long id) { //已调用服务名称获取所有的元数据 List<ServiceInstance> instances = discoveryClient.getInstances("service-product"); //获取唯一的一个元数据 ServiceInstance instance = instances.get(0); //根据元数据中的主机地址和端口号拼接请求微服务的URL //如何调用商品服务? Product product = restTemplate.getForObject("http://"+instance.getHost()+":"+instance.getPort()+"/product/"+id,Product.class); //Product product = restTemplate.getForObject("http://localhost:9001/product/"+id,Product.class); return product; }
2.2 负载均衡策略
Ribbon内置了多种负载均衡策略,内部负责复杂均衡的顶级接口为com.netflix.loadbalancer.IRule ,实现方式如下
-
com.netflix.loadbalancer.RoundRobinRule
:以轮询的方式进行负载均衡。 -
com.netflix.loadbalancer.RandomRule
:随机策略 -
com.netflix.loadbalancer.RetryRule
:重试策略。 -
com.netflix.loadbalancer.WeightedResponseTimeRule
:权重策略。会计算每个服务的权重,越高的被调用的可能性越大。
在服务消费者order-server模块的application.yml配置文件中修改负载均衡策略
#修改ribbon的负载均衡策略 被消费的服务名 - ribbon - NFLoadBalancerRuleClassName : 策略 service-product: ribbon: NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
策略选择:
1、如果每个机器配置一样,则建议不修改策略 (推荐)
2、如果部分机器配置强,则可以改为权重策略WeightedResponseTimeRule
2.3 请求重试
情景:
服务A,B提供相同服务。如果消费者请求服务A,此时如果A挂掉或者请求超时,如果没有配置【请求重试】,则当消费者访问服务A时会报错,不会自动去访问服务B。这样在负载轮询去访问时就会出现请求报错。
解决方案:
配置Ribbon的请求重试,当负载均衡策略轮询访问服务A时,发现A挂掉,则去访问另一个服务B,不会报错。
1、在服务消费者order-server模块的application.yml配置文件中修改Ribbon的请求重试配置
#修改ribbon的负载均衡策略 服务名 - ribbon - NFLoadBalancerRuleClassName : 策略 service-product: ribbon: NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #权重策略 ConnectTimeout: 250 # Ribbon的连接超时时间 ReadTimeout: 1000 # Ribbon的数据读取超时时间 OkToRetryOnAllOperations: true # 是否对所有操作都进行重试 MaxAutoRetriesNextServer: 1 # 切换实例的重试次数 MaxAutoRetries: 1 # 对当前实例的重试次数
2、添加spring提供的请求重试坐标
<dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> </dependency>
3、测试
正常 SERVICE-PRODUCT 两个服务
现在关闭其中一个,重复负载均衡去调用,不会出现报错。可以看下订单服务的请求日志,可以发现请求重试的操作:
三、源码分析