Ali-Sentinel-入口控制

归档

  • GitHub: Ali-Sentinel-入口控制

测试


// 在 demo-spring-webmvc 里新建测试类

public class TestMain {

    public static void main(String[] args) throws InterruptedException {

        initFlowRules(); // 初始化规则

        while (true) {

            // 1.5.0 版本开始可以直接利用 try-with-resources 特性

            try (Entry entry = SphU.entry("HelloWorld")) { // 进入资源 sign_demo_010

                // 被保护的逻辑

                System.out.println("hello world");

                // entry.exit(); // 在自动 close() 方法里会调用 exit(),不需要再手动调用

            } catch (BlockException ex) {

                // 处理被流控的逻辑

                System.err.println("blocked!");

                break;

            }

            Thread.sleep(10);

        }

    }

    // 初始化规则

    private static void initFlowRules() {

        List<FlowRule> rules = new ArrayList<>();

        FlowRule rule = new FlowRule();

        rule.setResource("HelloWorld");

        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);

        rule.setCount(20);                  // Set limit QPS to 20.

        rules.add(rule);

        FlowRuleManager.loadRules(rules);   // 设置规则 sign_demo_020

    }

}

查看异常栈

  • BlockExceptionFlowException 类的 fillInStackTrace() 方法注释掉才会打印完整调用栈记录

com.alibaba.csp.sentinel.slots.block.flow.FlowException

    at com.alibaba.csp.sentinel.slots.block.flow.FlowRuleChecker.checkFlow(FlowRuleChecker.java:53) // 流控校验

    at com.alibaba.csp.sentinel.slots.block.flow.FlowSlot.checkFlow(FlowSlot.java:171)

    at com.alibaba.csp.sentinel.slots.block.flow.FlowSlot.entry(FlowSlot.java:164)

    at com.alibaba.csp.sentinel.slots.block.flow.FlowSlot.entry(FlowSlot.java:141)

    at com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot.transformEntry(AbstractLinkedProcessorSlot.java:40)

    at com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot.fireEntry(AbstractLinkedProcessorSlot.java:32)

    at com.alibaba.csp.sentinel.slots.system.SystemSlot.entry(SystemSlot.java:39)

    at com.alibaba.csp.sentinel.slots.system.SystemSlot.entry(SystemSlot.java:32)

    at com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot.transformEntry(AbstractLinkedProcessorSlot.java:40)

    at com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot.fireEntry(AbstractLinkedProcessorSlot.java:32)

    at com.alibaba.csp.sentinel.slots.block.authority.AuthoritySlot.entry(AuthoritySlot.java:42)

    at com.alibaba.csp.sentinel.slots.block.authority.AuthoritySlot.entry(AuthoritySlot.java:35)

    at com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot.transformEntry(AbstractLinkedProcessorSlot.java:40)

    at com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot.fireEntry(AbstractLinkedProcessorSlot.java:32)

    at com.alibaba.csp.sentinel.slots.statistic.StatisticSlot.entry(StatisticSlot.java:59)

    at com.alibaba.csp.sentinel.slots.statistic.StatisticSlot.entry(StatisticSlot.java:51)

    at com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot.transformEntry(AbstractLinkedProcessorSlot.java:40)

    at com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot.fireEntry(AbstractLinkedProcessorSlot.java:32)

    at com.alibaba.csp.sentinel.slots.logger.LogSlot.entry(LogSlot.java:38)

    at com.alibaba.csp.sentinel.slots.logger.LogSlot.entry(LogSlot.java:31)

    at com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot.transformEntry(AbstractLinkedProcessorSlot.java:40)

    at com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot.fireEntry(AbstractLinkedProcessorSlot.java:32)

    at com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot.entry(ClusterBuilderSlot.java:104)

    at com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot.entry(ClusterBuilderSlot.java:49)

    at com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot.transformEntry(AbstractLinkedProcessorSlot.java:40)

    at com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot.fireEntry(AbstractLinkedProcessorSlot.java:32)

    at com.alibaba.csp.sentinel.slots.nodeselector.NodeSelectorSlot.entry(NodeSelectorSlot.java:174)

    at com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot.transformEntry(AbstractLinkedProcessorSlot.java:40)

    at com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot.fireEntry(AbstractLinkedProcessorSlot.java:32)

    at com.alibaba.csp.sentinel.slotchain.DefaultProcessorSlotChain$1.entry(DefaultProcessorSlotChain.java:31)

    at com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot.transformEntry(AbstractLinkedProcessorSlot.java:40)

    at com.alibaba.csp.sentinel.slotchain.DefaultProcessorSlotChain.entry(DefaultProcessorSlotChain.java:75)

    at com.alibaba.csp.sentinel.CtSph.entryWithPriority(CtSph.java:149)

    at com.alibaba.csp.sentinel.CtSph.entry(CtSph.java:177)

    at com.alibaba.csp.sentinel.CtSph.entry(CtSph.java:316)

    at com.alibaba.csp.sentinel.SphU.entry(SphU.java:85)

    at com.alibaba.csp.sentinel.demo.spring.webmvc.test.TestMain.main(TestMain.java:22) // 在 demo-spring-webmvc 里建的测试类

