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