springboot 提供的远程调用工具
相似于 HttpClient,能够发送 http 请求,并处理响应。RestTemplate简化了Rest API调用,只须要使用它的一个方法,就能够完成请求、响应、Json转换java
方法:web
getForObject(url, 转换的类型.class, 提交的参数) postForObject(url, 协议体数据, 转换的类型.class)
RestTemplate 和 Dubbo 远程调用的区别:spring
RestTemplate:浏览器
Dubbo:springboot
以前的系统结构是浏览器直接访问后台服务
后面咱们经过一个Demo项目演示 Spring Cloud 远程调用服务器
下面咱们先不使用ribbon, 单独使用RestTemplate来执行远程调用网络
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); } }
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); }
Springcloud集成的工具,做用是负载均衡,和重试app
Ribbon 对 RestTemplate 作了封装,加强了 RestTemplate 的功能负载均衡
添加负载均衡dom
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency>
@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); } }
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); } }
一种容错机制,当调用远程服务失败,能够自动重试调用
添加剧试:
<dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> </dependency>
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
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); } }
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(); } }