- 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的处理流程还是比较清晰的,只要能够理解其设计原理,我们就能容易地整合进去;
我已经设置了 Azure API 管理服务,并在自定义域上配置了它。在 Azure 门户中 API 管理服务的配置部分下,我设置了以下内容: 因为这是一个客户端系统,我必须屏蔽细节,但以下是基础知识:
我是一名习惯 React Native 的新程序员。我最近开始学习 Fetch API 及其工作原理。我的问题是,我找不到人们使用 API key 在他们的获取语句中访问信息的示例(我很难清楚地表达有
这里有很多关于 API 是什么的东西,但是我找不到我需要的关于插件 API 和类库 API 之间的区别。反正我不明白。 在 Documenting APIs 一书中,我读到:插件 API 和类库 AP
关闭。这个问题不满足Stack Overflow guidelines .它目前不接受答案。 想改善这个问题吗?更新问题,使其成为 on-topic对于堆栈溢出。 7年前关闭。 Improve thi
我正在尝试找出设计以下场景的最佳方法。 假设我已经有了一个 REST API 实现,它将从不同的供应商那里获取书籍并将它们返回给我自己的客户端。 每个供应商都提供单独的 API 来向其消费者提供图书。
请有人向我解释如何使用 api key 以及它有什么用处。 我对此进行了很多搜索,但得到了不同且相互矛盾的答案。有人说 API key 是保密的,它从不作为通信的一部分发送,而其他人则将它发送给客户端
关闭。这个问题是opinion-based .它目前不接受答案。 想改进这个问题?更新问题,以便 editing this post 可以用事实和引用来回答它. 4年前关闭。 Improve this
谁能告诉我为什么 WSo2 API 管理器不进行身份验证?我已经设置了两个 WSo2 API Manager 1.8.0 实例并创建了一个 api。它作为原型(prototype) api 工作正常。
我在学习 DSL 的过程中遇到了 Fluent API。 我在流利的 API 上搜索了很多……我可以得出的基本结论是,流利的 API 使用方法链来使代码流利。 但我无法理解——在面向对象的语言中,我们
基本上,我感兴趣的是在多个区域设置 WSO2 API 管理器;例如亚洲、美国和欧洲。一些 API 将部署在每个区域的数据中心内,而其他 API 将仅部署在特定区域内。 理想情况下,我想要的是一个单一的
我正在构建自己的 API,供以下用户使用: 1) 安卓应用 2) 桌面应用 我的网址之一是:http://api.chatapp.info/order_api/files/getbeers.php我的
我需要向所有用户显示我的站点的分析,但使用 OAuth 它显示为登录用户配置的站点的分析。如何使用嵌入 API 实现仪表板但仅显示我的网站分析? 我能想到的最好的可能性是使用 API key 而不是客
我正在研究大公司如何管理其公共(public) API。我想到的是拥有成熟 API 的公司,例如 Google、Facebook、Twitter 和 Amazon。 这些公司向公众公开了许多不同的 A
在定义客户可访问的 API 时,以下是首选的行业惯例: a) 定义一组显式 API 方法,每个方法都有非常狭窄和特定的目的,例如: SetUserName SetUserAge Se
这在本地 deserver 和部署时都会发生。我成功地能够通过留言簿教程使用 API 资源管理器,但现在我已经创建了自己的项目并尝试访问我编写的第一个 API,它从未出现过。搜索栏旁边的黄色“正在加载
我正在尝试使用 http://ip-api.com/ api通过我的ip地址获取经度和纬度。当我访问 http://ip-api.com/json从我的浏览器或使用 curl,它以 json 格式返回
这里的典型示例是 Twitter 的 API。我从概念上理解 REST API 的工作原理,本质上它只是针对您的特定请求向他们的服务器查询,然后您会在其中收到响应(JSON、XML 等),很棒。 但是
我能想到的最好的标题,但要澄清的是,情况是这样的: 我正在开发一种类似短 url 的服务,该服务允许用户使用他们的 Twitter 帐户“登录”并发布内容。现在这项服务可以包含在 Tweetdeck
我正在设计用于管理评论和讨论线程的 API 方案。我想有一个点 /discussions/:discussionId 当您GET 时,它会返回一组评论和一些元数据。评论也许可以单独访问 /discus
关闭。这个问题需要更多focused .它目前不接受答案。 想改进这个问题吗? 更新问题,使其只关注一个问题 editing this post . 关闭去年。 Improve this quest
我是一名优秀的程序员,十分优秀!