上一篇文章中,重点说明了Ribbon中封装了RestTempalte,是通过Rest api进行远程调用。这篇文章则会使用Ribbon进行负载均衡和重试的操作。
现在使item-service服务分别使用8001和8002启动,搭建集群
一、负载均衡
由于在Eureka中已经包含了Ribbon的相关依赖,所以配置了Eureka实际上已经可以进行Ribbon的负载均衡。
原理解释
其中的原理是:
Ribbon会从Eureka服务器上获取所有服务主机的清单,从而进行负载均衡操作。
配置说明
1、添加ribbon依赖(可选)
Eureka中默认包含了Ribbon的依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
2、RestTemplate添加@LoadBalanced
@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<List<Item>> 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<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")
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<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);
}
}
4、测试负载均衡
访问
http://localhost:3001/item-service/34
多次访问此路径,会发现访问到不同的服务器
二、重试
简介
重试是一种容错机制,如果请求后台服务出错,或者服务器故障,会向另一台服务器重试访问。
配置说明
1、pom文件添加retry依赖
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
2、yml配置ribbon重试
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