Ribbon

客户端负载均衡

服务端负载均衡:分为硬件负载均衡和软件负载均衡。软件负载均衡,例如Nginx,通过Nginx进行负载均
衡,先发送请求,然后通过负载均衡算法,在多个服务器之间选择一个进行访问;即在服务器端再进行负载均衡算法分配。
客户端负载均衡:例如spring cloud中的ribbon,客户端节点会维护着自己要访问的服务端清单,而这些
清单来自于服务注册中心,在发送请求前通过负载均衡算法选择一个服务器,然后进行访问,这是客户端负载均衡;即在客户端就进行负载均衡算法分配。
通过Spring Cloud Ribbon封装,使用客户端负载均衡步骤:

  1. 服务提供者启动多个服务实例并注册到一个或是多个相关联的服务注册中心。
  2. 服务消费者直接通过调用被@LoadBalanced注解修饰过的RestTemplate来实现面向服务的

RestTemplate详解

数据的重要性

关于软件和数据的重要性,更重要的是数据。虽然数据只是软件完成工作时要处理的原材料,但对于实际业务
来说,数据是许多业务的生命之血。软件通常是可以替换的,但数据却是多年的积累,不能替换。

REST流行原因

以信息为中心的表述性状态转移(Representational State Transfer,REST)被称为替代传统SOAP
Web服务的流行方案。SOAP关注的是行为和处理,而REST关注的是要处理的数据。
从Spring3.0开始,Spring为创建Rest API提供了良好支持。

REST基础知识

理解RESTful架构 Restful API 设计指南
当谈论REST时,有一种常见的错误就是将其视为“基于URL的Web服务”——将REST作为另一种类型的远程过程
调用(remote procedure call,RPC)机制,就像SOAP一样,只不过是通过简单的HTTP URL来触发,而不是使用SOAP大量的XML命名空间。恰好相反,REST与RPC几乎没有任何关系。RPC是面向服务的,并关注于行为和动作;而REST 是面向资源的,强调描述应用程序的事物和名词。
更简洁地讲,REST就是将资源的状态以最适合客户端或服务端的形式从服务器端转移到客户端(或者反过
来)。在REST中,资源通过URL进行识别和定位。至于RESTful URL的结构并没有严格的规则,但是URL应该能够识别资源,而不是简单的发一条命令到服务器上。再次强调,关注的核心是事物,而不是行为。

应用

RestTemplate对象会使用Ribbon的自动化配置,通过配置@LoadBalanced还能开启客户端负载均衡。
RestTemplate针对不同请求类型和参数类型的服务调用实现:

2.1 GET请求

两种方法进行调用实现。

第一种:getForEntity函数。

