- 921. Minimum Add to Make Parentheses Valid 使括号有效的最少添加
- 915. Partition Array into Disjoint Intervals 分割数组
- 932. Beautiful Array 漂亮数组
- 940. Distinct Subsequences II 不同的子序列 II
这里介绍三个重要的API。
@GetMapping("/test-sentinel-api")
public String testSentinelApi(@RequestParam(required = false) String a) {
//定义一个sentinel受保护的资源,名称是test-sentinel-api
String resourceName = "test-sentinel-api";
//
ContextUtil.enter(resourceName, "test-wfw");
Entry entry = null;
try {
entry = SphU.entry(resourceName);
//被保护的逻辑
if (StringUtils.isEmpty(a)) {
throw new IllegalArgumentException("a is not null");
}
return a;
} catch (BlockException be) {
//如果受保护的资源被限流或者降级了 就会抛BlockException
log.warn("限流或者降级了", be);
return "限流或者降级了";
} catch (IllegalArgumentException ie) {
//统计 IllegalArgumentException 发生的次数、占比。。。
Tracer.trace(ie);
return "a is not null";
} finally {
if(entry != null) {
//退出entry
entry.exit();
}
ContextUtil.exit();
}
}
属性 | 作用 | 是否必须 |
---|---|---|
value | 资源名称 | 是 |
entryType | entry类型,标记流量的方向,取值IN/OUT,默认是OUT | 否 |
blockHandler | 处理BlockException的函数名称。函数要求:1.必须是 public 2.返回类型与原方法一致 3.参数类型需要和原方法相匹配,并在最后加 BlockException 类型的参数。4.默认需和原方法在同一个类中。若希望使用其他类的函数,可配置 blockHandlerClass ,并指定blockHandlerClass里面的方法。 | 否 |
blockHandlerClass | 存放blockHandler的类。对应的处理函数必须static修饰,否则无法解析,其他要求:同blockHandler。 | 否 |
fallback | 用于在抛出异常的时候提供fallback处理逻辑。fallback函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。函数要求:1. 返回类型与原方法一致 2. 参数类型需要和原方法相匹配,Sentinel 1.6开始,也可在方法最后加 Throwable 类型的参数。3.默认需和原方法在同一个类中。若希望使用其他类的函数,可配置 fallbackClass ,并指定fallbackClass里面的方法。 | 否 |
fallbackClass【1.6】 | 存放fallback的类。对应的处理函数必须static修饰,否则无法解析,其他要求:同fallback。 | 否 |
defaultFallback【1.6】 | 用于通用的 fallback 逻辑。默认fallback函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,以fallback为准。函数要求:1. 返回类型与原方法一致 2. 方法参数列表为空,或者有一个 Throwable 类型的参数。3. 默认需要和原方法在同一个类中。若希望使用其他类的函数,可配置 fallbackClass ,并指定 fallbackClass 里面的方法。 | 否 |
exceptionsToIgnore【1.6】 | 指定排除掉哪些异常。排除的异常不会计入异常统计,也不会进入fallback逻辑,而是原样抛出。 | 否 |
exceptionsToTrace | 需要trace的异常 | Throwable |
限流处理的方法。
@GetMapping("/test-sentinel-resource")
@SentinelResource(value = "test-sentinel-resource",
blockHandlerClass = TestBlock.class,
blockHandler = "block",
fallbackClass = TestFallBack.class,
fallback = "fallBack"
)
public String testSentinelResource(@RequestParam(required = false) String a) {
if (StringUtils.isEmpty(a)) {
throw new IllegalArgumentException("a is not null");
}
return a;
}
限流处理类。
@Slf4j
@Component
public class TestBlock {
public static String block(String a, BlockException e) {
log.warn("限流 或者 降级 block a:{}", a, e);
return "限流 或者 降级 block";
}
}
降级处理类。
@Slf4j
@Component
public class TestFallBack {
public static String fallBack(String a, Throwable e) {
log.warn("限流 或者 降级 fall a:{}", a, e);
return "限流 或者 降级 fall";
}
}
TIPS
SentinelResourceAspect
是对 @SentinelResource
的处理类
@Aspect
public class SentinelResourceAspect extends AbstractSentinelAspectSupport {
@Pointcut("@annotation(com.alibaba.csp.sentinel.annotation.SentinelResource)")
public void sentinelResourceAnnotationPointcut() {
}
@Around("sentinelResourceAnnotationPointcut()")
public Object invokeResourceWithSentinel(ProceedingJoinPoint pjp) throws Throwable {
// 获取当前访问的方法
Method originMethod = resolveMethod(pjp);
// 获取方法上的SentinelResource注解
SentinelResource annotation = originMethod.getAnnotation(SentinelResource.class);
if (annotation == null) {
// Should not go through here.
throw new IllegalStateException("Wrong state for SentinelResource annotation");
}
// 获取资源名
String resourceName = getResourceName(annotation.value(), originMethod);
EntryType entryType = annotation.entryType();
Entry entry = null;
try {
entry = SphU.entry(resourceName, entryType, 1, pjp.getArgs());
Object result = pjp.proceed();
return result;
} catch (BlockException ex) {
// 处理被限制的异常,回调事先配置的异常处理方法
return handleBlockException(pjp, annotation, ex);
} catch (Throwable ex) {
Tracer.trace(ex);
throw ex;
} finally {
if (entry != null) {
entry.exit();
}
}
}
}
handleBlockException
通过反射获取到注解上配置的fallback
方法
private Object handleBlockException(ProceedingJoinPoint pjp, SentinelResource annotation, BlockException ex)
throws Exception {
// Execute fallback for degrading if configured.
Object[] originArgs = pjp.getArgs();
if (isDegradeFailure(ex)) {
Method method = extractFallbackMethod(pjp, annotation.fallback());
if (method != null) {
return method.invoke(pjp.getTarget(), originArgs);
}
}
// Execute block handler if configured.
Method blockHandler = extractBlockHandlerMethod(pjp, annotation.blockHandler(), annotation.blockHandlerClass());
if (blockHandler != null) {
// Construct args.
Object[] args = Arrays.copyOf(originArgs, originArgs.length + 1);
args[args.length - 1] = ex;
if (isStatic(blockHandler)) {
return blockHandler.invoke(null, args);
}
return blockHandler.invoke(pjp.getTarget(), args);
}
// If no block handler is present, then directly throw the exception.
throw ex;
}
增加配置
feign:
sentinel:
enabled: true
创建限流处理类
@Slf4j
@Component
public class UserFallBackFactoryFeign implements FallbackFactory<UserFeign> {
@Override
public UserFeign create(Throwable throwable) {
return id -> {
log.warn("限流或降级 id:{}", id, throwable);
User user = new User();
user.setName("默认用户");
return user;
};
}
}
@Slf4j
@Component
public class UserFallBackFeign implements UserFeign {
@Override
public User getUserById(Long id) {
User user = new User();
user.setName("默认用户");
return user;
}
}
feign 注解修改
需要注意的是 fallbackFactory 和 fallback 只能存在一个。
@FeignClient(value = "user",
//fallbackFactory = UserFallBackFactoryFeign.class,
fallback = UserFallBackFeign.class
)
public interface UserFeign {
@GetMapping("/user/{id}")
User getUserById(@PathVariable Long id);
}
我们先分析下Feign的整个构造流程。
public <T> T newInstance(Target<T> target) {
Map<String, MethodHandler> nameToHandler = this.targetToHandlersByName.apply(target);
Map<Method, MethodHandler> methodToHandler = new LinkedHashMap();
List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList();
Method[] var5 = target.type().getMethods();
int var6 = var5.length;
for(int var7 = 0; var7 < var6; ++var7) {
Method method = var5[var7];
if (method.getDeclaringClass() != Object.class) {
if (Util.isDefault(method)) {
DefaultMethodHandler handler = new DefaultMethodHandler(method);
defaultMethodHandlers.add(handler);
methodToHandler.put(method, handler);
} else {
methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
}
}
}
// 使用 InvocationHandlerFactory 根据接口的方法信息和 target 对象构造 InvocationHandler
InvocationHandler handler = this.factory.create(target, methodToHandler);
// 构造代理
T proxy = Proxy.newProxyInstance(target.type().getClassLoader(), new Class[]{
target.type()}, handler);
Iterator var12 = defaultMethodHandlers.iterator();
while(var12.hasNext()) {
DefaultMethodHandler defaultMethodHandler = (DefaultMethodHandler)var12.next();
defaultMethodHandler.bindTo(proxy);
}
return proxy;
}
这里的
InvocationHandlerFactory
是通过构造Feign
的时候传入的:
使用原生的 DefaultTargeter
: 那么会使用 feign.InvocationHandlerFactory.Default
这个 factory
,并且构造出来的 InvocationHandler
是 feign.ReflectiveFeign.FeignInvocationHandler
。
使用 hystrix 的 HystrixTargeter: 那么会在feign.hystrix.HystrixFeign.Builder#build(feign.hystrix.FallbackFactory) 方法中调用父类的 invocationHandlerFactory方法传入一个匿名的 InvocationHandlerFactory 实现类,该类内部构造出的 InvocationHandler 为 HystrixInvocationHandler。
理解了Feign 的执行过程之后,Sentinel 想要整合 Feign,可以参考 Hystrix 的实现:
实现 Targeter 接口 SentinelTargeter。 很不幸,Targeter 这个接口属于包级别的接口,在外部包中无法使用,这个 Targeter 无法使用。没关系,我们可以沿用默认的HystrixTargeter(实际上会用DefaultTargeter)。
FeignClientFactoryBean 内部构造 Targeter、feign.Feign.Builder 的时候,都会从 FeignContext 中获取。所以我们沿用默认的 DefaultTargeter 的时候,内部使用的 feign.Feign.Builder 可控,而且这个 Builder 不是包级别的类,可在外部使用。
创建 SentinelFeign.Builder
继承 feign.Feign.Builder
,用来构造 Feign
。
SentinelFeign.Builder
内部需要获取 FeignClientFactoryBean
中的属性进行处理,比如获取 fallback
, name
, fallbackFactory
。很不幸,FeignClientFactoryBean
这个类也是包级别的类。没关系,我们知道它存在在 ApplicationContext
中的 beanName, 拿到 bean 之后根据反射获取属性就行(该过程在初始化的时候进行,不会在调用的时候进行,所以不会影响性能)。
SentinelFeign.Builder
调用 build
方法构造 Feign
的过程中,我们不需要实现一个新的 Feign
,跟 hystrix
一样沿用 ReflectiveFeign
即可,在沿用的过程中调用父类 feign.Feign.Builder
的一些方法进行改造即可,比如 invocationHandlerFactory
方法设置 InvocationHandlerFactory
,contract
的调用。
跟 hystrix 一样实现自定义的 InvocationHandler
接口 SentinelInvocationHandler
用来处理方法的调用。
SentinelInvocationHandler
内部使用 Sentinel 进行保护,这个时候涉及到资源名的获取。SentinelInvocationHandler
内部的 feign.Target
能获取服务名信息,feign.InvocationHandlerFactory.MethodHandler
的实现类 feign.SynchronousMethodHandler
能拿到对应的请求路径信息。很不幸,feign.SynchronousMethodHandler
这个类也是包级别的类。没关系,我们可以自定义一个 feign.Contract
的实现类SentinelContractHolder
在处理 MethodMetadata
的过程把这些 metadata 保存下来(feign.Contract
这个接口在 Builder
构造 Feign
的过程中会对方法进行解析并验证)。
在 SentinelFeign.Builder
中调用 contract
进行设置,SentinelContractHolder
内部保存一个 Contract
使用委托方式不影响原先的 Contract
过程。
1、 Feign的内部很多类都是package级别的,外部package无法引用某些类,这个时候只能想办法绕过去,比如使用反射;
2、 目前这种实现有风险,万一哪天starter内部使用的Feign相关类变成了package级别,那么会改造代码所以把Sentinel的实现放到Feign里并给Feign官方提pr可能更加合适;
3、 Feign的处理流程还是比较清晰的,只要能够理解其设计原理,我们就能容易地整合进去;
注解@CrossOrigin 出于安全原因,浏览器禁止Ajax调用驻留在当前原点之外的资源。例如,当你在一个标签中检查你的银行账户时,你可以在另一个选项卡上拥有EVILL网站。来自EVILL的脚本
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界. 这篇CFSDN的博客文章深入理解Java高级特性——注解由作者收集整理,如果你对这篇文章有兴趣,
概述 在这个快速教程中,我们将探索 Spring 的 @RequestParam 注解。 简单地说,我们可以使用 @RequestParam 从请求中提取查询参数、表单参数甚至文件。我们将讨论如何使用
我有一个关于 Spring @Async 注释的问题。我有一个 Controller 自动连接了一个服务(GnInsuranceDetailsService) @RequestMapping(va
我在使用注释来调用注释所属的方法时遇到了一些麻烦......我将举一个例子: class MyEventHandlers { @handler(id=“1”) public doSom
我是.Net程序员,但是这次我正在从事Java项目,并且遇到了一些困难。这个 java 项目不是我的,它是由其他开发人员开发的,并且使用 Hibernate。 当我运行 Ant 构建器时,我收到此错误
我在 Grails 文档(第 9 章:测试)中读到过这个注解。但是我不明白这是什么... 问题是我需要模拟 GORM 的动态方法,有一种方法可以自动模拟它们,而不必编写我需要的所有方法吗? 最佳答案
这个问题在这里已经有了答案: How to get annotation class name, attribute values using reflection (2 个答案) 关闭 5 年前。
如何了解 Java EE 6 JMS 注释规范支持的所有有效属性集@ActivationConfigProperty Java EE 6 Documentation for @ActivationCo
我认为这是不可能的,但也许我错了。所以我问你,如果可能的话。 ;-) 如果我定义了一个注释,它只接受类引用,它扩展了一些可能的接口(interface)或类: Class serviceIFProv
我正在尝试使用 javax.validation 验证一些 DTO,但似乎注释 @NotEmpty 没有检查参数是否为 null。 这是我的类(class): Person.class public
我是 hibernate 新手,我正在尝试学习它,但在尝试使一对多关系正常工作时遇到了问题。我尝试了几个例子,但似乎没有一个起作用。 有人可以看一下下面的代码并告诉我哪里出了问题吗?我尝试了很多不同的
这个问题已经有答案了: Why doesn't Java offer operator overloading? (17 个回答) 已关闭 9 年前。 每个人都知道 Java 中的简单算术如何用于基元
有人知道如何用 Python 处理这种 XML 注释,这是我第一次看到。 <?link id="752760" resource-uuid="UUID-9f0575a3-1847-1cde-fd
我遇到了这个link这解释了如何继承 bean。假设此示例中的 HelloWorld 类使用 @Component 注释作为 bean 公开,如何创建另一个继承此 bean 的 bean?我可以使用
谁能告诉我这段代码是否: public class OvTester { @Override public int hashCode() { return toStri
我有一个实体,它有一个非键列,我已将其设置为在我的数据库中自动生成。 我不能使用 @GeneratedValue,因为据我所知,它仅适用于关键字段。 在这种情况下,如何指示非键列是自动生成的? 最佳答
所以可能像很多人一样,我通常会临时注释掉代码,主要是为了调试目的。我目前放了类似 **DEBUG** 或任何容易搜索的内容,但我认为让编译器在发现临时注释掉的代码时输出警告(甚至错误)可能很有用。我想
此组件解决的问题是: 「谁」在「什么时间」对「什么」做了「什么事」 本组件目前针对 Spring-boot 做了 Autoconfig,如果是 SpringMVC,也可自己在 xml 初始化 b
配置全局乱码过滤器 参数绑定注解@RequestParam 注解@RequestParam的参数使用说明 获得Restful风格的参数 自定义类型转换器 自定义转换器的开发步骤: 获得Servlet相
我是一名优秀的程序员,十分优秀!