Spring Cloud Ribbon负载均衡

本文涉及的产品
应用型负载均衡 ALB,每月750个小时 15LCU
网络型负载均衡 NLB,每月750个小时 15LCU
传统型负载均衡 CLB,每月750个小时 15LCU
简介: Spring Cloud Ribbon是一个基于HTTP 和 TCP的客户端负载工具,它基于Netflix Ribbon实现,我们可以使用它来进行远程服务负载均衡的调用。它不像Zuul 和 Eureka 等可以独立部署,它虽然是一个工具类框架,但是几乎所有的Spring Cloud微服务架构和基础设施都离不开它,包括后面所介绍的Feign 远程调用,也是基于Ribbon实现的工具

Spring Cloud Ribbon负载均衡

Spring Cloud Ribbon负载均衡一、简介二、客户端负载均衡三、RestTemplate详解GET请求POST请求PUT请求DELETE请求

一、简介

Spring Cloud Ribbon是一个基于HTTP 和 TCP的客户端负载工具,它基于Netflix Ribbon实现,我们可以使用它来进行远程服务负载均衡的调用。它不像Zuul 和 Eureka 等可以独立部署,它虽然是一个工具类框架,但是几乎所有的Spring Cloud微服务架构和基础设施都离不开它,包括后面所介绍的Feign 远程调用,也是基于Ribbon实现的工具

二、客户端负载均衡

负载均衡是在一个架构中非常重要,而且不得不去实施的内容。_因为负载均衡对系统的高可用,网络压力的缓解和处理能力扩容的重要手段之一。通常负载均衡分为两种:硬件负载均衡软件负载均衡,硬件负载均衡一般是通过硬件来实现,在_服务器节点之间安装特定的负载均衡设备_,比如F5。 而软件负载均衡是采用软件控制的手段实现的,它实在_服务器之间安装某种特定功能的软件来完成特定的请求分开工作,比如Nginx等。无论硬件负载还是软件负载,只要是服务端负载均衡都能以下图的架构方式构建起来:

19.jpg



硬件负载均衡的设备和软件负载均衡的模块都会维护一个下挂可用的服务清单,通过心跳检测剔除故障的服务节点以保证清单中都是可以访问的服务端节点。当客户发送请求到负载均衡的设备时。设备按照服务负载均衡的算法(随机访问,轮询访问,权重访问,最少访问次数算法)来找到对应的服务端。

而客户端负载均衡和服务端负载均衡最大的不同点在于上面所提到服务清单的存储位置。在客户端负载均衡中,所有客户端节点都维护着自己要访问的服务清单,而这些服务清单都来自注册中心,比如我们上一章介绍的Eureka服务端。

通过Spring Cloud Ribbon的封装,我们在微服务架构中使用负载均衡就比较简单,只需要下面两步:

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

三、RestTemplate详解

在上一章中,我们已经引入了Spring Cloud Ribbon实现了客户端负载均衡的一个简单的实例,其中,我们使用了一个非常有用的对象RestTemplate。该对象会使用Ribbon的自动化配置,同时通过配置@LoadBalanced开启客户端负载均衡。下面我们将详细介绍RestTemplate 针对几种不同的请求类型和参数类型的服务调用实现。

准备工作

在上一篇博客中,我们搭建了一个注册中心一个服务提供者一个ribbon消费者客户端,现在我们也需要这三个组件来做Ribbon 服务消费

GET请求

在RestTemplate中,对GET请求可以通过如下两个方法进行调用实现。

第一种:getForEntity()函数,该方法返回的是ResponseEntity,该对象是Spring对HTTP请求响应的封装,其中主要存储了HTTP的几个重要元素,比如HTTP请求状态码的枚举对象HttpStatus(常用的404,500这些错误),在它的父类HttpEntity中还存储着HTTP请求的头信息对象HttpHeaders以及泛型类型集合的请求体对象。

它的一般形式有三种:

/*
* url是远程服务端的路径,responseType是返回值类型,urlVariables是可变参数,给服务端传递的参数
*/
getForEntity(String url, Class<T> responseType, Object... urlVariables)
/*
* 可以使用Map封装参数传递给客户端
*/
getForEntity(String url, Class<T> responseType, Map<String, ?> urlVariables)
/*
* 也是一直接使用uri地址
*/
getForEntity(URI url, Class<T> responseType) throws RestClientException
/*
* getForObject 用法和getForEntity基本相同
*/
getForObject(String url, Class<T> responseType, Object... urlVariables) throws RestClientException
getForObject(String url, Class<T> responseType, Map<String, ?> urlVariables) throws RestClientException
getForObject(URI url, Class<T> responseType) throws RestClientException

