1200页Java架构面试专题及答案
小编整理不易,对这份1200页Java架构面试专题及答案感兴趣劳烦帮忙转发/点赞
百度、字节、美团等大厂常见面试题
}
/**
- 如果没有使用 @SentinelResource 注解,默认资源名称为接口路径 /getOrderConsole
*/
- 在控制台添加规则
- 被限流返回的结果
Blocked by Sentinel (flow limiting)
- 如果要实现和上述一样的返回效果,只需要加上 @SentinelResouce 注解即可
PS:如果配置了注解,就会和默认的资源名称冲突,所以要删除默认的资源名称
6.3.4 实现信号量隔离
限流规则设置 线程数
// 基于信号量, 在控制台设置规则的时候,配置为 线程数
@SentinelResource(value = GETORDER_KEY, blockHandler = “getOrderQpsException”)
@RequestMapping(“/getOrderSemaphore”)
public String getOrderSemaphore () {
log.info(“>>>” + Thread.currentThread().getName());
return “getOrderSemaphore”;
}
阙值改为线程数
访问效果
如果看不到效果,可以在代码中加入 Thread.sleep(500) ,使线程阻塞即可
6.4 Sentinel 数据持久化 & 实现服务降级 & 服务熔断
-
Sentinel 持久化方案
-
网关整合 Sentinel 实现限流
-
如何对服务实现动态降级
-
降级的策略 RT、错误比例、次数
6.4.1 Sentinel 数据持久化的方式(基于 nacos 配置中心实现持久化)
Sentinel 支持数据规则持久化的,官方默认支持 5 种(sentinel-nacos 1.7.0 版本)
-
nacos 分布式配置中心
-
携程阿波罗
-
存放本地文件
-
Zookeeper
-
redis
一、在 nacos 中创建流控规则
gorit-order-sentinel
我们可以在 Nacos 编写 Sentinel 的资源配置,然后整合 sentinel-nacos 的配置
[
{
“resource”: “/getOrderSentinel”,
“limitApp”: “default”,
“grade”: 1,
“count”: 1,
“strategy”: 0,
“controlBehavior”: 0,
“clusterMode”: false
}
]
-
resource: 资源名,即限流规则的作用对象
-
limitApp: 流控针对的调用来源,若为 default 则不区分调用来源
-
grade:限流阙值类型(QPS 或并发线程数);0 代表根据并发数量来限流, 1 代表 QPS 来进行流量控制
-
count:限流阙值
-
strategy:调用关系限流策略
二、SpringBoot 客户端 整合 nacos-sentinel
依赖配置
com.alibaba.cloud
spring-cloud-starter-alibaba-sentinel
2.1.0.RELEASE
com.alibaba.csp
sentinel-datasource-nacos
1.7.0
PS:我用的 SpringBoot 版本是 2.3.4 RELEASE,之前用的 nacos-sentinel 的版本是 1.5.6,启动老是报错,好来改成 1.7.0 就出来了
配置文件 (application.yml)
spring:
application:
name: gorit-order
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
sentinel:
transport:
dashboard: 127.0.0.1:8718 # 这个是控制台的地址,因为我启动时,指定了控制台启动的端口为 8718
eager: true
datasource:
- nacos:
server-addr: localhost:8848 # nacos 连接地址
group-id: DEFAULT_GROUP # nacos 连接分组
rule-type: flow # 路由存储规则 flow、degrade、param-flow、gw-flow
data-id: ${spring.application.name}-sentinel # 读取配置文件的 data-id
data-type: json # 读取配置文件累心为 JSON 和 XML
server:
port: 8091
编写 getOrderSentinel 接口
// 集成 nacos 实现 sentinel 流控规则的持久化
@RequestMapping(“/getOrderSentinel”)
public String getOrderSentinel() {
return “OrderSentinel”;
}
启动项目
三、更新前面 Nacos 的配置
更新前
更新 nacos 配置
更改后
看,我们已经实现了通过 nacos 配置中心修改限流规则了
PS: 如果发现重启没有生效的话,是因为我们之前编写的 SentinelApplicationRunner 类会初始化我们的流控配置,所以将其注释掉,就可以解决 nacos 持久化被覆盖的问题
6.4.2 Gateway 整合 sentinel 实现服务限流
Gateway 是采用 netty 启动的,不支持 Servlet-API。所以添加了 Springboot-web 的依赖就会报错
网关流控原理
当通过
GatewayRuleManager
加载网关流控规则(GatewayFlowRule
)时,无论是否针对请求属性进行限流,Sentinel 底层都会将网关流控规则转化为热点参数规则(ParamFlowRule
),存储在GatewayRuleManager
中,与正常的热点参数规则相隔离。转换时 Sentinel 会根据请求属性配置,为网关流控规则设置参数索引(idx
),并同步到生成的热点参数规则中。
外部请求进入 API Gateway 时会经过 Sentinel 实现的 filter,其中会依次进行 路由/API 分组匹配、请求属性解析和参数组装。Sentinel 会根据配置的网关流控规则来解析请求属性,并依照参数索引顺序组装参数数组,最终传入
SphU.entry(res, args)
中。Sentinel API Gateway Adapter Common 模块向 Slot Chain 中添加了一个GatewayFlowSlot
,专门用来做网关规则的检查。GatewayFlowSlot
会从GatewayRuleManager
中提取生成的热点参数规则,根据传入的参数依次进行规则检查。若某条规则不针对请求属性,则会在参数最后一个位置置入预设的常量,达到普通流控的效果。
-
新建一个 module,并命名为 gorit-gateway
-
添加依赖
org.springframework.cloud
spring-cloud-starter-gateway
2.2.4.RELEASE
com.alibaba.csp
sentinel-spring-cloud-gateway-adapter
1.7.0
- 编写项目配置依赖 application.yml
spring:
application:
name: gorit-order-gateway-8080
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
gateway:
routes:
- id: my-member # 这个用不到,是之前写过的,可以直接 copy 过来
uri: lb://gorit-member
匹配规则
predicates:
-
Path=/gorit-member/**
-
id: gorit
uri: https://www.gorit.cn/ # 转发到我的个人网站
匹配规则
predicates:
- Path=/**
enabled: true
server:
port: 8080
- 注入对应的
SentinelGatewayFilter
实例以及SentinelGatewayBlockExceptionHandler
实例即可
package cn.gorit.config;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.result.view.ViewResolver;
import java.util.Collections;
import java.util.List;
/**
-
@Classname GatewayConfiguration
-
@Description TODO
-
@Date 2021/1/31 22:34
-
@Created by CodingGorit
-
@Version 1.0
*/
@Configuration
public class GatewayConfiguration {
private final List viewResolvers;
private final ServerCodecConfigurer serverCodecConfigurer;
public GatewayConfiguration(ObjectProvider<List> viewResolversProvider,
ServerCodecConfigurer serverCodecConfigurer) {
this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
this.serverCodecConfigurer = serverCodecConfigurer;
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
// Register the block exception handler for Spring Cloud Gateway.
return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public GlobalFilter sentinelGatewayFilter() {
return new SentinelGatewayFilter();
}
}
- 配置限流的接口(对应上述第一段实现原理)
package cn.gorit.config;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.annotation.Configuration;
import java.util.HashSet;
import java.util.Set;
/**
-
@Classname SentinelApplicationRunner
-
@Description TODO
-
@Date 2021/1/31 22:37
-
@Created by CodingGorit
-
@Version 1.0
*/
@Configuration
public class SentinelApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
initGatewayRules();
}
/**
- 配置网关限流规则
*/
private void initGatewayRules() {
Set rules = new HashSet<>();
// 设置规则
rules.add(new GatewayFlowRule(“gorit”)
// 设置阙值
.setCount(1)
// 设置时间窗口,单位是秒,默认是 1 s
.setIntervalSec(1));
GatewayRuleManager.loadRules(rules);
}
}
然后即可实现限流效果
PS:
-
如果要实现动态限流,并且持久化,将配置写入 nacos 配置中心即可
-
如果要实现自定义限流返回的信息,需要重写该方法:
public class SentinelGatewayBlockExceptionHandler implements WebExceptionHandler
- 然后注入我们重写的 handler 即可
*/
public class JsonSentinelGatewayBlockExceptionHandler implements WebExceptionHandler {
public JsonSentinelGatewayBlockExceptionHandler(List viewResolvers) {
}
@Override
public Mono handle(ServerWebExchange exchange, Throwable throwable) {
ServerHttpResponse serverHttpResponse = exchange.getResponse();
serverHttpResponse.getHeaders().add(“Content-Type”,“application/json;charset=utf-8”);
byte[] datas = “{“code”:403,“msg”:“API 接口被限流”}”.getBytes(StandardCharsets.UTF_8);
DataBuffer buffer = serverHttpResponse.bufferFactory().wrap(datas);
return serverHttpResponse.writeWith(Mono.just(buffer));
}
}
6.4.3 实现服务动态降级
服务出现异常时,会自动服务降级,禁止外来继续访问,不会执行真正的逻辑
Sentinel 支持服务的熔断降级
熔断类似于保险丝,如果超出了我们的阙值的情况下,在一定时间内不会执行我们的业务逻辑,直接执行我们的服务降级方法 (fallback) 返回一个友好的提示给客户端,不会真实的执行业务逻辑
// 基于信号量, 在控制台设置规则的时候,配置为 线程数
@SentinelResource(value = GETORDER_KEY, fallback = “”,blockHandler = “getOrderQpsException”)
@RequestMapping(“/getOrderSemaphore”)
public String getOrderSemaphore () {
log.info(“>>>” + Thread.currentThread().getName());
return “getOrderSemaphore”;
}
服务降级策略
- rt(平均响应时间)
如果在 1s 内访问五次,平均响应时间超出了我们在平台设置的阙值的情况下,直接触发我们的熔断,执行我们的服务降级的方法。
时间窗口:在规定的时间窗口内一直执行服务降级的方法,不执行业务逻辑
- 错误比例
客户端,发出了连续 5 个请求,每个请求全部错误,这说明错误率百分之百
每 s 秒发出 5个请求,如果请求异常占比超过我们设置的阈值占比的情况下,就会执行服务降级的方法,在规定时间窗口不执行业务逻辑
- 错误次数
官方的熔断策略
-
慢调用比例 (
SLOW_REQUEST_RATIO
):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs
)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。 -
异常比例 (
ERROR_RATIO
):当单位统计时长(statIntervalMs
)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是[0.0, 1.0]
,代表 0% - 100%。 -
异常数 (
ERROR_COUNT
):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
一、编写慢调用比例策略
接口
/**
- 基于我们的平均响应时间海鲜降级
*/
@SentinelResource(value = “getOrderDowngradeRtType”,fallback = “getOrderDowngradeRtTypeFallback”)
@RequestMapping(“/getOrderDowngradeRtType”)
public String getOrderDowngradeRtType() {
try {
Thread.sleep(300);
} catch (Exception e) {
}
return “order 业务逻辑正常执行”;
}
public String getOrderDowngradeRtTypeFallback() {
return “执行本地的服务降级的方法”;
}
sentinel 添加降级规则
测试相应即可
超出响应时间了,就会返回 fallback 的内容
二、异常比例熔断测试
接口(不带参数就会报错)
/**
-
基于异常比例 服务降级
*/
@SentinelResource(value = “getOrderDowngradeErrorType”,fallback = “getOrderDowngradeErrorTypeFallback”)
@RequestMapping(“/getOrderDowngradeErrorType”)
public String getOrderDowngradeErrorType(@RequestParam int age) {
int i = 1/age;
return "业务逻辑请求成功 i "+ i;
}
public String getOrderDowngradeErrorTypeFallback(int age) {
return “错误率太高,请稍后重试访问该接口”;
}
配置
三、基于异常数实现服务降级
复用上面的接口
最后
在面试前我整理归纳了一些面试学习资料,文中结合我的朋友同学面试美团滴滴这类大厂的资料及案例
由于篇幅限制,文档的详解资料太全面,细节内容太多,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!
大家看完有什么不懂的可以在下方留言讨论也可以关注。
觉得文章对你有帮助的话记得关注我点个赞支持一下!
三、基于异常数实现服务降级
复用上面的接口
最后
在面试前我整理归纳了一些面试学习资料,文中结合我的朋友同学面试美团滴滴这类大厂的资料及案例
[外链图片转存中…(img-G7wpf0Xs-1715469283412)]
[外链图片转存中…(img-DM8lsIYV-1715469283413)]
由于篇幅限制,文档的详解资料太全面,细节内容太多,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!
大家看完有什么不懂的可以在下方留言讨论也可以关注。
觉得文章对你有帮助的话记得关注我点个赞支持一下!