雪崩问题(Avalanche effect)是指在分布式系统中,一个节点的崩溃导致其它节点也跟着崩溃的一种现象。当一个节点崩溃时,由于其它节点对它的依赖关系,其它节点也会受到影响,进而导致整个系统出现故障,形成一个连锁反应。
雪崩问题通常是由于系统中某个节点的故障导致大量请求被转移到其它节点,而这些节点由于负载过重而崩溃,进而导致整个系统不可用。雪崩问题的出现,会导致系统的可用性急剧下降,给用户带来极大的损失。
常用措施:
超时处理是指在系统中设置一个超时时间,当某个请求在规定时间内没有得到响应时,系统会自动将该请求取消,避免其继续占用系统资源,从而避免雪崩问题的出现。超时时间的设置应该根据系统的实际情况和性能调优进行设置,不宜设置过短或过长,以免影响系统的正常运行。
舱壁模式是指在系统中将不同的请求隔离在不同的“舱壁”中,以避免某个请求的失败影响到其它请求。
例如,可以将不同的请求分配到不同的线程池中,避免某个请求的失败导致整个线程池崩溃。舱壁模式也可以应用于不同的服务之间,例如将不同的服务部署在不同的服务器上,避免某个服务的故障影响到其它服务的正常运行。
熔断降级是指在系统中设置一个阈值,当某个服务的错误率或响应时间超过阈值时,系统会自动关闭该服务的访问,避免其继续向下游传递错误请求。熔断降级可以避免错误请求的扩散,保证整个系统的稳定性和可用性。当服务恢复正常后,系统会自动重新开启该服务的访问。
流量控制是指在系统中对请求进行限流,避免请求过多而导致系统负载过重。常见的流量控制方式包括:令牌桶算法、漏桶算法等。限流可以避免请求的扩散,保护系统的稳定性和可用性。
Sentinel是一个由阿里巴巴开源的微服务框架,主要用于解决分布式系统中的流量控制、熔断降级、系统负载保护等问题。Sentinel提供了实时监控、规则配置、簇点链路控制等多种功能,可以帮助用户实现系统的自我保护,提升系统的可用性和稳定性。
Sentinel的主要特点包括:
官网地址:Sentinel官网
将下载的jar包放置在非中文目录以免出现bug,使用java -jar命令运行:
java -jar sentinel-dashboard-xx.xx.xx.jar
Sentinel的默认端口为8080、用户名和密码都是 sentinel ,如果要修改Sentinel的默认端口、账户、密码,可以通过下列配置:
配置项 | 说明 |
---|---|
server.port | 服务端口 |
sentinel.dashboard.auth.username | 默认用户名 |
sentinel.dashboard.auth.password | 默认密码 |
例如,修改端口:
java -Dserver.port=8088 -jar sentinel-dashboard-xx.xx.xx.jar
运行成功后浏览器访问 http://localhost:8080 即可看到sentinel的控制台
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
dependency>
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8080 #sentinel控制台地址
簇点链路(Cluster Linkage)是微服务架构中的一个重要概念,它是指将多个服务组织在一起形成的一个簇点(Cluster),并且这些服务之间是通过一些共同的连接点(Linkage)相互联系的。簇点链路的设计可以帮助微服务架构实现高可用性、容错性和可伸缩性。
在簇点链路中,每个服务都是一个簇点,不同的簇点之间通过共享的连接点相互联系。连接点可以是一些共享的库、消息队列、数据库等资源,也可以是通过一些通信协议(如HTTP、RPC等)建立的网络连接。通过簇点链路的设计,可以将服务按照不同的功能模块进行组织和拆分,提高服务的灵活性和可维护性。
流控是Sentinel的核心功能之一,用于控制系统的并发度、QPS等,避免系统因为过载而崩溃或者响应变慢。Sentinel提供了多种流控规则,如基于QPS的流控、线程数流控、关联流控等,可以根据具体的场景选择合适的流控规则,从而实现精细化的流控策略。
降级是指在系统出现异常情况或者超负荷的情况下,临时关闭某些功能模块,以保证系统的稳定性和可用性。Sentinel提供了基于异常比例和异常数的降级策略,可以根据异常情况对服务进行自动降级,避免异常向下游扩散。
热点是指在分布式系统中,某些资源的访问频率非常高,容易导致系统瓶颈和性能问题。Sentinel提供了热点参数流控功能,可以根据某些参数的访问频率进行流控,避免因为热点资源导致系统崩溃。
授权是指对服务的访问进行授权管理,确保只有合法的用户或服务可以访问某些资源。Sentinel提供了基于黑白名单的授权管理功能,可以对服务进行精细的授权管理,保证系统的安全性和可靠性。
流控模式 | 介绍 |
---|---|
直接 | 直接模式是指流量控制规则仅对当前资源生效,不会对其他资源产生影响。这种模式适用于独立的资源,可以实现精确的流量控制和限流。 |
关联 | 关联模式是指流量控制规则对当前资源及其关联的资源生效,可以控制一组相关的资源的流量。关联模式适用于一组相关的资源,如同一个业务功能模块中的多个资源,可以将它们组织在一起进行流量控制。 |
链路 | 链路模式是指流量控制规则对当前资源及其下游资源生效,可以控制整个调用链路的流量。链路模式适用于分布式系统中的微服务调用链路,可以实现对整个调用链路的流量控制和限流。 |
流控模式 | 介绍 |
---|---|
快速失败 | 快速失败(快速失败模式)是指当达到流控阈值时,请求会直接失败,不再等待资源处理。这种模式可以快速释放系统资源,但可能会影响用户体验。快速失败模式适用于对响应时间要求较高的场景。 |
warm up | Warm Up(预热模式)是指当系统负载过高时,对流量进行预热,逐渐增加资源的处理能力,以避免因为短时间内大量请求而导致的系统瓶颈和性能问题。在Warm Up模式下,当资源被流控后,Sentinel会逐渐增加资源的处理能力,使得资源能够逐渐恢复正常的请求处理能力,避免请求直接失败。 |
排队等待 | 排队等待(排队等待模式)是指当系统负载过高时,对请求进行排队等待,以避免资源直接被拒绝。在排队等待模式下,当资源被流控后,请求会进入队列等待处理,直到队列中的请求得到资源的处理能力后,才会得到处理。这种模式可以保证请求的处理能力,但可能会增加系统的响应时间。 |
热点参数限流是一种流量控制方式,它通过对系统中的热点参数进行限流,来保护系统的稳定性和可用性。热点参数通常指的是在系统中频繁出现的参数,例如用户ID、商品ID等。由于这些参数的访问频率较高,如果没有进行限流,可能会导致系统瓶颈和性能问题。
在Sentinel中,可以通过定义热点参数规则来对热点参数进行限流。热点参数规则可以根据热点参数的名称或者位置(在参数列表中的位置)进行匹配,然后对满足条件的请求进行限流处理。对于热点参数,可以配置限流阈值、限流模式(快速失败、Warm Up、排队等待)以及限流处理器等信息。
热点参数限流的原理是通过统计热点参数的QPS(每秒钟处理的请求次数),当QPS超过限流阈值时,就触发限流处理。在限流处理期间,可以采用快速失败、Warm Up、排队等待等限流模式进行流量控制,保护系统的稳定性和可用性。
注:在配置热点参数限流的时候需要在代码中对对应请求添加@SentinelResource注解;
线程隔离和熔断降级是分布式系统中常用的两种保护机制,用于保护系统的稳定性和可用性。
线程隔离是指将不同类型的请求或不同的业务逻辑隔离开来,避免相互之间的影响,提高系统的可用性和稳定性。在Sentinel中,可以通过线程池隔离、信号量隔离等方式对请求进行隔离处理,避免不同类型的请求相互影响。例如,将耗时的操作放在一个独立的线程池中进行处理,可以避免请求相互影响,提高系统的稳定性和可用性。
线程隔离的实现方式主要有两种,分别是线程池隔离和信号量隔离:
( 1 )线程池隔离是指将不同类型的请求放置在不同的线程池中进行处理,避免请求之间相互影响。在Sentinel中,线程池隔离可以通过使用不同的线程池来对请求进行隔离。例如,可以定义一个用于处理CPU密集型任务的线程池,另一个用于处理IO密集型任务的线程池,以此来避免不同类型的请求相互影响。
优点:
- 线程池隔离可以避免不同类型的请求之间相互影响,提高系统的可用性和稳定性。
- 线程池隔离可以通过不同的线程池来优化系统的资源使用,例如可以定义一个用于处理CPU密集型任务的线程池,另一个用于处理IO密集型任务的线程池。
- 线程池隔离可以在不同的线程中进行资源隔离,以避免线程间的相互影响,提高系统的稳定性和可用性。
缺点:
- 线程池隔离需要创建不同的线程池来处理不同类型的请求,这会增加系统的开销。
- 线程池隔离可能会导致线程资源浪费,例如某些线程池可能会被闲置,而另一些线程池可能会被频繁使用。
( 2 )信号量隔离是指通过定义不同的信号量,来限制并发请求的数量,避免请求之间相互竞争,提高系统的可用性和稳定性。在Sentinel中,信号量隔离可以通过定义不同的信号量规则,来对请求进行隔离处理。例如,可以定义一个最大并发数为10的信号量规则,限制同时处理的请求数量,以此来避免请求之间相互竞争。
优点:
- 信号量隔离可以限制并发请求的数量,避免请求之间相互竞争,提高系统的可用性和稳定性。
- 信号量隔离可以灵活地控制系统的资源使用,例如可以设置不同的最大并发数来适应不同的业务场景。
- 信号量隔离可以实现细粒度的请求隔离,可以根据具体的请求类型或者请求来源进行隔离。
缺点:
- 信号量隔离可能会导致请求排队等待,增加请求的响应时间。
- 信号量隔离可能会因为过度限制并发数,导致系统处理能力下降。
熔断降级是指在系统出现异常情况或者超出处理能力时,临时关闭一些不必要的服务或功能,保证系统的核心服务能够继续正常运行。在Sentinel中,可以通过定义降级规则,对系统中的服务进行降级处理,例如在系统出现异常情况时,可以临时关闭一些不必要的服务,保证核心服务的正常运行。降级处理可以有效地避免系统崩溃或者无法正常处理请求的情况,提高系统的可用性和稳定性。
在Sentinel中,断路器熔断策略主要分为三种:
http客户端整合Sentinel(以feign为例):
在服务内添加配置
feign:
sentinel:
enabled: true #开启fegin对sentinel的支持
@Slf4j
public class UserClientFallbackFactory implements FallbackFactory<UserClient> {
@Override
public UserClient create(Throwable throwable) {
return new UserClient() {
@Override
public User findById(Long id) {
log.error("查询用户异常", throwable);
return new User();
}
};
}
}
@Bean
public UserClientFallbackFactory userClientFallbackFactory(){
return new UserClientFallbackFactory();
}
@FeignClient(value = "userservice",fallbackFactory = UserClientFallbackFactory.class)
在Sentinel中,授权规则可以用来限制用户或服务调用者的访问权限,以保障系统的安全性。授权规则通常基于调用来源、调用参数或者调用内容等维度进行限制。
授权规则的具体实现可以采用黑名单或白名单的方式,即允许或禁止某些调用者或参数访问特定的资源。
黑名单是一种禁止访问的授权规则,即列出不允许访问某个资源的调用者或者参数。对于黑名单中列出的调用者或参数,系统将禁止其访问被保护的资源。
白名单则是一种允许访问的授权规则,即列出允许访问某个资源的调用者或者参数。对于不在白名单中的调用者或参数,系统将禁止其访问被保护的资源。
@Component
public class HeaderOriginParser implements RequestOriginParser {
@Override
public String parseOrigin(HttpServletRequest request) {
//1.获取请求头
String origin = request.getHeader("origin");
//2.非空判断
if(StringUtils.isEmpty(origin)){
//如果为空设置默认值
origin = "xxx";
}
return origin;
}
}
spring:
cloud:
gateway:
default-filters:
- AddRequestHeader=origin,XXXX # XXXX为自定义值
@Component
public class SentinelExceptionHandler implements BlockExceptionHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
String msg = "未知异常";
int status = 429;
//根据场景不同设置不同的状态码、消息
if (e instanceof FlowException) {
msg = "请求被限流了";
} else if (e instanceof ParamFlowException) {
msg = "请求被热点参数限流";
} else if (e instanceof DegradeException) {
msg = "请求被降级了";
} else if (e instanceof AuthorityException) {
msg = "没有权限访问";
status = 401;
}
//设置内容类型
response.setContentType("application/json;charset=utf-8");
//设置状态码
response.setStatus(status);
//写出
response.getWriter().println("{\"msg\": " + msg + ", \"status\": " + status + "}");
}
}