URI 和 URL 的关系:

URI : 统一资源标志符:

URL: 统一资源定位符

URN : 统一资源名称

三者之间的关系:


20.jpg


一般用法

  • getForEntity

Ribbon 消费者

/**
     * 文章基于spring-boot-starter-parent 1.3.7 版本
     * 如果读者使用1.5.9 以上的版本,可以用GetMapping
     * @return
     */
    @RequestMapping(value = "/ribbon-consumer1", method = RequestMethod.GET)
    public ResponseEntity<String> helloUser(){
      // 返回值是String类型,所以对应第一个逗号后面的类型
      // /user/{1} 中的{1}表示的是第一个参数,传的值是didi
      // 也可以用getForEntity().getBody() 方法,此时返回值就只是一个String类型
        return restTemplate.getForEntity("http://server-provider/user/{1}",String.class,"didi");
    }
    @RequestMapping(value = "/ribbon-consumer2", method = RequestMethod.GET)
    public ResponseEntity<User> helloUser2(){
          // 返回值是一个User类型
          // 多个参数之间用& 隔开
        return restTemplate.getForEntity("http://server-provider/user2?id=001&name=didi",User.class);
    }
        // 传递一个Map类型的对象
    @RequestMapping(value = "/ribbon-consumer3", method = RequestMethod.GET)
    public ResponseEntity<String> helloUser3(){
        Map params = new HashMap();
        params.put("name","data");
          // {name}表示的是params中的key
        return restTemplate.getForEntity("http://server-provider/user3?name={name}", String.class,params);
    }
        // 其实最核心的就是通过uri进行调用,上面所有的写法都会转换为下面这种写法
        // 也就是说下面这种写法是最根本的。
    @RequestMapping(value = "/ribbon-consumer4", method = RequestMethod.GET)
    public ResponseEntity<String> helloUser4(){
        UriComponents uriComponents = UriComponentsBuilder.fromUriString(
                "http://server-provider/user4?name={name}")
                .build()
                .expand("lx")
                .encode();
        URI uri = uriComponents.toUri();
        return restTemplate.getForEntity(uri,String.class);
    }

User 对象

public class User {
    private Integer id;
    private String name;
    public User(){}
    public User(Integer id, String name) {
        this.id = id;
        this.name = name;
    }
        get and set...
}

服务提供者

来看一下服务提供者的代码:

// 返回的类型是String
    @RequestMapping(value = "/user/{name}", method = RequestMethod.GET)
    public String helloUser(@PathVariable("name") String name){
        return "Hello " + name;
    }
    // 返回的类型是User
    @RequestMapping(value = "/user2", method = RequestMethod.GET)
    public User helloUser(User user){
        return user;
    }
    @RequestMapping(value = "/user3", method = RequestMethod.GET)
    public String helloUser1(@RequestParam("name") String name){
        return "Hello " + name;
    }
    @RequestMapping(value = "/user4", method = RequestMethod.GET)
    public String helloUser2(@RequestParam("name") String name){
        return "Hello " + name;
    }


  • getForObject()

Ribbon 消费者

@RequestMapping(value = "/ribbonGet", method = RequestMethod.GET)
    public String ribbonGet(){
          // {1} 和 {2} 都是占位符,分别代表着 001 和 lx的值 
        return restTemplate.getForObject("http://server-provider/ribbon?id={1}&name={2}",String.class,
                new Object[]{"001","lx"});
    }
        // 和上面用法基本相同
    @RequestMapping(value = "/ribbonGet2", method = RequestMethod.GET)
    public String ribbonGet2(){
        Map params = new HashMap();
        params.put("id","001");
        params.put("name","lx");
        return restTemplate.getForObject("http://server-provider/ribbon?id={id}&name={name}",String.class,
                params);
    }
    @RequestMapping(value = "/ribbonGet3", method = RequestMethod.GET)
    public String ribbonGet3(){
        UriComponents uriComponents = UriComponentsBuilder.fromUriString(
                "http://server-provider/ribbon?id={id}&name={name}")
                .build()
                .expand("001","lx")
                .encode();
        URI uri = uriComponents.toUri();
        return restTemplate.getForObject(uri,String.class);
    }

服务提供者

// 上面所有的url共用下面一个方法
        @RequestMapping(value = "/ribbon", method = RequestMethod.GET)
    public String acceptRibbon(@RequestParam("id")String id,
                               @RequestParam("name") String name){
        System.out.println("id = " + id + "name = " + name);
        return "Hello " + id + " World " + name;
    }

