简介

最近都在弄微服务的东西,现在来记录下收获。我从一知半解到现在能从0搭建使用最大的感触有两点

1.微服务各大组件的版本很多,网上很多博客内容不一定适合你的版本,很多时候苦苦琢磨都是无用功

2.网上博客参差不齐,有些甚至错误的。更离谱的是,好的文章阅读量除非高出天际,不然就都很低,比那些复制粘贴,随便应付的都低(这个搜索推荐算法不知道基于什么的)

通过这段时间学习,我觉得最重要是从好的博客入手,先不要着急怎么组件怎么使用,而是先了解组件的作用,大概的原理,然后才是使用,这样搭建和尝试的过程中才能更好的定位问题,最后再次回到原理和一些实际问题的处理(不知道实际问题怎样的,直接搜那个组件的面试题往往效果最好)

接下来的内容,都以导航的形式展现给大家(毕竟优秀的轮子很多,直接看大佬写的不香嘛),再顺带提些自己的理解

传送门

更多微服务的介绍可点击下方链接

微服务介绍Nginx导航Nacos导航Gateway导航Ribbon导航Feign导航Sentinel导航

博主微服务git练手项目:https://github.com/NiceJason/SpringCloudDemo

Sentinel简介

1.限流相关

1.1限流点

在聊Sentinel之前,先简单梳理下微服务限流的几个地方:

1.Nginx限流,系统的最外层限流地点

2.Gateway网关限流,Gateway可以用内置的限流Filter(RequestRateLimiterGatewayFilterFactory,依赖于redis),或者其他插件的限流Filter(如Redis的Redis RateLimiter),或者自定义Filter(自己实现限流算法),或者结合Sentinel或者Hystrix来限流。

参考:https://blog.csdn.net/qq_38380025/article/details/102968559(博客的第八点 请求限流)

3.微服务内部的限流,结合Sentinel或者Hystrix

4.消息队列的限流,通过控制生产者和消费者的速度

5.数据库连接池限流,一定时间内只能有一定数量的连接

当需要限流的时候可以从这5个点去思考

1.2限流算法

参考:https://blog.csdn.net/qq_38380025/article/details/102968559(博客的第八点 请求限流)

文章简述了常用的限流算法,如令牌桶算法、漏桶算法,可以大致了解一下,以后万一有场景需要手动实现的时候就能有个思路

2.Sentinel相关

Sentinel十分友好的中文安装使用文档:https://github.com/alibaba/Sentinel/wiki/%E6%96%B0%E6%89%8B%E6%8C%87%E5%8D%97

里面介绍了基础的安装与使用(十分简单易上手),因为同是阿里系的组件,所以和Nacos结合的特别好,熔断限流等配置直接在Nacos上写即可,具体使用看文档(重点了解“资源”这个概念),这里讲下文档没有或者被忽视的地方

2.1Json参数配置

参考(规则种类):https://github.com/alibaba/Sentinel/wiki/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8#%E8%A7%84%E5%88%99%E7%9A%84%E7%A7%8D%E7%B1%BB

可以看到一些参数说明,具体参数值如果还不明白的,看该规则的类,里面会有默认参数及注释,一般都是使用RuleConst里的值,而里面的值代表什么意思可以看具体的Rule类

 
如:限流规则 FlowRule

2.2Sentinel和SpringCloud结合

有一点比较重要,就是Sentinel和SpringCloud结合开始的地方在哪,在AbstractSentinelInterceptor开始

public abstract class AbstractSentinelInterceptor implements HandlerInterceptor {
...
}

所以Sentinel可以把控程序的入口和出口,而掌握不了里面业务的处理(这点单这样听好像没卵用,但在实际功能开发和BUG查找中还是挺有用的,了解来龙去脉)

2.2.1Sentinel熔断失效,不起作用

比如:Sentinel设置了熔断,但是@FeignClient设置了Fallback方法对异常进行了处理,那么熔断是不生效的,以Sentinel的视角来看,没抛出异常就是正常执行。

还有项目中往往@ExceptionHandle进行全局异常处理,这要也会导致熔断失效,下面简单的分析分析