原理

类结构

  • com.alibaba.csp.sentinel.slotchain.ProcessorSlot

/** sign_i_001 处理器接口 */

public interface ProcessorSlot<T> {

    /** sign_im_001 进入资源处理 */

    void entry(Context context, ResourceWrapper resourceWrapper, T param, int count, boolean prioritized,

               Object... args) throws Throwable;

}

  • com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot

/** sign_c_001 处理器(单向)链节点 */

public abstract class AbstractLinkedProcessorSlot<T> implements ProcessorSlot<T> { // 实现接口 sign_i_001

    private AbstractLinkedProcessorSlot<?> next = null; // 下一个节点

}

进入资源

  • 相当于获取资源,进行计数处理,被限制就会报错

  • com.alibaba.csp.sentinel.SphU


    private static final Object[] OBJECTS0 = new Object[0];

    // sign_demo_010 进入资源

    public static Entry entry(String name) throws BlockException {

        // sph 为 CtSph 实例

        return Env.sph.entry(name, EntryType.OUT, 1, OBJECTS0); // sign_m_010

    }

  • com.alibaba.csp.sentinel.CtSph

    // sign_m_010

    @Override

    public Entry entry(String name, EntryType type, int count, Object... args) throws BlockException {

        StringResourceWrapper resource = new StringResourceWrapper(name, type);

        return entry(resource, count, args); // sign_m_001

    }

    // sign_m_001

    public Entry entry(ResourceWrapper resourceWrapper, int count, Object... args) throws BlockException {

        return entryWithPriority(resourceWrapper, count, false, args); // sign_m_002

    }

    // sign_m_002

    private Entry entryWithPriority(ResourceWrapper resourceWrapper, int count, boolean prioritized, Object... args)

        throws BlockException {

        Context context = ContextUtil.getContext();

        ... // 省略其他处理

        ProcessorSlot<Object> chain = lookProcessChain(resourceWrapper); // sign_m_003

        ... // 省略 chain 为空处理

        Entry e = new CtEntry(resourceWrapper, chain, context); // 创建入口(相当于锁)

        try {

            /**

             * 链式处理进入资源逻辑 sign_im_001

             * 首节点处理 sign_m_020

             */

            chain.entry(context, resourceWrapper, null, count, prioritized, args);

        } ... // 省略 catch 处理

        return e;

    }

    // sign_m_003

    ProcessorSlot<Object> lookProcessChain(ResourceWrapper resourceWrapper) {

        ProcessorSlotChain chain = chainMap.get(resourceWrapper);

        if (chain == null) {

            synchronized (LOCK) {

                chain = chainMap.get(resourceWrapper);

                if (chain == null) {

                    ... // 省略超出限制的处理

                    /**

                     * 通过 SPI 获取处理链。

                     *   配置文件: META-INF/services/com.alibaba.csp.sentinel.slotchain.ProcessorSlot

                     *   链节点类可通过 @Spi 注解设置(顺排)顺序。

                     * 结构参考: sign_chain_001

                     */

                    chain = SlotChainProvider.newSlotChain();

                    Map<ResourceWrapper, ProcessorSlotChain> newMap = new HashMap<ResourceWrapper, ProcessorSlotChain>(

                        chainMap.size() + 1);

                    newMap.putAll(chainMap);

                    newMap.put(resourceWrapper, chain);

                    chainMap = newMap; // COW 更新缓存

                }

            }

        }

        return chain;

    }