POST请求

了解完GET请求后,再来看一下POST请求:

RestTemplate中,POST请求可以用一下几种方式来实现

// postForEntity
postForEntity(String url, Object request, Class<T> responseType, Object... uriVariables)   
postForEntity(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables)
postForEntity(URI url, Object request, Class<T> responseType) throws RestClientException
// postForObject
postForObject(String url, Object request, Class<T> responseType, Object... uriVariables)
postForObject(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables)
postForObject(URI url, Object request, Class<T> responseType) throws RestClientException
// postForLocation
postForLocation(String url, Object request, Object... urlVariables) throws RestClientException
postForLocation(String url, Object request, Map<String, ?> urlVariables) throws RestClientException 
postForLocation(URI url, Object request) throws RestClientException

Ribbon服务端

/**
     * 文章基于spring-boot-starter-parent 1.3.7 版本
     * 如果读者使用1.5.9 以上的版本,可以用 PostMapping
     * @return
     */
    @RequestMapping(value = "/ribbonPost", method = RequestMethod.POST)
    public User ribbonPost(){
        User user = new User(001,"lx");
        return restTemplate.postForEntity("http://server-provider/rpost",user,User.class)
                .getBody();
    }
    @RequestMapping(value = "/ribbonPost2", method = RequestMethod.POST)
    public User ribbonPost2(){
        User user = new User(001,"lx");
        UriComponents uriComponents = UriComponentsBuilder.fromUriString("http://server-provider/location")
                .build()
                .expand(user)
                .encode();
        URI uri = uriComponents.toUri();
        return restTemplate.postForEntity(uri,user,User.class).getBody();
    }
    @RequestMapping(value = "/ribbonPost3", method = RequestMethod.POST)
    public String ribbonPost3(){
        User user = new User(001,"lx");
          // 占位符石str, 服务端可以用 @PathVariable获取
        return restTemplate.postForEntity("http://server-provider/rbPost/{str}",user,String.class,"hello")
                .getBody();
    }
    @RequestMapping(value = "/ribbonPost4", method = RequestMethod.POST)
    public String ribbonPost4(){
        Map<String,String> params = new HashMap<>();
        params.put("id","001");
        params.put("name","lx");
        return restTemplate.postForEntity("http://server-provider/mapPost",params,String.class).getBody();
    }
    /**
     *  restTemplate.postForObject()方法与上面用法几乎相同
     *  postForEntity(String url, Object request, Class<T> responseType, Object... uriVariables)
     *  postForEntity(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables)
     *  postForEntity(URI url, Object request, Class<T> responseType)
     *  
     *. postForLocation 也相似,这里就不再举例说明了
     */

服务提供者

@RequestMapping(value = "/rpost", method = RequestMethod.POST)
    public User accpetRibbonPost(@RequestBody User user){
        log.info("id = " + user.getId() + " name = " + user.getName());
        return user;
    }
    @RequestMapping(value = "/location", method = RequestMethod.POST)
    public User acceptRibbonPost2(@RequestBody User user){
        log.info("id = " + user.getId() + " name = " + user.getName());
        return user;
    }
    @RequestMapping(value = "/rbPost/{str}", method = RequestMethod.POST)
    public String accpetRibbonPost3(@PathVariable String str, @RequestBody User user){
        log.info("str = " + str);
        log.info("id = " + user.getId() + " name = " + user.getName());
        return str + " " + user.getId() + " " + user.getName();
    }
    @RequestMapping(value = "/mapPost", method = RequestMethod.POST)
    public String acceptRibbonPost4(@RequestBody Map map){
        String id = (String)map.get("id");
        String name = (String)map.get("name");
        return "id = " + id + " name = " + name;
    }

PUT请求

Restful中的put请求经常用来修改某些属性的值,他和POST请求相似

一般形式

/*
* 它的形式比较少,只有一种比较形式
*/
put(String url, Object request, Object... urlVariables) throws RestClientException
put(String url, Object request, Map<String, ?> urlVariables) throws RestClientException
put(URI url, Object request) throws RestClientException

Ribbon服务端

@RequestMapping(value = "/putRibbon", method = RequestMethod.PUT)
  public void putRibbon(){
    restTemplate.put("http://server-provider/ribbonPut",new User(21,"lx"));
  }

这里只采用了一种简单形式,用法和Post很相似,没有再详细说明

PUT请求没有返回值,可以理解为只把需要的值传过去就可以,修改成功不成功与我没有关系

服务提供者

