Ribbon
RestTemplate 近程调用
springboot 提供的近程调用工具
相似于 HttpClient,能够发送 http 申请,并解决响应。RestTemplate 简化了 Rest API 调用,只须要应用它的一个办法,就能够实现申请、响应、Json 转换
办法:
getForObject(url, 转换的类型.class, 提交的参数)
postForObject(url, 协定体数据, 转换的类型.class)
RestTemplate 和 Dubbo 近程调用的区别:
-
RestTemplate:
- http 调用
- 效率低
-
Dubbo:
- RPC 调用,Java 的序列化
- 效率高
之前的系统结构是浏览器间接拜访后盾服务
前面咱们通过一个 Demo 我的项目演示 Spring Cloud 近程调用
上面咱们先不应用 ribbon, 独自应用 RestTemplate 来执行近程调用
- 新建 ribbon 我的项目
- pom.xml
- application.yml
- 主程序
- controller
- 启动,并拜访测试
新建 sp06-ribbon 我的项目
pom.xml
- eureka-client 中曾经蕴含 ribbon 依赖
- 须要增加 sp01-commons 依赖
application.yml
spring:
application:
name: ribbon
server:
port: 3001
eureka:
client:
service-url:
defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
主程序
- 创立
RestTemplate
实例RestTemplate
是用来调用其余微服务的工具类,封装了近程调用代码,提供了一组用于近程调用的模板办法,例如:getForObject()
、postForObject()
等
package cn.tedu.sp06;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@EnableDiscoveryClient
@SpringBootApplication
public class Sp06RibbonApplication {
// 创立 RestTemplate 实例,并存入 spring 容器
@Bean
public RestTemplate getRestTemplate() {return new RestTemplate();
}
public static void main(String[] args) {SpringApplication.run(Sp06RibbonApplication.class, args);
}
}
RibbonController
package cn.tedu.sp06.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import cn.tedu.sp01.pojo.Item;
import cn.tedu.sp01.pojo.Order;
import cn.tedu.sp01.pojo.User;
import cn.tedu.web.util.JsonResult;
@RestController
public class RibbonController {
@Autowired
private RestTemplate rt;
@GetMapping("/item-service/{orderId}")
public JsonResult<List<Item>> getItems(@PathVariable String orderId) {
// 向指定微服务地址发送 get 申请,并取得该服务的返回后果
//{1} 占位符,用 orderId 填充
return rt.getForObject("http://localhost:8001/{1}", JsonResult.class, orderId);
}
@PostMapping("/item-service/decreaseNumber")
public JsonResult decreaseNumber(@RequestBody List<Item> items) {
// 发送 post 申请
return rt.postForObject("http://localhost:8001/decreaseNumber", items, JsonResult.class);
}
Ribbon
Springcloud 集成的工具,作用是负载平衡,和重试
负载平衡
Ribbon 对 RestTemplate 做了封装,加强了 RestTemplate 的性能
- 取得地址表
- 轮询一个服务的主机地址列表
- RestTemplate 负责执行最终调用
增加负载平衡
- ribbon 依赖
- @LoadBalanced 注解,对 RestTemplate 进行性能加强
- 批改调用地址,应用 服务 id,而不是具体主机地址
增加 ribbon 起步依赖(可选)
- eureka 依赖中曾经蕴含了 ribbon
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
RestTemplate 设置 @LoadBalanced
@LoadBalanced
负载平衡注解,会对 RestTemplate
实例进行封装,创立动静代理对象,并切入(AOP)负载平衡代码,把申请散发到集群中的服务器
package cn.tedu.sp06;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@EnableDiscoveryClient
@SpringBootApplication
public class Sp06RibbonApplication {
@LoadBalanced // 负载平衡注解
@Bean
public RestTemplate getRestTemplate() {return new RestTemplate();
}
public static void main(String[] args) {SpringApplication.run(Sp06RibbonApplication.class, args);
}
}
拜访门路设置为服务 id
package cn.tedu.sp06.controller;
import cn.tedu.sp01.pojo.Item;
import cn.tedu.sp01.pojo.Order;
import cn.tedu.sp01.pojo.User;
import cn.tedu.web.util.JsonResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController
@Slf4j
public class RibbonController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/item-service/{orderId}")
public JsonResult<List<Item>> getItems(@PathVariable String orderId){
// 近程调用商品服务
//http://localhost:8001/{orderId}
//{1} -- RestTemplate 定义的一种占位符格局, 传递参数 orderId
//return restTemplate.getForObject("http://localhost:8001/{1}",JsonResult.class,orderId);
return restTemplate.getForObject("http://item-service/{1}",JsonResult.class,orderId);//Ribbon 的形式,将 ip:port 改为服务名称
}
@PostMapping("/item-service/decreaseNumber")
public JsonResult<?> decreaseNumber(@RequestBody List<Item> items){return restTemplate.postForObject("http://item-service/decreaseNumber", items, JsonResult.class);
}
// -----------------------
@GetMapping("/user-service/{userId}")
public JsonResult<User> getUser(@PathVariable Integer userId){return restTemplate.getForObject("http://user-service/{1}", JsonResult.class,userId);
}
@GetMapping("/user-service/{userId}/score")
public JsonResult<?> addScore(@PathVariable Integer userId,Integer score){return restTemplate.getForObject("http://user-service/{1}/score?score={2}", JsonResult.class,userId,score);
}
@GetMapping("/order-service/{orderId}")
public JsonResult<Order> getOrder(@PathVariable String orderId){return restTemplate.getForObject("http://order-service/{1}", JsonResult.class,orderId);
}
@GetMapping("/order-service/")
public JsonResult<?> addOrder(){return restTemplate.getForObject("http://order-service/", JsonResult.class);
}
}
拜访测试
- 拜访测试,ribbon 会把申请散发到 8001 和 8002 两个服务端口上
http://localhost:3001/item-service/34
Ribbon 重试
一种容错机制,当调用近程服务失败,能够主动重试调用
增加重试:
1. 增加 spring-retry 依赖
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
2. 配置重试参数
在 yml 中配置
MaxAutoRetries:单台服务器的
MaxAutoRetriesNextServer:更换服务器的次数
OkToRetryOnAllOperations=true: 默认只对 GET 申请重试, 当设置为 true 时, 对 POST 等所有类型申请都重试
spring:
application:
name: ribbon
server:
port: 3001
eureka:
client:
service-url:
defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
ribbon:
MaxAutoRetriesNextServer: 2
MaxAutoRetries: 1
OkToRetryOnAllOperations: true
在 java 代码中设置
ConnectTimeout:与近程服务建设网络连接的超时工夫
ReadTimeout:连贯已建设,申请已发送,期待响应的超时工夫
package cn.tedu.sp06;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
public class Sp06RibbonApplication {public static void main(String[] args) {SpringApplication.run(Sp06RibbonApplication.class, args);
}
/*
创立 RestTemplate 实例,放入 spring 容器
*/
@Bean
@LoadBalanced// 对 RestTemplate 进行加强与封装,增加 Ribbon 负载平衡性能
public RestTemplate restTemplate(){
// 设置调用超时工夫,超时后认为调用失败
SimpleClientHttpRequestFactory f = new SimpleClientHttpRequestFactory();
f.setConnectTimeout(1000); // 建设连贯的等待时间
f.setReadTimeout(1000); // 连贯建设后,发送申请后。期待接管响应的工夫
return new RestTemplate(f);
}
}
item-service 的 ItemController 增加提早代码,以便测试 ribbon 的重试机制
package cn.tedu.sp02.item.controller;
import java.util.List;
import java.util.Random;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import cn.tedu.sp01.pojo.Item;
import cn.tedu.sp01.service.ItemService;
import cn.tedu.web.util.JsonResult;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RestController
public class ItemController {
@Autowired
private ItemService itemService;
@Value("${server.port}")
private int port;
@GetMapping("/{orderId}")
public JsonResult<List<Item>> getItems(@PathVariable String orderId) throws Exception {log.info("server.port="+port+", orderId="+orderId);
///-- 设置随机提早
if(Math.random()<0.9) {long t = new Random().nextInt(5000);
log.info("item-service-"+port+"- 暂停"+t);
Thread.sleep(t);
}
///~~
List<Item> items = itemService.getItems(orderId);
return JsonResult.ok(items).msg("port="+port);
}
@PostMapping("/decreaseNumber")
public JsonResult decreaseNumber(@RequestBody List<Item> items) {itemService.decreaseNumbers(items);
return JsonResult.ok();}
}
拜访,测试 ribbon 重试机制
- 通过 ribbon 拜访 item-service,当超时,ribbon 会重试申请集群中其余服务器
http://localhost:3001/item-service/35
- ribbon 的重试机制,在 feign 和 zuul 中进一步进行了封装,后续能够应用 feign 或 zuul 的重试机制