处理链
  • sign_chain_001

// 链结构: cur -> next

DefaultProcessorSlotChain   // 默认节点     (用于组装链)

  -> first                  // 头节点       (匿名实现,只传递,无逻辑) sign_f_001

  -> NodeSelectorSlot       // 设置统计节点 (供下游统计)

  -> ClusterBuilderSlot     // 设置集群节点 (供下游统计)

  -> LogSlot                // 日志记录     (只记录异常日志)

  -> StatisticSlot          // 统计         (记录 QPS、线程数等,供下游(下次)流控)

  -> AuthoritySlot          // 权限管理     (黑名单、白名单处理)

  -> SystemSlot             // 系统流制     (整个应用管控)

  -> FlowSlot               // 单资源流控

  -> DegradeSlot            // 降级处理     (相当于熔断);尾节点 end

  • 链路处理具体介绍参考:链路控制
链处理进入
  • com.alibaba.csp.sentinel.slotchain.DefaultProcessorSlotChain

public class DefaultProcessorSlotChain extends ProcessorSlotChain {

    // sign_f_001

    AbstractLinkedProcessorSlot<?> first = new AbstractLinkedProcessorSlot<Object>() {

        @Override

        public void entry(Context context, ResourceWrapper resourceWrapper, Object t, int count, boolean prioritized, Object... args)

            throws Throwable {

            super.fireEntry(context, resourceWrapper, t, count, prioritized, args); // 只是传递给下一个节点 sign_m_022

        }

        @Override

        public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {

            super.fireExit(context, resourceWrapper, count, args);

        }

    };

    // sign_m_020 实现进入处理方法 sign_im_001

    @Override

    public void entry(Context context, ResourceWrapper resourceWrapper, Object t, int count, boolean prioritized, Object... args)

        throws Throwable {

        // 从头节点开始

        first.transformEntry(context, resourceWrapper, t, count, prioritized, args); // sign_m_021

    }

}

  • com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot

    // sign_m_021 传递处理进入

    void transformEntry(Context context, ResourceWrapper resourceWrapper, Object o, int count, boolean prioritized, Object... args)

        throws Throwable {

        T t = (T)o;

        entry(context, resourceWrapper, t, count, prioritized, args); // 各自节点应实现的进入处理方法 sign_im_001

    }

    // sign_m_022 发送进入

    @Override

    public void fireEntry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, boolean prioritized, Object... args)

        throws Throwable {

        if (next != null) {

            // 向下一个节点传递

            next.transformEntry(context, resourceWrapper, obj, count, prioritized, args); // sign_m_021

        }

    }

  • 链路处理具体介绍参考:链路控制

设置规则

  • 有点绕,用监听器做最终更改

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


public class FlowRuleManager {

    private static volatile Map<String, List<FlowRule>> flowRules = new HashMap<>();

    private static final FlowPropertyListener LISTENER = new FlowPropertyListener();

    private static SentinelProperty<List<FlowRule>> currentProperty = new DynamicSentinelProperty<List<FlowRule>>();

    static {

        currentProperty.addListener(LISTENER);  // 添加监听器

        startMetricTimerListener();             // 开启度量定时任务,默认 1s 一次

    }

    // sign_demo_020 设置规则

    public static void loadRules(List<FlowRule> rules) {

        currentProperty.updateValue(rules);     // 更新值 sign_m_201

    }

    // 内部监听器

    private static final class FlowPropertyListener implements PropertyListener<List<FlowRule>> {

        @Override

        public synchronized void configUpdate(List<FlowRule> value) {

            Map<String, List<FlowRule>> rules = FlowRuleUtil.buildFlowRuleMap(value);   // 构建规则集 Map  sign_m_210

            if (rules != null) {

                flowRules = rules; // 更改规则集 Map

            }

            RecordLog.info("[FlowRuleManager] Flow rules received: {}", rules);

        }

    }

}

  • com.alibaba.csp.sentinel.property.DynamicSentinelProperty