Sentinel与SpringCloud结合,是从AbstractSentinelInterceptor开始的

 1 public abstract class AbstractSentinelInterceptor implements HandlerInterceptor {
2 @Override
3 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
4 throws Exception {
5 try {
6 String resourceName = getResourceName(request);
7
8 if (StringUtil.isNotEmpty(resourceName)) {
9 // Parse the request origin using registered origin parser.
10 String origin = parseOrigin(request);
11 ContextUtil.enter(SENTINEL_SPRING_WEB_CONTEXT_NAME, origin);
12 Entry entry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_WEB, EntryType.IN);
13
14 setEntryInRequest(request, baseWebMvcConfig.getRequestAttributeName(), entry);
15 }
16 return true;
17 } catch (BlockException e) {
18 handleBlockException(request, response, e);
19 return false;
20 }
21 }
22
23 @Override
24 public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
25 Object handler, Exception ex) throws Exception {
26 Entry entry = getEntryInRequest(request, baseWebMvcConfig.getRequestAttributeName());
27 if (entry != null) {
28 //跟踪记录异常
29 traceExceptionAndExit(entry, ex);
30 removeEntryInRequest(request);
31 }
32 ContextUtil.exit();
33 }
34
35 protected void traceExceptionAndExit(Entry entry, Exception ex) {
36 if (entry != null) {
37 //要ex参数不为空才记录,但这个参数是可能为空的
38 if (ex != null) {
39 Tracer.traceEntry(ex, entry);
40 }
41 entry.exit();
42 }
43 }
44 }
可以看出,它就是一个Spring的拦截器,preHandle方法就进行资源绑定(然后进行Sentinel的一连串的Slot判断),通过后才能正常访问资源
而afterCompletion方法则进行收尾工作,如资源执行时抛出异常则记录,为熔断功能提供数据
失效的原因恰恰是这个afterCompletion,我们可以看到traceExceptionAndExit方法里,需要ex不为空才记录异常信息,但这个参数是有可能会空的
这就要到DispatcherServlet的doDispatch里面来,这里面负责调用interceptor,具体看里面的注释
 1 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
2 ...
3
4 try {
5 ModelAndView mv = null;
6 Exception dispatchException = null;
7
8 try {
9 ...
10
11 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
12
13 ...
14
15 // 这里会调用intercepter过滤链,调用其preHandle方法
16 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
17
18 ...
19 }
20 catch (Exception ex) {
21 //这里捕获了所有异常
22 dispatchException = ex;
23 }
24 catch (Throwable err) {
25 //这里捕获了所有的Throwable
26 dispatchException = new NestedServletException("Handler dispatch failed", err);
27 }
28 //这个方法很关键,它抛不抛异常,取决于后面的catch能不能执行!!
29 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
30 }
31 catch (Exception ex) {
32 //调用interceptor的afterCompletion,有没熔断记录就看它有没执行了
33 triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
34 }
35 catch (Throwable err) {
36 //调用interceptor的afterCompletion,有没熔断记录就看它有没执行了
37 triggerAfterCompletion(processedRequest, response, mappedHandler,
38 new NestedServletException("Handler processing failed", err));
39 }
40 finally {
41 ...
42 }
43 }

我们首先来看看非常关键的processDispatchResult方法,这个方法只有两个地方抛出异常,如果这两个地方都没抛出异常,则doDispatch方法就不可能执行43或47行代码,意思是熔断信息将不会被记录,从而导致熔断失效

 1 private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
