上一篇文章中,重点说明了Ribbon中封装了RestTempalte,是通过Rest api进行远程调用。这篇文章则会使用Ribbon进行负载均衡和重试的操作。
现在使item-service服务分别使用8001和8002启动,搭建集群
一、负载均衡由于在Eureka中已经包含了Ribbon的相关依赖,所以配置了Eureka实际上已经可以进行Ribbon的负载均衡。
原理解释其中的原理是:
Ribbon会从Eureka服务器上获取所有服务主机的清单,从而进行负载均衡操作。
配置说明 1、添加ribbon依赖(可选)Eureka中默认包含了Ribbon的依赖
2、RestTemplate添加@LoadBalancedorg.springframework.cloud spring-cloud-starter-netflix-ribbon
@LoadBalanced 负载均衡注解,会对 RestTemplate 实例进行封装,创建动态代理对象,并切入(AOP)负载均衡代码,把请求分发到集群中的服务器
package cn.tedu.sp06;
...
@EnableDiscoveryClient //都是能够让注册中心能够发现,扫描到该服务
@SpringBootApplication
public class Sp06RibbonApplication {
public static void main(String[] args) {
SpringApplication.run(Sp06RibbonApplication.class, args);
}
//创建 RestTemplate 实例,并存入 spring 容器
@LoadBalanced
//@LoadBalanced 负载均衡注解,会对 RestTemplate 实例进行封装,创建动态代理对象,并切入(AOP)负载均衡代码,把请求分发到集群中的服务器
@Bean
public RestTemplate restTemplate() {
SimpleClientHttpRequestFactory f = new SimpleClientHttpRequestFactory();
return new RestTemplate();
}
}
3、访问路径修改为服务id
将RibbonController中每个方法中getForObject或postForObject发送的具体路径修改为服务的id,ribbon会实现负载均衡
package cn.tedu.sp06.controller;
...
@RestController
public class RibbonController {
@Autowired
private RestTemplate rt;
@GetMapping("/item-service/{orderId}")
public JsonResult> getItems(@PathVariable String orderId) {
//这里服务器路径用 service-id 代替,ribbon 会向服务的多台集群服务器分发请求
return rt.getForObject("http://item-service/{1}", JsonResult.class, orderId);
}
@PostMapping("/item-service/decreaseNumber")
public JsonResult decreaseNumber(@RequestBody List- items) {
return rt.postForObject("http://item-service/decreaseNumber", items, JsonResult.class);
}
//
@GetMapping("/user-service/{userId}")
public JsonResult
getUser(@PathVariable Integer userId) {
return rt.getForObject("http://user-service/{1}", JsonResult.class, userId);
}
@GetMapping("/user-service/{userId}/score")
public JsonResult addScore(
@PathVariable Integer userId, Integer score) {
return rt.getForObject("http://user-service/{1}/score?score={2}", JsonResult.class, userId, score);
}
//
@GetMapping("/order-service/{orderId}")
public JsonResult 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);
}
}
4、测试负载均衡
访问
http://localhost:3001/item-service/34
多次访问此路径,会发现访问到不同的服务器
二、重试 简介
重试是一种容错机制,如果请求后台服务出错,或者服务器故障,会向另一台服务器重试访问。
配置说明 1、pom文件添加retry依赖2、yml配置ribbon重试org.springframework.retry spring-retry
ribbon: #单台服务器的重试次数 MaxAutoRetries: 1 #更换服务器次数 MaxAutoRetriesNextServer: 2 OkToRetryOnAllOperations: true #OkToRetryOnAllOperations 是否对所有类型请求都重试,默认只对GET重试 #ConnectTimeout 建立连接等待超时时间(不能再yml中配置,需要些Java代码进行配置) #ReadTimeout 连接已建立并已发送请求,等待接收响应的超时时间(不能再yml中配置,需要些Java代码进行配置)
其中
- ribbon.MaxAutoRetries=1
表示单台服务器重试一次
- ribbon.AutoRetriesNextServer=2
表示更换服务器的次数为两次
- ribbon.OkToRetryOnAllOperations=true
表示对所有类型的请求都进行重试,默认只对GET请求重试
另外在ribbon中
ConnectTimeout和ReadTimeout是需要Java去配置的
ConnectTimeout表示建立来等待超时时间
ReadTimeout表示连接已建立并已发送请求,等待接收响应的超时时间
3、主程序中RestTemplate的超时设置其中,需要创建一个SimpleClientHttpRequestFactory的实例对象,并分别使用setReadTimeout()和setConnectTimeout()的方法设置超时时间为1s
package cn.tedu.sp06;
...
@EnableDiscoveryClient //都是能够让注册中心能够发现,扫描到改服务
@SpringBootApplication
public class Sp06RibbonApplication {
public static void main(String[] args) {
SpringApplication.run(Sp06RibbonApplication.class, args);
}
//创建 RestTemplate 实例,并存入 spring 容器
@LoadBalanced
//@LoadBalanced 负载均衡注解,会对 RestTemplate 实例进行封装,创建动态代理对象,并切入(AOP)负载均衡代码,把请求分发到集群中的服务器
@Bean
public RestTemplate restTemplate() {
SimpleClientHttpRequestFactory f = new SimpleClientHttpRequestFactory();
//设置超时时间1s
//连接已建立并发送请求,等待响应结果的超时时间
f.setReadTimeout(1000);
//建立连接等待超时时间为1s
f.setConnectTimeout(1000);
return new RestTemplate(f);
//RestTemplate 中默认的 Factory 实例中,两个超时属性默认是 -1,
//未启用超时,也不会触发重试
//return new RestTemplate();
}
}
4、模拟延迟
在item-service服务中通过Tread.sleep()的方式设置延迟,再通过random产生随机数的方法概率差生延迟,测试超时。如果超时,则会将访问请求到另外一个服务器上。
模拟随机延迟代码
///--设置随机延迟
if(Math.random()<0.6) {
long t = new Random().nextInt(5000);
log.info("item-service-"+port+" - 暂停 "+t);
Thread.sleep(t);
}
测试发送
http://localhost:3001/item-service/30