返回ResponseEntity,该对象是Spring对HTTP请求响应的封装,主要存储了HttpStatus,它的父类HttpEntity中存储着HttpHeaders以及范型类型的请求体。
getForEntity提供以下三种重载实现:

  • getForEntity(String url , Class responseType , Object… urlVariables);
    {1}是占位符,最后一个参数会替换掉{1},返回的ResponseEntity对象中的body内容类型会根据第二个参数转换成String类型,urlVariables数组用来顺序匹配url中占位符定义的数字顺序。

    1
    2
    3
    RestTemplate restTemplate = new RestTemplate();
    ResponseEntity<String> responseEntity = restTemplate.getForEntity(“http://HELLO-SERVICE/user?name={1}”,String.class,”didi”);
    String name = responseEntity.getBody();
  • getForEntity(String url , Class responseType , Map urlVariables);
    urlVariables是Map类型,参数绑定是需要传入Map中的key值。

    1
    2
    3
    4
    5
    6
    RestTemplate restTemplate = new RestTemplate();
    Map<String,String> params = new HashMap<>();
    params.put("name","dada");
    ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://HELLO-SERVICE/user?name={name}",String.class,params);
    String name =
    responseEntity.getBody();
  • getForEntity(URL url , Class responseType);

    1
    2
    3
    4
    5
    6
    7
    8
    9
    RestTemplate restTemplate = new RestTemplate();
    UriComponents uriComponents = UriComponentsBuilder
    .fromUriString("http://HELLO-SERVICE/user?name={name}")
    .build()
    .expand("dodo")
    .encode();
    URI uri = uriComponents.toUri();
    ResponseEntity<String> responseEntity = restTemplate.getForEntity(uri,String.class);
    String name = responseEntity.getBody();
第二种:getForObject函数。

对getForEntity进一步封装,通过HttpMessageConverterExtractor对HTTP的请求响应体body内容进行对象转换,实现直接返回包装好的对象内容。

1
2
RestTemplate restTemplate = new RestTemplate();
String result = restTemplate.getForObject(uri, String.class);

getForObject提供了与getForEntity同样的三种重载实现,用法与getForEntity一样。

2.2 POST请求

RestTemplate对POST请求可以通过如下三个方法进行调用实现。

第一种:postForEntity函数。

postForEntity函数实现三种不同的重载方法。

  • postForEntity(String url, Object request, Class responseType, Object…uriVariables);
  • postForEntity(String url, Object request, Class responseType, Map uriVariables);
  • postForEntity(URI url, Object request, Class responseType);
    postForEntity的三种重载方法与getForEntity类似,只是多了第二个参数request。这个参数表示上传的参数,该对象可以是一个普通对象,也可以是一个HttpEntity对象。如果是普通对象,RestTemplate会将请求对象转换为一个HttpEntity对象来处理,Object就是request的类型,request内容会被视作完整的body来处理;如果request是一个HttpEntity对象,那就当做一个完整的HTTP请求对象来处理,这时request中不仅包含body内容,还包含了header。
    1
    2
    3
    4
    RestTemplate restTemplate = new RestTemplate();
    User user=new User("didi",30);
    ResponseEntity<User> responseEntity = restTemplate.postForEntity("http://USER-SERVICE/addUser",user,User.class);
    User body = responseEntity.getBody();
第二种:postForObject函数

postForObject函数也实现了三种不同的重载方法:

  • postForObject(String url, Object request, Class responseType, Object…uriVariables);
  • postForObject(String url, Object request, Class responseType, Map uriVariables);
  • postForObject(URI url, Object request, Class responseType);
    第三种:postForLocation函数,实现了以POST请求提交资源,并返回新资源的URI。
    1
    2
    3
    RestTemplate restTemplate = new RestTemplate();
    User user=new User("didi”,40);
    URI responseURI = restTemplate.postForLocation("http://USER-SERVICE/addUser",user);

postForLocation函数也实现了三种不同的重载方法:

  • postForLocation(String url, Object request, Object…uriVariables);
  • postForLocation(String url, Object request, Map uriVariables);
  • postForLocation(URI url, Object request);
    postForLocation直接返回新资源的URI,所以不需要返回类型。

2.3 PUT请求

对PUT请求通过put方法调用实现

1
2
3
4
RestTemplate restTemplate = new RestTemplate();
Long id = 1L;
User user = new User("didi",50);
restTemplate.put("http://USER-SERVICE/user/{1}",user,id);

put函数也实现了三种重载方法

  • put(String url, Object request, Object…uriVariables);
  • put(String url, Object request, Map uriVariables);
  • put(URI url, Object request);
    put函数为void类型,没有返回类容,所以没有responseType参数。

2.4 DELETE请求

对DELETE请求可以通过delete方法调用实现。
1
2
3
RestTemplate restTemplate = new RestTemplate();
Long id= 1L;
restTemplate.delete("http://USER-SERVICE/users/{1}",id);

delete函数也实现了三种重载方法

  • delete(String url, Object…uriVariables);
  • delete(String url, Map uriVariables);
  • delete(URI url);
    相对于PUT,DELETE请求的唯一标拼接在url中,所以DELETE请求连request的body信息也不需要。

Ribbon和Feign

在实践中,会发现Ribbon和Hystrix几乎同时使用,Spring Cloud Feign是对这两个工具更高层次的封装来简化开发。
Spring Cloud Feign基于Netflix Feign实现,整合了Spring Cloud Ribbon与Spring Cloud Hystrix,除了提供这两者的功能外,还提供了一种声明式的Web服务端定义方式。
RestTemplate实现了对HTTP请求的封装处理,形成了一套模版化的调用方法。在实际开发中,往往一个接口会被多处调用,但是由于RestTemplate的封装,几乎每一个调用都是简单的模版化内容。而Spring Cloud Feign在此基础进行了封装,可以帮助我们定义和实现依赖服务接口的定义,我们只需要创建一个接口并用注解的方式来配置它,即可完成对服务提供方的接口绑定。
Feign优点:

  • 可以简化在使用Ribbon时自行封装服务调用客户端的开发量。
  • 具备可插拔的注解支持,包括Feign注解和JAX-RS注解。
  • 在Netflix Feign的基础上扩展了对Spring MVC的注解的支持。
  • 对自身的主要组件,如编码器和解码器,以插拔的方式提供,方便在有需求的时候扩展和替换。

JAX-RS注解:JAX-RS规范-常用注解浅析 JAX-RS注解的使用