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);

    @Override
    public 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) {   // DCL
                node = 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_011
        fireEntry(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_011
    public Context setCurNode(Node node) {
        this.curEntry.setCurNode(node); //  sign_m_030
        return this;
    }
  • com.alibaba.csp.sentinel.node.DefaultNode
    // sign_m_020 添加子节点
    public void addChild(Node node) {
        ... // 省略 node 空判断
        if (!childList.contains(node)) {
            synchronized (this) {       // DCL
                if (!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_030
    public void setCurNode(Node node) {
        this.curNode = node;
    }

    // sign_m_031
    public Node getCurNode() {
        return curNode;
    }
  • com.alibaba.csp.sentinel.CtEntry
    // sign_m_040
    @Override
    public 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;

    @Override
    public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
                      boolean prioritized, Object... args) throws Throwable 
    {
        if (clusterNode == null) {
            synchronized (lock) {   // DCL
                if (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> {

    @Override
    public 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
    @Override
    public void addPassRequest(int count) {
        super.addPassRequest(count);            // ref: sign_m_410
        this.clusterNode.addPassRequest(count); // ref: sign_m_410
    }
  • com.alibaba.csp.sentinel.node.StatisticNode
    • Metric.addPass 方法参考:节点与度量-addpass
    // sign_m_410
    @Override
    public void addPassRequest(int count) {
        rollingCounterInSecond.addPass(count);  // 参考: 节点与度量-addpass
        rollingCounterInMinute.addPass(count);
    }

AuthoritySlot

  • 黑、白名单权限校验

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

@Spi(order = Constants.ORDER_AUTHORITY_SLOT)
public class AuthoritySlot extends AbstractLinkedProcessorSlot<DefaultNode> {
    @Override
    public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, boolean prioritized, Object... args)
        throws Throwable 
    {
        checkBlackWhiteAuthority(resourceWrapper, context);                     // 先校验  ref: sign_m_501
        fireEntry(context, resourceWrapper, node, count, prioritized, args);    // 再传给下游
    }

    // sign_m_501
    void 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 #loadAuthorityConf
        for (AuthorityRule rule : rules) {
            if (!AuthorityRuleChecker.passCheck(rule, context)) {           // 依次校验  ref: sign_m_510
                throw 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> {
    @Override
    public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
                      boolean prioritized, Object... args) throws Throwable 
    {
        SystemRuleManager.checkSystem(resourceWrapper, count);                  // 先校验  ref: sign_m_610
        fireEntry(context, resourceWrapper, node, count, prioritized, args);    // 再传给下游
    }
}
  • com.alibaba.csp.sentinel.slots.system.SystemRuleManager
    // sign_m_610
    public static void checkSystem(ResourceWrapper resourceWrapper, int count) throws BlockException {
        ... // 省略 resourceWrapper 为 null 返回
        ... // 省略 checkSystemStatus 为 false 返回
        ... // 省略 资源类型 不为 入站 返回

        // total qps
        double currentQps = Constants.ENTRY_NODE.passQps(); // 在 StatisticSlot 里记录,ref: sign_m_400
        if (currentQps + count > qps) {
            throw new SystemBlockException(resourceWrapper.getName(), "qps");
        }

        ... // 省略 线程数 校验
        ... // 省略 平均RT 校验

        // load. BBR algorithm.
        if (highestSystemLoadIsSet && getCurrentSystemAvgLoad() > highestSystemLoad) {
            if (!checkBbr(currentThread)) { // ref: sign_m_611
                throw new SystemBlockException(resourceWrapper.getName(), "load");
            }
        }

        ... // 省略 CPU 校验 (CPU 数据每秒读取一次)
    }

    // sign_m_611
    private 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;

    @Override
    public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
                      boolean prioritized, Object... args) throws Throwable 
    {
        checkFlow(resourceWrapper, context, node, count, prioritized);          // 先校验 sign_m_710
        fireEntry(context, resourceWrapper, node, count, prioritized, args);    // 再传给下游
    }

    // sign_m_710
    void 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>>() {
        @Override
        public Collection<FlowRule> apply(String resource) {
            Map<String, List<FlowRule>> flowRules = FlowRuleManager.getFlowRuleMap();   // ref: sign_demo_020
            return flowRules.get(resource);
        }
    };
}
  • com.alibaba.csp.sentinel.slots.block.flow.FlowRuleChecker
    // sign_m_720
    public 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_721
                    throw new FlowException(rule.getLimitApp(), rule);
                }
            }
        }
    }

    // sign_m_721
    public boolean canPassCheck(FlowRule rule, Context context, DefaultNode node, int acquireCount,
                                                    boolean prioritized) 
    {
        ... // 省略 rule.limitApp 为 null 返回 true
        if (rule.isClusterMode()) {
            // ref: 集群流控-流控原理-sign_m_411
            return passClusterCheck(rule, context, node, acquireCount, prioritized);
        }
        return passLocalCheck(rule, context, node, acquireCount, prioritized);  // sign_m_722
    }

    // sign_m_722
    private 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 判断是否可以通过
    @Override
    public boolean canPass(Node node, int acquireCount, boolean prioritized) {
        int curCount = avgUsedTokens(node);     // sign_m_732
        if (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> {
    @Override
    public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
                      boolean prioritized, Object... args) throws Throwable 
    {
        performChecking(context, resourceWrapper);                              // 先校验 sign_m_801
        fireEntry(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 熔断判断
    @Override
    public 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() 回调
                @Override
                public 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 断路器计数与状态变更处理
    @Override
    public 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.mfbz.cn/a/576577.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python脚本实现PC端大麦网自动购票(Selenium自动化测试工具)

文章目录 Selenium 简介Selenium webdriver 文档chromedriver&#xff08;谷歌浏览器驱动&#xff09;chromedriver 下载配置环境变量 大麦网购票脚本网页 dom 元素 启用远程调试&#xff08;操作已打开的窗口&#xff09; Selenium 简介 Selenium 是一个用于自动化测试的工具…

目标检测与追踪AI算法模型及边缘计算智能分析网关V4的算法应用

目标检测与追踪是计算机视觉领域中的一个重要任务&#xff0c;主要用于识别图像或视频中的目标&#xff0c;并跟踪它们的运动轨迹。针对这一任务&#xff0c;有许多先进的AI算法模型&#xff0c;例如&#xff1a; YOLO&#xff08;You Only Look Once&#xff09;&#xff1a;…

linux 设备树-of_address_to_resource

实例分析-reg 属性解析(基于ranges属性) /{#address-cells <0x01>;#size-cells <0x01>;soc {compatible "simple-bus";#address-cells <0x01>;#size-cells <0x01>;ranges <0x7e000000 0x3f000000 0x1000000 0x40000000 0x40000000…

怎么把图片转换为二维码?3个步骤轻松制作图片二维码

图片的二维码是怎么做成的呢&#xff1f;现在很多场景下的二维码&#xff0c;用手机扫码可以展现出对应的图片信息。通过这种方式来传递图片对于用户体验与很好的效果&#xff0c;而且也方便制作者通过这种方式来快速完成图片信息的传递&#xff0c;与传统方式相比成本更低&…

客服专用的宝藏快捷回复软件

提起客服&#xff0c;很多人会觉得现在智能机器人的自动回复功能就可以将其替代。我始终不是这么认为的。人工客服始终能为一家店铺乃至一个企业起着非常关键重要的作用。今天就来给大家推荐一个客服专用的宝藏软件——客服宝聊天助手。感兴趣的话&#xff0c;可以发给你的客服…

面向对象开发技术(第三周)

回顾 上一堂课主要学习了面向对象编程与非面向对象编程&#xff08;面向功能、过程编程&#xff09;&#xff0c;本节课就重点来看看面向对象编程中的一个具体思想——抽象 面向对象编程的特性&#xff1a;1、封装性 2、继承性 3、多态性 封装&#xff1a;意味着提供服务接口…

异常风云:解码 Java 异常机制

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一个人虽可以走的更快&#xff0c;但一群人可以走的更远。 我是一名后…

【论文阅读】《Octopus v2: On-device language model for super agent》,端侧大模型的应用案例

今年LLM的发展趋势之一&#xff0c;就是端侧LLM快速发展&#xff0c;超级APP入口之争异常激烈。不过&#xff0c;端侧LLM如何应用&#xff0c;不知道细节就很难理解。正好&#xff0c;《Octopus v2: On-device language model for super agent》这篇文章可以解惑。 对比部署在…

JTAG访问xilinx FPGA的IDCODE

之前调试过xilinx的XVC&#xff08;Xilinx virtual cable&#xff09;&#xff0c;突然看到有人搞wifi-JTAG&#xff08;感兴趣可以参考https://github.com/kholia/xvc-esp8266&#xff09;&#xff0c;也挺有趣的。就突然想了解一下JTAG是如何运作的&#xff0c;例如器件识别&…

普通话水平测试用朗读作品60篇-(练习版)

普通话考试题型有读单音节字词、读多音节字词、朗读作品和命题说话。 具体分值如下&#xff1a; 1、读单音节字词100个&#xff0c;占10分&#xff1b;目的考查应试人普通话声母、韵母和声调的发音。 2、读双音节词语50个&#xff0c;占20分&#xff1b;目的是除了考查应试人声…

骨传导耳机怎么选?精心挑选热销排行前五的骨传导耳机推荐!

近几年&#xff0c;骨传导耳机作为新型蓝牙耳机款式&#xff0c;已经得到大家有效认可&#xff0c;可以说已经适用于日常中的各种场景中&#xff0c;比如运动场景&#xff0c;凭借舒适的佩戴体验和保护运动安全的特点深受到运动爱好者的欢迎&#xff0c;作为一个经验丰富的数码…

Linux网络—DNS域名解析服务

目录 一、BIND域名服务基础 1、DNS系统的作用及类型 DNS系统的作用 DNS系统类型 DNS域名解析工作原理&#xff1a; DNS域名解析查询方式&#xff1a; 2、BIND服务 二、使用BIND构建域名服务器 1、构建主、从域名服务器 1&#xff09;主服务器配置&#xff1a; 2&…

Windows主机入侵检测与防御内核技术深入解析

第2章 模块防御的设计思想 2.1 执行与模块执行 本章内容为介绍模块执行防御。在此我将先介绍“执行”分类&#xff0c;以及“模块执行”在“执行”中的位置和重要性。 2.1.1 初次执行 恶意代码&#xff08;或者行为&#xff09;要在被攻击的机器上执行起来&#xff0c;看起…

C语言----单链表的实现

前面向大家介绍了顺序表以及它的实现&#xff0c;今天我们再来向大家介绍链表中的单链表。 1.链表的概念和结构 1.1 链表的概念 链表是一种在物理结构上非连续&#xff0c;非顺序的一种存储结构。链表中的数据的逻辑结构是由链表中的指针链接起来的。 1.2 链表的结构 链表…

茴香豆:搭建你的RAG智能助理-笔记三

本次课程由书生浦语社区贡献者【北辰】老师讲解【茴香豆&#xff1a;搭建你的 RAG 智能助理】课程 课程视频&#xff1a;https://www.bilibili.com/video/BV1QA4m1F7t4/ 课程文档&#xff1a;Tutorial/huixiangdou/readme.md at camp2 InternLM/Tutorial GitHub 该课程&…

江苏开放大学2024年春《会计基础 050266》第二次任务:第二次过程性考核参考答案

电大搜题 多的用不完的题库&#xff0c;支持文字、图片搜题&#xff0c;包含国家开放大学、广东开放大学、超星等等多个平台题库&#xff0c;考试作业必备神器。 公众号 答案&#xff1a;更多答案&#xff0c;请关注【电大搜题】微信公众号 答案&#xff1a;更多答案&#…

记账本React案例(Redux管理状态)

文章目录 整体架构流程 环境搭建 创建项目 技术细节 一、别名路径配置 1.路径解析配置&#xff08;webpack&#xff09; &#xff0c;将/解析为src/ 2.路径联想配置&#xff08;vsCode&#xff09;&#xff0c;使用vscode编辑器时&#xff0c;自动联想出来src文件夹下的…

【java数据结构-优先级队列向下调整Topk问题,堆的常用的接口详解】

&#x1f308;个人主页&#xff1a;努力学编程’ ⛅个人推荐&#xff1a;基于java提供的ArrayList实现的扑克牌游戏 |C贪吃蛇详解 ⚡学好数据结构&#xff0c;刷题刻不容缓&#xff1a;点击一起刷题 &#x1f319;心灵鸡汤&#xff1a;总有人要赢&#xff0c;为什么不能是我呢 …

SCI一区级 | Matlab实现BES-CNN-GRU-Mutilhead-Attention多变量时间序列预测

SCI一区级 | Matlab实现BES-CNN-GRU-Mutilhead-Attention秃鹰算法优化卷积门控循环单元融合多头注意力机制多变量时间序列预测 目录 SCI一区级 | Matlab实现BES-CNN-GRU-Mutilhead-Attention秃鹰算法优化卷积门控循环单元融合多头注意力机制多变量时间序列预测预测效果基本介绍…

【C++杂货铺】多态

目录 &#x1f308;前言&#x1f308; &#x1f4c1;多态的概念 &#x1f4c1; 多态的定义及实现 &#x1f4c2; 多态的构成条件 &#x1f4c2; 虚函数 &#x1f4c2; 虚函数重写 &#x1f4c2; C11 override 和 final &#x1f4c2; 重载&#xff0c;覆盖&#xff08;重写…