Ali-Sentinel-链路控制

归档

  • GitHub: Ali-Sentinel-链路控制

链结构

  • 参考:入口控制-处理链

具体实现

NodeSelectorSlot

  • 给上下文设置统计节点

  • com.alibaba.csp.sentinel.slots.nodeselector.NodeSelectorSlot

@Spi(isSingleton = false, order = Constants.ORDER_NODE_SELECTOR_SLOT)
public class NodeSelectorSlot extends AbstractLinkedProcessorSlot<Object> {private volatile Map<String, DefaultNode> map = new HashMap<String, DefaultNode>(10);@Overridepublic void entry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, boolean prioritized, Object... args) throws Throwable {DefaultNode node = map.get(context.getName());if (node == null) {synchronized (this) {   // DCLnode = map.get(context.getName());if (node == null) {node = new DefaultNode(resourceWrapper, null);              // 创建默认节点HashMap<String, DefaultNode> cacheMap = new HashMap<String, DefaultNode>(map.size());cacheMap.putAll(map);cacheMap.put(context.getName(), node);map = cacheMap;                                             // COW((DefaultNode) context.getLastNode()).addChild(node);       // 构建调用树  ref: sign_m_010 | sign_m_020}}}context.setCurNode(node);                                               // 保存到上下文  ref: sign_m_011fireEntry(context, resourceWrapper, node, count, prioritized, args);    // (将统计节点) 流转给下游}
}
  • com.alibaba.csp.sentinel.context.Context
    • entranceNode 在 上下文-创建 sign_m_020 中赋值
    // sign_m_010 获取尾节点public Node getLastNode() {if (curEntry != null && curEntry.getLastNode() != null) {return curEntry.getLastNode();  // sign_m_040} else {return entranceNode;    // 一般返回此}}// sign_m_011public Context setCurNode(Node node) {this.curEntry.setCurNode(node); //  sign_m_030return this;}
  • com.alibaba.csp.sentinel.node.DefaultNode
    // sign_m_020 添加子节点public void addChild(Node node) {... // 省略 node 空判断if (!childList.contains(node)) {synchronized (this) {       // DCLif (!childList.contains(node)) {Set<Node> newSet = new HashSet<>(childList.size() + 1);newSet.addAll(childList);newSet.add(node);childList = newSet; // COW}}}}
  • com.alibaba.csp.sentinel.Entry
public abstract Node getLastNode();// sign_m_030public void setCurNode(Node node) {this.curNode = node;}// sign_m_031public Node getCurNode() {return curNode;}
  • com.alibaba.csp.sentinel.CtEntry
    // sign_m_040@Overridepublic Node getLastNode() {return parent == null ? null : parent.getCurNode(); // sign_m_031}

ClusterBuilderSlot

  • 给统计节点设置集群(统计)节点

    • 只是用于统计
  • com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot

@Spi(isSingleton = false, order = Constants.ORDER_CLUSTER_BUILDER_SLOT)
public class ClusterBuilderSlot extends AbstractLinkedProcessorSlot<DefaultNode> {private static volatile Map<ResourceWrapper, ClusterNode> clusterNodeMap = new HashMap<>(); // 记录所有的节点private static final Object lock = new Object();private volatile ClusterNode clusterNode = null;@Overridepublic void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,boolean prioritized, Object... args) throws Throwable {if (clusterNode == null) {synchronized (lock) {   // DCLif (clusterNode == null) {clusterNode = new ClusterNode(resourceWrapper.getName(), resourceWrapper.getResourceType());HashMap<ResourceWrapper, ClusterNode> newMap = new HashMap<>(Math.max(clusterNodeMap.size(), 16));newMap.putAll(clusterNodeMap);newMap.put(node.getId(), clusterNode);clusterNodeMap = newMap;    // COW}}}node.setClusterNode(clusterNode);       // 设置集群节点... // 省略设置源节点处理fireEntry(context, resourceWrapper, node, count, prioritized, args);    // 传递给下游节点}}

LogSlot

  • 日志异常记录