// sign_c_100 动态属性,可通过监听器扩展 (值更改的) 处理

public class DynamicSentinelProperty<T> implements SentinelProperty<T> {

    protected Set<PropertyListener<T>> listeners = new CopyOnWriteArraySet<>();

    private T value = null;

    // sign_m_201 更新值

    @Override

    public boolean updateValue(T newValue) {

        ... // 省略:新值与旧值相同时的返回处理

        value = newValue; // 改值

        for (PropertyListener<T> listener : listeners) {

            listener.configUpdate(newValue); // 通知监听器

        }

        return true;

    }

}

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

    // sign_f_201

    private static final Function<FlowRule, String> extractResource = new Function<FlowRule, String>() {

        @Override

        public String apply(FlowRule rule) {

            return rule.getResource(); // 使用资源名作 key

        }

    };

    // sign_m_210 构建规则集 Map

    public static Map<String, List<FlowRule>> buildFlowRuleMap(List<FlowRule> list) {

        return buildFlowRuleMap(list, null); // sign_m_211

    }

    // sign_m_211

    public static Map<String, List<FlowRule>> buildFlowRuleMap(List<FlowRule> list, Predicate<FlowRule> filter) {

        return buildFlowRuleMap(list, filter, true); // sign_m_212

    }

    // sign_m_212

    public static Map<String, List<FlowRule>> buildFlowRuleMap(List<FlowRule> list, Predicate<FlowRule> filter,

                                                               boolean shouldSort) {

        /**

         * extractResource 参考 sign_f_201

         */

        return buildFlowRuleMap(list, extractResource, filter, shouldSort); // sign_m_213

    }

    // sign_m_213

    public static <K> Map<K, List<FlowRule>> buildFlowRuleMap(List<FlowRule> list, Function<FlowRule, K> groupFunction,

                                                              Predicate<FlowRule> filter, boolean shouldSort) {

        Map<K, List<FlowRule>> newRuleMap = new ConcurrentHashMap<>();

        ... // 省略 list 为空处理

        Map<K, Set<FlowRule>> tmpMap = new ConcurrentHashMap<>();

        for (FlowRule rule : list) {

            ... // 省略对 rule 进行无效校验和过滤处理

            ... // 省略对 rule 的 limitApp 为空时填充默认值("default")的处理

            TrafficShapingController rater = generateRater(rule); // 创建流量控制器 sign_m_220

            rule.setRater(rater); // 设置规则的流量控制器

            K key = groupFunction.apply(rule);

            ... // 省略 key 为空的处理

            Set<FlowRule> flowRules = tmpMap.get(key);

            if (flowRules == null) {

                flowRules = new HashSet<>(); // 使用 Set 防止添加重复的规则

                tmpMap.put(key, flowRules);

            }

            flowRules.add(rule);

        }

        Comparator<FlowRule> comparator = new FlowRuleComparator();

        for (Entry<K, Set<FlowRule>> entries : tmpMap.entrySet()) {

            List<FlowRule> rules = new ArrayList<>(entries.getValue());

            if (shouldSort) {

                Collections.sort(rules, comparator); // 对规则进行排序

            }

            newRuleMap.put(entries.getKey(), rules);

        }

        return newRuleMap;

    }

    // sign_m_220 创建流量控制器

    private static TrafficShapingController generateRater(FlowRule rule) {

        if (rule.getGrade() == RuleConstant.FLOW_GRADE_QPS) {

            switch (rule.getControlBehavior()) {

                case RuleConstant.CONTROL_BEHAVIOR_WARM_UP:

                    return new WarmUpController(rule.getCount(), rule.getWarmUpPeriodSec(),

                            ColdFactorProperty.coldFactor);

                case RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER:

                    return new RateLimiterController(rule.getMaxQueueingTimeMs(), rule.getCount());

                case RuleConstant.CONTROL_BEHAVIOR_WARM_UP_RATE_LIMITER:

                    return new WarmUpRateLimiterController(rule.getCount(), rule.getWarmUpPeriodSec(),

                            rule.getMaxQueueingTimeMs(), ColdFactorProperty.coldFactor);

                case RuleConstant.CONTROL_BEHAVIOR_DEFAULT:

                default:

                    // Default mode or unknown mode: default traffic shaping controller (fast-reject).

            }

        }

        return new DefaultController(rule.getCount(), rule.getGrade()); // 默认返回此控制器

    }