2 @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
3 @Nullable Exception exception) throws Exception {
4
5 boolean errorView = false;
6
7 if (exception != null) {
8 if (exception instanceof ModelAndViewDefiningException) {
9 logger.debug("ModelAndViewDefiningException encountered", exception);
10 mv = ((ModelAndViewDefiningException) exception).getModelAndView();
11 }
12 else {
13 Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
14 //这里是执行异常处理器,如果用了@ExceptionHandler全局异常处理,就会被处理
15 //这里是能够抛出异常的第一个地方,但我们全局异常处理的目的就是终止程序抛出的异常
16 mv = processHandlerException(request, response, handler, exception);
17 errorView = (mv != null);
18 }
19 }
20
21 // Did the handler return a view to render?
22 if (mv != null && !mv.wasCleared()) {
23 //这里是能够抛出异常的第二个地方,读取视图抛异常
24 //可是有大部分情况都是返回数据,并不需要查找视图,所以mv经常等于null
25 //并不会进这个if里面来
26 render(mv, request, response);
27 if (errorView) {
28 WebUtils.clearErrorRequestAttributes(request);
29 }
30 }
31 else {
32 if (logger.isTraceEnabled()) {
33 logger.trace("No view rendering, null ModelAndView returned.");
34 }
35 }
36
37 if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
38 // Concurrent handling started during a forward
39 return;
40 }
41
42 //这里注意第三个参数是null,即传入AbstractSentinelInterceptor类里的afterCompletion方法的ex参数为null
43 //这样是做不了熔断记录的,所以上面的方法必须要抛出异常
44 if (mappedHandler != null) {
45 mappedHandler.triggerAfterCompletion(request, response, null);
46 }
47 }

从上述流程可以看出,当我们使用@ExceptionHandler进行全局异常处理的时候,Sentinel并不能成功记录熔断信息,因为代码执行到它这异常已经被处理了,所以视为此次业务逻辑被正确执行

2.2热点参数的限流原理

 参考:
主要是要知道,相同参数是根据equal方法来比较是不是同一个对象,所以Object要重写此方法来符合自己的逻辑,不然的话热点参数设置无效

2.3Sentinel统一降级处理

参考:https://blog.csdn.net/theOldCaptain/article/details/107756801

需要注意

统一降级处理的资源不能用@SentinelResources修饰,否则是不走这个路的,需要自己配置@SentinelResources里的blockHandler和fallback这些方法
在Sentinel2.0之后,统一降级处理这样写(实践过可行)
 1 import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
2 import com.alibaba.csp.sentinel.slots.block.BlockException;
3 import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
4 import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
5 import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
6 import com.alibaba.csp.sentinel.slots.system.SystemBlockException;
7 import com.alibaba.fastjson.JSONObject;
8 import com.syni.common.result.ResultJson;
9 import lombok.extern.slf4j.Slf4j;
10 import org.springframework.stereotype.Component;
11
12 import javax.servlet.ServletOutputStream;
13 import javax.servlet.http.HttpServletRequest;
14 import javax.servlet.http.HttpServletResponse;
15 import java.nio.charset.StandardCharsets;
16
17 /**
18 * @Author DiaoJianBin
19 * @Description 别看idea标着@Component未被使用,实际上是被使用了的
20 * 去掉的话此类无法生效
21 * @Date 2021/4/22 10:44
22 */
23 @Component
24 @Slf4j
25 public class SystemBlockExceptionHandler implements BlockExceptionHandler {
26 @Override
27 public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException blockException) throws Exception {
28 StringBuilder errorMessage = new StringBuilder();
29 errorMessage.append("资源名称=");
30 String resourceName = blockException.getRule().getResource();
31
32 if (blockException instanceof FlowException) {
33 errorMessage.append(" 被限流了");
34 }else if(blockException instanceof DegradeException){
35 errorMessage.append(" 被熔断了");
36 }else if(blockException instanceof SystemBlockException){
37 SystemBlockException systemBlockException = (SystemBlockException)blockException;
38 errorMessage.append(" 触发系统保护 limitType=").append(systemBlockException.getLimitType());
39 }else if(blockException instanceof ParamFlowException){
40 //具体异常有特殊的方法
41 ParamFlowException paramFlowException = (ParamFlowException)blockException;
42 errorMessage.append(" 触发热点参数限流 limitParam=").append(paramFlowException.getLimitParam());
43 }
44 errorMessage.insert(5,resourceName);
45 //日志记录具体原因
46 log.error(errorMessage.toString());
47
48 //这里返回个前端,不能说明具体原因的
49 ResultJson resultJson = new ResultJson("102","系统繁忙,稍后再试");
50
51 httpServletResponse.setHeader("Content-type", "text/html;charset=UTF-8");
52 ServletOutputStream outputStream = httpServletResponse.getOutputStream();
53 try{
54 outputStream.write(JSONObject.toJSONString(resultJson).getBytes(StandardCharsets.UTF_8));
55 }finally {
56 outputStream.close();
57 }
58 }
59 }
sentinel降级方式的顺序,源码贴出,代码通俗易懂.
(1).查看是否有自定义了BlockExceptionHandler异常处理器.
(2).查看是否配置了降级页面.
(3).以上都没有,则创建默认的BlockExceptionHandler异常处理器,也就是响应Blocked by Sentinel (flow limiting)的那个类.
SentinelWebAutoConfiguration.java
 1 @Bean