  • com.alibaba.csp.sentinel.slots.logger.LogSlot

@Spi(order = Constants.ORDER_LOG_SLOT)
public class LogSlot extends AbstractLinkedProcessorSlot<DefaultNode> {@Overridepublic void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode obj, int count, boolean prioritized, Object... args)throws Throwable {try {fireEntry(context, resourceWrapper, obj, count, prioritized, args); // 先传给下游} catch (BlockException e) {EagleEyeLogUtil.log(resourceWrapper.getName(), e.getClass().getSimpleName(), e.getRuleLimitApp(),context.getOrigin(), e.getRule().getId(), count);throw e; // 继续往上抛} catch (Throwable e) {// 下游处理出错,则记录异常日志RecordLog.warn("Unexpected entry exception", e);}}
}

StatisticSlot

  • 统计各种数据

  • com.alibaba.csp.sentinel.slots.statistic.StatisticSlot

@Spi(order = Constants.ORDER_STATISTIC_SLOT)
public class StatisticSlot extends AbstractLinkedProcessorSlot<DefaultNode> {@Override // sign_m_400 记录通过数public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,boolean prioritized, Object... args) throws Throwable {try {/*** 先传给下游处理,让下游先处理,* 后面自己才做统计,* 这样,下游要处理 QPS 等,要等下次才有数据*/fireEntry(context, resourceWrapper, node, count, prioritized, args);// 添加线程计数和通过计数node.increaseThreadNum();node.addPassRequest(count);                     // ref: sign_m_401... // 省略入口源节点添加计数// 全局入站节点添加计数 (其用于系统流控)if (resourceWrapper.getEntryType() == EntryType.IN) {Constants.ENTRY_NODE.increaseThreadNum();Constants.ENTRY_NODE.addPassRequest(count); // ref: sign_m_401}... // sign_call_100 省略注册的回调器处理} catch (PriorityWaitException ex) {... // 省略此异常处理 (相当于下游抛出此异常,上面的统计 (除 pass 外) 再走一次)} catch (BlockException e) {context.getCurEntry().setBlockError(e); // 记录异常node.increaseBlockQps(count);           // Add block count.... // 省略入口源节点添加计数// 全局入站节点添加计数if (resourceWrapper.getEntryType() == EntryType.IN) {Constants.ENTRY_NODE.increaseBlockQps(count);}... // 省略注册的回调器处理throw e;} catch (Throwable e) {context.getCurEntry().setError(e);      // 记录异常throw e;}}@Override // 记录完成数public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {Node node = context.getCurNode();if (context.getCurEntry().getBlockError() == null) {// 计算响应时间 (当前时间 - 入口创建时间)long completeStatTime = TimeUtil.currentTimeMillis();context.getCurEntry().setCompleteTimestamp(completeStatTime);long rt = completeStatTime - context.getCurEntry().getCreateTimestamp();Throwable error = context.getCurEntry().getError();// 记录响应时间和成功次数recordCompleteFor(node, count, rt, error);recordCompleteFor(context.getCurEntry().getOriginNode(), count, rt, error);if (resourceWrapper.getEntryType() == EntryType.IN) {recordCompleteFor(Constants.ENTRY_NODE, count, rt, error);}}... // sign_call_200 省略注册的回调器处理fireExit(context, resourceWrapper, count, args); // 传给下游}private void recordCompleteFor(Node node, int batchCount, long rt, Throwable error) {... // 省略 node 为空返回处理node.addRtAndSuccess(rt, batchCount);       // 添加 RT 和完成数node.decreaseThreadNum();                   // 减线程数if (error != null && !(error instanceof BlockException)) {node.increaseExceptionQps(batchCount);  // 添加异常计数}}
}
  • com.alibaba.csp.sentinel.node.DefaultNode
    // sign_m_401@Overridepublic void addPassRequest(int count) {super.addPassRequest(count);            // ref: sign_m_410this.clusterNode.addPassRequest(count); // ref: sign_m_410}
  • com.alibaba.csp.sentinel.node.StatisticNode
    • Metric.addPass 方法参考:节点与度量-addpass
    // sign_m_410@Overridepublic void addPassRequest(int count) {rollingCounterInSecond.addPass(count);  // 参考: 节点与度量-addpassrollingCounterInMinute.addPass(count);}

AuthoritySlot

  • 黑、白名单权限校验