总结

  • 对资源的监控是通过处理器链进行处理

  • 放不放行是通过流量控制器进行判断

  • 链路处理具体介绍参考:链路控制

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

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

相关文章

docker安装【zookeeper】【kafka】【provectuslabs/kafka-ui】记录

目录 1.安装zookeeper:3.9.2-jre-172.安装kafka:3.7.03.安装provectuslabs/kafka-ui &#xff08;选做&#xff09;新环境没有jdk&#xff0c;安装jdk-17.0.10备用 mkdir -p /export/{data,apps,logs,conf,downloads}cd /export/downloadscurl -OLk https://download.oracle.…

【VScode】VScode+如何从git上面拉取代码?

目录标题 1、打开VSCode。File>New Window。2、打开集成终端&#xff08;Terminal > New Terminal 或使用快捷键Ctrl \)。3、在终端中&#xff0c;使用Git命令克隆仓库。4、打开项目。 1、打开VSCode。File>New Window。 2、打开集成终端&#xff08;Terminal > …

基于HAL库的stm32中定时器的使用--定时器中断每隔一秒进行led灯的闪烁以及定时器生成PWM

一&#xff1a;什么是定时器 &#xff08;1&#xff09;stm32定时器&#xff0c;是存在于stm32单片机中的一个外设。stm32共有八个定时器&#xff0c;两个高级定时器&#xff08;TIM1、TIM8&#xff09;&#xff0c;四个通用定时器&#xff08;TIM2、TIM3、TIM4、TIM5&#xff…

Java中的ArrayList集合

特点&#xff1a; ArrayList中的一些方法&#xff1a; 1、add(Object element):向集合的末尾添加元素 add(int index,Object element):在列表的指定位置&#xff08;从0开始&#xff09;插入指定元素 2、size():返回列表的中的元素个数 3、get(int index):返回下标为index位置的…

基于昇腾AI 使用AscendCL实现垃圾分类和视频物体分类应用

现如今&#xff0c;人工智能迅猛发展&#xff0c;AI赋能产业发展的速度正在加快&#xff0c;“AI”的需求蜂拥而来&#xff0c;但AI应用快速落地的过程中仍存在很大的挑战&#xff1a;向下需要适配的硬件&#xff0c;向上需要完善的技术支持&#xff0c;两者缺一不可。 基于此&…

SQL中的锁

一、概述 介绍 锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中&#xff0c;除传统的计算资(CPU、RAM、I/0)的争用以外&#xff0c;数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题&#xff0c;锁冲…

02-JVM学习记录-运行时数据区

二、运行时数据区 每个JVM只有一个Runtime实例&#xff0c;只有一个运行时数据区。 虚拟机栈、堆、方法区最重要 方法区和堆与虚拟机的生命周期相同&#xff08;随虚拟机启动而创建&#xff0c;虚拟机退出而销毁&#xff09;&#xff0c;程序计数器、虚拟机栈、本地方法栈生命…

JavaScript云LIS系统概述 前端框架JQuery+EasyUI+Bootstrap医院云HIS系统源码 开箱即用

云LIS系统概述JavaScript前端框架JQueryEasyUIBootstrap医院云HIS系统源码 开箱即用 云LIS&#xff08;云实验室信息管理系统&#xff09;是一种结合了计算机网络化信息系统的技术&#xff0c;它无缝嵌入到云HIS&#xff08;医院信息系统&#xff09;中&#xff0c;用于连…

wps/word中字体安装教程

问题&#xff1a;下载的字体怎么导入wps/word wps或word中没有相应字体&#xff0c;怎么导入。其实方法很简单。 Step 1&#xff1a;下载字体 首先&#xff0c;在网上搜索自己喜欢的字体&#xff0c;然后下载到本地。字体的格式通常是.ttf 下面是我网上找的字体&#xff08…

Vue 3 路由机制详解与实践

一、路由的理解 路由是指导用户界面导航的一种机制。它通过映射 URL 到应用程序的不同视图组件来实现页面间的切换和导航。 二、路由基本切换效果 路由基本切换效果指的是当用户在应用程序中进行页面导航时&#xff0c;通过路由可以实现页面的切换&#xff0c;从而展示不同的…

