造成服务雪崩的主要原因:
1.服务提供者不可用(硬件故障,程序bug,缓存击穿,用户大量请求)
2.重试加大流量(用户重试,代码逻辑重试)
3.服务调用者不可用(同步等待造成的资源耗尽)
服务降级
超时降级、资源不足时降级,降级后可以配合降级接口返回托底数据。实现一个fallback方法,当请求后端 服务出现异常的时候,可以使用fallback方法返回的值。
pom中加入:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
配置文件不需要添加什么
目录结构:
ProductController中的代码:
@RestController
public class ProductController {
@Autowired
private ProductService productService;
@RequestMapping(value="list",method=RequestMethod.GET)
public List<Product> listProduct(){
List<Product> list= this.productService.listProduct();
return list;
}
}
ProductService中的代码:
@Service
public class ProductService {
@Autowired
private LoadBalancerClient loadBalancerClient; //ribbon 负载均衡客户端
@HystrixCommand(fallbackMethod = "fallback",
commandProperties = {
//默认10秒;如果并发数达到该设置值,请求会被拒绝和抛出异常并且fallback不会被调用。 @HystrixProperty(name=HystrixPropertiesManager.FALLBACK_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS, value="15")
})
public List<Product> listProduct(){
//获取eurekaone-provider客户端
ServiceInstance si=loadBalancerClient.choose("e-book-product");
StringBuffer sb = new StringBuffer("");
sb.append("http://");
//获取ip地址
sb.append(si.getHost());
sb.append(":");
//获取端口号
sb.append(si.getPort());
sb.append("/product/list");
System.out.println(sb.toString());
RestTemplate rt = new RestTemplate();
//对象为Product集合
ParameterizedTypeReference<List<Product>> typeRef =new ParameterizedTypeReference<List<Product>>(){};
//参数是浏览器地址,请求方法,和转化为的对象
ResponseEntity<List<Product>> resp= rt.exchange(sb.toString(),HttpMethod.GET, null, typeRef);
List<Product> plist = resp.getBody();
return plist;
}
public List<Product> fallback() {
List<Product> list = new ArrayList<Product>();
list.add(new Product(-1,"fallback"));
return list;
}
}
使用降级,我们只是多添加了一个注解,然后创建fallback的方法,如果并发达到15的时候或则product没有,就会自动降级。当我们关闭product的时候:
它就会自动降级,执行fallback方法。
请求缓存
pom文件中加入:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
这回的pom文件多加入了redis的依赖。
配置文件中加入:
#程序启动时创建的缓存名称
#spring.cache.cache-names=com.agan.book
# Redis数据索引(默认为0)
spring.redis.database=1
#Redis服务器地址
spring.redis.host=192.168.23.129
#Redis服务器连接端口
spring.redis.port=6379
#Redis服务器连接密码(默认为空)
spring.redis.password=
#连接池最大连接数(负值表示没有限制)
spring.redis.pool.max-active=100
#连接池最大阻塞等待时间(负值表示没有限制)
spring.redis.pool.max-wait=3000
#连接池最大空闭连接数
spring.redis.pool.max-idle=200
#连接汉最小空闲连接数
spring.redis.pool.min-idle=50
#连接超时时间(毫秒)
spring.redis.pool.timeout=600
注意你的电脑虚拟机Linux中必须要安装了redis并且注意服务器的地址。
目录文件:
ProductController代码:
@RestController
public class ProductController {
@Autowired
private ProductService productService;
@RequestMapping(value="list",method=RequestMethod.GET)
public List<Product> listProduct(){
List<Product> list= this.productService.listProduct();
return list;
}
@RequestMapping(value="get",method=RequestMethod.GET)
public Product get(Integer id) {
return this.productService.get(id);
}
@RequestMapping(value="del",method=RequestMethod.GET)
public void del(Integer id) {
this.productService.del(id);
}
}
这里的代码主要是加入了get和del。
ProductService加入代码:
@Cacheable(key="'product' + #id")
public Product get(Integer id) {
System.out.println("=========get============"+id);
return new Product(id,"test6");
}
@CacheEvict(key="'product' + #id")
public void del(Integer id) {
System.out.println("=========del============"+id);
}
在上面降级的基础上,加入了以上代码。
测试结果:
我开始将id为2的删除掉,然后get?id=2两次,发现只有第一次打印,第二次没有打印,说明第二次是缓存。
服务器中,注意要选择数据库
请求合并
在一定时间内将多个请求合并为一个请求。
在pom中加入:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-javanica</artifactId>
<version>RELEASE</version>
</dependency>
配置文件不需要添加。
目录结构:
ProductController代码
@RestController
public class ProductController {
@Autowired
private ProductService productService;
@RequestMapping(value="getproduct",method=RequestMethod.GET)
public void getproduct() throws InterruptedException, ExecutionException{
Future<Product> p1= this.productService.getProduct(1);
Future<Product> p2= this.productService.getProduct(2);
Future<Product> p3= this.productService.getProduct(3);
System.out.println(p1.get().toString());
System.out.println(p2.get().toString());
System.out.println(p3.get().toString());
}
}
ProductService代码:
@Service
public class ProductService {
//利用hystrix合并请求
@HystrixCollapser(batchMethod = "batchProduct", scope = com.netflix.hystrix.HystrixCollapser.Scope.GLOBAL,
collapserProperties = {
//请求时间间隔在50ms之内的请求会被合并为一个请求
@HystrixProperty(name = "timerDelayInMilliseconds", value = "20"),
//设置触发批处理执行之前,在批处理中允许的最大请求数。
@HystrixProperty(name = "maxRequestsInBatch", value = "200"),
})
public Future<Product> getProduct(Integer id) {
System.out.println("---------"+ id + "---------");
return null;
}
@HystrixCommand
public List<Product> batchProduct(List<Integer> ids) {
for (Integer id:ids) {
System.out.println(id);
}
List<Product> list= new ArrayList<Product>();
list.add(new Product(1,"test1无的放矢付撒发付付撒奥过过过过过过过过"));
list.add(new Product(2,"test2无的放矢付撒发付付撒奥过过过过过过过过"));
list.add(new Product(3,"test3无的放矢付撒发付付撒奥过过过过过过过过"));
list.add(new Product(4,"44444444444444444444444444444444444444444"));
return list;
}
}
测试结果:
并没有打印---------"+ id + "---------说明请求合并了。
服务熔断
当失败率达到阙值自动触发降级,熔断器触发的快速失败会进行快速恢复。
在pom中加入:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
目录结构:
ProductController代码:
@RestController
public class ProductController {
@Autowired
private ProductService productService;
@RequestMapping(value="list",method=RequestMethod.GET)
public List<Product> listProduct(@RequestParam("n") Integer n){
List<Product> list= this.productService.listProduct(n);
return list;
}
}
ProductService代码:
@Service
public class ProductService {
@Autowired
private LoadBalancerClient loadBalancerClient; //ribbon 负载均衡客户端
@HystrixCommand(fallbackMethod = "fallback",
commandProperties = {
//默认20个;10s内请求数大于20个时就启动熔断器,当请求符合熔断条件时将触发getFallback()。
@HystrixProperty(name=HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD, value="10"),
//请求错误率大于50%时就熔断,然后for循环发起请求,当请求符合熔断条件时将触发getFallback()。 @HystrixProperty(name=HystrixPropertiesManager.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE, value="50"),
//默认5秒;熔断多少秒后去尝试请求 @HystrixProperty(name=HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS, value="5000"),
})
public List<Product> listProduct(int n){
System.out.println(n);
if (n==1) {
throw new RuntimeException();
}
//获取eurekaone-provider客户端
ServiceInstance si=loadBalancerClient.choose("e-book-product");
StringBuffer sb = new StringBuffer("");
sb.append("http://");
//获取ip地址
sb.append(si.getHost());
sb.append(":");
//获取端口号
sb.append(si.getPort());
sb.append("/product/list");
System.out.println(sb.toString());
RestTemplate rt = new RestTemplate();
//对象为Product集合
ParameterizedTypeReference<List<Product>> typeRef =new ParameterizedTypeReference<List<Product>>(){};
//参数是浏览器地址,请求方法,和转化为的对象
ResponseEntity<List<Product>> resp= rt.exchange(sb.toString(),HttpMethod.GET, null, typeRef);
List<Product> plist = resp.getBody();
return plist;
}
public List<Product> fallback(int n) {
List<Product> list = new ArrayList<Product>();
list.add(new Product(-1,"fallback"));
return list;
}
}
主要就是改变了注解。
测试:
当n为1的时候为关闭的状态,n不为1的时候就是开启的状态。