  • com.alibaba.csp.sentinel.slots.block.authority.AuthoritySlot

@Spi(order = Constants.ORDER_AUTHORITY_SLOT)
public class AuthoritySlot extends AbstractLinkedProcessorSlot<DefaultNode> {@Overridepublic void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, boolean prioritized, Object... args)throws Throwable {checkBlackWhiteAuthority(resourceWrapper, context);                     // 先校验  ref: sign_m_501fireEntry(context, resourceWrapper, node, count, prioritized, args);    // 再传给下游}// sign_m_501void checkBlackWhiteAuthority(ResourceWrapper resource, Context context) throws AuthorityException {Map<String, Set<AuthorityRule>> authorityRules = AuthorityRuleManager.getAuthorityRules();  // 获取总规则... // 省略 authorityRules 为 null 返回Set<AuthorityRule> rules = authorityRules.get(resource.getName());  // 获取当前资源的规则... // 省略 rules 为 null 返回// 一般一个资源只有一条规则  ref: AuthorityRuleManager.RulePropertyListener #loadAuthorityConffor (AuthorityRule rule : rules) {if (!AuthorityRuleChecker.passCheck(rule, context)) {           // 依次校验  ref: sign_m_510throw new AuthorityException(context.getOrigin(), rule);    // 校验不通过则抛异常}}}
}
  • com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleChecker
    // sign_m_510 校验权限规则static boolean passCheck(AuthorityRule rule, Context context) {String requester = context.getOrigin();... // 省略 requester 和 rule.getLimitApp() 空判断int pos = rule.getLimitApp().indexOf(requester);boolean contain = pos > -1; // 包含// 加此判断可省略不包含时的多余处理// 下面的处理相当于:逗号分隔再依次精确匹配if (contain) {boolean exactlyMatch = false;String[] appArray = rule.getLimitApp().split(",");  // 英文逗号分隔for (String app : appArray) {if (requester.equals(app)) {    // 精确匹配exactlyMatch = true;        // 匹配上,才算包含break;}}contain = exactlyMatch;}int strategy = rule.getStrategy();if (strategy == RuleConstant.AUTHORITY_BLACK && contain) {  // 黑名单,包含:则不通过return false;}if (strategy == RuleConstant.AUTHORITY_WHITE && !contain) { // 白名单,不包含:则不通过return false;}return true;    // 通过}

SystemSlot

  • com.alibaba.csp.sentinel.slots.system.SystemSlot
@Spi(order = Constants.ORDER_SYSTEM_SLOT)
public class SystemSlot extends AbstractLinkedProcessorSlot<DefaultNode> {@Overridepublic void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,boolean prioritized, Object... args) throws Throwable {SystemRuleManager.checkSystem(resourceWrapper, count);                  // 先校验  ref: sign_m_610fireEntry(context, resourceWrapper, node, count, prioritized, args);    // 再传给下游}
}
  • com.alibaba.csp.sentinel.slots.system.SystemRuleManager
    // sign_m_610public static void checkSystem(ResourceWrapper resourceWrapper, int count) throws BlockException {... // 省略 resourceWrapper 为 null 返回... // 省略 checkSystemStatus 为 false 返回... // 省略 资源类型 不为 入站 返回// total qpsdouble currentQps = Constants.ENTRY_NODE.passQps(); // 在 StatisticSlot 里记录,ref: sign_m_400if (currentQps + count > qps) {throw new SystemBlockException(resourceWrapper.getName(), "qps");}... // 省略 线程数 校验... // 省略 平均RT 校验// load. BBR algorithm.if (highestSystemLoadIsSet && getCurrentSystemAvgLoad() > highestSystemLoad) {if (!checkBbr(currentThread)) { // ref: sign_m_611throw new SystemBlockException(resourceWrapper.getName(), "load");}}... // 省略 CPU 校验 (CPU 数据每秒读取一次)}// sign_m_611private static boolean checkBbr(int currentThread) {if (currentThread > 1 &&currentThread > Constants.ENTRY_NODE.maxSuccessQps() * Constants.ENTRY_NODE.minRt() / 1000) {return false;}return true;}

FlowSlot

  • 对当前资源进行流控

  • com.alibaba.csp.sentinel.slots.block.flow.FlowSlot

