《深入理解 Spring Cloud 与微服务构建》第七章 负载均衡 Ribbon

文章目录

  • 《深入理解 Spring Cloud 与微服务构建》第七章 负载均衡 Ribbon
  • 一、RestTemplate 简介
    • 1.简介
    • 2.案例
  • 二、Ribbon 简介
  • 三、使用 RestTemplate 和 Ribbon 来消费服务
  • 四、LoadBalancerClient 简介

一、RestTemplate 简介

1.简介

RestTemplate 是 Spring Resources 中一个访问第三方 RESTful API 接口的网络请求框架。RestTemplate 的设计原则和其它 Spring Template(例如 JdbcTemplate、JmsTemplate)类似,都是为执行复杂任务提供了一个具有默认行为的简单方法

RestTemplate 是用来消费 REST 服务的,所以 RestTemplate 的主要方法都与 REST 的 HTTP 协议的一些方法紧密相连,例如 HEAD、GET、POST、PUT、DELETE 和 OPTIONS 等方法,这些方法在 RestTemplate 类对应的方法为 headForHeaders()、getForObject()、postForObject()、put() 和 delete() 等

2.案例

写一个 RestTestController 类,获取 https://www.baidu.com/ 的网页 HTML 代码

通过 RestTemplate 的 getForObject() 方法就可以获取 https://www.baidu.com/ 的网页 HTML 代码,并在 API 接口 “/testRest” 返回该网页的 HTML 字符串。代码如下:

package com.sisyphus.controller;import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;@RestController
public class RestTestController {public String testRest(){RestTemplate restTemplate = new RestTemplate();return restTemplate.getForObject("https://www.baidu.com/", String.class);}
}

RestTemplate 支持常见的 HTTP 协议的请求方法,例如 GET、POST、PUT 和 DELETE 等,所以用 RestTemplate 很容易构建 RESTful API。在上面的 RestTestController 中,RestTemplate 用 Get 方法获取 https://ww.baidu.com 网页的 HTML 字符串。RestTemplate 的使用很简单,它支持 XML 和 JSON 数据格式,默认实现了序列化,可以自动将 JSON 字符串转换为实体。例如以下代码可以将返回的 JSON 字符串转换成一个 User 对象

User user = restTemplate.getForObject("https://wwww.~~~.com/", User.class);

二、Ribbon 简介

负载均衡是指将负载分摊到多个执行单元上,常见的负载均衡有两种方式:一种是独立进程单元,通过负载均衡策略,将请求转发到不同的执行单元上,例如 Nginx;另一种是将负载均衡逻辑以代码的形式封装到服务消费者的客户端上,服务消费者客户端维护了一份服务提供者的信息列表,有了信息列表,通过负载均衡策略将请求分摊给多个服务提供者,从而达到负载均衡的目的

Ribbon 是 Netflix 公司开源的一个负载均衡的组件,属于上述的第二种方式,是将负载均衡逻辑封装在服务消费者的客户端中,并且运行在客户端的进程里。Ribbon 是一个经过了云端测试的 IPC 库,可以很好地控制 HTTP 和 TCP 客户端的负载均衡行为

在 Spring Cloud 构建的微服务系统中,Ribbon 作为服务消费者的负载均衡器,有两种使用方式,一种是和 RestTemplate 相结合,另一种是和 Feign 相结合,Feign 已经默认集成了 Ribon

Ribbon 有很多子模块,但很多模块没有用于生产环境,目前 Netflix 公司用于生产环境的 Ribbon 子模块如下:

  • ribbon-loadbalancer:可以独立使用或与其它模块一起使用的负载均衡器 API
  • ribbon-eureka:Ribbon 结合 Eureka 客户端的 API,为负载均衡器提供动态服务注册列表信息
  • ribbon-core:Ribbon 的核心 API

三、使用 RestTemplate 和 Ribbon 来消费服务

本案例是在第六章 Eureka 案例的基础上进行改造的,先回顾一下 Eureka 案例的代码结构,它包括一个服务注册中心 eureka-server、一个服务提供者 eureka-client。eureka-client 向 eureka-server 注册服务,并且 eureka-client 提供了一个 “/hi” API 接口用于提供服务

启动 eureka-server,端口为 8761.启动两个 eureka-client 实例,端口分别为 8762 和 8763.启动完成后,在浏览器上访问 http://localhost:8761/,浏览器显示 eureka-client 的两个实例已经成功向服务注册中心注册,它们的端口分别为 8762 和 8763