@RequestMapping(value = "/ribbonPut", method = RequestMethod.PUT)
  public void acceptRibbonPut(@RequestBody User user){
    log.info("user.id = " + user.getId() + " user.name = " + user.getName());
  }

DELETE请求

delete请求在Restful API中一般用于根据id删除某条信息,用法也比较简单,没有返回值

一般形式

delete(String url, Object... urlVariables) throws RestClientException
delete(String url, Map<String, ?> urlVariables) throws RestClientException
delete(URI url) throws RestClientException

Ribbon服务端

@RequestMapping(value = "/deleteRibbon", method = RequestMethod.DELETE)
  public void deleteUser(){
    User user = new User(21,"lx");
    restTemplate.delete("http://server-provider/ribbonDelete/{1}",user.getId());
  }

服务提供者

@RequestMapping(value = "/ribbonDelete/{id}", method = RequestMethod.DELETE)
  public void deleteRibbon(@PathVariable Integer id){
    log.info("delete user " + id);
  }

以上就是Ribbon介绍的全部内容,其实关于Ribbon的源码分析根本没有这么简单,敬请期待……

相关实践学习
SLB负载均衡实践
本场景通过使用阿里云负载均衡 SLB 以及对负载均衡 SLB 后端服务器 ECS 的权重进行修改,快速解决服务器响应速度慢的问题
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
相关文章
|
7月前
|
负载均衡 算法
ribbon的7种负载均衡算法和替换方法
ribbon的7种负载均衡算法和替换方法
176 0
ribbon的7种负载均衡算法和替换方法
|
7月前
|
JSON 负载均衡 Java
Spring Cloud Ribbon:负载均衡的服务调用
Spring Cloud Ribbon:负载均衡的服务调用
107 0
|
18天前
|
负载均衡 Java Nacos
常见的Ribbon/Spring LoadBalancer的负载均衡策略
自SpringCloud 2020版起,Ribbon被弃用,转而使用Spring Cloud LoadBalancer。Ribbon支持轮询、随机、加权响应时间和重试等负载均衡策略;而Spring Cloud LoadBalancer则提供轮询、随机及Nacos负载均衡策略,基于Reactor实现,更高效灵活。
43 0
|
3月前
|
负载均衡 Java Nacos
SpringCloud基础1——远程调用、Eureka,Nacos注册中心、Ribbon负载均衡
微服务介绍、SpringCloud、服务拆分和远程调用、Eureka注册中心、Ribbon负载均衡、Nacos注册中心
SpringCloud基础1——远程调用、Eureka,Nacos注册中心、Ribbon负载均衡
|
4月前
|
缓存 负载均衡 Java
OpenFeign最核心组件LoadBalancerFeignClient详解(集成Ribbon负载均衡能力)
文章标题为“OpenFeign的Ribbon负载均衡详解”,是继OpenFeign十大可扩展组件讨论之后,深入探讨了Ribbon如何为OpenFeign提供负载均衡能力的详解。
OpenFeign最核心组件LoadBalancerFeignClient详解(集成Ribbon负载均衡能力)
|
5月前
|
负载均衡 算法 网络协议
Ribbon 负载均衡源码解读
Ribbon 负载均衡源码解读
64 15
Ribbon 负载均衡源码解读
|
3月前
|
负载均衡 Java 开发者
Ribbon框架实现客户端负载均衡的方法与技巧
Ribbon框架为微服务架构中的客户端负载均衡提供了强大的支持。通过简单的配置和集成,开发者可以轻松地在应用中实现服务的发现、选择和负载均衡。适当地使用Ribbon,配合其他Spring Cloud组件,可以有效提升微服务架构的可用性和性能。
42 0
|
5月前
|
负载均衡 Java API
Feign 进行rpc 调用时使用ribbon负载均衡源码解析
Feign 进行rpc 调用时使用ribbon负载均衡源码解析
80 11
|
6月前
|
缓存 负载均衡 Java
Java一分钟之-Spring Cloud Netflix Ribbon:客户端负载均衡
【6月更文挑战第9天】Spring Cloud Netflix Ribbon是客户端负载均衡器,用于服务间的智能路由。本文介绍了Ribbon的基本概念、快速入门步骤,包括添加依赖、配置服务调用和使用RestTemplate。此外,还讨论了常见问题,如服务实例选择不均、超时和重试设置不当、服务列表更新不及时,并提供了相应的解决策略。最后,展示了如何自定义负载均衡策略。理解并正确使用Ribbon能提升微服务架构的稳定性和效率。
243 3
|
6月前
|
负载均衡 算法 Java
Ribbon怎么实现的负载均衡
Ribbon怎么实现的负载均衡
56 0