    • 流控规则设置参考:入口控制-设置规则 sign_demo_020
@Spi(order = Constants.ORDER_FLOW_SLOT)
public class FlowSlot extends AbstractLinkedProcessorSlot<DefaultNode> {private final FlowRuleChecker checker;@Overridepublic void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,boolean prioritized, Object... args) throws Throwable {checkFlow(resourceWrapper, context, node, count, prioritized);          // 先校验 sign_m_710fireEntry(context, resourceWrapper, node, count, prioritized, args);    // 再传给下游}// sign_m_710void checkFlow(ResourceWrapper resource, Context context, DefaultNode node, int count, boolean prioritized)throws BlockException {checker.checkFlow(ruleProvider, resource, context, node, count, prioritized);   // sign_m_720}// 返回资源对应的流控规则集合private final Function<String, Collection<FlowRule>> ruleProvider = new Function<String, Collection<FlowRule>>() {@Overridepublic Collection<FlowRule> apply(String resource) {Map<String, List<FlowRule>> flowRules = FlowRuleManager.getFlowRuleMap();   // ref: sign_demo_020return flowRules.get(resource);}};
}
  • com.alibaba.csp.sentinel.slots.block.flow.FlowRuleChecker
    // sign_m_720public void checkFlow(Function<String, Collection<FlowRule>> ruleProvider, ResourceWrapper resource,Context context, DefaultNode node, int count, boolean prioritized) throws BlockException {... // 省略 ruleProvider 和 resource 为 null 返回Collection<FlowRule> rules = ruleProvider.apply(resource.getName());if (rules != null) {for (FlowRule rule : rules) {if (!canPassCheck(rule, context, node, count, prioritized)) {   // sign_m_721throw new FlowException(rule.getLimitApp(), rule);}}}}// sign_m_721public boolean canPassCheck(FlowRule rule, Context context, DefaultNode node, int acquireCount,boolean prioritized) {... // 省略 rule.limitApp 为 null 返回 trueif (rule.isClusterMode()) {// ref: 集群流控-流控原理-sign_m_411return passClusterCheck(rule, context, node, acquireCount, prioritized);}return passLocalCheck(rule, context, node, acquireCount, prioritized);  // sign_m_722}// sign_m_722private static boolean passLocalCheck(FlowRule rule, Context context, DefaultNode node, int acquireCount,boolean prioritized) {Node selectedNode = selectNodeByRequesterAndStrategy(rule, context, node);  // 查找统计节点if (selectedNode == null) {return true;}return rule.getRater().canPass(selectedNode, acquireCount, prioritized);    // 使用规则控制器进行判断,ref: sign_m_731}
  • com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController
    // sign_m_731 判断是否可以通过@Overridepublic boolean canPass(Node node, int acquireCount, boolean prioritized) {int curCount = avgUsedTokens(node);     // sign_m_732if (curCount + acquireCount > count) {  // 判断是否超过自身设置的限制数if (prioritized && grade == RuleConstant.FLOW_GRADE_QPS) {long currentTime = TimeUtil.currentTimeMillis();long waitInMs = node.tryOccupyNext(currentTime, acquireCount, count);if (waitInMs < OccupyTimeoutProperty.getOccupyTimeout()) {node.addWaitingRequest(currentTime + waitInMs, acquireCount);node.addOccupiedPass(acquireCount);sleep(waitInMs);throw new PriorityWaitException(waitInMs);  // 报此异常可通过}}return false;   // 超过限制则返回 false (表示不通过)}return true;}// sign_m_732 返回当前计数private int avgUsedTokens(Node node) {if (node == null) {return DEFAULT_AVG_USED_TOKENS;}return grade == RuleConstant.FLOW_GRADE_THREAD ? node.curThreadNum() : (int)(node.passQps());}

DegradeSlot

  • 熔断处理

