客户端负载均衡:Spring Cloud Ribbon
Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,基于Netflix Ribbon实现。微服务间的调用,API网关的请求转发等内容,实际上都是通过Ribbon来实现的,包括后续的Feign。
客户端负载均衡
负载均衡是对系统高可用,网络压力的缓解和处理能力扩容的重要手段之一。通常所说的负载均衡都指的是服务端负载均衡,其中发呢为硬件负载均衡和软件负载均衡。硬件负载均衡主要通过在服务器节点之间安装专门用于负载均衡的设备,比如F5等;而软件负载均衡则是通过在服务器上安装一些具有负载均衡功能或模块的软件来完成请求分发工作,比如Nginx。架构如下:
硬件和软件负载均衡都会维护一个下挂可用的服务端清单,通过心跳检测来剔除故障的服务器节点以保证清单中都是可以正常访问的服务端节点。当客户端发送请求到负载均衡设备的时候,该设备按某种算法(比如线性轮询,按权重负载,按流量负载等)从维护的可用服务端清单中取出一台服务端的地址,然后进行转发。
客户端负载均衡和服务端负载均衡最大的不通电在于服务清单所存储的位置。在客户端负载均衡中,所有客户端节点都维护者自己要访问的服务端清单,这些服务端的清单来自于服务注册中心。同负载均衡的架构类似,在客户端负载均衡中也需要心跳去维护服务端清单的健康性。
RestTemplate详解
RestTemplate会使用Ribbon的自动化配置,同时通过配置@LoadBalanced还能够开启客户端负载均衡。
下面详细介绍RestTemplate针对几种不同请求类型和参数类型的服务调用实现:
GET请求
在RestTemplate中,对GET请求可以通过下面两种方法进行调用实现:
第一种:getForEntity函数。该方法返回的是ResponseEntity,该对象是Spring对HTTP请求响应的封装,其中主要存储了HTTP的几个重要元素,比如HTTP请求状态码的枚举对象HttpStatus(也就是常说的404,500这些状态码),在它的父类HttpEntity中还存储者HTTP请求的头信息对象HttpHeader以及泛型类型的请求体对象。
比如下面的例子,就是访问USER-SERVER服务的/user请求,返回的ResponseEntity对象中的body内容会根据第二个参数转换为String类型。
若希望返回的body是一个User对象类型,可以这项实现:
getForEntity函数实际上提供了以下三种不同的重载实现:
1. getForEntity(String url,Class responseType,Object …urlVariables)该方法提供了三个参数,url为请求的地址,responseType为请求响应体body的包装类型,UrlVariables为url中的参数绑定。例如:ResponseEntity<String> tity =restTemplate.getForentity(http://USER-SERVICE/user?name={1},String class,”didi”);
2. getForEntity(String url,Class responseType,Map urlVariables),例如:
3. getForEntity(URI url,Class responseType):该方法使用URI对象来替代之前的url和urlVarivables参数来指定访问地址和参数绑定。URI是JDK java.net包下的一个类,表示一个统一资源标识符引用。例如:
第二种:getForObject函数。该方法可以理解为对getForEntity的进一步封装,它通过HttpMessageConverterExtractor对HTTP请求响应体body内容进行对象转换,实现请求直接返回包装好的对象内容。比如:
当body是一个User对象时,可以直接这样实现:
同样也提供了三种不同的重载实现:
1. getForObject(String url,Class responseType,Object … urlVariables);
2. getForObject(String url,Class responseType,Map urlVariables);(Map类型中的key需要与url中占位符名称一一对应)
3. getForObject(URI url,Class responseType);
POST请求
在RestForEntity中,对POST请求时可以通过如下三个方法进行调用实现。
第一种:postForEntity函数。
三种不同的重载方法。
1. postForEntity(String url,object request,Class responseType,Object …uriVariables);
2. postForEntity(String url,object request,Class responseType,Map uriVariables);
3. postForEntity(URI url,object request,Class responseType);
第二种:postForObject函数
postForObject函数也实现了三种不同的重载方法:
1. postForObject(String url,Object request,Class responseType,Object …uriVariables)
2. postForObject(String url,Object request,Class responseType,Map …uriVariables)
3. postForObject( URI url,Object request,Class responseType)
第三种:postForLocation函数
postForLocation函数也实现了三种不同的重载方法:
1. postForLocation(String url,Object request,Object …urlVariables)
2. postForLocation(String url,Object request,Map …urlVariables)
3. postForLocation(URI url,Object request)
PUT请求
Put函数也实现了三种不同的重载方法:
1. put(String url,Object request,Object …urlVariables)
2. put(String url,Object request,Map …urlVariables)
3. put(URI url,Object request)
put函数为void类型,所以没有返回值
DELETE请求:
重载方法:
1. delete(String url,Object …urlVariables)
2. delete(String url,Map …urlVariables)
3. delete(URI url)
源码分析
Ribbon通过RestTemplate实现客户端负载均衡。
客户端负载均衡器应具备几种能力:
1. ServiceInstance choose(String serviceId):根据传入的服务名serviceId,从负载均衡器中挑选一个对应服务的实例。
2. T execute(String serviceId,LoadBalanceRequest request) throws IOException:使用从负载均衡器中挑选出的服务实例来执行请求内容
3. URI reconstructURI(ServiceInstance instance,URI original):为系统构建一个合适的host:port形式的URI。
Ribbon实现的负载均衡自动化配置需要满足下面两个条件:
1. @ConditionalOnBean(RestTemplate.class):RestTemplate类必须存在当前工程的环境中
2. @ConditionalOnBean(LoadBalancerClient.class):在Spring的Bean工程必须有LoadBalancerClient的实现bean
在自动化配置类中,主要做了三件事:
1. 创建了一个LoadBalancerInterceptor的Bean,用于实现对客户端发起请求时进行拦截,以实现客户端负载均衡。
2. 创建了一个RestTemplateCustomizer的Bean,用于给RestTemplate增加LoadBalancerInterceptor拦截器
3. 维护了一个呗@LoadBalanced注解修饰的RestTemplate对象列表,并进行初始化,通过调用RestTemplateCustomizer的实例来给需要客户端负载均衡的RestTemplate增加LoadBalancerInterceptor拦截器