再创建一个 Module 工程,取名为 eureka-ribbon-client,其作为服务消费者,通过 RestTemplate 来远程调用 eureka-client 服务 API 接口的 “/hi”,即消费服务

创建完成 eureka-ribbon-client 的 Module 工程之后,在其 pom 文件中引入相关的依赖,包括继承了主 Maven 工程的 pom 文件,引入了 Eureka Client 的起步依赖 spring-cloud-starter-eureka、Ribbon 的起步依赖 spring-cloud-starter-ribbon,以及 WEB 的起步依赖 spring-boot-starter-web,代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>Eureka</artifactId><groupId>org.sisyphus</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>eureka-ribbon-client</artifactId><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-ribbon</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency></dependencies></project>

在工程的配置文件 application.yml 做程序的相关配置,包括指定程序名为 eureka-ribbon-client,程序的端口号为 8764,服务的注册地址为 http://localhost:8761/eureka/,代码如下:

spring:application:name: eureka-ribbon-clientserver:port: 8764eureka:client:serviceUrl:defaultZone: http://localhost:8761/eureka/

另外,作为 Eureka Client 需要在程序的入口类加上注解 @EnableEurekaClient 开启 Eureka Client 功能,代码如下:

package com.sisyphus;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;@SpringBootApplication
@EnableEurekaClient
public class EurekaRibbonClientApplication {public static void main(String[] args) {SpringApplication.run(EurekaRibbonClientApplication.class, args);}
}

写一个 RESTful API 接口,在该 API 接口内部需要调用 eureka-client 的 API 接口 “/hi”,即服务消费。由于 eureka-client 为两个实例,它们的端口为 8762 和 8763。在调用 eureka-client 的 API 接口 “/hi” 时希望做到轮流访问这两个实例,它们的端口为 8762 和 8763。在调用 eureka-client 的 API 接口 “/hi” 时希望做到轮流访问这两个实例,这时就需要将 RestTemplate 和 Ribbon 相结合进行负载均衡

通过查阅官方文档,可以知道如何将它们结合在一起,只需要在程序的 IoC 容器中注入一个 restTemplate 的 Bean。并在这个 Bean 上加上 @LoadBalanced 注解,此时 RestTemplate 就结合了 Ribbon 开启了负载均衡功能,代码如下:

package com.sisyphus.config;import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;@Configuration
public class RibbonConfig {@Bean@LoadBalancedRestTemplate restTemplate(){return new RestTemplate();}
}

写一个 RibbonService 类,在该类的 hi() 方法用 restTemplate 调用 eureka-client 的 API 解耦,此时 Uri 上不需要使用硬编码(例如 IP 地址),只需要写服务命 eureka-client 即可,代码如下:

package com.sisyphus.service;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;@Service
public class RibbonService {@AutowiredRestTemplate restTemplate;public String hi(String name){return restTemplate.getForObject("http://eureka-client/hi?name=" + name, String.class);}
}

写一个 RibbonController 类,为该类加上 @RestController 注解,开启 RestController 的功能,写一个 “/hi” Get 方法的接口,调用 RibbonService 类的 hi() 方法,代码如下:

package com.sisyphus.controller;import com.sisyphus.service.RibbonService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;@RestController
public class RibbonController {@AutowiredRibbonService ribbonService;@GetMapping("/hi")public String hi(@RequestParam(required = false, defaultValue = "sisyphus") String name){return ribbonService.hi(name);}
}

启动 eureka-ribbon-client 工程,在浏览器上访问 http://localhost:8761,显示的 Eureka Server 的主界面如下图所示。在主界面上发现有两个服务被注册,分别为 eureka-client 和 eureka-ribbon-client,其中 eureka-client 有两个实例,端口为 8762 和 8763,而 eureka-ribbon-client 的端口为 8764

在浏览器上多次访问 http://localhost:8764/hi?name=sisyphus,浏览器会轮流显示两个端口

这时可以发现,当访问 http://localhost:8764/hi?name=sisyphus 的 API 接口时,负载均衡起了作用,负载均衡器会轮流地请求 eureka-client 的两个实例中的 “/hi” API 接口

四、LoadBalancerClient 简介