  • com.alibaba.csp.sentinel.slots.block.degrade.DegradeSlot

@Spi(order = Constants.ORDER_DEGRADE_SLOT)
public class DegradeSlot extends AbstractLinkedProcessorSlot<DefaultNode> {@Overridepublic void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,boolean prioritized, Object... args) throws Throwable {performChecking(context, resourceWrapper);                              // 先校验 sign_m_801fireEntry(context, resourceWrapper, node, count, prioritized, args);    // 再传给下游}// sign_m_801 熔断校验void performChecking(Context context, ResourceWrapper r) throws BlockException {List<CircuitBreaker> circuitBreakers = DegradeRuleManager.getCircuitBreakers(r.getName());if (circuitBreakers == null || circuitBreakers.isEmpty()) {return;}for (CircuitBreaker cb : circuitBreakers) {if (!cb.tryPass(context)) { // 熔断判断 sign_m_810// 不通过则进行熔断报错throw new DegradeException(cb.getRule().getLimitApp(), cb.getRule());}}}@Override // 退出时进行计数和状态处理public void exit(Context context, ResourceWrapper r, int count, Object... args) {Entry curEntry = context.getCurEntry();... // 出现熔断 (有熔断异常) 则传给下游处理并返回List<CircuitBreaker> circuitBreakers = DegradeRuleManager.getCircuitBreakers(r.getName());... // 无断路器则传给下游处理并返回if (curEntry.getBlockError() == null) {for (CircuitBreaker circuitBreaker : circuitBreakers) {circuitBreaker.onRequestComplete(context);  // 断路器计数与状态变更处理,ref: sign_m_830}}fireExit(context, r, count, args);}
}
  • com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.AbstractCircuitBreaker
    // sign_m_810 熔断判断@Overridepublic boolean tryPass(Context context) {if (currentState.get() == State.CLOSED) {return true;    // 断路器关闭:直接通过}if (currentState.get() == State.OPEN) {// 断路器已打开:超过指定熔断时间,尝试半打开处理return retryTimeoutArrived() && fromOpenToHalfOpen(context);    // sign_m_811 | sign_m_812}return false;       // 断路器半打开:不通过}// sign_m_811 判断是否超过熔断时长protected boolean retryTimeoutArrived() {return TimeUtil.currentTimeMillis() >= nextRetryTimestamp;}// sign_m_812 尝试半打开处理protected boolean fromOpenToHalfOpen(Context context) {if (currentState.compareAndSet(State.OPEN, State.HALF_OPEN)) {notifyObservers(State.OPEN, State.HALF_OPEN, null);Entry entry = context.getCurEntry();entry.whenTerminate(new BiConsumer<Context, Entry>() {  // 添加 entry.exit() 回调@Overridepublic void accept(Context context, Entry entry) {  // 在 entry.exit() 被调用if (entry.getBlockError() != null) {                            // 尝试请求时出错currentState.compareAndSet(State.HALF_OPEN, State.OPEN);    // 重新打开notifyObservers(State.HALF_OPEN, State.OPEN, 1.0d);}}});return true;    // 只让一个线程 (且只进行一次) 处理}return false;       // 被其他线程抢占 (不通过)}
  • com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.ExceptionCircuitBreaker
    // sign_m_830 断路器计数与状态变更处理@Overridepublic void onRequestComplete(Context context) {Entry entry = context.getCurEntry();if (entry == null) {return;}Throwable error = entry.getError();SimpleErrorCounter counter = stat.currentWindow().value();if (error != null) {counter.getErrorCount().add(1); // 有异常,添加异常计数}counter.getTotalCount().add(1);     // 添加请求 (总) 计数handleStateChangeWhenThresholdExceeded(error);  // sign_m_831}// sign_m_831 状态变更处理private void handleStateChangeWhenThresholdExceeded(Throwable error) {... // 当前状态为打开,则返回if (currentState.get() == State.HALF_OPEN) {// 当前为半打开状态if (error == null) {fromHalfOpenToClose();              // 无异常,则关闭} else {fromHalfOpenToOpen(1.0d);           // 有异常,则继续打开}return;}List<SimpleErrorCounter> counters = stat.values();long errCount = 0;long totalCount = 0;for (SimpleErrorCounter counter : counters) {errCount += counter.errorCount.sum();totalCount += counter.totalCount.sum();}if (totalCount < minRequestAmount) {return; // 小于最小请求数,不处理}double curCount = errCount;if (strategy == DEGRADE_GRADE_EXCEPTION_RATIO) {curCount = errCount * 1.0d / totalCount;    // 使用比率计算}if (curCount > threshold) {transformToOpen(curCount);                  // 超过阈值,开启熔断 (并设置开始重试的时间戳)}}

总结