2 @ConditionalOnProperty(name = "spring.cloud.sentinel.filter.enabled", matchIfMissing = true)
3 public SentinelWebMvcConfig sentinelWebMvcConfig() {
4 SentinelWebMvcConfig sentinelWebMvcConfig = new SentinelWebMvcConfig();
5 sentinelWebMvcConfig.setHttpMethodSpecify(properties.getHttpMethodSpecify());
6
7 if (blockExceptionHandlerOptional.isPresent()) {
8 blockExceptionHandlerOptional
9 .ifPresent(sentinelWebMvcConfig::setBlockExceptionHandler);
10 }
11 else {
12 if (StringUtils.hasText(properties.getBlockPage())) {
13 sentinelWebMvcConfig.setBlockExceptionHandler(((request, response,
14 e) -> response.sendRedirect(properties.getBlockPage())));
15 }
16 else {
17 sentinelWebMvcConfig
18 .setBlockExceptionHandler(new DefaultBlockExceptionHandler());
19 }
20 }
21
22 urlCleanerOptional.ifPresent(sentinelWebMvcConfig::setUrlCleaner);
23 requestOriginParserOptional.ifPresent(sentinelWebMvcConfig::setOriginParser);
24 return sentinelWebMvcConfig;
25 }
 
在Sentinel 2.0以前,据说是这样处理(未实践过)

2.4Sentinel与网关结合

参考:https://github.com/alibaba/Sentinel/wiki/%E7%BD%91%E5%85%B3%E9%99%90%E6%B5%81#spring-cloud-gateway

大体原理(以SpringCloudGateway为例)
由于SpringCloudGateway里有一系列的Filter组成
Sentinel会构造一个Filter放入其中,这样就能被Sentinel处理
同时,Sentinel将路由名称或者一组API定义成资源,用新的路由规则来控制这些资源(明面上是GatewayFlowRule实际就变成了ParamFlowRule),这样就回到了Sentinel本身的代码逻辑,从网关逻辑里分离出来

小结

本篇博客围绕着Sentinel的小知识点开始讲,把我在学习了解,使用碰到的知识点分享给大家,希望能帮到大家~

