一、Ribbon概述
Ribbon是 Netflixfa 发布的一个负载均衡器,有助于控制 HTTP 和 TCP客户端行为。在 SpringCloud 中,Eureka一般配合Ribbon进行使用,Ribbon提供了客户端负载均衡的功能,Ribbon利用从Eureka中读取到的服务信息,在调用服务节点提供的服务时,会合理的进行负载。
在SpringCloud中可以将注册中心和Ribbon配合使用,Ribbon自动的从注册中心中获取服务提供者的列表信息,并基于内置的负载均衡算法,请求服务。
(1)服务调用
基于Ribbon实现服务调用, 是通过拉取到的所有服务列表组成(服务名-请求路径的)映射关系。借助RestTemplate 最终进行调用
(2)负载均衡
当有多个服务提供者时,Ribbon可以根据负载均衡的算法自动的选择需要调用的服务地址
不论是基于Eureka的注册中心还是基于Consul的注册中心,SpringCloudRibbon统一进行了封装,所
以对于服务调用,两者的方式是一样的。
坐标依赖
在springcloud提供的服务发现的jar中以及包含了Ribbon的依赖。所以这里不需要导入任何额外的坐标
工程改造
(1) 服务提供者
修改 shop_service_product模块中ProductController#findById() 方法如下
1 |
<span class="token annotation punctuation">@Value</span><span class="token punctuation">(</span><span class="token string">"${server.port}"</span><span class="token punctuation">)</span> <span class="token keyword">private</span> String port<span class="token punctuation">;</span> <span class="token annotation punctuation">@Value</span><span class="token punctuation">(</span><span class="token string">"${spring.cloud.client.ip-address}"</span><span class="token punctuation">)</span> <span class="token keyword">private</span> String ip<span class="token punctuation">;</span> <span class="token annotation punctuation">@GetMapping</span><span class="token punctuation">(</span><span class="token string">"/{id}"</span><span class="token punctuation">)</span> <span class="token keyword">public</span> Product <span class="token function">findById</span><span class="token punctuation">(</span><span class="token annotation punctuation">@PathVariable</span> Long id<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> Product product <span class="token operator">=</span> productService<span class="token punctuation">.</span><span class="token function">findById</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span> product<span class="token punctuation">.</span><span class="token function">setProductDesc</span><span class="token punctuation">(</span><span class="token string">"调用shop-service-product服务,ip:"</span><span class="token operator">+</span>ip<span class="token operator">+</span>"<span class="token punctuation">,</span>服务提供者端 口<span class="token operator">:</span>"<span class="token operator">+</span>port<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> product<span class="token punctuation">;</span> <span class="token punctuation">}</span> |
(2) 服务消费者
修改服务消费者 shop_service_order模块中的启动类OrderApplication ,在创建RestTemplate方法上添加 @LoadBalanced 注解
1 |
<span class="token annotation punctuation">@LoadBalanced</span> <span class="token annotation punctuation">@Bean</span> <span class="token keyword">public</span> RestTemplate <span class="token function">getRestTemplate</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">RestTemplate</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> |
在 shop_service_order的OrderController 中添加下单方法,并使用RestTemplate完成服务调用
1 |
<span class="token annotation punctuation">@Autowired</span> <span class="token keyword">private</span> RestTemplate restTemplate<span class="token punctuation">;</span> <span class="token annotation punctuation">@GetMapping</span><span class="token punctuation">(</span><span class="token string">"/buy/{id}"</span><span class="token punctuation">)</span> <span class="token keyword">public</span> Product <span class="token function">order</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> restTemplate<span class="token punctuation">.</span><span class="token function">getForObject</span><span class="token punctuation">(</span><span class="token string">"http://127.0.0.1:9002/product/1"</span><span class="token punctuation">,</span> Product<span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span> Product product <span class="token operator">=</span> restTemplate<span class="token punctuation">.</span><span class="token function">getForObject</span><span class="token punctuation">(</span><span class="token string">"http://shop-serviceproduct/product/1"</span><span class="token punctuation">,</span> Product<span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> product<span class="token punctuation">;</span> <span class="token punctuation">}</span> |
(3) 测试
浏览器中请求http://localhost:9001/order/buy/1查看展示效果如下,已经可以在订单微服务中已服务
名称的形式调用商品微服务获取数据
二、Ribbon进阶
在搭建网站时,如果单节点的 web服务性能和可靠性都无法达到要求;或者是在使用外网服务时,经常担心被人攻破,一不小心就会有打开外网端口的情况,通常这个时候加入负载均衡就能有效解决服务问题。
负载均衡是一种基础的网络服务,其原理是通过运行在前面的负载均衡服务,按照指定的负载均衡算
法,将流量分配到后端服务集群上,从而为系统提供并行扩展的能力。
负载均衡的应用场景包括流量包、转发规则以及后端服务,由于该服务有内外网个例、健康检查等功
能,能够有效提供系统的安全性和可用性。
服务端负载均衡
先发送请求到负载均衡服务器或者软件,然后通过负载均衡算法,在多个服务器之间选择一个进行访
问;即在服务器端再进行负载均衡算法分配
客户端负载均衡
客户端会有一个服务器地址列表,在发送请求前通过负载均衡算法选择一个服务器,然后进行访问,这是客户端负载均衡;即在客户端就进行负载均衡算法分配
搭建多服务实例
修改 shop_service_product 的 application.yml 配置文件,已profiles的形式配置多个实例
1 |
<span class="token key atrule">spring</span><span class="token punctuation">:</span> <span class="token key atrule">profiles</span><span class="token punctuation">:</span> product1 <span class="token key atrule">application</span><span class="token punctuation">:</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> shop<span class="token punctuation">-</span>service<span class="token punctuation">-</span>product <span class="token key atrule">datasource</span><span class="token punctuation">:</span> <span class="token key atrule">driver-class-name</span><span class="token punctuation">:</span> com.mysql.jdbc.Driver <span class="token key atrule">url</span><span class="token punctuation">:</span> jdbc<span class="token punctuation">:</span>mysql<span class="token punctuation">:</span>//localhost<span class="token punctuation">:</span>3306/shop<span class="token punctuation">?</span>useUnicode=true<span class="token important">&characterEncoding</span>=utf8 <span class="token key atrule">username</span><span class="token punctuation">:</span> root <span class="token key atrule">password</span><span class="token punctuation">:</span> <span class="token number">111111</span> <span class="token key atrule">jpa</span><span class="token punctuation">:</span> <span class="token key atrule">database</span><span class="token punctuation">:</span> MySQL <span class="token key atrule">show-sql</span><span class="token punctuation">:</span> <span class="token boolean important">true</span> <span class="token key atrule">open-in-view</span><span class="token punctuation">:</span> <span class="token boolean important">true</span> <span class="token key atrule">cloud</span><span class="token punctuation">:</span> <span class="token key atrule">consul</span><span class="token punctuation">:</span> <span class="token key atrule">host</span><span class="token punctuation">:</span> localhost <span class="token key atrule">port</span><span class="token punctuation">:</span> <span class="token number">8500 </span> <span class="token key atrule">discovery</span><span class="token punctuation">:</span> <span class="token key atrule">instance-id</span><span class="token punctuation">:</span> $<span class="token punctuation">{<!-- --></span>spring.application.name<span class="token punctuation">}</span><span class="token punctuation">-</span><span class="token number">1</span> <span class="token key atrule">prefer-ip-address</span><span class="token punctuation">:</span> <span class="token boolean important">true</span> <span class="token key atrule">ip-address</span><span class="token punctuation">:</span> $<span class="token punctuation">{<!-- --></span>spring.cloud.client.ip<span class="token punctuation">-</span>address<span class="token punctuation">}</span> <span class="token key atrule">server</span><span class="token punctuation">:</span> <span class="token key atrule">port</span><span class="token punctuation">:</span> <span class="token number">9002</span> <span class="token punctuation">---</span> <span class="token key atrule">spring</span><span class="token punctuation">:</span> <span class="token key atrule">profiles</span><span class="token punctuation">:</span> product2 <span class="token key atrule">application</span><span class="token punctuation">:</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> shop<span class="token punctuation">-</span>service<span class="token punctuation">-</span>product <span class="token key atrule">datasource</span><span class="token punctuation">:</span> <span class="token key atrule">driver-class-name</span><span class="token punctuation">:</span> com.mysql.jdbc.Driver <span class="token key atrule">url</span><span class="token punctuation">:</span> jdbc<span class="token punctuation">:</span>mysql<span class="token punctuation">:</span>//localhost<span class="token punctuation">:</span>3306/shop<span class="token punctuation">?</span>useUnicode=true<span class="token important">&characterEncoding</span>=utf8 <span class="token key atrule">username</span><span class="token punctuation">:</span> root <span class="token key atrule">password</span><span class="token punctuation">:</span> <span class="token number">111111</span> <span class="token key atrule">jpa</span><span class="token punctuation">:</span> <span class="token key atrule">database</span><span class="token punctuation">:</span> MySQL <span class="token key atrule">show-sql</span><span class="token punctuation">:</span> <span class="token boolean important">true</span> <span class="token key atrule">open-in-view</span><span class="token punctuation">:</span> <span class="token boolean important">true</span> <span class="token key atrule">cloud</span><span class="token punctuation">:</span> <span class="token key atrule">consul</span><span class="token punctuation">:</span> <span class="token key atrule">host</span><span class="token punctuation">:</span> localhost <span class="token key atrule">port</span><span class="token punctuation">:</span> <span class="token number">8500 </span> <span class="token key atrule">discovery</span><span class="token punctuation">:</span> <span class="token key atrule">instance-id</span><span class="token punctuation">:</span> $<span class="token punctuation">{<!-- --></span>spring.application.name<span class="token punctuation">}</span><span class="token punctuation">-</span><span class="token number">2</span> <span class="token key atrule">prefer-ip-address</span><span class="token punctuation">:</span> <span class="token boolean important">true</span> <span class="token key atrule">ip-address</span><span class="token punctuation">:</span> $<span class="token punctuation">{<!-- --></span>spring.cloud.client.ip<span class="token punctuation">-</span>address<span class="token punctuation">}</span> <span class="token key atrule">server</span><span class="token punctuation">:</span> <span class="token key atrule">port</span><span class="token punctuation">:</span> <span class="token number">9004</span> |
分别启动两次服务器验证效果,并查看两个控制台发现已轮询的方式调用了商品服务
Ribbon内置了多种负载均衡策略,内部负责复杂均衡的顶级接口为com.netflix.loadbalancer.IRule ,实现方式如下:
- com.netflix.loadbalancer.RoundRobinRule :以轮询的方式进行负载均衡。
- com.netflix.loadbalancer.RandomRule :随机策略
- com.netflix.loadbalancer.RetryRule :重试策略。
- com.netflix.loadbalancer.WeightedResponseTimeRule :权重策略。会计算每个服务的权重,越高的被调用的可能性越大。
- com.netflix.loadbalancer.BestAvailableRule :最佳策略。遍历所有的服务实例,过滤掉故障实例,并返回请求数最小的实例返回。
- com.netflix.loadbalancer.AvailabilityFilteringRule :可用过滤策略。过滤掉故障和请求数超过阈值的服务实例,再从剩下的实力中轮询调用。
在服务消费者的application.yml配置文件中修改负载均衡策略
1 |
<span class="token key atrule">shop-service-product</span><span class="token punctuation">:</span> <span class="token key atrule">ribbon</span><span class="token punctuation">:</span> <span class="token key atrule">NFLoadBalancerRuleClassName</span><span class="token punctuation">:</span> com.netflix.loadbalancer.RandomRule |
策略选择:
1、如果每个机器配置一样,则建议不修改策略 (推荐)
2、如果部分机器配置强,则可以改为 WeightedResponseTimeRule