first节点的entry要领,现实又是执行的super的fireEntry要领,那继承把眼光转移到fireEntry要领,详细如下:
- @Override
-
- public void fireEntry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, Object... args)
-
- throws Throwable {
-
- if (next != null) {
-
- next.transformEntry(context, resourceWrapper, obj, count, args);
-
- }
-
- }
从这里可以看到,从fireEntry要领中就开始转达执行entry了,这里会执行当前节点的下一个节点transformEntry要领,上面已经说明过了,transformEntry要了解触发当前节点的entry,也就是说fireEntry要领现实是触发了下一个节点的entry要领。详细的流程如下图所示:
从图中可以看出,从最初的挪用Chain的entry()要领,转酿成了挪用SlotChain中Slot的entry()要领。从上面的说明可以知道,SlotChain中的第一个Slot节点是NodeSelectorSlot。
执行Slot的entry要领
此刻可以把眼光转移到SlotChain中的第一个节点NodeSelectorSlot的entry要领中去了,详细的代码如下:
- @Override
-
- public void entry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, Object... args)
-
- throws Throwable {
-
- DefaultNode node = map.get(context.getName());
-
- if (node == null) {
-
- synchronized (this) {
-
- node = map.get(context.getName());
-
- if (node == null) {
-
- node = Env.nodeBuilder.buildTreeNode(resourceWrapper, null);
-
- HashMap<String, DefaultNode> cacheMap = new HashMap<String, DefaultNode>(map.size());
-
- cacheMap.putAll(map);
-
- cacheMap.put(context.getName(), node);
-
- map = cacheMap;
-
- }
-
- // Build invocation tree
-
- ((DefaultNode)context.getLastNode()).addChild(node);
-
- }
-
- }
-
- context.setCurNode(node);
-
- // 由此触发下一个节点的entry要领
-
- fireEntry(context, resourceWrapper, node, count, args);
-
- }
从代码中可以看到,NodeSelectorSlot节点做了一些本身的营业逻辑处理赏罚,详细的各人可以深入源码继承追踪,这里或许的先容下每种Slot的成果职责:
- NodeSelectorSlot 认真网络资源的路径,并将这些资源的挪用路径,以树状布局存储起来,用于按照挪用路径来限流降级;
- ClusterBuilderSlot 则用于存储资源的统计信息以及挪用者信息,譬喻该资源的 RT, QPS, thread count 等等,这些信息将用作为多维度限流,降级的依据;
- StatistcSlot 则用于记录,统计差异纬度的 runtime 信息;
- FlowSlot 则用于按照预设的限流法则,以及前面 slot 统计的状态,来举办限流;
- AuthorizationSlot 则按照利害名单,来做利害名单节制;
- DegradeSlot 则通过统计信息,以及预设的法则,来做熔断降级;
- SystemSlot 则通过体系的状态,譬喻 load1 等,来节制总的进口流量;
执行完营业逻辑处理赏罚后,挪用了fireEntry()要领,由此触发了下一个节点的entry要领。此时我们就知道了sentinel的责任链就是这样转达的:每个Slot节点执行完本身的营业后,会挪用fireEntry来触发下一个节点的entry要领。
以是可以将上面的图完备了,详细如下:
至此就通过SlotChain完成了对每个节点的entry()要领的挪用,每个节点会按照建设的法则,举办本身的逻辑处理赏罚,当统计的功效到达配置的阈值时,就会触发限流、降级等变乱,详细是抛出BlockException非常。
总结
sentinel首要是基于7种差异的Slot形成了一个链表,每个Slot都各司其职,本身做完分内的事之后,会把哀求转达给下一个Slot,直到在某一个Slot中掷中法则后抛出BlockException而终止。
前三个Slot认真做统计,后头的Slot认真按照统计的功效团结设置的法则举办详细的节制,是Block该哀求照旧放行。
节制的范例也有许多可选项:按照qps、线程数、冷启动等等。
然后基于这个焦点的要领,衍生出了许多其他的成果:
- 1、dashboard节制台,可以可视化的对每个毗连过来的sentinel客户端 (通过发送heartbeat动静)举办节制,dashboard和客户端之间通过http协议举办通信。
- 2、法则的耐久化,通过实现DataSource接口,可以通过差异的方法对设置的法则举办耐久化,默认法则是在内存中的
- 3、对主流的框架举办适配,包罗servlet,dubbo,rRpc等
Dashboard节制台
sentinel-dashboard是一个单独的应用,通过spring-boot举办启动,首要提供一个轻量级的节制台,它提供呆板发明、单机资源及时监控、集群资源汇总,以及法则打点的成果。
我们只必要对应用举办简朴的设置,就可以行使这些成果。
1 启动节制台
1.1 下载代码并编译节制台
- 下载 节制台 工程
- 行使以下呼吁将代码打包成一个 fat jar: mvn cleanpackage
1.2 启动
行使如下呼吁启动编译后的节制台:
$ java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -jar target/sentinel-dashboard.jar
上述呼吁中我们指定了一个JVM参数, -Dserver.port=8080 用于指定 Spring Boot 启动端口为 8080。
2 客户端接入节制台
节制台启动后,客户端必要凭证以下步调接入到节制台。
2.1 引入客户端jar包
通过 pom.xml 引入 jar 包:
- <dependency>
-
- <groupId>com.alibaba.csp</groupId>
-
- <artifactId>sentinel-transport-simple-http</artifactId>
-
- <version>x.y.z</version>
-
- </dependency>
2.2 设置启动参数
启动时插手 JVM 参数 -Dcsp.sentinel.dashboard.server=consoleIp:port 指定节制台地点和端口。若启动多个应用,则必要通过 -Dcsp.sentinel.api.port=xxxx 指定客户端监控 API 的端口(默认是 8719)。
除了修改 JVM 参数,也可以通过设置文件取得同样的结果。更具体的信息可以参考 启动设置项。
2.3 触发客户端初始化
确保客户端有会见量,Sentinel 会在客户端初次挪用的时辰举办初始化,开始向节制台发送心跳包。
sentinel-dashboard是一个独立的web应用,可以接管客户端的毗连,然后与客户端之间举办通信,他们之间行使http协议举办通信。他们之间的相关如下图所示:
dashboard
dashboard启动后会守候客户端的毗连,详细的做法是在 MachineRegistryController 中有一个 receiveHeartBeat 的要领,客户端发送心跳动静,就是通过http哀求这个要领。
dashboard吸取到客户端的心跳动静后,会把客户端的转达过来的ip、port等信息封装成一个 MachineInfo工具,然后将该工具通过 MachineDiscovery 接口的 addMachine 要领添加到一个ConcurrentHashMap中生涯起来。
这里会有题目,由于客户端的信息是生涯在dashboard的内存中的,以是当dashboard应用重启后,之前已经发送过来的客户端信息城市丢失掉。
client
client在启动时,会通过CommandCenterInitFunc选择一个,而且只选择一个CommandCenter举办启动。
启动之前会通过spi的方法扫描获取到全部的CommandHandler的实现类,然后将全部的CommandHandler注册到一个HashMap中去,待后期行使。
PS:思量一下,为什么CommandHandler不必要做耐久化,而是直接生涯在内存中。
注册完CommandHandler之后,紧接着就启动CommandCenter了,今朝CommandCenter有两个实现类:
- SimpleHttpCommandCenter 通过ServerSocket启动一个处事端,接管socket毗连
- NettyHttpCommandCenter 通过Netty启动一个处事端,接管channel毗连
CommandCenter启动后,就守候dashboard发送动静过来了,当吸取到动静后,会把动静通过详细的CommandHandler举办处理赏罚,然后将处理赏罚的功效返回给dashboard。
这里必要留意的是,dashboard给client发送动静是通过异步的httpClient举办发送的,在HttpHelper类中。
可是诡异的是,既然通过异步发送了,又通过一个CountDownLatch来守候动静的返回,然后获取功效,那这样不就失去了异步的意义的吗?详细的代码如下:
- private String httpGetContent(String url) { final HttpGet httpGet = new HttpGet(url); final CountDownLatch latch = new CountDownLatch(1);
- final AtomicReference<String> reference = new AtomicReference<>();
-
- httpclient.execute(httpGet, new FutureCallback<HttpResponse>() {
-
- @Override
-
- public void completed(final HttpResponse response) {
-
- try {
-
- reference.set(getBody(response));
-
- } catch (Exception e) {
-
- logger.info("httpGetContent " + url + " error:", e);
-
- } finally {
-
- latch.countDown();
-
- }
-
- }
-
- @Override
-
- public void failed(final Exception ex) {
-
- latch.countDown();
-
- logger.info("httpGetContent " + url + " failed:", ex);
-
- }
-
- @Override
-
- public void cancelled() {
-
- latch.countDown();
-
- }
-
- });
-
- try {
-
- latch.await(5, TimeUnit.SECONDS);
-
- } catch (Exception e) {
-
- logger.info("wait http client error:", e);
-
- }
-
- return reference.get();
-
- }
主流框架的适配
sentinel也对一些主流的框架举办了适配,使得在行使主流框架时,也可以享受到sentinel的掩护。今朝已经支持的适配器包罗以下这些:
- Web Servlet
- Dubbo
- Spring Boot / Spring Cloud
- gRPC
- Apache RocketMQ
着实做适配就是通过那些主流框架的扩展点,然后在扩展点上插手sentinel限流降级的代码即可。拿Servlet的适配代码看一下,详细的代码是:
- public class CommonFilter implements Filter {
-
- @Override
-
- public void init(FilterConfig filterConfig) {
-
- }
-
- @Override
-
- public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
-
- throws IOException, ServletException
-
- HttpServletRequest sRequest = (HttpServletRequest)request;
-
- Entry entry = null;
-
- try {
-
- // 按照哀求天生的资源
-
- String target = FilterUtil.filterTarget(sRequest);
-
- target = WebCallbackManager.getUrlCleaner().clean(target);
-
- // “申请”该资源
-
- ContextUtil.enter(target);
-
- entry = SphU.entry(target, EntryType.IN);
-
- // 假如能乐成“申请”到资源,则声名未被限流
-
- // 则将哀求放行
-
- chain.doFilter(request, response);
-
- } catch (BlockException e) {
-
- // 不然假如捕捉了BlockException非常,声名哀求被限流了
-
- // 则将哀求重定向到一个默认的页面
-
- HttpServletResponse sResponse = (HttpServletResponse)response;
-
- WebCallbackManager.getUrlBlockHandler().blocked(sRequest, sResponse);
-
- } catch (IOException e2) {
-
- // 省略部门代码
-
- } finally {
-
- if (entry != null) {
-
- entry.exit();
-
- }
-
- ContextUtil.exit();
-
- }
-
- }
-
- @Override
-
- public void destroy() {
-
- }
-
- }
通过Servlet的Filter举办扩展,实现一个Filter,然后在doFilter要领中对哀求举办限流节制,假如哀求被限流则将哀求重定向到一个默认页面,不然将哀求放行给下一个Filter。
法则耐久化,动态化
Sentinel 的理念是开拓者只必要存眷资源的界说,当资源界说乐成,可以动态增进各类流控降级法则。
Sentinel 提供两种方法修改法则:
- 通过 API 直接修改 ( loadRules)
- 通过 DataSource适配差异数据源修改
通过 API 修改较量直观,可以通过以下三个 API 修改差异的法则:
FlowRuleManager.loadRules(List<FlowRule> rules); // 修改流控法则
DegradeRuleManager.loadRules(List<DegradeRule> rules); // 修改降级法则 (编辑:湖南网)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|