Sentinel导航的更多相关文章

  1. Nginx导航

    简介 最近都在弄微服务的东西,现在来记录下收获.我从一知半解到现在能从0搭建使用最大的感触有两点 1.微服务各大组件的版本很多,网上很多博客内容不一定适合你的版本,很多时候苦苦琢磨都是无用功 2.网上 ...

  2. Gateway导航

    简介 最近都在弄微服务的东西,现在来记录下收获.我从一知半解到现在能从0搭建使用最大的感触有两点 1.微服务各大组件的版本很多,网上很多博客内容不一定适合你的版本,很多时候苦苦琢磨都是无用功 2.网上 ...

  3. Ribbon导航

    简介 最近都在弄微服务的东西,现在来记录下收获.我从一知半解到现在能从0搭建使用最大的感触有两点 1.微服务各大组件的版本很多,网上很多博客内容不一定适合你的版本,很多时候苦苦琢磨都是无用功 2.网上 ...

  4. Redis设计与实现3.2:Sentinel

    Sentinel哨兵 这是<Redis设计与实现>系列的文章,系列导航:Redis设计与实现笔记 哨兵:监视.通知.自动故障恢复 启动与初始化 Sentinel 的本质只是一个运行在特殊模 ...

  5. “四核”驱动的“三维”导航 -- 淘宝新UI(需求分析篇)

    前言 孔子说:"软件是对客观世界的抽象". 首先声明,这里的"三维导航"和地图没一毛钱关系,"四核驱动"和硬件也没关系,而是为了复杂的应用而 ...

  6. ABP文档 - 导航

    文档目录 本节内容: 创建菜单 注册导航供应器 显示菜单 每个web应用都有一些菜单用来在页面/屏幕之间导航,ABP提供了一个通用的基础框架创建并显示菜单给用户. 创建菜单 一个应用可能由不同模块组成 ...

  7. 谈谈一些有趣的CSS题目(八)-- 纯CSS的导航栏Tab切换方案

    开本系列,谈谈一些有趣的 CSS 题目,题目类型天马行空,想到什么说什么,不仅为了拓宽一下解决问题的思路,更涉及一些容易忽视的 CSS 细节. 解题不考虑兼容性,题目天马行空,想到什么说什么,如果解题 ...

  8. GJM : C#设计模式汇总整理——导航 【原创】

    感谢您的阅读.喜欢的.有用的就请大哥大嫂们高抬贵手"推荐一下"吧!你的精神支持是博主强大的写作动力以及转载收藏动力.欢迎转载! 版权声明:本文原创发表于 [请点击连接前往] ,未经 ...

  9. SAP CRM 显示消息/在消息中进行导航

    向用户展示消息,在任何软件中都是十分重要的. 在SAP CRM WEB UI中展示消息,不是一项很难的任务,只需要创建消息并在之后调用方法来显示它 消息类和消息号: 我在SE91中创建了如下的消息类和 ...

随机推荐

  1. java例题_04 分解质因数

    1 /*4 [程序 4 分解质因数] 2 题目:将一个大于 2 正整数分解质因数.例如:输入 3, 3=3, 输入 6, 6=2*3,输入 90, 90=2*3*3*5. 3 程序分析:对 n 进行分 ...

  2. istio:在vs中实现ab测试和路径切割

    此篇内容 主要目的是总结vs中的match的有关规则和在istio中如何实现路径切割(当下版本1.8.2) 实验demo main.go package main import ( "git ...

  3. [GDKOI2021] 普及组 Day1 总结

    [ G D K O I 2021 ] 普 及 组 D a y 1 总 结 [GDKOI2021] 普及组 Day1 总结 [GDKOI2021]普及组Day1总结 长达3天的快乐GDKOI2021普及 ...

  4. Mediapipe 在RK3399PRO上的初探(一)(编译、运行CPU和GPU Demo, RK OpenglES 填坑,编译bazel)

    PS:要转载请注明出处,本人版权所有. PS: 这个只是基于<我自己>的理解, 如果和你的原则及想法相冲突,请谅解,勿喷. 前置说明   本文作为本人csdn blog的主站的备份.(Bl ...

  5. QT程序发布

    1.新建一个脚本文件,后缀为.bat 2.查看自己qt的windeployqt.exe路径,一般在QT安装的bin目录,而且脚本程序中需要去掉其后缀, 前面部分是windeployqt.exe的路径以 ...

  6. 5. Mybatis UPDATE更新,DELETE删除

    案例: 1. update <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper ...

  7. 嗨,你知道吗,Spring还有这些高级特性!

    目录 Spring介绍 设计理念 核心组件的协同工作 设计模式的应用 代理模式 策略模式 特性应用 事件驱动编程 异步执行 定时任务 日常开发使用非常多的Spring,它的设计理念是什么呢?有哪些核心 ...

  8. 自动化kolla-ansible部署openstack+GPU透传方法

    自动化kolla-ansible部署openstack+GPU透传方法 欢迎加QQ群:1026880196 进行交流学习 1. CentOS7.x-8.x系列为虚拟机配置GPU直通 1. 编辑文件vi ...

  9. 博客之初体验-----python初了解

    ---恢复内容开始--- 1.python2.x与python3.x的区别 (1) 2.x的默认编码是ASSIC码,不支持中文 (2) 3.x的默认编码是UNICODE,支持中文 (3) 2.x版本与 ...

  10. PAT甲级1100——1155题总结