[Flutter3] 记录Dio的简单封装(一)

文章目录 效果使用ResponseEntity类DioManager封装_onResponse / _onDioException 的设计Response的处理catch处理 效果 请求成功/失败/异常的日志输出效果 成功: 失败:500 失败:404 网络异常: 使用 举个使用的例子, 在调用 DioManager的时候, 直接通过返回值的状态, 来…

ESP32开发WebSocket报错TRANSPORT_WS: Sec-WebSocket-Accept not found

我的芯片是ESP32-S3&#xff0c;用ESP-IDF框架进行开发的时候&#xff0c;用官方的WebSocket的example创建了项目。然后把WebSocket连接uri替换为自己的服务器后&#xff0c;运行到esp_websocket_client_start开始连接后&#xff0c;直接报错&#xff1a; E (10615) TRANSPORT…

网络爬虫之爬虫原理

** 爬虫概述 Python网络爬虫是利用Python编程语言编写的程序&#xff0c;通过互联网爬取特定网站的信息&#xff0c;并将其保存到本地计算机或数据库中。 """ 批量爬取各城市房价走势涨幅top10和跌幅top10 """ ​ from lxml import etree impor…

AJAX——黑马头条-数据管理平台项目

1.项目介绍 功能&#xff1a; 登录和权限判断查看文章内容列表&#xff08;筛选&#xff0c;分页&#xff09;编辑文章&#xff08;数据回显&#xff09;删除文章发布文章&#xff08;图片上传&#xff0c;富文本编辑器&#xff09; 2.项目准备 技术&#xff1a; 基于Bootst…

【韩国】UE5的MetaHuman确实可以导入Blender进行编辑。

UE5的MetaHuman确实可以导入Blender进行编辑。根据网络上的信息&#xff0c;你可以将MetaHuman模型导出为FBX文件&#xff0c;然后在Blender中进行修改。修改完成后&#xff0c;你可以将其重新导入到Unreal Engine 5中4。请注意&#xff0c;当你在Blender中编辑模型时&#xff…

第12章 最佳的UI体验——Material Design实战

第12章 最佳的UI体验——Material Design实战 其实长久以来&#xff0c;大多数人都认为Android系统的UI并不算美观&#xff0c;至少没有iOS系统的美观。以至于很多IT公司在进行应用界面设计的时候&#xff0c;为了保证双平台的统一性&#xff0c;强制要求Android端的界面风格必…

使用Shell终端访问Linux

一、实验目的 1、熟悉Linux文件系统访问命令&#xff1b; 2、熟悉常用 Linux Shell的命令&#xff1b; 3、熟悉在Linux文件系统中vi编辑器的使用&#xff1b; 4、进一步熟悉虚拟机网络连接模式与参数配置&#xff01; 二、实验内容 1、使用root帐号登陆到Linux的X-windows…

artifactory配置docker本地存储库

​一、概述 本地 Docker 存储库是我们部署和托管内部 Docker 镜像的位置。实际上&#xff0c;它是一个 Docker 注册表&#xff0c;能够托管的 Docker 镜像的集合。通过本地存储库&#xff0c;你可以保存、加载、共享和管理自己的 Docker 镜像&#xff0c;而无需依赖于外部的镜像…

API提取IP

API代理作为IP代理的一项重要业务&#xff0c;在绕开地域网络限制&#xff0c;提高作业效率等方面提供强大的技术支持。它能够帮助用户快速实现软件与软件间的交流&#xff0c;无障碍连通不同应用程序逻辑开发的系统应用。API代理用途范围广泛&#xff0c;如使用API提取代理IP、…

AcWing 1264. 动态求连续区间和 ,详细讲解线段树与树状数组(Python,篇一)

本篇博客主要介绍一下什么是线段树与树状数组&#xff0c;它们的原理与结构是怎样&#xff0c;并通过实际题型来讲解&#xff0c;篇一主要讲解线段树&#xff0c;下一篇博客讲解树状数组。 线段树与树状数组的区别和特点&#xff1a; 它们的时间复杂度都是O(nlogn) 存储方式…