1.redis主从创建(一主两从):
在从节点上执行:slaveof 192.168.56.201 6379 #master的ip地址和端口
也可以在配置文件中配置:
slaveof 192.168.56.201 6379
确认:
[root@db1 ~]# redis-cli
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=192.168.56.202,port=6379,state=online,offset=1344537,lag=0
slave1:ip=192.168.56.203,port=6379,state=online,offset=1344537,lag=0
master_replid:78461067504e80e7adf61bd14ea1a385c38ca20c
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1344688
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:10485760
repl_backlog_first_byte_offset:1
repl_backlog_histlen:1344688
2.创建目录:
mkdir -p /data/redis-26379/logs/
mkdir -p /data/redis-26379/run
3.编辑哨兵参数(scp都其它两个机器):
vim /data/redis-26379/26379.conf
port 26379
daemonize yes
dir /data/redis-26379/
pidfile /data/redis-26379/redis_26379.pid
logfile /data/redis-26379/logs/26379.log
#bind填写本机ip地址和127.0.0.1
bind 192.168.56.201 127.0.0.1
sentinel monitor master-monitor1 192.168.56.201 6379 2
sentinel down-after-milliseconds master-monitor1 10000
sentinel parallel-syncs master-monitor1 1
sentinel failover-timeout master-monitor1 180000
4.启动哨兵节点:
redis-sentinel /data/redis-26379/26379.conf
登录查看信息:
redis-cli -p 26379
127.0.0.1:26379> info sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=master-monitor1,status=ok,address=192.168.56.201:6379,slaves=2,sentinels=3
此时看到哨兵配置文件已经改变,自动发现slave:
[root@db1 redis-26379]# cat 26379.conf
port 26379
daemonize yes
dir "/data/redis-26379"
pidfile "/data/redis-26379/redis_26379.pid"
logfile "/data/redis-26379/logs/26379.log"
#bind 信息填写本机ip地址 和127.0.0.1
bind 192.168.56.201 127.0.0.1
sentinel myid 6ba1b2245916983baabaacf1be1eb0303a2a1b8b
sentinel deny-scripts-reconfig yes
sentinel monitor master-monitor1 192.168.56.202 6379 2
sentinel down-after-milliseconds master-monitor1 10000
# Generated by CONFIG REWRITE
sentinel config-epoch master-monitor1 2
sentinel leader-epoch master-monitor1 2
sentinel known-slave master-monitor1 192.168.56.201 6379
sentinel known-slave master-monitor1 192.168.56.203 6379
sentinel known-sentinel master-monitor1 192.168.56.203 26379 67202a457efa3d68e5ada2912375e40a0c9b5262
sentinel known-sentinel master-monitor1 192.168.56.202 26379 7a089d3c9e68851cc70c6c24c51df829a910195e
-
2019-01-22 17:08:54
转载于:https://blog.51cto.com/1937519/2345514
更多相关内容 -
sentinel
2021-03-08 21:58:50Sentinel Discord Bot(开源)旨在帮助有兴趣创建具有基本功能的自己的全球禁止bot的开发人员。 我们将不提供Sentinel的确切副本,而是提供机器人及其核心功能的骨架。 系统要求 Node.js v14或更高版本 如果还创建... -
Sentinel
2022-04-06 19:38:01文章目录Sentinel控制台Sentinel资源监控Sentinel限流保护自定义限流错误页Fallback失败回退BlockHandler热点规则授权规则BlockExceptionHandlerSentinel集群流控Sentinel流控规则持久化 Sentinel控制台 Sentinel是...文章目录
Sentinel控制台
Sentinel是一个限流保护组件,而这个组件和Hystrix相比还有一定的优势:有一个完整的控制台,而且整个的应用可以直接部。Hystrix控制台:在使用的过程里面仅仅是做为一个流量的监控,也不能进行各种神奇的配置,Hystrix还需要自己搭建一系列的服务,但是这些问题在Sentinel组件里面就全部解决。
如果要想使用Sentinel组件进行开发,那么一定需要进行服务的部署,本次依然要通过Linux系统进行服务部署。1、【GITHUB】 Sentinel组件可以通过GITHUB直接下载打包完成,或者是依据开发者自己的需要进行应用的编译(通过下载的源代码进行组件的编译,从而得到一个部署的应用)。
git clone https://github.com/alibaba/Sentinel
2、【本地系统】既然要进行服务的部署就需要创有一个虚拟机,本次的虚拟机配置名称如下192.168.190.160 sentinel-server
3、【sentinel-server主机】新的主机需要根据当前的应用环境进行服务的相关配置:
打开网卡配置文件:
vi/etc/sysconfig/network-scripts/ifcfg-ens33
修改网卡的IP地址:IPADDR=192.168.190.160
修改本机的名称:vi/etc/hostname
修改主机映射列表:vi /etc/hosts
重新启动虚拟机:reboot
4、【sentinel-server主机】进入到“/usr/local/src”目录: cd /usr/local/src;
5、【sentinel-server主机】下载Sentinel打包后的应用:
wget https://github.com/alibaba/Sentinel/releases/download/1.8.1/sentinel-dashboard-1.8.1.jar
6、【sentinel-server主机】 Sentinel使用的是SpringBoot 技术开发的,所以这个开发包可以直接使用,这个时候就需要查看一下GITHUB上所给出的文档信息了,文档路径:
https://github.com/alibaba/Sentinel/wiki/控制台
7、【sentinel-server主机】启动当前的Sentinel应用:
前台启动 java -Dserver.port=8888 -Dcsp.sentinel.dashboard.server=localhost:8888 -Dproject.name=sentinel-dashboard -Dsentinel.dashboard.auth.username=muyan -Dsentinel.dashboard.auth.password=yootk -jar /usr/local/src/sentinel-dashboard-1.8.1.jar 后台启动 java -Dserver.port=8888 -Dcsp.sentinel.dashboard.server=localhost:8888 -Dproject.name=sentinel-dashboard -Dsentinel.dashboard.auth.username=muyan -Dsentinel.dashboard.auth.password=yootk -jar /usr/local/src/sentinel-dashboard-1.8.1.jar > /usr/local/src/sentinel.log 2>&1 &
8、【sentinel-server主机】查看当前系统的端口占用:
netstat -nptl
8888是当前设置的Sentinel管理控制台的端口(WEB用户访问的),而现在所给出的8719端口就是未来微服务与Sentinel组件对应的操作端口。
9、【sentinel-server主机】修改防火墙规则,添加新的端口访问
添加访问规则 firewall-cmd --zone=public --add-port=8888/tcp --permanent firewall-cmd --zone=public --add-port=8719/tcp --permanent 重新加载配置 firewall-cmd --reload
Sentinel资源监控
如果现在要想进行服务的整合处理,肯定要进行微服务的配置修改,同时引入所需要的相关的依赖库组件。
1、【microcloud项目】修改build.gradle配置文件,进行Sentinel依赖的配置,本次先在“provider-dept-8001”的子模块之中进行依赖库的配置
project(":provider-dept-8001") { // 部门微服务 dependencies { implementation(project(":common-api")) // 导入公共的子模块 implementation(libraries.'mybatis-plus-boot-starter') implementation(libraries.'mysql-connector-java') implementation(libraries.'druid') implementation(libraries.'springfox-boot-starter') implementation('org.springframework.boot:spring-boot-starter-security') implementation('com.alibaba.cloud:spring-cloud-starter-alibaba-sentinel') // 以下的依赖库为Nacos注册中心所需要的依赖配置 implementation('com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-discovery') { exclude group: 'com.alibaba.nacos', module: 'nacos-client' // 移除旧版本的Nacos依赖 } implementation('com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-config') { exclude group: 'com.alibaba.nacos', module: 'nacos-client' // 移除旧版本的Nacos依赖 } implementation(libraries.'nacos-client') // 引入与当前的Nacos匹配的依赖库 } }
2、【provider-dept-8001】application.yml
spring: application: # 配置应用信息 name: dept.provider # 是微服务的名称 cloud: # Cloud配置 sentinel: # 监控配置 transport: # 传输配置 port: 8719 # Sentinel组件启用之后默认会启动一个8719端口 dashboard: sentinel-server:8888 # 控制台地址
Sentinel限流保护
现在为止所能够见到的Sentinel使用全部都在流量统计上了(因为没有统计的信息就一定没有保护的配置),所以在使用Sentinel过程里面还可以实现限流保护(限流本质上就是针对于一个操作进行一些访问级别的设置,例如:这一个操作每秒只允许10个用户访问,这种操作机制就是限流处理),这种操作机制全部都是在Sentinel控制台里面完成的。
1、【Postman工具】为了便于观察限流的操作,建议先将一些方法多执行几次。
只有执行了某些访问的路径之后,才可以见到所谓的簇点链路,而有了这些簇点链路信息,才可以进行流控、熔断、热点、授权等限流控制。﹒1、流控规则
流量控制(Flow Control)主要是监控保护资源流量的QPS(Query per Second、每秒查询率)或者并发线程数量,当指定的监控指标达到阈值时对流量进行控制,以避免被瞬时流量高峰冲垮,从而保障服务负源的高可用性。开发者可以通过流控的选项为指定的资源添加流控规则。
在开启流量控制时需要明确的设置流控的阈值,只要超过了该阈值就会触发具体的流控处理操作,而在Sentinel之中对于流控提供有三种处理模式(需要开启“高级选项”才可以配置),具体的使用特点如下:
·快速失败: 默认流控模式,当访问量超过了规定阈值后新的请求会被立即拒绝;
·Warm Up: 采用“预热/冷启动”方式,当访问量瞬间激增时让通过的流量缓慢增加,给冷系统一个预热缓冲;
·匀速排队: 当出现间隔性流量激增时,会根据请求通过的间隔时间让所有的请求匀速通过(漏桶算法)。
当流控规则增加完成之后,就可以直接在流控配置列表里面见到如下的信息了。
如果此时没有违反流控规则,这个时候可以正常获取服务端的数据响应,但是一旦你违反了这个规则,那么在执行的时候就会触发熔断操作,得到“Blocked by Sentinel (flow limiting)”信息。2、降级规则(熔断规则):
熔断降级主要是对微服务调用链中某个资源出现不稳定状态时(例如:响应时间过长、产生异常),为避免其他资源调用而导致的级联错误,会在某个特定的时长内实现资源熔断处理
如果当前进行微服务调用的过程里面,响应的时间超过了1毫秒,那么最终就会触发熔断处理,返回“Blocked by Sentinel(flow limiting)”提示信息。
3、系统规则:
系统规则是对应用的入口流量进行控制,不是针对于某一个资源的保护,而是实现了一个应用整体纬度的保护规则,在系统规则中支持有如下的几种模式:
. Load 自适应模式: 交由操作系统(仅对Linux或类UNIX系统生效)进行保护控制—般的参考值为“系统硬件的CPU
内核数量*2.5”;
·平均响应时间: 当单台主机上的所有入口流量的平均RT达到阈值时触发系统保护;
·并发线程数: 当单台主机上的所有入口流量达到并发线程阈值时触发系统保护;
·入口QPS: 当单台主机上的所有入口流量达到阈值时触发系统保护;
·CPU使用率: 当系统CPU使用率超过阈值时触发系统保护。系统规则属于一个全局规则,所有的接入到Sentinel 中的微服务都可以使用系统规则进行流量的保护,但是由于其所涵盖的范围太大了,是否使用要根据具体的开发要求来决定。
自定义限流错误页
Sentinel已经发现了其可以实现流量监控,以及限流保护,但是在默认进行熔断处理的时候会出现一个问题:所产生的错误信息不是我们系统中想的,现在能不能根据自己的需要创建一个属于自己的错误页。
1、 【provider-dept-8001子模块】具体的限流的错误页肯定是由各自的微服务来定义的,创建一个BlockAction。
package com.yootk.provider.action; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletResponse; import java.util.HashMap; import java.util.Map; @RestController @RequestMapping("/errors/*") // 父路径 public class BlockAction { @RequestMapping("block_handler") public Object globalBlockHandler() { Map<String, Object> result = new HashMap<>(); // 保存错误信息 result.put("status", HttpServletResponse.SC_BAD_REQUEST); // 设置状态码 result.put("message", "Blocked by Sentinel (flow limiting)"); return result; } }
2、【provider-dept-8001子模块】修改 application.yml配置文件,追加错误的数据显示路径
spring: application: # 配置应用信息 name: dept.provider # 是微服务的名称 cloud: # Cloud配置 sentinel: # 监控配置 transport: # 传输配置 port: 8719 # Sentinel组件启用之后默认会启动一个8719端口 dashboard: sentinel-server:8888 # 控制台地址 block-page: /errors/block_handler # 阻断页
重新启动部门微服务的应用,随后再次进行限流访问,观察一下最终可以得到的响应的结果信息
如果此时微服务重新启动,那么基于该微服务所配置的全部的流控规则将全部消失,需要开发者自己重新定义。
Fallback失败回退
在之前进行的错误提示信息是针对于整个的Sentinel组件完成的,“block-page:/errors/block_ handler”,这个配置指的是只要出现了错误,就执行指定的路径,现在希望可以定义完全属于自己业务逻辑的Fallback处理,这种机制是完全可以实现的。
1、【provider-dept-8001子模块】如果要想手工进行Fallback配置,则需要创建一个Setntinel切面管理类,而且这个切面在进行处理的时候需要考虑到可能是针对于Action类完成的(它不是接口,不能使用JDK动态代理机制完成,应该基于CGLIB处理)。
package com.yootk.provider.config; import com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; @Configuration @EnableAspectJAutoProxy(exposeProxy = true, proxyTargetClass = true) // CGLIB代理 public class SentinelAOPConfig { // Sentinel配置 // 所有的Fallback的处理操作全部都是基于切面的形式负责完成的 @Bean public SentinelResourceAspect getSentinelResourceAspect() { return new SentinelResourceAspect(); } }
2、【provider-dept-8001子模块】既然已经定义好了配置的切面,那么随后就需要进行Action的修改
package com.yootk.provider.action; import com.alibaba.csp.sentinel.annotation.SentinelResource; import com.yootk.common.dto.DeptDTO; import com.yootk.service.IDeptService; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; @RestController @RequestMapping("/provider/dept/*") // 微服务提供者父路径 @Slf4j // 使用一个注解 public class DeptAction { @Autowired private IDeptService deptService; @SentinelResource(value = "/dept_get",fallback = "getFallback") @ApiOperation(value="部门查询", notes = "根据部门编号查询部门详细信息") @GetMapping("get/{id}") public Object get(@PathVariable("id") long id) { this.printRequestHeaders("get"); return this.deptService.get(id); } public Object getFallback(@PathVariable("id") long id) { DeptDTO dto = new DeptDTO(); dto.setDeptno(id); dto.setDname("【Fallback】部门名称"); dto.setLoc("【Fallback】部门位置"); return dto; } @ApiOperation(value="部门增加", notes = "增加新的部门信息") @ApiImplicitParams({ @ApiImplicitParam(name = "deptDTO", required = true, dataType = "DeptDTO", value = "部门传输对象实例") }) @PostMapping("add") @SentinelResource(value = "/dept_add", fallback = "addFallback") public Object add(@RequestBody DeptDTO deptDTO) { // 后面会修改参数模式为JSON this.printRequestHeaders("add"); return this.deptService.add(deptDTO); } public Object addFallback(@RequestBody DeptDTO deptDTO) { return false; } @ApiOperation(value="部门列表", notes = "查询部门的完整信息") @GetMapping("list") @SentinelResource(value = "/dept_list", fallback = "listFallback") public Object list() { this.printRequestHeaders("list"); return this.deptService.list(); } public Object listFallback() { return new ArrayList<>(); } @ApiOperation(value="部门分页查询", notes = "根据指定的数据库参数实现部门数据的分页加载") @ApiImplicitParams({ @ApiImplicitParam(name="cp", value = "当前所在页", required = true, dataType = "int"), @ApiImplicitParam(name="ls", value = "每页显示的数据行数", required = true, dataType = "int"), @ApiImplicitParam(name="col", value = "模糊查询列", required = true, dataType = "String"), @ApiImplicitParam(name="kw", value = "模糊查询关键字", required = true, dataType = "String") }) @GetMapping("split") @SentinelResource(value = "/dept_split", fallback = "splitFallback") public Object split(int cp, int ls, String col, String kw) { this.printRequestHeaders("split"); return this.deptService.split(cp, ls, col, kw); } public Object splitFallback() { return new HashMap<>(); } private void printRequestHeaders(String restName) { // 实现所有请求头信息的输出 HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); Enumeration<String> headerEnums = request.getHeaderNames(); while (headerEnums.hasMoreElements()) { String headerName = headerEnums.nextElement(); log.info("【{}】头信息:{} = {}", restName, headerName, request.getHeader(headerName)); } } }
3、【Sentinel控制台】随后又需要重新启动微服务,重新在Sentinel控制台里面进行微服务的访问规则配置。
Block Page是针对于整个的熔断配罩的信息,而Fallback是针对于每一个具体的服务的处理而提供的,针对于各自微服务接口的Fallback一定拥有最高的优先调度。BlockHandler
在之前使用Fallback配置的过程里面,每一个业务的处理方法内部都需要提供有一个Fallback方法的操作定义,但是这样会造成核心功能的语句和Fallback功能语句混淆的问题,那么就可以考虑将所有的Fallback处理方法定义在一个专属的类中。
通过当前的源代码分析可以发现BlockException是所有的可能出现的限流异常的最大的父类,而后每一个异常也同时会带有一个与之匹配的限流规则(Rule接口)。1、【provider-dept-8001子模块】创建一个与DeptAction有关的拦截处理类
·注意事项一: 所有的方法必须加上static关键字,进行方法唯一性的标记;
·注意事项二: 所有编写的Block处理方法定义时要与Action中的方法保持一致,最后可以追加一个BlockException异常。package com.yootk.provider.action.block; import com.alibaba.csp.sentinel.slots.block.BlockException; import com.yootk.common.dto.DeptDTO; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; public class DeptBlockHandler { // 限流信息 public static Object addBlockHandler(DeptDTO deptDTO, BlockException e) { Map<String, Object> map = new HashMap<>(); map.put("rule", e.getRule()); // 获取失败的信息 map.put("message", e.getMessage()); // 异常信息 map.put("result", false); // 本次的执行结果 return map; } public static Object getBlockHandler(long id, BlockException e) { Map<String, Object> map = new HashMap<>(); map.put("rule", e.getRule()); // 获取失败的信息 map.put("message", e.getMessage()); // 异常信息 DeptDTO dept = new DeptDTO(); dept.setDeptno(id); dept.setDname("【Block】部门名称"); dept.setLoc("【Block】部门位置"); map.put("result", dept); // 本次的执行结果 return map; } public static Object listBlockHandler(BlockException e) { Map<String, Object> map = new HashMap<>(); map.put("rule", e.getRule()); // 获取失败的信息 map.put("message", e.getMessage()); // 异常信息 map.put("result", new ArrayList<>()); // 本次的执行结果 return map; } public static Object splitBlockHandler(int cp, int ls, String col, String kw, BlockException e) { Map<String, Object> map = new HashMap<>(); map.put("rule", e.getRule()); // 获取失败的信息 map.put("message", e.getMessage()); // 异常信息 map.put("result", new HashMap<>()); // 本次的执行结果 return map; } }
2、【provider-dept-8001子模块】修改DeptAction程序类,进行block的相关配置
package com.yootk.provider.action; import com.alibaba.csp.sentinel.annotation.SentinelResource; import com.yootk.common.dto.DeptDTO; import com.yootk.provider.action.block.DeptBlockHandler; import com.yootk.service.IDeptService; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; @RestController @RequestMapping("/provider/dept/*") // 微服务提供者父路径 @Slf4j // 使用一个注解 public class DeptAction { @Autowired private IDeptService deptService; @SentinelResource(value = "/dept_get", blockHandlerClass = DeptBlockHandler.class, blockHandler = "getBlockHandler") @ApiOperation(value = "部门查询", notes = "根据部门编号查询部门详细信息") @GetMapping("get/{id}") public Object get(@PathVariable("id") long id) { this.printRequestHeaders("get"); return this.deptService.get(id); } @ApiOperation(value = "部门增加", notes = "增加新的部门信息") @ApiImplicitParams({ @ApiImplicitParam(name = "deptDTO", required = true, dataType = "DeptDTO", value = "部门传输对象实例") }) @PostMapping("add") @SentinelResource(value = "/dept_add", blockHandlerClass = DeptBlockHandler.class, blockHandler = "addBlockHandler") public Object add(@RequestBody DeptDTO deptDTO) { // 后面会修改参数模式为JSON this.printRequestHeaders("add"); return this.deptService.add(deptDTO); } @ApiOperation(value = "部门列表", notes = "查询部门的完整信息") @GetMapping("list") @SentinelResource(value = "/dept_add", blockHandlerClass = DeptBlockHandler.class, blockHandler = "listBlockHandler") public Object list() { this.printRequestHeaders("list"); return this.deptService.list(); } @ApiOperation(value = "部门分页查询", notes = "根据指定的数据库参数实现部门数据的分页加载") @ApiImplicitParams({ @ApiImplicitParam(name = "cp", value = "当前所在页", required = true, dataType = "int"), @ApiImplicitParam(name = "ls", value = "每页显示的数据行数", required = true, dataType = "int"), @ApiImplicitParam(name = "col", value = "模糊查询列", required = true, dataType = "String"), @ApiImplicitParam(name = "kw", value = "模糊查询关键字", required = true, dataType = "String") }) @GetMapping("split") @SentinelResource(value = "/dept_add", blockHandlerClass = DeptBlockHandler.class, blockHandler = "splitBlockHandler") public Object split(int cp, int ls, String col, String kw) { this.printRequestHeaders("split"); return this.deptService.split(cp, ls, col, kw); } private void printRequestHeaders(String restName) { // 实现所有请求头信息的输出 HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); Enumeration<String> headerEnums = request.getHeaderNames(); while (headerEnums.hasMoreElements()) { String headerName = headerEnums.nextElement(); log.info("【{}】头信息:{} = {}", restName, headerName, request.getHeader(headerName)); } } }
3、【Sentinel控制台】为相应的服务接口添加流控规则
4、【provider-dept-8001子模块】为DeptAction.get()方法添加Fallback处理package com.yootk.provider.action; @RestController @RequestMapping("/provider/dept/*") // 微服务提供者父路径 @Slf4j // 使用一个注解 public class DeptAction { @Autowired private IDeptService deptService; @SentinelResource(value = "/dept_get", fallback = "getFallback", blockHandlerClass = DeptBlockHandler.class, blockHandler = "getBlockHandler") @ApiOperation(value = "部门查询", notes = "根据部门编号查询部门详细信息") @GetMapping("get/{id}") public Object get(@PathVariable("id") long id) { this.printRequestHeaders("get"); return this.deptService.get(id); } public Object getFallback(long id) { DeptDTO dept = new DeptDTO(); dept.setDeptno(id); dept.setDname("【Fallback】部门名称"); dept.setLoc("【Fallback】部门位置"); return dept; } }
BlockException拥有限流异常处理的最高的级别,级别要高于Fallback,不管是Fallback还是BlockHandler都需要自己编写一大堆的配置项。
热点规则
在进行RESTful设计的时候一般都需要在Action的处理方法中进行请求参数的接收,于是这个时候可以针对于参数进行限流,这种规则就称为热点规则。
热点参数限流会统计参数中的热点参数,并根据配置的限流阀值与模式,对包含热点参数的资源调用进行限流。热点参数限制可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。如果要想使用热点规则,则需要在控制层的相关方法中使用“@SentinelResource”注解进行声明。1、【provider-dept-8001子模块】热点参数的限流操作直接在热点上进行一个注解的定义即可
package com.yootk.provider.action; @RestController @RequestMapping("/provider/dept/*") // 微服务提供者父路径 @Slf4j // 使用一个注解 public class DeptAction { @Autowired private IDeptService deptService; @SentinelResource(value = "/dept_get", fallback = "getFallback", blockHandlerClass = DeptBlockHandler.class, blockHandler = "getBlockHandler") @ApiOperation(value = "部门查询", notes = "根据部门编号查询部门详细信息") @GetMapping("get/{id}") public Object get(@PathVariable("id") long id) { this.printRequestHeaders("get"); return this.deptService.get(id); } }
2、【Sentinel控制台】热点限流的配置是依据参数来定义的,第一个参数的索引是0,而后继续叠加。
此时配置的操作形式与之前的流控很相似,但是唯一的不同在于,此时需要设置有一个参数的索引。3、【Sentinel控制台】热点规则与之前的流控规则的区别主要在于可以添加参数的例外。
相比较之前的流控处理,此时可以针对于参数的某些选项来进行例外的配置,而这种配置可以使得流控更加方便,在Spring框架里面提供有一个SpringCache缓存工具,这个缓存可以将一些数据保存在Redis里面,避免通过数据库查询。授权规则
所谓的授权指的就是在请求的时候需要一些特定的数据来进行请求操作,如果没有特定的数据则认为不符合授权检查的规则,所以不能够进行资源的访问。
1、【Sentinel控制台】为指定的请求资源添加授权规则,而这个授权规则添加的是一个白名单,只要拥有app 或者pc两个授权信息其中之一的客户即可进行访问。
2、【provider-dept-8001子模块】如果要想让现在的授权规则生效,则必须进行程序类的修改,进行规则参数的解析,现在假设每一次用户请求的时候都要附带有一个“serviceName”的参数,这个参数负责保存有授权信息。package com.yootk.provider.config; import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import javax.servlet.http.HttpServletRequest; @Component public class SentinelRequestOriginParser implements RequestOriginParser { // 请求解析 @Override public String parseOrigin(HttpServletRequest request) { String serviceName = request.getParameter("serviceName"); // 接收请求参数 if (serviceName == null || "".equals(serviceName)) { // 参数的内容是空 serviceName = request.getHeader("serviceName"); // 通过头信息传递参数 } if (StringUtils.isEmpty(serviceName)) { return request.getRemoteAddr(); // 根据IP地址处理 } return serviceName; } }
3、【Postman工具】对当前的操作进行测试,首先测试没有传递serviceName参数的操作:
4、【Postman工具】为项目添加一个头信息:
此时传递的pc服务名称正好符合当前的授权规则,所以可以直接进行服务的调用。5、【consumer-springboot-80子模块】现在使用的是Postman进行接口测试,而在实际的开发之中肯定会由消费者进行接口调用,这个时候就需要考虑通过拦截器的方式进行请求数据的传递。
package com.yootk.service.config; import feign.Logger; import feign.RequestInterceptor; import org.springframework.context.annotation.Bean; public class FeignConfig { // 定义Feign配置类 @Bean public Logger.Level level() { return Logger.Level.FULL; // 输出完全的日志信息 } @Bean public RequestInterceptor getFeignRequestInterceptor() { // 请求拦截器 return (template -> template.header("serviceName", "pc")); } }
在每次请求的时候都自动追加一个头信息,这样在最终调用的时候就可以通过头信息来进行授权数据内容的传递处理了。
BlockExceptionHandler
在进行微服务调用的过程之中,如果某一个微服务出现了错误,那么最终应该根据错误产生的个数来进行熔断的处理,整个的Sentinel组件都拥有熔断的功能。
每当触发了Sentinel防护规则时,实际上都会在系统内部产生BlockException异常实例,而后会基于默认的方式进行异常数据的响应,考虑到定制化异常信息显示的需要,Sentinel内部提供了BlockExceptionHandler接口,开发者可以直接依据此接口进行自定义异常数据的响应。1、【provider-dept-8001子模块】创建拦截异常处理类
package com.yootk.provider.action.block; import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler; import com.alibaba.csp.sentinel.slots.block.BlockException; import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException; import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException; import com.alibaba.csp.sentinel.slots.block.flow.FlowException; import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException; import com.alibaba.csp.sentinel.slots.system.SystemBlockException; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.stereotype.Component; import org.springframework.util.MimeTypeUtils; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.HashMap; import java.util.Map; @Component public class SentinelBlockExceptionHandler implements BlockExceptionHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception { Map<String, Object> errors = new HashMap<>(); // 数据的保存 errors.put("type", e.getClass().getName()); // 异常的类型 // 之所以这样编写是为了帮助大家加深关于Sentinel组件内部提供的异常类型 if (e instanceof FlowException) { errors.put("message", "服务限流"); } else if (e instanceof DegradeException) { errors.put("message", "服务降级"); } else if (e instanceof ParamFlowException) { errors.put("message", "热点参数限流"); } else if (e instanceof SystemBlockException) { errors.put("message", "系统拦截"); } else if (e instanceof AuthorityException) { errors.put("message", "授权拦截"); } else { errors.put("message", "其他异常"); } errors.put("path", request.getRequestURI()); // 产生异常的路径 response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); // 状态码 response.setCharacterEncoding("UTF-8"); // 设置响应编码 response.setHeader("Content-Type", MimeTypeUtils.APPLICATION_JSON_VALUE); // JSON直接响应 new ObjectMapper().writeValue(response.getWriter(), errors); // Jackson组件的输出 } }
2、【provider-dept-8001子模块】修改根据ID查询操作类,产生点异常
// @SentinelResource(value = "/dept_get", fallback = "getFallback", blockHandlerClass = DeptBlockHandler.class, blockHandler = "getBlockHandler") @ApiOperation(value = "部门查询", notes = "根据部门编号查询部门详细信息") @GetMapping("get/{id}") public Object get(@PathVariable("id") long id) { if (id % 2 == 0) { // 查询ID为偶数 throw new RuntimeException("查询iD不能为偶数!"); } this.printRequestHeaders("get"); return this.deptService.get(id); }
3、【Postman工具]为了便于观察,直接让当前的部门微服务产生错误,随后可以发现在Postman内部会出现一个SpringCloud开发框架自己所使用的异常信息的打印;
4、【Sentinel控制台】既然此时有了异常,就应该增加异常的相关熔断处理操作。
如果此时的接口产生了1个异常之后,那么在2秒内不允许进行调用,此时如果已经启用拦截,随后继续进行访问,那么就会出现如下的提示信息,而这个提示信息就是之前自己开发的提示信息。{ "path": "/provider/dept/ get/2", "type" : "com.alibaba.csp.sentinel.slots.block.degrade.DegradeException" , "message" :"服务降级" }
Sentinel集群流控
你的项目之中一旦使用到了微服务,那么永远不可避免的就是集群环境,例如:为了提高部门微服务的处理性能,可以设置多个微服务的实例节点,共同进行业务的处理完成,在之前进行流控规则添加的时候会发现提供有一个是否集群的配置选项。
例如:现在有一个“dept.provider”微服务集群,并为其设置限流规则(QPS设置为60),这样就意味着整个集群每秒可以处理的请求总量就是60(不管集群中有多少个微服务节点)。但是如果要想进行这种多实例节点的集群的限流控制,那么就一定要有一个共同的计数服务器。
如果要想在Sentinel 中实现集群限流,本质上都需要提供有一个相关访问数据的统计,在单一实例的情况下,这个统计操作是在每个实例中实现的。而如果在集群环境下,就需要提供有一个专门的实例(TokenServer)进行数据统计,并且该TokenServer要收集所有TokenClient 发送来的统计信息,而后根据集群流控规则来决定是否允许该请求进行资源访问。
Sentinel 从1.4.0版本开始引入了集群流控实现模块,基于Netty 实现了服务通信,当用户在SpringCloud项目模块中引入了“spring-cloud-starter-alibaba-sentinel”依赖库后就会自动引入这些相关模块,通过图可以发现,与集群流控有关的模块一共有三个,这三个模块的具体作用如下:
1、sentinel-cluster-common-default: 集群流控公共模块,包含了公共的接口以及实体类;.
2、sentinel-cluster-server-default: TokenServer实现模块,基于Sentinel核心逻辑进行规则扩展实现;
3、sentinel-cluster-client-default: TokenClient实现模块。
按照分布式项目的设计原则,此时应该提供有一个独立的TokenServer进行统计的计数,随后在Sentinel组件的内部要指定此计数服务器地址。
1、【microcloud项目】创建一个“sentinel-token-server”新的子模块;
2、【microcloud项目】修改build.gradle配置文件,为当前的项目配置所需要的依赖库;project(":sentinel-token-server") { dependencies { // 配置模块所需要的依赖库 implementation("org.springframework.boot:spring-boot-starter-web") // SpringBoot依赖 implementation('com.alibaba.cloud:spring-cloud-starter-alibaba-sentinel') { exclude group: 'com.alibaba.csp', module: 'sentinel-cluster-client-default' } } }
3、【sentinel-token-server子模块】直接创建一个Token应用启动类:
package com.yootk.sentinel; import com.alibaba.csp.sentinel.cluster.server.ClusterTokenServer; import com.alibaba.csp.sentinel.cluster.server.SentinelDefaultTokenServer; import com.alibaba.csp.sentinel.cluster.server.config.ClusterServerConfigManager; import com.alibaba.csp.sentinel.cluster.server.config.ServerTransportConfig; public class StartTokenServerApplication { // -Dcsp.sentinel.dashboard.server=sentinel-server:8888 -Dcsp.sentinel.api.port=8719 // -Dproject.name=sentinel-token-server -Dcsp.sentinel.log.use.pid=true static { // 使用系统属性代替启动参数 System.setProperty("csp.sentinel.dashboard.server", "sentinel-server:8888"); // 控制台地址 System.setProperty("csp.sentinel.api.port", "8719"); // sentinel端口 System.setProperty("project.name", "token-server"); // 服务名称 System.setProperty("csp.sentinel.log.use.pid", "true"); // 设置pid(可选) } public static void main(String[] args) throws Exception { ClusterTokenServer tokenServer = new SentinelDefaultTokenServer(); // 实例化Token集群管理 ClusterServerConfigManager.loadGlobalTransportConfig(new ServerTransportConfig().setIdleSeconds(600).setPort(10217)); tokenServer.start();// 启动服务器 } }
4、【Sentinel控制台】由于此时是静态设置了Sentinel连接地址,所以当应用启动的时候就可以直接向Sentinel控制台注册
5、【本地系统】为了便于TokenServer访问,修改 hosts主机名称进行地址配置:127.0.0.1 sentinel-token-server
6、【Sentinel控制台】此时已经提供了完整的 TokenServer,那么随后就需要在Sentinel控制台里面添加一个流控规则
7、【Sentinel控制台】进行流控规则的指定
此时所配置的服务限流可以考虑到集群之中所有的主机节点,而这些的统计的信息都被TokenServe所记录,一旦触发了流控规则之后,就会产生如下的错误信息。
Sentinel流控规则持久化
Sentinel为了便于资源限流规则的持久化管理,专门提供了ReadableDataSource(配置读取)与WritableDataSource(配置写入接口)两个操作接口,利用这两个接口可以向指定的存储设备中实现规则的读写处理。
import com.alibaba.csp.sentinel.datasource.ReadableDataSource; import com.alibaba.csp.sentinel.datasource.writableDataSource;
如果要想实现配置规则的读写操作,那么必然要有一个存储的终端,而且这个终端需要保证性能高,同时又可以很稳定,这样一来几乎就不会去考虑到SQL数据库了(配置的限流规则一般很少去改变,更多的是进行配置规则的读取)。
如果说现在你要开发一个系统,这个系统可以由管理人员在服务应用部署上线之前,就可以进行所有限流规则的维护,这个时候一定需要有一个完善的管理界面(排除那些没有完善管理界面: Redis ZooKeeper),所以最佳的做法就是使用Nacos,因为Nacos里面支持有一个完善的处理界面,而且其本身是提供有JSON数据(各种的数据类型都是支持的)的直接存储,最重要的是Nacos还是SpringCloudAlibaba 套件之中最为重要的注册中心。
Sentinel 提供的DataSource是一个逻辑上的概念,具体的存储可以是关系型数据库、文件、ZooKeeper、Redis、Nacos等存储终端,在终端中可以保存所需要的限流规则,而对于DataSource的操作形式也提供有两种:.
1、拉模式(Pull-Based):客户端主动向某个 DataSource存储中心定期轮询开读取规则,这个配置中心可能是一个文件,或者是关系型数据库,虽然此种方式简单,但是却无法及时获取到配置更新;
2、推模式(Push-Based):所有的限流规则由配置中心(Nacos、Zookeeper、Redis等)统一推送,客户端通过注册监听器的方式监听规则的变化,这样可以更好的保持配置的实时性和一致性。注意:
在早期讲解 Nacos的时候曾经说过,Nacos 不能够作为外网的服务使用
如果此时你的系统之中的Nacos使用了认证模式(配置用户名和密码),就会出现 BUG,因为如果要使用Nacos进行存储,Sentinel无法进行用户名和密码的配置。
为了解决Sentinel组件无法正常的与具有认证功能的Nacos整合的设计问题,最佳的做法是直接部署一个新的Nacos应用服务
( 也可以取消Nacos中的认证模式 )(以下为 直接部署一个新的Nacos应用服务)
1、【本地系统】为了便于Nacos的服务配置,本地修改hosts配置文件,追加一个新的主机映射:
192.168.190.169 sentinel-nacos-server
2、 【sentinel-nacos-server主机】将Nacos服务组件解压缩到“/usr/local”目录之中
tar xzvf /var/ftp/nacos-server-2.0.2.tar.gz -C /usr/local/
3、【sentinel-nacos-server主机】启动Nacos应用服务进程
/usr/local/nacos/bin/startup.sh -m standalone
4、【sentinel-nacos-server主机】修改防火墙的使用规则,开放相关的访问端口:开放访问端口:
firewall-cmd --zone=public --add-port=8848/tcp --permanent firewall-cmd --zone=public --add-port=9848/tcp --permanent firewall-cmd --zone=public --add-port=7848/tcp --permanent firewall-cmd --zone=public --add-port=9849/tcp --permanent
重新加载配置:
firewall-cmd --reload
5、【Nacos控制台】通过外部浏览器访问当前的Nacos 控制台,路径:
sentinel-nacos-server:8848/nacos
在Sentinel之中流控规则是最为常用的一项规则,也是系统保护之中最重要的一项技术了,如果要想进行流控配置的时候,是需要根据不同的应用名称来实现定义的。
1、【Sentiel-Nacos控制台】要想进行规则的存储,那么一定要存在有一个领域模型,首先创建一个新的命名空间。
当前我们所使用的UUID:51586a27-b10d-4165-9cd2-38f1464c780d2、【Sentiel-Nacos控制台】在Sentinel命名空间下创建一个新的配置项,配置项的名称为"dept.provider-flow-rules",其中“dept.provider”是注册微服务的名称,这个时候将配置项保存在“SENTINEL_GROUP”组之中;
[ { "resource": "/provider/dept/list", "limitApp": "default", "grade": 1, "count": 1, "strategy": 0, "controlBehavior": 0, "clusterMode": false } ]
3、【microcloud项目】既然此时已经通过了Nacos保存了Sentinel相关限流控制规则,那么就需要在部门微服务之中进行数据源数据的读取,所以修改配置文件,添加核心的依赖库。dependencies.gradle
ext.versions = [ sentinel : '1.8.2', ] ext.libraries = [ 'sentinel-datasource-nacos' : "com.alibaba.csp:sentinel-datasource-nacos:${versions.sentinel}" ]
build.gradle
project(":provider-dept-8001") { dependencies { implementation(project(":common-api")) implementation(libraries.'mybatis-plus-boot-starter') implementation(libraries.'mysql-connector-java') implementation(libraries.'druid') implementation(libraries.'springfox-boot-starter') implementation(libraries.'sentinel-datasource-nacos') //刚加入的依赖 implementation('org.springframework.boot:spring-boot-starter-security') implementation('com.alibaba.cloud:spring-cloud-starter-alibaba-sentinel') implementation('com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-discovery') { exclude group: 'com.alibaba.nacos', module: 'nacos-client' } implementation('com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-config') { exclude group: 'com.alibaba.nacos', module: 'nacos-client' } implementation(libraries.'nacos-client') } }
4.【provider-dept-8001子模块】此时已经成功的引入了Nacos 数据源的存储配置了,那么随后就需要修改项目之中的application.yml配置文件,引入相应的Nacos 的配置(这个Nacos 的配置里面可不能够使用用户认证信息)。
spring: application: # 配置应用信息 name: dept.provider # 是微服务的名称 cloud: # Cloud配置 sentinel: # 监控配置 transport: # 传输配置 port: 8719 # Sentinel组件启用之后默认会启动一个8719端口 dashboard: sentinel-server:8888 # 控制台地址 block-page: /errors/block_handler # 阻断页 eager: true # 饥饿加载 datasource: # 数据源配置 flow-datasource: # 流控的数据源 nacos: # 当前的存储介质为Nacos server-addr: sentinel-nacos-server:8848 # SentinelNacos地址 namespace: 51586a27-b10d-4165-9cd2-38f1464c780d # 命名空间ID group-id: SENTINEL_GROUP # 一般建议大写 data-id: ${spring.application.name}-flow-rules # 配置项的名称 data-type: json # 配置的文件结构 rule_type: flow # 流控规则
5、【provder-dept-8001子模块】修改DeptAction的路径,配置与规则相同的资源名称
去掉@SentinelResource注解中的value属性
6、【provider-dept-8001】一切配置完成之后,重新启动当前的部门微服务;加载日志信息
7、【Sentinel控制台】启动的时候应该已经进行了规则加载,那么随后打开Sentinel控制台,观察一下是否有规则
在以后的维护过程之中,只需要由管理员负责修改Nacos 中的配置项即可轻松的实现流控规则的定义,这种定义是非常方便使用的。8、【Sentinel-Nacos控制台】所有在 Nacos 中所定义的配置项都可以及时修改的,修改完成之后微服务一定可以及时获取到对应的服务配置(SpringCloudConfig 的特点决定的)。
当Nacos 中的配置修改完成之后,由于配置项允许动态的刷新(微服务会启动一个Nacos配置项的监听),会自动加载新的配置,随后可以得到如下的日志信息:[2a540420-51d6-4ea3-b6f7-0e1bcfb78190_config-0]receive server push request,request=ConfigChangeNotifyRequest,requestId=1 [2a540420-51d6-4ea3-b6f7-0e1bcfb78190_config-0] [server-push] config changed. dataId=dept.provider-flow-rules, group=SENTINEL_GROUP,tenant=51586a27-b10d-4165-9cd2-38f1464c780d [2a540420-51d6-4ea3-b6f7-0e1bcfb78190_config-0]ack server push request,request=ConfigChangeNotifyRequest,requestId=1 [config_rpc_client] [notify-listener] time cost=0ms in ClientWorker, dataId=dept.provider-flow-rules, group=SENTINEL_GROUP, md5=a648b66d966d505aec4490ba3e11be12, listener=com.alibaba.csp.sentinel.datasource.nacos.NacosDataSource$1@2f530d28 [config_rpc_client] [notify-ok] dataId=dept.provider-flow-rules, group=SENTINEL_GROUP, md5=a648b66d966d505aec4490ba3e11be12, listener=com.alibaba.csp.sentinel.datasource.nacos.NacosDataSource$1@2f530d28 ,cost=1 millis.
限流规则解析
Sentinel组件里面对于流控规则有很多种,例如:系统流控、授权流控、热点流控,对于这些流控该如何进行持久化的配置?
在整个实际的开发之中,如果要想更合理的去使用Sentinel组件,那么最重要的一点就是进行各类规则的配置,例如:现在需要定义一个系统规则,这个系统规则也应该在Nacos里面进行定义,但是具体的定义项,就需要根据SystemRule子类分析。1、【Sentinel源代码】查看一下 SystemRule子类之中的属性。
2、【SentinelNacos控制台】追加一个“dept.provider-system-rules”配置项,进行QPS限流规则定义。[ { "qps": 1 } ]
3、【provider-dept-8001子模块】修改application.yml 配置文件,追加系统规则数据源的读取。spring: application: # 配置应用信息 name: dept.provider # 是微服务的名称 cloud: # Cloud配置 sentinel: # 监控配置 transport: # 传输配置 port: 8719 # Sentinel组件启用之后默认会启动一个8719端口 dashboard: sentinel-server:8888 # 控制台地址 block-page: /errors/block_handler # 阻断页 eager: true # 饥饿加载 datasource: # 数据源配置 system-datasource: # 系统规则数据源 nacos: # 当前的存储介质为Nacos server-addr: sentinel-nacos-server:8848 # SentinelNacos地址 namespace: 51586a27-b10d-4165-9cd2-38f1464c780d # 命名空间ID group-id: SENTINEL_GROUP # 一般建议大写 data-id: ${spring.application.name}-system-rules # 配置项的名称 data-type: json # 配置的文件结构 rule_type: system # 流控规则 flow-datasource: # 流控的数据源 nacos: # 当前的存储介质为Nacos server-addr: sentinel-nacos-server:8848 # SentinelNacos地址 namespace: 51586a27-b10d-4165-9cd2-38f1464c780d # 命名空间ID group-id: SENTINEL_GROUP # 一般建议大写 data-id: ${spring.application.name}-flow-rules # 配置项的名称 data-type: json # 配置的文件结构 rule_type: flow # 流控规则
-
sentinel:在 Python 中创建哨兵和单例对象
2021-05-31 07:34:52如果您想创建自己的哨兵,请使用此库! 让你对dict.get()调用更有意义! 您可以用哨兵代替object()惯用语: d = { "a" : 1 , "b" : None } # Before sentinel: missing = object () if d . get ( "c" , missing )... -
Docker创建sentinel-dashboard容器
2020-12-10 01:46:51Docker创建sentinel-dashboard容器 1拉取镜像 docker pull bladex/sentinel-dashboard 2 创建启动容器 docker run --name sentinel --restart=always -d -p 8858:8858 bladex/sentinel-dashboard:latest...Docker创建sentinel-dashboard容器
1拉取镜像
docker pull bladex/sentinel-dashboard
2 创建启动容器
docker run --name sentinel --restart=always -d -p 8858:8858 bladex/sentinel-dashboard:latest
3查看容器
docker ps -a
4访问sentinel,http://192.168.15.101:8858 登录账号:sentinel 密码 sentinel
-
sentinel-dashboard-plus:sentinel dashboard 升级,规则管理及推送持久化到Nacos、实时监控数据存储在ES
2021-05-26 14:56:38sentinel 使用文档 sentinel dashboard 升级 ... 创建一个maven工程(sentinel-dashboard-plus),引入GitHub上开源的最新release发行版本的sentinel-dashboard.jar到项目工程中,进行sentinel控制 -
sentinel-parameter-flow-control-1.4.0.jar
2020-04-27 18:41:08sentinel-parameter-flow-control-1.4.0.jar -
Sentinel源码
2022-03-09 15:28:25一、概述 在SpringBoot启动过程中,会...下面针对这两个类进行分析,逐渐深入理解流量是如何被Sentinel拦截的。 图1 spring.factories 二、深入理解 2.1 SentinelAutoConfiguration package com.alibaba.cloud.sentin文章目录
一、概述
在SpringBoot启动过程中,会加载外部依赖中
META-INF/spring.factories
声明的类。其中最重要的两个类分别为SentinelWebAutoConfiguration
和SentinelAutoConfiguration
。下面针对这两个类进行分析,逐渐深入理解流量是如何被Sentinel拦截的。
SentinelWebAutoConfiguration
针对web项目,即springmvc扩展包,默认把所有的url的入口添加为sentinel资源SentinelAutoConfiguration
针对用户添加@SentinelResource注解的地方添加为sentinel资源
上述2个入口最终都是利用代理方式实现的,并且共用底层的代理类
二、源码
2.1 SentinelAutoConfiguration
META-INF/spring.factories
中包含SentinelAutoConfiguration,会在启动启动的时候进行加载,并且会加载@Bean的方法去实例化对象:package com.alibaba.cloud.sentinel.custom; /*省略部分代码。。。。。。*/ @Configuration public class SentinelAutoConfiguration { /*省略部分代码。。。。。。*/ /*注入切面SentinelResourceAspect*/ @Bean @ConditionalOnMissingBean public SentinelResourceAspect sentinelResourceAspect() { return new SentinelResourceAspect(); } }
SentinelResourceAspect负责创建代理,实际执行的时候,也确实会调用到该类:
SentinelResourceAspect源码:
package com.alibaba.csp.sentinel.annotation.aspectj; @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); /*Step 1 :获取@SentinelResource注解*/ SentinelResource annotation = originMethod.getAnnotation(SentinelResource.class); /*Step 2 :获取@SentinelResource中的资源名*/ String resourceName = getResourceName(annotation.value(), originMethod); EntryType entryType = annotation.entryType(); Entry entry = null; try { /*Step 3 :执行Sentinel核心功能*/ 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) { traceException(ex, annotation); throw ex; } finally { if (entry != null) { entry.exit(1, pjp.getArgs()); } } } }
SentinelResourceAspect是Sentinel中的核心切面,在SentinelAutoConfiguration被加载时,注入了一个SentinelResourceAspect的Bean对象,aspect的@around拦截标注有@SentinelResource的注解。
- Step1获取@SentinelResource注解;
- Step2获取@SentinelResource中的资源名;
- Step3执行Sentinel核心功能。
通过上面SentinelWebAutoConfiguration和SentinelAutoConfiguration两个类的加载,成功将流量引入Sentinel的核心入口
SphU.entry
,下面我们对Sentinel的归一入口进行讲解。为什么说是统一入口,即不管你是硬编码还是通过@SentinelResource注解 又或是通过集成alibaba+springmvc,sentinel最终的入口都是
SphU.entry
2.1.1 SphU.entry
SphU.entry是Sentinel的归一入口,所有流量在熔断限流前都由此进入,此功能实现了对资源进行规则检查。在SphU.entry中的核心功能是entryWithPriority。
com.alibaba.csp.sentinel.CtSph:
private Entry entryWithPriority(ResourceWrapper resourceWrapper, int count, boolean prioritized, Object... args) throws BlockException { /*Step 1-1 :创建上下文,如果在ThreadLocal中已存在,则直接返回上下文*/ Context context = ContextUtil.getContext(); /*省略部分代码。。。。。。*/ /*Step 1-2 :创建一条Slot Chain*/ ProcessorSlot<Object> chain = lookProcessChain(resourceWrapper); /*省略部分代码。。。。。。*/ /*Step 2 :将Chain和当前Context结合*/ Entry e = new CtEntry(resourceWrapper, chain, context); try { /*Step 3 :Slot Chain入口,开始规则检查*/ chain.entry(context, resourceWrapper, null, count, prioritized, args); } catch (BlockException e1) { e.exit(count, args); throw e1; } catch (Throwable e1) { RecordLog.info("Sentinel unexpected exception", e1); } return e; }
entryWithPriority()
是Sentinel的核心功能:- Step1创建上下文Context、Slot Chain;
- Step2结合Context和Slot Chain;
- Step3进入Slot Chain进行规则检查并返回结果。
2.2 SentinelWebAutoConfiguration
存在新版本合和旧版本之分
SentinelWebAutoConfiguration
针对web项目,即springmvc扩展包,默认把所有的url的入口添加为sentinel资源,这样不用每个url的入口手动添加@SentinelResource标签了2.2.1 新版本
可以参见 【Sentinel入门】05 springmvc 集成Sentinel & springboot集成Sentinel & 链路模式失效 & WebContextUnify & CommonFilter,里面包含新版本的用法,基于springboot,需要手动引入SentinelWebInterceptor代理,本篇是alibaba源码,通过spi机制,自动实现引入SentinelWebInterceptor代理
至少从
spring cloud alibaba 2.2.1.RELEASE
版本开始,已经采用新版本特点 是 引入
sentinel-spring-webmvc-dapter
包, 基于 Spring 的Interceptor
代理拦截资源:... @Configuration public class SentinelWebAutoConfiguration implements WebMvcConfigurer { //实现WebMvcConfigurer ,准备注册代理 //参数项集合,用于定制拦截器 @Autowired private SentinelProperties properties; @Autowired private Optional<UrlCleaner> urlCleanerOptional; @Autowired private Optional<BlockExceptionHandler> blockExceptionHandlerOptional; @Autowired private Optional<RequestOriginParser> requestOriginParserOptional; //step3 注入一个SentinelWebInterceptor实例 @Autowired private Optional<SentinelWebInterceptor> sentinelWebInterceptorOptional; //step4注册拦截器入口 @Override public void addInterceptors(InterceptorRegistry registry) { if (!sentinelWebInterceptorOptional.isPresent()) { return; } SentinelProperties.Filter filterConfig = properties.getFilter(); // step 4.1入参是前面步骤所创建的一个sentinel拦截器 registry.addInterceptor(sentinelWebInterceptorOptional.get()) .order(filterConfig.getOrder()) .addPathPatterns(filterConfig.getUrlPatterns()); } //step2 创建一个拦截器 @Bean @ConditionalOnProperty(name = "spring.cloud.sentinel.filter.enabled", matchIfMissing = true) public SentinelWebInterceptor sentinelWebInterceptor( SentinelWebMvcConfig sentinelWebMvcConfig) { return new SentinelWebInterceptor(sentinelWebMvcConfig); } //step1 创建一个配置,供创建SentinelWebInterceptor拦截器用,用于定制拦截器 @Bean @ConditionalOnProperty(name = "spring.cloud.sentinel.filter.enabled", matchIfMissing = true) public SentinelWebMvcConfig sentinelWebMvcConfig() { SentinelWebMvcConfig sentinelWebMvcConfig = new SentinelWebMvcConfig(); sentinelWebMvcConfig.setHttpMethodSpecify(properties.getHttpMethodSpecify()); //很重要,影响链路模式 sentinelWebMvcConfig.setWebContextUnify(properties.getWebContextUnify()); if (blockExceptionHandlerOptional.isPresent()) { blockExceptionHandlerOptional .ifPresent(sentinelWebMvcConfig::setBlockExceptionHandler); } else { if (StringUtils.hasText(properties.getBlockPage())) { sentinelWebMvcConfig.setBlockExceptionHandler(((request, response, e) -> response.sendRedirect(properties.getBlockPage()))); } else { sentinelWebMvcConfig .setBlockExceptionHandler(new DefaultBlockExceptionHandler()); } } urlCleanerOptional.ifPresent(sentinelWebMvcConfig::setUrlCleaner); requestOriginParserOptional.ifPresent(sentinelWebMvcConfig::setOriginParser); return sentinelWebMvcConfig; }
- SentinelWebAutoConfiguration 实现了WebMvcConfigurer ,用于创建一个拦截器,WebMvcConfigurer 作用参见【springmvc框架】详解WebMvcConfigurer接口(Interceptor拦截器、ResourceHandler、Formatter、CorsMapping)
- SentinelWebInterceptor 是内置的一个拦截器,用于拦截url,然后进行代理
- SentinelWebMvcConfig 很重要,用于配置SentinelWebInterceptor ,进行定制化,特别注意setWebContextUnify(),影响链路模式,为啥会影响,参见 【Sentinel入门】05 springmvc 集成Sentinel & springboot集成Sentinel & 链路模式失效 & WebContextUnify & CommonFilter中的<webContextUnify属性>章节
2.2.1 (旧版本)
META-INF/spring.factories
中包含SentinelWebAutoConfiguration ,会在启动启动的时候进行加载,并且会加载@Bean的方法去实例化对象:package com.alibaba.cloud.sentinel; /*省略部分代码。。。。。。*/ @Configuration public class SentinelWebAutoConfiguration { /*省略部分代码。。。。。。*/ @Bean @ConditionalOnProperty(name = "spring.cloud.sentinel.filter.enabled", matchIfMissing = true) public FilterRegistrationBean sentinelFilter() { /*Step 1 : 创建一个FilterRegistrationBean*/ FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>(); SentinelProperties.Filter filterConfig = properties.getFilter(); if (filterConfig.getUrlPatterns() == null || filterConfig.getUrlPatterns().isEmpty()) { List<String> defaultPatterns = new ArrayList<>(); defaultPatterns.add("/*"); filterConfig.setUrlPatterns(defaultPatterns); } registration.addUrlPatterns(filterConfig.getUrlPatterns().toArray(new String[0])); /*重点Step 2 :创建一个CommonFilter*/ Filter filter = new CommonFilter(); /*Step 3 : 将CommonFilter放入FilterRegistrationBean*/ registration.setFilter(filter); registration.setOrder(filterConfig.getOrder()); registration.addInitParameter("HTTP_METHOD_SPECIFY", String.valueOf(properties.getHttpMethodSpecify())); /*省略部分代码。。。。。。*/ return registration; } }
在
SentinelWebAutoConfiguration
被加载时,注入了一个FilterRegistrationBean
的Bean对象:- Step1创建一个注册Filter的FilterRegistrationBean对象registration;
- Step2创建了一个CommonFilter,这个后面重点研究;
- Step3将CommonFilter放入到filterChain中。
旧版本中,是利用Filter原理,进行过滤:
package com.alibaba.csp.sentinel.adapter.servlet; public class CommonFilter implements Filter { /*省略部分代码。。。。。。*/ @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { /*省略部分代码。。。。。。*/ try { /*Step 1 :截取请求URL作为资源名赋予target */ String target = FilterUtil.filterTarget(sRequest); /*省略部分代码。。。。。。*/ /*Step 2 :创建一个target名称的上下文*/ ContextUtil.enter(target, origin); /*Step 3 :执行Sentinel的核心功能*/ entry = SphU.entry(target, EntryType.IN); /*省略部分代码。。。。。。*/ chain.doFilter(request, response); } /*省略部分代码。。。。。。*/ ContextUtil.exit(); } } /*省略部分代码。。。。。。*/ }
CommonFilter,这是个内置的Filter 实现类,专门用于sentinel的功能:
package com.alibaba.csp.sentinel.adapter.servlet; public class CommonFilter implements Filter { /*省略部分代码。。。。。。*/ @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { /*省略部分代码。。。。。。*/ try { /*Step 1 :截取请求URL作为资源名赋予target */ String target = FilterUtil.filterTarget(sRequest); /*省略部分代码。。。。。。*/ /*Step 2 :创建一个target名称的上下文*/ ContextUtil.enter(target, origin); /*Step 3 :执行Sentinel的核心功能*/ entry = SphU.entry(target, EntryType.IN); /*省略部分代码。。。。。。*/ chain.doFilter(request, response); } /*省略部分代码。。。。。。*/ ContextUtil.exit(); } } /*省略部分代码。。。。。。*/ }
CommonFilter会拦截所有进入Tomcat的请求:
- Step1截取请求中的URL作为“资源名”;
- Step2创建一个name为“资源名”的Context;
- Step3执行Sentinel的核心功能SphU.entry,这个后面重点研究。
参考
-
Redis哨兵高可用性(包含创建sentinel节点以及配置文件)
2022-04-29 08:03:17Redis哨兵 (Redis Sentinel) 声明:本次笔记全本人手写,进行对烧饼有了理解,如果想看redis数据节点的配置文件和sentinel节点的配置文件请直接跳到9.2.2和9.2.3 22/4/29/lsj 本次笔记主要了解Redis Sentinel,实现Redis... -
sentinel技术分享
2022-02-13 19:23:56Sentinel是阿里开源的项目,提供了流量控制、熔断降级、系统负载保护等多个维度来保障服务之间的稳定性。 -
Docker搭建Sentinel控制台
2022-03-13 08:03:23一 获取镜像 ...docker run -d --restart always -p 8858:8858 --name sentinel bladex/sentinel-dashboard 参考:Docker部署sentinel - 简书 (jianshu.com)https://www.jianshu.com/p/373eb... -
Sentinel介绍与运行机制
2022-03-28 11:40:38一、sentinel介绍 二、Sentinel支持集群 三、关于Sentinel版本 四、运行Sentinel 五、Sentinel的配置 六、Sentinel的“仲裁会” 七、配置版本号 八、配置传播 九、SDOWN和ODOWN的更多细节 十、Sentinel之间... -
sentinel源码解析之-sentinel dashboard与客户端连接建立过程
2020-10-15 11:19:13sentinel 源码解析之sentinel dashboard与sentinel客户端连接过程 -
sentinel 基础
2021-12-28 13:29:501. Sentinel概述 1.1 Sentinel简介 随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控 制、熔断降级、系统负载保护等多个维度保护服务的稳定性。 Sentinel 具有以下特征... -
Sentinel源码解析之一次请求走进Sentinel
2020-11-25 00:53:10本篇开始,我们就正式进入Sentinel源码解析了,本篇主要是介绍下Sentinel与SpringMVC整合,发送批量请求去Sentinel控制台看看效果,最后我们就要揭秘一下Sentinel是怎样做到能够统计请求的。 1.整合Sentinel与Spring... -
Sentinel高可用集群搭建
2022-03-28 11:52:58目录 一、前言 二、集群搭建 1、环境 2、配置文件介绍 3、实验中的sentinel配置 ...4、启动sentinel: ...5、sentinel原理 ...三、sentinel核心概念 ...sentinel是一个"监视器",根据被监视实例的身份和 -
redis-sentinel-kubernetes:使用简短的代码将Redis with Sentinel部署到Kubernetes上
2021-02-12 03:53:27Redis-Sentinel-Kubernetes 在Kubernetes上部署Redis(带有Sentinel的Master-Replica)。 建议用法:查看此头盔图,了解它,然后创建自己的部署:) -
Sentinel使用
2021-07-20 22:56:32这里写目录标题Sentinel简介Sentinel 是什么?Sentinel 组成Sentinel 与 Hystrix 的对比使用Sentinel添加依赖限流测试使用方式Sentinel Dashboard概述Dashboard下载运行使用Dashboard使用限流 Sentinel简介 可以直接... -
sentinel 1.8.2 使用手册
2021-09-11 21:25:02作用简单描述 主要作用就是进行流量控制...java -Dserver.port=8180 -Dcsp.sentinel.dashboard.server=localhost:8180 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.2.jar 默认网站:http://local -
springboot整合sentinel
2021-03-31 01:29:29springboot 整合 sentinel 简单示例 pom.xml 中添加依赖 <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-core</artifactId> <version>1.8.0</... -
Alibaba Sentinel 一 :Sentinel初识
2020-05-21 18:43:35一: Sentinel简介 Sentinel是Alibaba Spring Cloud中的重要一员,它是面向分布式服务架构的流量控制组件,主要以流量为切入点,从流量控制、熔断降级、系统自适应保护等多个维度来帮助用户保障微服务的稳定性。 ... -
Sentinel(二)Docker搭建Sentinel 控制台环境搭建及使用介绍
2022-03-02 14:14:23Sentinel(二)Docker搭建Sentinel 控制台环境搭建及使用介绍 -
sentinel 集群限流
2022-04-01 09:17:14sentinel 集群限流 -
Sentinel 是什么
2021-08-26 15:48:50Sentinel 是什么? 概述 分布式系统的流量防卫兵 随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级系统负载保护等多个维度保护服务的稳定性。 ... -
Sentinel 原理:Sentinel 是什么?
2020-08-18 18:24:35Sentinel 是阿里中间件团队开源的,面向分布式服务架构的轻量级高可用流量控制组件,主要以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度来帮助用户保护服务的稳定性。 大家可能会问:Sentinel 和... -
Sentinel流量控制
2022-03-23 19:30:501.导学 1.1.软件环境 系统:windows10 开发工具:IntelliJ IDEA...Sentinel简介 Sentinel入门 开发者 Sentinel简介 Sentinel入门 Sentinel高级 测试、运维 Sentinel简介 Sentinel入门 技术爱好者、性能优.