第一节,服务雪崩简介
服务雪崩就是:一个服务不可用,导致一系列服务不可用,而这种后果往往无法预料。
造成雪崩原因可以归结为以下三个:
1,服务提供者不可用(硬件故障,程序bug,缓存击穿,用户大量请求)
2,重试加大流量(用户重试,代码逻辑重试)
3,服务调用者不可用(同步等待造成的资源耗尽)
解决方案有如下5个,其中隔离包括两种:
1,降级:超时降级,资源不足时(线程或信号量)降级,降级后可以配合降级接口放回托底数据。实现一个fallback方法,当请求后端服务出现异常的时候,可以使用fallback方法返回的值;
2,隔离(线程池隔离和信号量隔离):限制调用分布式服务的资源使用,某一个调用的服务出现问题不会影响其他服务调用;
3,熔断:当失败率(如因网络故障或超时 造成的失败率高)达到阈值自动触发降级,熔断器触发的快速失败会进行快速恢复;
4,缓存:提供了请求缓存;
5,请求合并:提供了请求合并。
下面的雪崩解决方案是基于ribbon的,即在发送请求的时候,需要自己手工构造远程调用。
解决方案
服务降级
场景:比如一个订单系统请求一个库存系统,一个请求发过去,由于各种原因,网络超时,在规定的时间内没有返回响应结果,这个时候更多的请求过来了,不断地请求库存服务,不断的创建线程,由于没有返回,也就没有资源的释放。时间一长,必将耗尽系统资源,导致系统崩溃。本来你的订单系统好好的,但是请求了一个有问题的库存系统,导致订单系统也崩溃了,然后订单系统又不断调用其他的依赖系统,然后导致其他依赖系统也崩溃了,造成服务雪崩。这个时候hystrix可以实现快速失败。
如果hystrix在一段时间内侦测到许多类似的错误,会强迫其以后的多个调用快速失败,不再访问远程服务器,从而防止应用程序不断地尝试执行可能会失败的操作而导致系统资源被耗尽。这时hystrix进行fallback操作来服务降级。
Fallback相当于是降级操作。对于查询操作,我们可以实现一个fallback方法,当请求后端服务出现异常的时候,可以使用fallback方法返回的值。fallback方法的返回值一般是默认的值或者是从缓存中得到的值,通知后面的请求服务暂时不可用了。
package com.twf.e.book.consumer.hystrix.ribbon.fallback.service;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.netflix.hystrix.contrib.javanica.conf.HystrixPropertiesManager;
import com.twf.e.book.consumer.hystrix.ribbon.fallback.domain.Product;
@Service
public class ProductService {
@Autowired
private LoadBalancerClient loadBalancerClient; // ribbon的负载均衡客户端
// @HystrixCommand这个注解指定了fallbackMethod为fallback。
@HystrixCommand(fallbackMethod="fallback",commandProperties={
@HystrixProperty(name=HystrixPropertiesManager.FALLBACK_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS,value="15")
})
public List<Product> listProduct() {
ServiceInstance si = loadBalancerClient.choose("e-book-product");
StringBuffer sb = new StringBuffer("");
sb.append("http://");
sb.append(si.getHost());
sb.append(":");
sb.append(si.getPort());
sb.append("/product/list");
System.out.println(sb.toString());
RestTemplate restTemplate = new RestTemplate();
ParameterizedTypeReference<List<Product>> typeRef = new ParameterizedTypeReference<List<Product>>(){};
ResponseEntity<List<Product>> resp = restTemplate.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;
}
}
服务请求缓存
比如一个请求过来请求我userId=1的数据,你后面的请求也过来请求同样的数据,这时我不会继续走原来的那条请求链路了,而是把第一次请求缓存过了,把第一次的请求结果返回给后面的请求,这样在一定程度上减少了请求的次数,降低网络资源的压力,减少雪崩发生的概率。
@EnableCaching // 开启缓存
@EnableCircuitBreaker // 开启服务降级断路器
@EnableEurekaClient
@SpringBootApplication
public class ConsumerHystrixRibbonCacheApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerHystrixRibbonCacheApplication.class, args);
}
}
服务请求合并
请求合并就是将单个请求合并成一个请求,去调用服务提供者,从而降低服务提供者负载的,一种应对高并发的解决方法。
在传统的线程池中,有多少个请求就启动多少个线程,比如上图有6个请求就会启动6条线程,这6条线程再去请求provider。如果一下子来了1000个请求,那么线程池中的队列就会被撑爆。这样怎么办呢?我们就来做一个请求合并。
现在将6个请求合并成一个请求,再由线程池执行,这样就可以解决线程池被撑爆的局面。
原理:通过请求合并器设置延迟时间,将延迟时间内的多个请求的请求参数取出来,封装成一个参数list,作为batchMethod指定的方法(比如该方法名称为batchProduct)的参数,然后调用这个batchProduct()方法。返回的对象list再通过一个方法(mapResponseToRequests方法),按照请求的次序将结果对象对应的装到Request对应的Response中返回结果。
package com.twf.e.book.consumer.hystrix.ribbon.batch.service;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Future;
import org.springframework.stereotype.Service;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.twf.e.book.consumer.hystrix.ribbon.batch.domain.Product;
@Service
public class ProductService {
// 利用hystrix合并请求
@HystrixCollapser(batchMethod = "batchProduct", scope = com.netflix.hystrix.HystrixCollapser.Scope.GLOBAL, collapserProperties = {
// 请求时间间隔在20ms之内的请求会被合并为一个请求
@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, "登山包"));
list.add(new Product(2, "登山杖"));
list.add(new Product(3, "冲锋衣"));
list.add(new Product(4, "帐篷"));
list.add(new Product(5, "睡袋"));
list.add(new Product(6, "登山鞋"));
return list;
}
}
服务熔断
熔断机制相当于电路的跳闸功能。
例如:我们可以配置熔断策略为当请求错误比例在10s内>50%时,该服务将进入熔断状态,后续请求都会进入fallback。
隔离技术之线程池隔离
场景:consumer端有一个线程池,里面有两个接口,短时间内接口A有10万次请求,接口B有10次请求,由于接口A的大并发量导致整个线程池瘫痪,也就会导致B接口的不可用。最后整个consumer端不可用。
解决的方法:进行线程池隔离。
给A接口单独设置一个线程池,给B接口也设置一个线程池,这样当A接口瘫痪之后,不会影响B接口的运行。
通过每次都开启一个单独线程运行。它的隔离是通过线程池,即每个隔离粒度都是个线程池,互相不干扰。
线程池隔离方式,等于多了一层的保护措施,可以通过hytrix直接设置超时,超时后直接返回。
package com.twf.e.book.consumer.hystrix.ribbon.threadpool.service;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.twf.e.book.consumer.hystrix.ribbon.threadpool.domain.Product;
@Service
public class ProductService {
@Autowired
private LoadBalancerClient loadBalancerClient; // ribbon的负载均衡客户端
@HystrixCommand(groupKey="e-book-product",commandKey="listProduct",
threadPoolKey="e-book-product",
threadPoolProperties={
@HystrixProperty(name="coreSize",value="30"), // 线程池大小
@HystrixProperty(name="maxQueueSize",value="100"), // 最大队列长度
@HystrixProperty(name="keepAliveTimeMinutes",value="2"), // 线程存活时间
@HystrixProperty(name="queueSizeRejectionThreshold",value="15") // 拒绝请求
},
fallbackMethod = "fallback")
public List<Product> listProduct() {
ServiceInstance si = loadBalancerClient.choose("e-book-product");
StringBuffer sb = new StringBuffer("");
sb.append("http://");
sb.append(si.getHost());
sb.append(":");
sb.append(si.getPort());
sb.append("/product/list");
System.out.println(sb.toString());
RestTemplate restTemplate = new RestTemplate();
ParameterizedTypeReference<List<Product>> typeRef = new ParameterizedTypeReference<List<Product>>(){};
ResponseEntity<List<Product>> resp = restTemplate.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;
}
}
转载自:
https://blog.csdn.net/tanwenfang/article/details/86478786
todo,还有很多,这只是我挑能懂的复制了些
漫天皆白,雪里行军情更迫。
头上高山,风卷红旗过大关。
为有牺牲多壮志,敢教日月换新天。
鬼吹灯
天下霸唱
减字木兰花·广昌路上
七律·到韶山