负载均衡器的核心类为 LoadBalancerClient,LoadBalancerClient 可以获取负载均衡的服务提供者的实例信息。为了演示,在 RibbonController 重新写一个接口 “/testRibbon”,通过 LoadBalancerClient 去选择一个 eureka-client 的服务实例的信息,并将该信息返回,代码如下:

package com.sisyphus.controller;import com.sisyphus.service.RibbonService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;@RestController
public class RibbonController {@AutowiredRibbonService ribbonService;private LoadBalancerClient loadBalancer;@GetMapping("/hi")public String hi(@RequestParam(required = false, defaultValue = "sisyphus") String name){return ribbonService.hi(name);}@GetMapping("/testRibbon")public String testRibbon(){ServiceInstance instance = loadBalancer.choose("eureka-client");return instance.getHost()+":"+instance.getPort();}
}

重新启动工程,在浏览器上多次访问 http://localhost:8764/testRibbon,浏览器会轮流显示如下内容:

localhost:8762
localhost:8763

可见,LoadBalancerClient 的 choose(“eureka-client”) 方法可以轮流地到 eureka-client 的两个服务实例的信息

那么负载均衡器是怎么获取到这些客户端的信息的呢?查看官方文档可以知道,负载均衡器 LoadBalancerClient 是从 Eureka Client 获取服务注册列表信息的,并将服务注册列表信息缓存了一份。在 LoadBalancerClient 调用 choose() 方法时,根据负载均衡策略选择一个服务实例的信息,从而进行了负载均衡。LoadBalancerClient 也可以不从 Eureka Client 获取注册列表信息,这时需要自己维护一份服务注册列表信息

我们再创建一个工程,取名为 ribbon-client。ribbon-client 工程的 pom 文件同 eureka-ribbon-client 工程的类似

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>Eureka</artifactId><groupId>org.sisyphus</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>ribbon-client</artifactId><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-ribbon</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency></dependencies></project>

在配置文件 application.yml 中,通过配置 ribbon.eureka.enable 为 false 来禁止调用 Eureka Client 获取注册列表。在配置文件 application.yml 中有一个程序名为 stores 的服务,有两个不同 Url 地址(例如 example.com 和 google.com)的服务实例,通过 stores.ribbon.listOfServers 来配置这些服务实例的 URL,代码如下:

stores:ribbon:listOfServers: example.com,google.comribbon:eureka:enabled: false
server:port: 8769

新建一个 RerstController 类,创建一个 API 接口 “/testRibbon”。在 RestController 类注入 LoadBalancerClient,通过 LoadBalancerClient 的 choose() 方法获取服务实例的信息,代码如下:

package com.sisyphus.controller;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class RibbonController {@Autowiredprivate LoadBalancerClient loadBalancer;@GetMapping("/testRibbon")public String testRibbon(){ServiceInstance instance = loadBalancer.choose("stores");return instance.getHost()+":"+instance.getPort();}
}

启动工程,在浏览器上多次访问 http://localhost:8769/testRibbon,浏览器会交替出现如下内容:

example.com:80
google.com:80

现在我们可以知道,在 Ribbon 中的负载均衡客户端为 LoadBalancerClient。在 Spring Cloud 项目中,负载均衡器 Ribbon 会默认从 Eureka Client 的服务注册列表中获取服务的信息,并缓存一份。根据缓存的服务注册列表信息,可以通过 LoadBalancerClient 来选择不同的服务实例,从而实现负载均衡。如果禁止 Ribbon 从 Eureka 获取注册列表信息,则需要自己去维护一份服务注册列表信息。根据自己维护服务注册列表的信息,Ribbon 也可以实现负载均衡

《深入理解 Spring Cloud 与微服务构建》第七章 负载均衡 Ribbon相关推荐

  1. 《深入理解 Spring Cloud 与微服务构建》第九章 熔断器 Hystrix

    <深入理解 Spring Cloud 与微服务构建>第九章 熔断器 Hystrix 文章目录 <深入理解 Spring Cloud 与微服务构建>第九章 熔断器 Hystrix ...

  2. 《深入理解 Spring Cloud 与微服务构建》第二章 微服务应该具备的功能

    <深入理解 Spring Cloud 与微服务构建>第二章 微服务应该具备的功能 文章目录 <深入理解 Spring Cloud 与微服务构建>第二章 微服务应该具备的功能 一 ...

  3. 《深入理解 Spring Cloud 与微服务构建》第一章 微服务简介

    <深入理解 Spring Cloud 与微服务构建>第一章 微服务简介 文章目录 <深入理解 Spring Cloud 与微服务构建>第一章 微服务简介 一.单体架构及其存在的 ...

  4. 《深入理解Spring Cloud与微服务构建》出版啦!

    作者简介 方志朋,毕业于武汉理工大学,CSDN博客专家,专注于微服务.大数据等领域,乐于分享,爱好开源,活跃于各大开源社区.著有<史上最简单的Spring Cloud教程>,累计访问量超过 ...

  5. 《深入理解 Spring Cloud 与微服务构建》第十八章 使用 Spring Security OAuth2 和 JWT 保护微服务系统

    <深入理解 Spring Cloud 与微服务构建>第十八章 使用 Spring Security OAuth2 和 JWT 保护微服务系统 文章目录 <深入理解 Spring Cl ...

  6. 《深入理解 Spring Cloud 与微服务构建》第十七章 使用 Spring Cloud OAuth2 保护微服务系统

    <深入理解 Spring Cloud 与微服务构建>第十七章 使用 Spring Cloud OAuth2 保护微服务系统 文章目录 <深入理解 Spring Cloud 与微服务构 ...

  7. 《深入理解 Spring Cloud 与微服务构建》第十六章 Spring Boot Security 详解

    <深入理解 Spring Cloud 与微服务构建>第十六章 Spring Boot Security 详解 文章目录 <深入理解 Spring Cloud 与微服务构建>第十 ...

  8. 《深入理解 Spring Cloud 与微服务构建》第十五章 微服务监控 Spring Boot Admin

    <深入理解 Spring Cloud 与微服务构建>第十五章 微服务监控 Spring Boot Admin 文章目录 <深入理解 Spring Cloud 与微服务构建>第十 ...

  9. 《深入理解 Spring Cloud 与微服务构建》第十四章 服务链路追踪 Spring Cloud Sleuth

    <深入理解 Spring Cloud 与微服务构建>第十四章 服务链路追踪 Spring Cloud Sleuth 文章目录 <深入理解 Spring Cloud 与微服务构建> ...

  10. 《深入理解 Spring Cloud 与微服务构建》第十三章 配置中心 Spring Cloud Config

    <深入理解 Spring Cloud 与微服务构建>第十三章 配置中心 Spring Cloud Config 文章目录 <深入理解 Spring Cloud 与微服务构建>第 ...

最新文章

  1. 毕业后五年之内将决定你的一生
  2. weather at Cambridge will be fine next week
  3. 安徽大学计算机考研失败,回馈:2014年安徽大学计算机初试回忆题,我是雷锋,低调...
  4. 三相逆变器双pi控制器参数如何调节_一分钟看懂维也纳三相整流器
  5. SAP的SqlAnyWhere中数据库日志文件删除后如何新建
  6. C语言、嵌入式重点知识:回调函数
  7. 02ODBC基本概念
  8. java 减少内存_java – 减少内存流失的方法
  9. pytorch损失函数(正在更新中,勿点进来看)
  10. Linux 使用 ffmpeg 开发
  11. atitit.查看预编译sql问号 本质and原理and查看原生sql语句
  12. 一招,提高 Github 成员微信群运营效率
  13. 工具类|Java生成商城常用的核销码,长度12位~
  14. 设计模式之里氏替换原则
  15. python 加速度_「加速度公式」加速度公式1 - seo实验室
  16. PTA 7-192 浪漫的表白
  17. 小米的抢购骗局+小米的抢购页面的源代码分析(二)文本数组的分析
  18. 全面分析前端的网络请求方式
  19. CircuitBreaker断路器
  20. 测试markdown语法

热门文章

  1. c语言告白,C语言告白代码,一闪一闪亮晶晶~
  2. xamppmysql访问被拒绝_XAMPP中无法开始MySQL的问题
  3. codeforces 892E(离散化+可撤销并查集)
  4. 【bzoj4868】[Shoi2017]期末考试 前缀和+暴力
  5. Android入门:封装一个HTTP请求的辅助类
  6. c++ lamda表达式调用自身实现定时器
  7. Linux常用命令备忘(启动管理、文件或目录管理、系统管理)
  8. php添加gd库,linux下为php添加GD库(重新编译php)
  9. Spark源码分析之Sort-Based Shuffle读写流程
  10. SubscriptionState分析