一些私人网站网站建设服务器租用

当前位置: 首页 > news >正文

一些私人网站,网站建设服务器租用,校园门户网站建设方案,张家界网站制作与代运营文章目录 一、Entry1、Entry的声明2、使用API自定义资源3、基于SentinelResource注解标记资源 二、Context1、Context介绍2、Context的初始化3、AbstractSentinelInterceptor4、ContextUtil 一、Entry 1、Entry的声明 默认情况下#xff0c;Sentinel会将controller中的方法作… 文章目录 一、Entry1、Entry的声明2、使用API自定义资源3、基于SentinelResource注解标记资源 二、Context1、Context介绍2、Context的初始化3、AbstractSentinelInterceptor4、ContextUtil 一、Entry 1、Entry的声明 默认情况下Sentinel会将controller中的方法作为被保护资源那么问题来了我们该如何将自己的一段代码标记为一个Sentinel的资源呢 Sentinel中的资源用Entry来表示。 声明Entry的API示例try后面直接加括号写上对应的资源就不用自己再加finally语句去关闭了即try-with-resource // 资源名可使用任意有业务语义的字符串比如方法名、接口名或其它可唯一标识的字符串。 try (Entry entry SphU.entry(resourceName)) {// 被保护的业务逻辑// 你想做的操作比如异常次数1、调用次数1// do something here… } catch (BlockException ex) {// 业务代码抛的异常限流的异常// 资源访问阻止被限流或被降级// 在此处进行相应的处理操作 }try-with-resource的写法需要注意 特别地若 entry 的时候传入了热点参数那么 exit 的时候也一定要带上对应的参数exit(count, args)否则可能会有统计错误。这个时候不能使用 try-with-resources 的方式。另外通过 Tracer.trace(ex) 来统计异常信息时由于 try-with-resources 语法中 catch 调用顺序的问题会导致无法正确统计异常数因此统计异常信息时也不能在 try-with-resources 的 catch 块中调用 Tracer.trace(ex)。 不用try-with-resource手动exit的写法 Entry entry null; // 务必保证 finally 会被执行 try {// 资源名可使用任意有业务语义的字符串注意数目不能太多超过 1K超出几千请作为参数传入而不要直接作为资源名// EntryType 代表流量类型inbound/outbound其中系统规则只对 IN 类型的埋点生效entry SphU.entry(自定义资源名);// 被保护的业务逻辑// do something… } catch (BlockException ex) {// 资源访问阻止被限流或被降级// 进行相应的处理操作 } catch (Exception ex) {// 若需要配置降级规则需要通过这种方式记录业务异常Tracer.traceEntry(ex, entry); } finally {// 务必保证 exit务必保证每个 entry 与 exit 配对if (entry ! null) {entry.exit();} }关于SphU.entry()方法的参数 注意SphU.entry(xxx) 需要与 entry.exit() 方法成对出现匹配调用否则会导致调用链记录异常抛出 ErrorEntryFreeException 异常。常见的错误 1、自定义埋点只调用 SphU.entry()没有调用 entry.exit() 2、顺序错误比如entry1 - entry2 - exit1 - exit2应该为 entry1 - entry2 - exit2 - exit12、使用API自定义资源 在demo工程的order-service服务中将OrderService的queryOrderById()方法标记为一个资源queryOrderById()方法未修改前 首先处理下依赖与配置 !–sentinel– dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-sentinel/artifactId /dependencyspring:cloud:sentinel:transport:dashboard: localhost:8090 # 这里我的sentinel用了8089的端口修改OrderService类的queryOrderById方法创建Entry资源将要保护的一段代码放入try语句来做为一个资源 public Order queryOrderById(Long orderId) {// 创建Entry标记资源资源名为resource1try (Entry entry SphU.entry(resource1)) {// 1.查询订单Order order orderMapper.findById(orderId);// 2.查询用户基于Feign的远程调用User user userClient.findById(order.getUserId());// 3.设置order.setUser(user);// 4.返回return order;}catch (BlockException e){log.error(被限流或降级, e);return null;} }重启后可以看到自定义资源成功 很明显上面的逻辑在自定义资源时是重复的动作即创建Entry再拿个try-catch把要定义的代码包起来。 ⇒ AOP环绕实现在前面之前try在之后catchAOP再兑换成一个注解这个注解官方已实现就是SentinelResource 3、基于SentinelResource注解标记资源 同样标记Service里的queryOrderById方法 从自动装配开始找到源码看下实现 匹配SentinelResource注解做AOP环绕增强的源码 /*** Aspect for methods with {link SentinelResource} annotation.** author Eric Zhao*/ Aspect public class SentinelResourceAspect extends AbstractSentinelAspectSupport {// 切点是添加了 SentinelResource注解的类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();int resourceType annotation.resourceType();Entry entry null;try {// 创建资源 Entryentry SphU.entry(resourceName, resourceType, entryType, pjp.getArgs());// 执行受保护的方法Object result pjp.proceed();return result;} catch (BlockException ex) {return handleBlockException(pjp, annotation, ex);} catch (Throwable ex) {Class? extends Throwable[] exceptionsToIgnore annotation.exceptionsToIgnore();// The ignore list will be checked first.if (exceptionsToIgnore.length 0 exceptionBelongsTo(ex, exceptionsToIgnore)) {throw ex;}if (exceptionBelongsTo(ex, annotation.exceptionsToTrace())) {traceException(ex);return handleFallback(pjp, annotation, ex);}// No fallback function can handle the exception, so throw it out.throw ex;} finally {if (entry ! null) {entry.exit(1, pjp.getArgs());}}} } 简单来说SentinelResource注解就是一个标记而Sentinel基于AOP思想对被标记的方法做环绕增强完成资源Entry的创建。 二、Context 如图除了簇点链路中的controller方法以及我自定义的资源还有一个默认的入口节点sentinel_spring_web_context它是一个EntranceNode类型的节点这个节点是在初始化Context的时候由Sentinel帮我们创建的。 1、Context介绍 Context 代表调用链路上下文贯穿一次调用链路中的所有资源 Entry基于ThreadLocal。Context 维护着入口节点entranceNode、本次调用链路的 curNode当前资源节点、调用来源origin等信息。后续的Slot都可以通过Context拿到DefaultNode或者ClusterNode从而获取统计数据完成规则判断Context初始化的过程中会创建EntranceNodecontextName就是EntranceNode的名称 创建Context的API为 // 创建context包含两个参数context名称、 来源名称 ContextUtil.enter(contextName, originName);2、Context的初始化 查看Sentinel依赖包下的spring.factories
spring.factories声明需要就是自动装配的配置类 先看SentinelWebAutoConfiguration这个类 这个类实现了WebMvcConfigurer我们知道这个是SpringMVC自定义配置用到的类可以配置HandlerInterceptor 可以看到这里泛型中配置了一个SentinelWebInterceptor的拦截器。SentinelWebInterceptor的声明如下 发现它继承了AbstractSentinelInterceptor这个类而AbstractSentinelInterceptor最终实现了HandlerInterceptor HandlerInterceptor拦截器会拦截一切进入controller的方法执行preHandle前置拦截方法而Context的初始化就是在这里完成的。 3、AbstractSentinelInterceptor HandlerInterceptor拦截器会拦截一切进入controller的方法执行preHandle前置拦截方法而Context的初始化就是在这里完成的。来看看这个类的preHandle实现 Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {try {// 获取资源名称一般是controller方法的RequestMapping路径例如/order/{orderId}String resourceName getResourceName(request);if (StringUtil.isEmpty(resourceName)) {return true;}// 从request中获取请求来源将来做 授权规则 判断时会用String origin parseOrigin(request);// 获取 contextName默认是sentinel_spring_web_contextString contextName getContextName(request);// 创建 ContextContextUtil.enter(contextName, origin);// 创建资源名称就是当前请求的controller方法的映射路径Entry entry SphU.entry(resourceName, ResourceTypeConstants.COMMON_WEB, EntryType.IN);request.setAttribute(baseWebMvcConfig.getRequestAttributeName(), entry);return true;} catch (BlockException e) {try {handleBlockException(request, response, e);} finally {ContextUtil.exit();}return false;} }4、ContextUtil 创建Context的方法就是 ContextUtil.enter(contextName, origin); 我们进入entry方法 public static Context enter(String name, String origin) {if (Constants.CONTEXT_DEFAULT_NAME.equals(name)) {throw new ContextNameDefineException(The Constants.CONTEXT_DEFAULT_NAME cant be permit to defined!);}return trueEnter(name, origin); }进入trueEnter方法 protected static Context trueEnter(String name, String origin) {// 尝试获取contextContext context contextHolder.get();// 判空if (context null) {// 如果为空开始初始化MapString, DefaultNode localCacheNameMap contextNameNodeMap;// 尝试获取入口节点DefaultNode node localCacheNameMap.get(name);if (node null) {LOCK.lock();try {node contextNameNodeMap.get(name);if (node null) {// 入口节点为空初始化入口节点 EntranceNodenode new EntranceNode(new StringResourceWrapper(name, EntryType.IN), null);// 添加入口节点到 ROOTConstants.ROOT.addChild(node);// 将入口节点放入缓存MapString, DefaultNode newMap new HashMap(contextNameNodeMap.size() 1);newMap.putAll(contextNameNodeMap);newMap.put(name, node);contextNameNodeMap newMap;}} finally {LOCK.unlock();}}// 创建Context参数为入口节点 和 contextNamecontext new Context(node, name);// 设置请求来源 origincontext.setOrigin(origin);// 放入ThreadLocalcontextHolder.set(context);}// 返回return context; }画图表示Entry和Context两个核心API完成资源的创建不管是controller中的资源还是自定义的资源接下来就是执行ProcessorSlotChain插槽链关于ProcessorSlotChain的执行见下篇。