目录
一, 简介
二, 客户端负载均衡
三, RestTemplate 详解
GET 请求
POST 请求
PUT 请求
DELETE 请求
一, 简介
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 微服务实战》
来源: https://www.cnblogs.com/cxuanBlog/p/10941576.html