  • 权限 (内置) 2 种规则 (但只支持设置 1 种,黑白名单互斥)
  • 流控 (内置) 有 4 (也可说 5) 种规则 (QPS 或线程数、速率、慢热 QPS、慢热速率)
  • 熔断 (内置) 有 2 (也可说 3) 种规则 (异常数或比率、响应时长)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/3790.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2023-2024年度广东省职业院校学生技能大赛“ 网络系统管理”赛项竞赛规程(正式稿)

2023-2024年度广东省职业院校学生技能大赛“ 网络系统管理”赛项竞赛规程&#xff08;正式稿&#xff09; 信息安全管理与评估 网络系统管理 网络搭建与应用 云计算 软件测试 移动应用开发 任务书&#xff0c;赛题&#xff0c;解析等资料&#xff0c;知识点培训服务 添加博主wx…

Python 中的下划线变量

Python 中的下划线变量 _name: 这是一种约定俗成的方式&#xff0c;表示这是一个私有变量&#xff0c;意味着它应该在类的内部使用&#xff0c;而不应该直接从外部访问。但实际上&#xff0c;Python并没有严格的私有化机制&#xff0c;这只是一种约定&#xff0c;因为外部仍然可…

Nginx高级配置与重写规则详解:从入门到实践

摘要: 本文将深入探讨Nginx的高级配置与重写规则,通过实际案例讲解如何优化Nginx的配置,实现URL重写、负载均衡、缓存优化等功能。文章旨在帮助读者更好地理解和应用Nginx,提升网站性能和用户体验。 一、Nginx简介 Nginx是一款高性能的HTTP和反向代理服务器,也是IMAP/POP…

代码随想录训练营day43

第九章 动态规划 part05 1.LeetCode. 最后一块石头的重量 II 1.1题目链接&#xff1a;1049.最后一块石头的重量II 文章讲解&#xff1a;代码随想录 视频讲解&#xff1a;B站卡哥视频 1.2思路&#xff1a;本题其实就是尽量让石头分成重量相同的两堆&#xff0c;相撞之后剩下…

【嵌入式DIY实例】-RFID门禁系统

DIY RFID门禁系统 文章目录 DIY RFID门禁系统1、RFID RC522介绍2、硬件准备与接线2、代码实现RFID(Radio Frequency Identification)简称为射频识别。 在本文中,将演示如何使用这项技术来实现门安全系统。 1、RFID RC522介绍 RFID rc522是一种基于射频的模块,其感应范围接…

Java中的多线程编程与并发控制

Java中的多线程编程与并发控制 一、引言 随着计算机硬件技术的不断发展&#xff0c;多核处理器已经成为现代计算机系统的标配。在这样的背景下&#xff0c;多线程编程与并发控制成为了Java开发中不可或缺的一部分。多线程编程能够充分利用多核处理器的并行计算能力&#xff0…

嵌入式软件工程师要会画板子吗?

答案:要 作为嵌入式软件工程师,本来就经常和硬件打交道。调试各种协议也要看波形,用示波器什么的。软件玩多了也会腻,可以玩玩硬件,自己从 0 做出一个东西的感觉是不一样的。职业生涯那么长,多尝试一些总是好的,一个软硬件都懂的人,才能更好的带项目。 博主玩过很多板…

个人学习-前端相关(1):ECMAScript 6-变量、模板字符串、解构表达式

前言 由于最近公司有接到一些小程序或者app相关的内容&#xff0c;可能考虑要做一些样例供参考。虽然在20年短暂学习和使用过vue2&#xff0c;但由于时间久远且技术发展太快&#xff0c;需要对一些旧知识进行巩固&#xff0c;新的内容进行学习。 ECMAScript 6简介 ECMAScrip…

设置Linux开发板开机自启动QT程序的报错解决办法

设置Linux开发板开机自启动QT程序报错解决办法 设置开发板开机自启动QT 打开 /etc/init.d/rsC 文件&#xff0c;添加以下内容 cd / ./my_start_run.shmy_start_run.sh 是自己编写的自启动脚本&#xff0c;内容例如下&#xff1a;(也可以将这些直接写到 /etc/init.d/rsC 文件…

C#算法之归并排序

算法释义&#xff1a;归并排序是一种分治算法&#xff0c;它采用分而治之的策略&#xff0c;将待排序的数组分成更小的部分&#xff0c;递归地对这些部分进行排序&#xff0c;然后将排序好的部分合并起来。它是一种比较稳定的排序算法。排序过程总结如下&#xff1a; 1、分解&a…

请编写一个函数void fun(char *tt,int pp[]),统计在tt字符串中“a”到“z”26个字母各自出现的次数,并依次放在pp所指数组中。

本文收录于专栏:算法之翼 https://blog.csdn.net/weixin_52908342/category_10943144.html 订阅后本专栏全部文章可见。 本文含有题目的题干、解题思路、解题思路、解题代码、代码解析。本文分别包含C语言、C++、Java、Python四种语言的解法和详细的解析。 题干 请编写一个函…

Python基础11-字符串处理

连接字符串 要将字符串连接在一起&#xff1a; greeting "Hello" name "Alice" message greeting ", " name "!" print(message)使用 str.format 进行字符串格式化 要将值插入字符串模板&#xff1a; message "{}, {}…

快来把wordpress图片放到免费的cloudflare R2上去

网站一直是用的wordpress&#xff0c;做了几年uploads下的图片已经大到了几十GB单位&#xff0c;VPS也一路升级&#xff0c;吃不消啊吃不消。 一直在想有什么办法能解决下这一大堆图片&#xff0c;网站逛了一圈发现cloudflare的R2有10G免费空间&#xff0c;而且本来就用的他家的…

Day17.一刷数据结构算法(C语言版) 654最大二叉树;617合并二叉树;700二叉搜索树中的搜索;98验证二叉搜索树

又是破防的一天...... 一.654最大二叉树 又是构造二叉树&#xff0c;昨天大家刚刚做完 中序后序确定二叉树&#xff0c;今天做这个 应该会容易一些&#xff0c; 先看视频&#xff0c;好好体会一下 为什么构造二叉树都是 前序遍历 题目链接&#xff1a;最大二叉树 文章讲解&…

Django框架之原始web框架优化

一、web应用框架简介及手撸web框架 软件开发架构详细&#xff1a;网络编程之网络架构及其趋势 - Xiao0101 - 博客园 (cnblogs.com) 软件开发架构分为两种&#xff1a; c/s架构&#xff1a;客户端软件(client)—服务端软件(server)b/s架构 &#xff1a;浏览器(Browser)------…

电子负载仪的远端控制

前言 最近研究了电子负载仪的远端控制&#xff08;区别于前面板控制&#xff09;&#xff0c;主要是用于程序控制&#xff0c;避免繁琐复杂的人工控制&#xff0c;举了南京嘉拓和艾维泰科的例子。 有纰漏请指出&#xff0c;转载请说明。 学习交流请发邮件 1280253714qq.com …

今日arXiv最热大模型论文:大模型也来看球,还能判断是否犯规

在足球世界&#xff0c;裁判的哨声可谓“千金难买”&#xff0c;因为它能直接决定俱乐部的钱包是鼓是瘪。但球场变化莫测&#xff0c;非常考验裁判的水平。 2022年卡塔尔世界杯上&#xff0c;半自动越位识别技术&#xff08;SAOT&#xff09;闪亮登场&#xff0c;通过12台摄像…

APP推广防作弊策略

背景 为保障下载、使用数量&#xff0c;APP厂商通常会投巨资给第三方渠道&#xff0c;用以推广宣传吸纳更多用户。在这一过程中&#xff0c;见有利可图的内鬼、羊毛党、刷量中介等则会趁势而入&#xff0c;成为刷量推广数据造假的中坚。据一本财经报道称&#xff0c;过千万流量…

企业微信私有化部署对接oauth2.0

1.添加依赖&#xff1a;JustAuth <dependency><groupId>me.zhyd.oauth</groupId><artifactId>JustAuth</artifactId><version>1.16.6</version> </dependency> 2.添加 ElephantAuthSource.java package com.elephant.devop…

玩转nginx的配置文件3

1. limit_req_zone配置限流 limit_req_zone $binary_remote_addr zonemylimit:10m rate10r/s;upstream myweb {server 10.0.105.196:80 weight1 max_fails1 fail_timeout1;}server {listen 80;server_name localhost;location /login {limit_req zonemylimit;proxy_pass http:…