什么是Ribbon
在微服务项目中,一个后端服务我们一般会配置一个集群在不同服务器中进行启动,用来保证即使一个服务器宕机其他服务器也能进行服务的提供。这样一来我们获取的服务列表中就会有多个,那么到底该访问哪一个呢?一般这种情况下我们就需要编写负载均衡算法,在多个实例列表中进行选择。
Ribbon是netflix发布的负载均衡器,有助于控制HTTP和TCP客户端的行为,为Ribbon配置服务提供者列表后,Ribbon就可基于某种负载均衡算法,自动的帮助服务消费者去请求。Ribbon默认为我们提供了很多的负载均衡算法(轮询、随机等),并且我们也可以自己定义算法。
从某种程度上说,Ribbon是作为了一种服务的调用功能存在,即当一个后端服务部署成了集群之后,到底需要调用那个具体的服务,可以有Ribbon来为我们解决。
RestTemplate进行客户端访问
Spring提供了一个RestTemplate模板工具类,对基于Http的客户端进行了封装,并且实现了对象与json的序列化和反序列化,非常方便。RestTemplate并没有限定Http的客户端类型,而是进行了抽象,目前常用的3种都有支持:
- HttpClient
- OkHttp
- JDK原生的URLConnection(默认的)
在上一篇文章中我们已经对RestTemplate的使用进行了讲解,我们来回顾一下:
首先我们进行了一个RestTemplate对象的注册(注入到spring容器,当然也可以在启动类位置注册)
@Configuration
public class ApplicationContext {
@Bean
public RestTemplate GetrestTemplate(){
return new RestTemplate();
}
}
之后就可以在controller中注入并使用:
@RestController
public class TestServer {
private static final String ITEMSERVER_URL="http://localhost:8002";
@Autowired
private RestTemplate restTemplate;
@GetMapping("/test")
public CommonResult<Integer> test(){
return restTemplate.getForObject(ITEMSERVER_URL+"/test",CommonResult.class);
}
}
通过RestTemplate的getForObject()方法,传递url地址及实体类的字节码,RestTemplate会自动发起请求,接收响应,并且帮我们对响应结果进行反序列化。
上面只是对於单个服务进行访问时的使用,那么对于服务集群,我们需要怎样进行服务调用呢?
Ribbon的使用
下面我们接着上一篇文章服务注册中心-Eureka,做一个item-server服务的服务集群:
两个服务提供的功能完全一样,只是在配置文件中进行了不一样的端口配置:
我们来重写controller中的调用(加上端口号,这样我们在访问的时候能够识别出来消费者是访问了哪一个后端服务):
@RestController
public class TestServer {
@Value("${server.port}")
private String serverPort;
@GetMapping("/test")
public CommonResult test(){
Integer a=1;
return new CommonResult(200,"查询成功"+serverPort,a) ;
}
}
因为Eureka中已经集成了Ribbon,所以我们无需引入新的依赖,直接修改代码,在消费者服务中,我们在RestTemplate对象的注册类中添加一个注解 @LoadBalanced,开启负载均衡算法:
@Configuration
public class ApplicationContext {
@Bean
@LoadBalanced
public RestTemplate GetrestTemplate(){
return new RestTemplate();
}
}
修改消费者服务对其他服务的调用方式:
@RestController
public class TestServer {
// private static final String ITEMSERVER_URL="http://localhost:8002";
private static final String ITEMSERVER_URL="http://ITEM-SERVER";
@Autowired
private RestTemplate restTemplate;
@GetMapping("/test")
public CommonResult<Integer> test(){
return restTemplate.getForObject(ITEMSERVER_URL+"/test",CommonResult.class);
}
}
重起所有服务,对消费者路径进行访问,可以看到访问的后端服务是交替进行的:
在上面的调用中我们只输入了service名称就可以访问了,并不需要获取ip和端口。
这显然有人帮我们根据service名称,获取到了服务实例的ip和端口。它就是LoadBalancerInterceptor
。
由上可以看出,RestTemplate的服务调用,默认是通过简单的轮询方式进行的。
那么我们是否能够修改负载均衡的策略呢?
在Ribbon的底层,有两个IRule的实现类,一个是轮询一个是随机:
SpringBoot也帮我们提供了修改负载均衡规则的配置入口,在消费者服务的的application.yml中添加如下配置:
service-provider:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
格式是:{服务名称}.ribbon.NFLoadBalancerRuleClassName
,值就是IRule的实现类(文章开头已经说明,我们也可以进行自定义算法实现服务调用,自定义的类需要实现IRule接口)。
再次重启服务进行测试,发现是随机访问了。