Spring-Cloud之Ribbon
Ribbon的引入将为我们解决服务集群部署时需要实现相关的负载均衡的算法,简化服务调用的过程;引入Ribbon后在进行服务调用时只需要指定服务名称即可,无需去关心服务的部署地址以及负载均衡实现。下面我们通过一些Ribbon的示例带你快速了解如何使用它。下面示例使用的Spring-Cloud的版本是Hoxton.SR8,Spring-Boot的版本是2.3.4.RELEASE。示例项目的源代码
添加相关的依赖
由于引入的spring-cloud-starter-netflix-eureka-client中就包含了ribbon的东西,因此我们无需再额外的引入东西。
<!--eureka注册中心客户端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
未引入ribbon组件时调用服务的示例
传统的方案就通过httpclient来请求,为了简便我们这里直接使用RestTemplate来操作。
@Autowired
private RestTemplateBuilder restTemplateBuilder;
@Autowired
private EurekaClient eurekaClient;
@Override
public void findGoods(Long id){
// 从eureka注册表中获取到要调用服务的地址和端口号
String domain = getServerAddress("ribbon-b");
// 拼接调用地址
String url = String.format(domain+"/goods?id=%d", id);
// 发送请求
String res = restTemplateBuilder.build().getForObject(url, String.class);
System.out.println(id+" 请求["+url+"]结果:"+res);
}
private String getServerAddress(String serverName){
Assert.notNull(serverName, "服务名称不能为空");
Application application = eurekaClient.getApplication(serverName);
Assert.notNull(application, "服务不可用");
List<InstanceInfo> instances = application.getInstances();
Assert.notNull(instances, "无可用服务");
Assert.isTrue(instances.size()>0, "无可用服务");
// 负载均衡算法: 这里就使用简单的随机算法
InstanceInfo instance = instances.get(RandomUtils.nextInt(instances.size()));
return String.format("http://%s:%d", instance.getHostName(), instance.getPort());
}
可以看出我们在调用服务时需要从eureka client的本地注册表中获取注册信息,为了方便我们可以封装相关的工具来做个事情;而ribbon主要做的事情就是这个。
引入ribbon组件时调用服务的示例
因为spring对RestTemplate的网络请求进行过封装,将其和ribbon这种进行结合,实现我们直接将host写成服务名称依然可以直接调用,要实现这种方式我们需要加上@LoadBalanced注解;下面是使用方式:
// 创建RestTemplate
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
在其他地方使用(这里举例2种方式,一种是让RestTemplate自动的替换掉服务名称,一种是我们通过LoadBalancerClient来获取服务主机端口信息手动去替换)
@Autowired
private RestTemplateBuilder restTemplateBuilder;
@Autowired
private RestTemplate restTemplate;
@Autowired
private LoadBalancerClient loadBalancerClient;
public void findGoods1(Long id){
// 通过ribbon来获取要调用服务的一个地址和端口号
ServiceInstance instance = loadBalancerClient.choose("ribbon-b");
// 拼接调用的地址
String url = String.format("http://%s:%s/goods?id=%d", instance.getHost(), instance.getPort(), id);
// 发送请求
String res = restTemplateBuilder.build().getForObject(url, String.class);
System.out.println("方式一请求结果:"+res);
}
public void findGoods2(Long id){
String res = restTemplate.getForObject("http://ribbon-b/goods?id="+id, String.class);
System.out.println("方式二请求结果:"+res);
}
@Override
public void findGoods(Long id){
// 方式一:通过LoadBalancerClient来获取服务地址
findGoods1(id);
// 方式二:直接使用spring封装过的RestTemplate
findGoods2(id);
}
ribbon 实现了多种负载均衡策略(就是集群部署时选择那台机器的规则),我们可以通过配置来切换相关的策略;同时也可以自定义负载均衡规则。
ribbon内置负载均衡规则
- RoundRobinRule:轮询;系统内置的默认负载均衡,直接是安装服务列表挨着一个个的来,请求到每个服务的数据是平均的;
- AvailabilityFilteringRule:可用性;ribbon会根据服务的状况,来判断是否继续使用它,比如某个连续失败3次就会稍后再去使用它,或者是某个的并发请求太高了,那么就会绕过它不再去访问了;
- WeightedResponseTimeRule:权重;每个服务器可以有权重,权重越高优先访问,如果某个服务器响应时间比较长,那么权重就会降低,减少访问量;
- ZoneAvoidanceRule:根据区域和服务器来进行负载均衡;
- BestAvailableRule:忽略那些连接失败的服务器,然后尽量找并发比较低的服务器来请求
- RandomRule:随机;
- RetryRule:重试;就是在请求失败后,会重新再找个再次尝试。
注意由于netfix已经宣布ribbon进入维护模式了,因此spring重新写了个替代方案 loadbalancer;在Spring-Cloud的Hoxton版本中,已经开始推荐使用这个loadbalancer了。
切换负载均衡规则
通过代码配置注入:
/**
* 自定义的规则
*/
public class MyRule extends AbstractLoadBalancerRule {
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
}
@Override
public Server choose(Object key) {
return null;
}
}
/**
* 注入这个规则:@RibbonClient中如果填写name就表示只对指定的服务名称生效
*/
@RibbonClient(name="server-name", configuration=IRuleConfig.class)
public class IRuleConfig {
@Bean
public IRule getRule(){
// 使用默认的实现的
return new RandomRule();
}
// @Bean
// public IRule getRule(){
// 使用自己自定义的
// return new MyRule();
// }
}
在配置中直接配置:
# 配置指定服务名称的规则
server-name:
ribbon:
NFLoadBalancerRuleClassName: top.vchar.config.MyRule
# 这样配置就是全局的
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule