一、简介

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

二、客户端负载均衡

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

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

​ 而客户端负载均衡和服务端负载均衡最大的不同点在于上面所提到服务清单的存储位置。在客户端负载均衡中,所有客户端节点都维护着自己要访问的服务清单,而这些服务清单都来自注册中心,比如我们上一章介绍的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 : 统一资源名称****

三者之间的关系:

一般用法

  • 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);
}

参考资料:

https://www.jb51.net/article/138563.htm#comments

《Spring Cloud 微服务实战》

Spring Cloud Ribbon负载均衡的更多相关文章

  1. Spring Cloud Ribbon负载均衡(快速搭建)

    Spring Cloud Ribbon 是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现.通过 Spring Cloud 的封装, 可以让我们轻松地将面向服务的 ...

  2. SpringCloud微服务实战二:Spring Cloud Ribbon 负载均衡 + Spring Cloud Feign 声明式调用

    1.Spring Cloud Ribbon的作用 Ribbon是Netflix开发的一个负载均衡组件,它在服务体系中起着重要作用,Pivotal将其整合成为Spring Cloud Ribbon,与其 ...

  3. Spring Cloud Ribbon负载均衡配置类放在Spring boot主类同级增加Exclude过滤后报Field config in com.cloud.web.controller.RibbonConfiguration required a bean of type 'com.netflix.client.config.IClientConfig' that could not b

    环境: Spring Cloud:Finchley.M8 Spring Boot:2.0.0.RELEASE 目录结构: 可以看到代码第13行的注释,我已经在@ComponentScan注解中添加了E ...

  4. spring cloud 客户端负载均衡 - Ribbon

    Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,基于Netflix Ribbon实现的,Ribbon不像注册中心.网关那样需要单独部署,它是作为一个工具直接集成到 ...

  5. Spring Cloud06: Ribbon 负载均衡

    一.使用背景 前面的学习中,我们已经使用RestTemplate来实现了服务消费者对服务提供者的调用,如果在某个具体的业务场景下,对某个服务的调用量突然大幅提升,这个时候就需要对该服务实现负载均衡以满 ...

  6. Spring Cloud之负载均衡组件Ribbon原理分析

    目录 前言 一个问题引发的思考 Ribbon的简单使用 Ribbon 原理分析 @LoadBalanced 注解 @Qualifier注解 LoadBalancerAutoConfiguration ...

  7. 客户端负载均衡Ribbon之一:Spring Cloud Netflix负载均衡组件Ribbon介绍

    Netflix:['netfliːks] ribbon:英[ˈrɪbən]美[ˈrɪbən]n. 带; 绶带; (打印机的) 色带; 带状物;v. 把…撕成条带; 用缎带装饰; 形成带状;     L ...

  8. spring boot分布式技术,spring cloud,负载均衡,配置管理器

    spring boot分布式的实现,使用spring cloud技术. 下边是我理解的spring cloud的核心技术: 1.配置服务器 2.注册发现服务器eureka(spring boot默认使 ...

  9. 微服务架构之spring cloud ribbon

    现在负载均衡是通用的解决分压的技术方案,实现方式一般分为服务端或者客户端,服务端大部分是使用中间件实现,spring cloud ribbon 是一个客户端负载均衡组件.跟spring cloud e ...

随机推荐

  1. nginx gzip

    # 开启gzip gzip on;   # 启用gzip压缩的最小文件,小于设置值的文件将不会压缩 gzip_min_length 1k;   # gzip 压缩级别,1-10,数字越大压缩的越好,也 ...

  2. C语言学习笔记--const 和 volatile关键字

    1.const关键字 (1)const 修饰的变量是只读的,它不是真正的常量,本质还是变量,只是告诉编译器不能出现在赋值号左边! (2)const 修饰的局部变量在栈上分配空间 (3)const 修饰 ...

  3. <table>的使用以及确定取消按钮的设置

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  4. hadoop job 重要性能参数

    name 说明 mapred.task.profile 是否对任务进行profiling,调用java内置的profile功能,打出相关性能信息 mapred.task.profile.{maps|r ...

  5. img src 直接显示图片字符串,微信例子

    <div class="weui-cell__hd"><img src="data:image/png;base64,iVBORw0KGgoAAAANS ...

  6. C#中的?问号用法总结

    在C#中有个较为重要,而常被一些人忽视的符号——问号(?).在这里整理一下它在C#的几种情况: 可空类型修饰符“T?”:可空类型的基础类型可以是任何非可空值类型或任何具有struct约束的类型参数,但 ...

  7. 32.我的wafBypass之道

    0x01 搞起 当我们遇到一个waf时,要确定是什么类型的?先来看看主流的这些waf,狗.盾.神. 锁.宝.卫士等等...(在测试时不要只在官网测试,因为存在版本差异导致规则库并不一致) 1.云waf ...

  8. 1.2 DVWA亲测sql注入漏洞(blind)

    LOW等级     我们尝试输入:   即如果页面返回为假,则说明后面的语句成功注入 据此我们可以知道 1' and 真 --> 页面显示 “User ID exists in the data ...

  9. 【mysql存储引擎】

    看你的mysql现在已提供什么存储引擎: mysql> show engines;   看你的mysql当前默认的存储引擎: mysql> show variables like '%st ...

  10. 在Android中使用FlatBuffers(上篇)

    本文来自网易云社区. 总览 先来看一下 FlatBuffers 项目已经为我们提供了什么,而我们在将 FlatBuffers 用到我们的项目中时又需要做什么的整体流程.如下图: 在使用 FlatBuf ...