一、ribbon 服务消费者
ribbon 提供了负载平衡和重试性能, 它底层是应用 RestTemplate 进行 Rest api 调用
1.RestTemplate
RestTemplate 是SpringBoot提供的一个Rest近程调用工具
它的罕用办法:
- getForObject() - 执行get申请
- postForObject() - 执行post申请
之前的系统结构是浏览器间接拜访后盾服务
前面咱们通过一个Demo我的项目演示 Spring Cloud 近程调用
上面咱们先不应用ribbon, 独自应用RestTemplate来执行近程调用
1.1 新建 sp06-ribbon 我的项目
1.2 批改pom文件
- eureka-client 中曾经蕴含 ribbon 依赖
- 须要增加 sp01-commons 依赖
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.1.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>cn.tedu</groupId> <artifactId>sp06-ribbon</artifactId> <version>0.0.1-SNAPSHOT</version> <name>sp06-ribbon</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Hoxton.RELEASE</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>cn.tedu</groupId> <artifactId>sp01-commons</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build></project>
1.3 批改application.yml
spring: application: name: ribbon server: port: 3001 eureka: client: service-url: defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
1.4 主程序
- 创立
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@SpringBootApplicationpublic class Sp06RibbonApplication { //创立 RestTemplate 实例,并存入 spring 容器 @Bean public RestTemplate getRestTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(Sp06RibbonApplication.class, args); }}
1.5 创立 RibbonController 类
package com.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@Slf4jpublic class RibbonController { @Autowired private RestTemplate rt; @GetMapping("/item-service/{orderId}") public JsonResult<List<Item>> getItems(@PathVariable String orderId) { // 调用近程的商品服务,取得订单的商品列表 // {1} - RestTemplate本人定义的一种站位符格局 return rt.getForObject("http://localhost:8001/{1}", JsonResult.class, orderId); //用 orderId 填充占位符 {1} } @PostMapping("/item-service/decreaseNumber") public JsonResult<?> decreaseNumber(@RequestBody List<Item> items) { return rt.postForObject( "http://localhost:8001/decreaseNumber", items, JsonResult.class); } @GetMapping("/user-service/{userId}") public JsonResult<User> getUser(@PathVariable Integer userId) { return rt.getForObject("http://localhost:8101/{1}", JsonResult.class, userId); } @GetMapping("/user-service/{userId}/score") // ?score=1000 public JsonResult<?> addScore(@PathVariable Integer userId, @RequestParam Integer score) { return rt.getForObject("http://localhost:8101/{1}/score?score={2}", JsonResult.class, userId, score); } @GetMapping("/order-service/{orderId}") public JsonResult<Order> getOrder(@PathVariable String orderId) { return rt.getForObject("http://localhost:8201/{1}", JsonResult.class, orderId); } @GetMapping("/order-service/") public JsonResult<?> addOrder() { return rt.getForObject("http://localhost:8201/",JsonResult.class); }}
1.6 启动服务,并拜访测试
http://eureka1:2001
http://localhost:3001/item-service/35http://localhost:3001/item-service/decreaseNumber
应用postman,POST发送以下格局数据:[{"id":1, "name":"abc", "number":23},{"id":2, "name":"def", "number":11}]
http://localhost:3001/user-service/7
http://localhost:3001/user-service/7/score?score=100
http://localhost:3001/order-service/123abc
http://localhost:3001/order-service/
2. ribbon 负载平衡和重试
2.1 Ribbon 负载平衡
2.1.1 增加 ribbon 依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId></dependency>
2.1.2 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@SpringBootApplicationpublic class Sp06RibbonApplication { @LoadBalanced //负载平衡注解 @Bean public RestTemplate getRestTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(Sp06RibbonApplication.class, args); }}
2.1.3 拜访门路设置为服务id
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;@RestControllerpublic class RibbonController { @Autowired private RestTemplate rt; @GetMapping("/item-service/{orderId}")@HystrixCommand(fallbackMethod = "getItemsFB")public JsonResult<List<Item>> getItems(@PathVariable String orderId) { //调用近程的商品服务,取得订单的商品列表 //{1} - RestTemplate本人定义的一种站位符格局@PostMapping("/item-service/decreaseNumber")public JsonResult<?> decreaseNumber(@RequestBody List<Item> items) { return rt.postForObject("http://item-service/decreaseNumber", items, JsonResult.class);}@GetMapping("/user-service/{userId}")public JsonResult<User> getUser(@PathVariable Integer userId) { return rt.getForObject("http://user-service/{1}", JsonResult.class, userId);}@GetMapping("/user-service/{userId}/score") // ?score=1000public JsonResult<?> addScore(@PathVariable Integer userId, @RequestParam Integer score) { return rt.getForObject("http://user-service/{1}/score?score={2}", JsonResult.class, userId, score);}@GetMapping("/order-service/{orderId}")public JsonResult<Order> getOrder(@PathVariable String orderId) { return rt.getForObject("http://order-service/{1}", JsonResult.class, orderId);}@GetMapping("/order-service")public JsonResult<?> addOrder() { return rt.getForObject("http://order-service", JsonResult.class);}
2.1.4 拜访测试
- 拜访测试,ribbon 会把申请散发到 8001 和 8002 两个服务端口上
http://localhost:3001/item-service/34
2.2 ribbon 重试
2.2.1 增加 ribbon 依赖
<dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId></dependency>
2.2.2 配置 application.yml
spring: application: name: ribbon server: port: 3001#连贯eurekaeureka: client: service-url: defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eurekaribbon: #单台重试次数 MaxAutoRetries: 1 #更换服务器次数 MaxAutoRetriesNextServer: 2
2.2.3 主程序设置 RestTemplate 的申请工厂的超时属性
package com.tedu.sp06;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;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@EnableCircuitBreaker//启用断路器public class Sp06RibbonApplication { public static void main(String[] args) { SpringApplication.run(Sp06RibbonApplication.class, args); } //创立 RestTemplate 实例,并存入 spring 容器 @LoadBalanced//负载平衡注解,对 RestTemplate 进行封装,加强RestTemplate的性能 @Bean public RestTemplate getRestTemplate (){ SimpleClientHttpRequestFactory f=new SimpleClientHttpRequestFactory(); //两个超时参数默认-1,示意不启用 f.setConnectTimeout(1000); f.setReadTimeout(1000); return new RestTemplate();}}
2.2.4 ItemController 增加提早代码,以便测试 ribbon 的重试机制
package cn.tedu.sp02.item.controller;import cn.tedu.sp01.pojo.Item;import cn.tedu.sp01.service.ItemService;import cn.tedu.web.util.JsonResult;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.springframework.web.bind.annotation.*;import java.util.List;import java.util.Random;@RestController@Slf4jpublic class ItemController { @Autowired private ItemService itemService; // 为了前面测试集群多台服务器,这里注入端口号 @Value("${server.port}") private int port; @GetMapping("/{orderId}") public JsonResult<List<Item>> getItems(@PathVariable String orderId) throws InterruptedException { log.info("orderId="+orderId+", port="+port); //增加随机提早代码 if (Math.random()<0.9){ //随机产生0到5000的随机提早时长 long t=new Random().nextInt(5000); log.info("提早:"+t); Thread.sleep(t); } // List<Item> items = itemService.getItems(orderId); return JsonResult.ok().msg("port="+port).data(items); } // 只解决post申请 // @RequestBody 注解,残缺接管post申请协定体数据 @PostMapping("/decreaseNumber") public JsonResult<?> decreaseNumber(@RequestBody List<Item> items) { itemService.decreaseNumber(items); return JsonResult.ok().msg("缩小商品库存胜利"); }}
2.2.5 测试 ribbon 重试机制
- 通过 ribbon 拜访 item-service,当超时,ribbon 会重试申请集群中其余服务器
http://localhost:3001/item-service/35