设计模式学习笔记 - 项目实战三:设计实现一个支持自定义规则的灰度发布组件(实现)

概述

上两篇文章,我们讲解了灰度组件的需求和设计的思路。不管之前讲的限流、幂等框架,还是现在讲的灰度组件,功能性需求都不复杂,相反,非功能性需求是开发的重点。

本章,按照上篇文章的灰度组件的设计思路,讲解如何进行编码实现。不过,本章对实现的讲解,前前面两个实战项目有所不同。在前两个项目中,我们都是手把手从最基础的 MVP 代码将其。然后讲解如何 review 代码发现问题、重构代码解决问题,最终得到一份高质量的代码。考虑到前面两个项目的学习锻炼,你应该对开发套路、思考路径已经很熟悉了,所以,本章就不从最基础的将其了,而是重点讲解实现思路。


灰度组件功能需求整理

针对上连篇文章的开发需求和设计思路,我们还是按照老套路,从中剥离出 V1 版本要实现的内容。为了方便讲解,我把灰度组件的开发需求和设计思路,重新整理罗列了一下,放到了这里。

1.灰度规则的格式和存储方式

支持不同格式(JSON、YAML、XML 等)、不同存储方式(本地配置我呢间、Redis、Zookeeper、或者自研配置中心)的灰度规则配置方式。实际上,这一点和之前的限流框架中限流规则的格式和存储方式完全一致,代码实现也是相同的。所以这个就不重复了,你可以回头去看《限流框架(实现)》章节内容。

2.灰度组件的语法格式

我们支持三种灰度规则语法格式:具体值(比如 893)、区间值(比如 1020-1120)、比例值(比如 %30)。此外,对于更加复杂的灰度规则,比如只对 30 天内购买过某某商品并且退款次数少于 10 次的用户进行更改,我们通过编程的方式来实现。

3.灰度规则的内存组织方式

类似于限流框架中的限流规则,我们需要把灰度规则组织成支持快速查询的数据结构能够快速判定某个灰度对象(darkTarget,比如用户 ID),是否落在灰度规则设定的范围内。

4.灰度规则热更新

修改了灰度规则之后,我们不希望重新部署和重启系统,新的灰度规则就能生效,所以,我们需要支持灰度规则热更新。

在 V1 版本中,对于第一点灰度规则的合适和存储方式,我们只支持 YAML 格式本地文件的配置存储方式。对于剩下的三点,我们都要进行实现。考虑到 V1 版本要实现的内容比较多,我们分两步来实现代码,第一步先讲大的流程、框架搭建好,第二步再进一步添加、丰富、优化功能。

实现灰度组件基本功能

// 目录结构
com.example.darklaunch
  --DarkLaunch (框架的最顶层入口类)
  --DarkFeature (每个feature的灰度规则)
  --DarkRule (灰度规则)
  --DarkRuleConfig (用来映射配置到内存中)

// Demo示例
public class Demo {
    public static void main(String[] args) {
        DarkLaunch darkLaunch = new DarkLaunch();
        DarkFeature darkFeature = darkLaunch.getDarkFeature("call_newapi_getUserById");
        System.out.println(darkFeature.enabled());
        System.out.println(darkFeature.dark(893));
    }
}

// 灰度规则配置(dark-rule.yaml)放在classpath路径下
features:
- key: call_newapi_getUserById
  enabled: true
  rule: {893,342,1020-1120,%30}
- key: call_newapi_registerUser
  enabled: true
  rule: {1391198723, %10}
- key: newlog_loan
  enabled: true
  rule: {0-1000}

从 Demo 代码中,可以看出,对于业务系统来说,灰度组件的两个直接使用的类是 DarkLaunchDarkFeature

我们先来看 DarkLaunch 类。这个类是灰度组件最顶层入口类。它用来组装其他类对象,串联整个操作流程,提供外部调用的接口。

DarkLaunch 类先读取灰度规则配置文件,映射为内存中的 Java 对象(DarkRuleConfig),然后再将这个中间结构,构建成一个支持快速查询的数据结构(DarkRule)。此外,它还负责定期更新灰度规则,也就是前面提到的灰度热更新。

为了避免更新规则和查询规则并发冲突,在更新灰度时,我们并非直接操作老的 DarkRule,而是先创建一个新的 DarkRule,然后等新的 DarkRule 都构建好之后,再 “瞬间” 赋值给老的 DarkRule

public class DarkLaunch {
    private static final Logger log = LoggerFactory.getLogger(DarkLaunch.class);
    private static final int DEFAULT_RULE_UPDATE_TIME_INTERVAL = 60; // in seconds
    private DarkRule rule;
    private ScheduledExecutorService executor;

    public DarkLaunch(int ruleUpdateTimeInterval) {
        loadRule();
        this.executor = Executors.newSingleThreadScheduledExecutor();
        this.executor.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                loadRule();
            }
        }, ruleUpdateTimeInterval, ruleUpdateTimeInterval, TimeUnit.SECONDS);
    }

    public DarkLaunch() {
        this(DEFAULT_RULE_UPDATE_TIME_INTERVAL);
    }

    private void loadRule() {
        // 将灰度规则配置文件dark-rule.yaml中的内容读取到DarkRuleConfig中
        InputStream in = null;
        DarkRuleConfig ruleConfig = null;
        try {
            in = this.getClass().getResourceAsStream("dark-rule.yaml");
            if (in != null) {
                Yaml yaml = new Yaml();
                ruleConfig = yaml.loadAs(in, DarkRuleConfig.class);
            }
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    log.error("close file error:", e);
                }
            }
        }
        if (ruleConfig == null) {
            throw new RuntimeException("can not load dark rule.");
        }
        // 更新规则并非直接在this.rule上进行
        // 而是通过创建一个新的 DarkRule,然后赋值给this.rule
        // 来避免更新规则和规则查询的并发冲突问题
        DarkRule darkRule = new DarkRule(ruleConfig);
        this.rule = darkRule;
    }

    public DarkFeature getDarkFeature(String featureKey) {
        return this.rule.getDarkFeature(featureKey);
    }
}

**再来看下 DarkRuleConfig 类。**这个类功能非常简单,只是用来将灰度规则映射到内存中。

public class DarkRuleConfig {
    private List<DarkFeatureConfig> features;

    public List<DarkFeatureConfig> getFeatures() {
        return features;
    }

    public void setFeatures(List<DarkFeatureConfig> features) {
        this.features = features;
    }

    public static class DarkFeatureConfig {
        private String key;
        private boolean enable;
        private String rule;

        public String getKey() {
            return key;
        }

        public void setKey(String key) {
            this.key = key;
        }

        public boolean isEnable() {
            return enable;
        }

        public void setEnable(boolean enable) {
            this.enable = enable;
        }

        public String getRule() {
            return rule;
        }

        public void setRule(String rule) {
            this.rule = rule;
        }
    }
}

从代码中,我们可以看出来,DarkRuleConfig 类嵌套了一个内部类 DarkFeatureConfig。这两个类跟配置文件中的两层嵌套结构完全对应 。

# 对应DarkRuleConfig
features:
- key: call_newapi_getUserById   # 对应DarkFeatureConfig
  enabled: true
  rule: {893,342,1020-1120,%30}
- key: call_newapi_registerUser  # 对应DarkFeatureConfig
  enabled: true
  rule: {1391198723, %10}
- key: newlog_loan               # 对应DarkFeatureConfig
  enabled: true
  rule: {0-1000}

再来看下 DarkRuleDarkRule 包含所有要灰度的业务功能的灰度规则。它用来支持根据业务功能标识(feature key),快速查询灰度规则(DarkFeture)。

public class DarkRule {
    private Map<String, DarkFeature> darkFeatures = new HashMap<>();

    public DarkRule(DarkRuleConfig darkRuleConfig) {
        List<DarkRuleConfig.DarkFeatureConfig> darkRuleConfigFeatures = darkRuleConfig.getFeatures();
        for (int i = 0; i < darkRuleConfigFeatures.size(); i++) {
            DarkRuleConfig.DarkFeatureConfig darkFeatureConfig = darkRuleConfigFeatures.get(i);
            darkFeatures.put(darkFeatureConfig.getKey(), new DarkFeature(darkFeatureConfig));
        }
    }

    public DarkFeature getDarkFeature(String featureKey) {
        return this.darkFeatures.get(featureKey);
    }
}

最后,我们来看下 DarkFeature 类。 DarkFeature 类标识每个要灰度的业务功能的灰度规则。DarkFeature 将配置文件中的灰度规则,解析成一定的结构(比如 RangeSet),方便快速判定某个灰度对象是否落在灰度规则范围内。

public class DarkFeature {
    private String key;
    private boolean enabled;
    private int percentage;
    private RangeSet<Long> rangeSet = TreeRangeSet.create();

    public DarkFeature(DarkRuleConfig.DarkFeatureConfig darkFeatureConfig) {
        this.key = darkFeatureConfig.getKey();
        this.enabled = darkFeatureConfig.isEnable();
        String darkRule = darkFeatureConfig.getRule().trim();
        parseDarkRule(darkRule);
    }

    @VisibleForTesting
    protected void parseDarkRule(String darkRule) {
        if (!darkRule.startsWith("{") && !darkRule.endsWith("}")) {
            throw new RuntimeException("failed to parse dark rule: " + darkRule);
        }
        String[] rules = darkRule.substring(1, darkRule.length() - 1).split(",");
        this.rangeSet.clear();
        this.percentage = 0;
        for (String rule : rules) {
            rule = rule.trim();
            if (StringUtils.isEmpty(rule)) {
                continue;
            }
            if (rule.startsWith("%")) {
                this.percentage = Integer.parseInt(rule.substring(1));
            } else if (rule.contains("-")) {
                String[] parts = rule.split("-");
                if (parts.length != 2) {
                    throw new RuntimeException("failed to parse dark rule: " + darkRule);
                }
                long start = Long.parseLong(parts[0]);
                long end = Long.parseLong(parts[1]);
                if (start > end) {
                    throw new RuntimeException("failed to parse dark rule: " + darkRule);
                }
                this.rangeSet.add(Range.closed(start, end));
            } else {
                long val = Long.parseLong(rule);
                this.rangeSet.add(Range.closed(val, val));
            }
        }
    }

    public boolean isEnabled() {
        return enabled;
    }

    public boolean dark(long darkTarget) {
        boolean selected = this.rangeSet.contains(darkTarget);
        if (selected) {
            return true;
        }
        long reminder = darkTarget % 100;
        if (reminder > 0 && reminder < this.percentage) {
            return true;
        }
        return false;
    }

    public boolean dark(String darkTarget) {
        long target = Long.parseLong(darkTarget);
        return dark(target);
    }
}

添加、优化灰度组件

在第一步中,我们完成了灰度组件的基本功能。在第二步中,我们再实现基于编程的灰度规则配置方式,用来支持更复杂、更加灵活的灰度规则。

我们需要对第一步实现的代码,进行一些改造。改造之后的目录结构如下所示。其中,DarkFeatureDarkRuleConfig 的代码基本不变, 新增了 IDarkFeature 接口,DarkLaunchDarkRule 的代码有所改动,用来支持编程实现灰度规则。

// 第一步的代码目录结构
com.example.darklaunch
  --DarkLaunch (框架的最顶层入口类)
  --DarkFeature (每个feature的灰度规则)
  --DarkRule (灰度规则)
  --DarkRuleConfig (用来映射配置到内存中)

// 第二步的代码目录结构
com.example.darklaunch
  --DarkLaunch (框架的最顶层入口类,代码有所改动)
  --IDarkFeature (抽象接口)
  --DarkFeature (实现IDarkFeature接口,基于配置文件的灰度规则,代码不变)
  --DarkRule (灰度规则,代码有改动)
  --DarkRuleConfig (用来映射配置到内存中,代码不变)

我们先来看下 IDarkFeature 接口,它用来抽象从配置文件中得到的灰度规则,以及编程实现的灰度规则。具体代码如下所示:

public interface IDarkFeature {
    boolean isEnabled();
    boolean dark(long darkTarget);
    boolean dark(String darkTarget);
}

基于这个抽象接口,业务系统可以自己编程实现复杂的灰度规则,然后添加到 DarkRule 中。为了避免配置文件中的灰度规则热更新时,覆盖编程实现的灰度规则,在 DarkRule 中,我们对从配置文件中加载的灰度规则和编程实现的灰度规则分开存储。按照这个设计思路,我们对 DarkRule 类进行重构。重构之后的代码如下所示:

public class DarkRule {
    // 从配置文件中加载的灰度规则
    private Map<String, IDarkFeature> darkFeatures = new HashMap<>();
    // 编程实现的灰度规则
    private Map<String, IDarkFeature> programmedDarkFeatures = new ConcurrentHashMap<>();

    public void addProgrammingDarkFeature(String featureKey, IDarkFeature darkFeature) {
        programmedDarkFeatures.put(featureKey, darkFeature);
    }

    public void setDarkRuleFeatures(Map<String, IDarkFeature> newDarkFeatures) {
        this.darkFeatures = newDarkFeatures;
    }

    public IDarkFeature getDarkFeature(String featureKey) {
        IDarkFeature darkFeature = programmedDarkFeatures.get(featureKey);
        if (darkFeature != null) {
            return darkFeature;
        }
        return this.darkFeatures.get(featureKey);
    }
}

因为 DarkRule 代码有所修改,对应地, DarkLaunch 的代码也需要做少许改动,主要有一处修改和一处新增代码,具体如下所示。

public class DarkLaunch {
    private static final Logger log = LoggerFactory.getLogger(DarkLaunch.class);
    private static final int DEFAULT_RULE_UPDATE_TIME_INTERVAL = 60; // in seconds
    private DarkRule rule;
    private ScheduledExecutorService executor;

    public DarkLaunch(int ruleUpdateTimeInterval) {
        loadRule();
        this.executor = Executors.newSingleThreadScheduledExecutor();
        this.executor.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                loadRule();
            }
        }, ruleUpdateTimeInterval, ruleUpdateTimeInterval, TimeUnit.SECONDS);
    }

    public DarkLaunch() {
        this(DEFAULT_RULE_UPDATE_TIME_INTERVAL);
    }

    private void loadRule() {
        // 将灰度规则配置文件dark-rule.yaml中的内容读取到DarkRuleConfig中
        InputStream in = null;
        DarkRuleConfig ruleConfig = null;
        try {
            in = this.getClass().getResourceAsStream("dark-rule.yaml");
            if (in != null) {
                Yaml yaml = new Yaml();
                ruleConfig = yaml.loadAs(in, DarkRuleConfig.class);
            }
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    log.error("close file error:", e);
                }
            }
        }
        if (ruleConfig == null) {
            throw new RuntimeException("can not load dark rule.");
        }
        // 修改:单独更新从配置文件中得到的灰度规则
        Map<String, IDarkFeature> darkFeatures = new HashMap<>();
        List<DarkRuleConfig.DarkFeatureConfig> darkFeatureConfigs = ruleConfig.getFeatures();
        for (DarkRuleConfig.DarkFeatureConfig darkFeatureConfig : darkFeatureConfigs) {
            darkFeatures.put(darkFeatureConfig.getKey(), new DarkFeature(darkFeatureConfig));
        }
        this.rule.setDarkRuleFeatures(darkFeatures);
    }

    // 新增,添加编程实现的灰度规则的接口
    public void addProgrammingDarkFeature(String featureKey, IDarkFeature darkFeature) {
        this.rule.addProgrammingDarkFeature(featureKey, darkFeature);
    }

    public IDarkFeature getDarkFeature(String featureKey) {
        return this.rule.getDarkFeature(featureKey);
    }
}

灰度组件的代码实现完了。我们在通过一个 Demo 看下,目前实现的灰度组件该如何使用。结合着 Demo,再去理解上面的代码,会更容易些。

// 灰度规则配置(dark-rule.yaml), 放到classpath路径下
features:
- key: call_newapi_getUserById
  enabled: true
  rule: {893,342,1020-1120,%30}
- key: call_newapi_registerUser
  enabled: true
  rule: {1391198723, %10}
- key: newlog_loan
  enabled: true
  rule: {0-1000}

// 编程实现的灰度规则
public class UserPromotionDarkRule implements IDarkFeature {
    @Override
    public boolean isEnabled() {
        return true;
    }

    @Override
    public boolean dark(long darkTarget) {
        // 灰度规则自己想怎么写就怎么写
        return false;
    }

    @Override
    public boolean dark(String darkTarget) {
        // 灰度规则自己想怎么写就怎么写
        return false;
    }
}

// demo
public class Demo {
    public static void main(String[] args) {
        DarkLaunch darkLaunch = new DarkLaunch(); // 默认加载classpath下的dark-rule.yaml文件中的灰度规则
        darkLaunch.addProgrammingDarkFeature("user_promotion", new UserPromotionDarkRule());
        IDarkFeature darkFeature = darkLaunch.getDarkFeature("user_promotion");
        System.out.println(darkFeature.isEnabled());
        System.out.println(darkFeature.dark(833));
    }
}

总结

到本章为止,项目实战环节就彻底结束了。在这一部分中,我们通过限流、幂等、灰度这三个实战项目,带你从需求分析、系统设计、代码实现三个环节,学习了如何进行功能性、非功能性需求分析,如何通过合理的设计,完成功能性需求,满足非功能性需求,以及如何编写高质量的代码实现。

实际上,项目本身的分析、设计、实现并不重要,不必对细节过于纠结。希望通过这三个例子,分享思考思路、开发套路,让你借鉴并举一反三地应用到你的项目开发中。这才是最有价值的,才是你学习的重点。

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

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

相关文章

linux 搭建知识库文档系统 mm-wiki

目录 一、前言 二、常用的知识库文档工具 2.1 PingCode 2.2 语雀 2.3 Tettra 2.4 Zoho Wiki 2.5 Helpjuice 2.6 SlimWiki 2.7 Document360 2.8 MM-Wiki 2.9 其他工具补充 三、MM-Wiki 介绍 3.1 什么是MM-Wiki 3.2 MM-Wiki 特点 四、搭建MM-Wiki前置准备 4.1 前置…

带环链表及例题

环形链表&#xff0c;链表中的尾节点指向链表中的某个节点导致形成循环的链表。 通过图可以这样表示。 我们一般采用快慢指针的方式解决带环链表的题目&#xff0c;下面直接上例题 环形链表 力扣链接&#xff1a; . - 力扣&#xff08;LeetCode&#xff09; 让我们判断一个…

渗透测试之sql注入绕过技巧

在sql注入中&#xff0c;通常会将某些关键的字符过滤掉&#xff0c;以此来达到预防sql注入的目的。这时我们就可以通过某些技巧来绕过。 绕过技巧1&#xff1a; 这个是在某个比赛中出现的&#xff0c;当时并没有多少人成功绕过。 如下&#xff1a; 如下图&#xff1a;在php中…

Django前后端项目部署

Django前后端分离项目部署 本文采用阿里云服务器&#xff0c;centos7.9操作系统 本文默认服务器已安装nginx,mysql并且可以正常运行Django vue uwsgi nginx注意&#xff1a;先部署后端&#xff0c;使用postman测试请求没有问题后在修改vue中的axios文件中baseURL&#xff0…

PLC通过Modbus转Profinet网关连接变频器与电机通讯

Modbus转Profinet网关&#xff08;XD-MDPN100&#xff09;是一种能够实现Modbus协议和Profinet协议之间转换的设备。Modbus转Profinet网关可提供单个或多个RS485接口&#xff0c;PLC作为控制中枢&#xff0c;变频器作为控制电机转速&#xff0c;通过Modbus转Profinet网关&#…

美国站群服务器的定义、功能以及在网站运营中的应用

美国站群服务器的定义、功能以及在网站运营中的应用 在当今互联网的蓬勃发展中&#xff0c;站群服务器已成为网站运营和SEO优化中不可或缺的重要工具之一。尤其是美国站群服务器&#xff0c;在全球范围内备受关注。本文将深入探讨美国站群服务器的定义、功能以及在网站运营中的…

【城市】应届生第一次打工需要知道的常识(薪资结构,社保,五险二金,个税,专项扣除)

【城市】应届生第一次打工需要知道的常识&#xff08;薪资结构&#xff0c;社保&#xff0c;五险二金&#xff0c;个税&#xff0c;专项扣除&#xff09; 文章目录 1、什么是应届生 & 如何界定应届生2、社保&#xff0c;五险一金&#xff0c;五险二金3、薪资结构&#xff0…

运存与内存?内存与存储? 傻傻分不清

主页: 元存储博客 图片来源: Blackblaze 文章目录 名词为何“内存”的含义混乱内存和存储含义内存和存储作用RAM 与 存储差异速度和性能容量和尺寸易失性和持久性常见问题:总结名词 内存: Memory,如内存条 存储器: Storage, 包括硬盘等 为何“内存”的含义混乱 <

class089 贪心经典题目专题1【左程云算法】

class089 贪心经典题目专题1【左程云算法】 前言版权推荐class089 贪心经典题目专题1code1 179. 最大数code2 1029. 两地调度code3 1553. 吃掉 N 个橘子的最少天数code4 253. 会议室IIcode5 630. 课程表 IIIcode6 1167. 连接棒材的最低费用(leetcode测试)code6 P1090 连接棒材的…

C进阶-数据的存储

文章目录 1. 数据类型介绍类型的基本归类 2. 整型在内存中的存储:原码,反码,补码2.1. 原码,反码,补码 2.2. 大小端介绍大端字节序存储小端字节序存储例:设计程序判断是大端还是小端? 2.3. 练习练习1练习2练习3练习4 3. 浮点型在内存中的存储 1. 数据类型介绍 数据类型数据类型…

YARN详解

YARN 简介 YARN 是Yet Another Resource Negotiator的缩写。 YARN是第二代MapReduce,即MRv2,是在第一代MapReduce基础上演变而来的,主要是为了解决原始Hadoop扩展性较差,不支持多计算框架而提出的;通俗讲就是资源管理器. YARN核心思想: 将 MR1 中资源管理和作业调度两个功能分…

node.js 解析post请求 方法二

前提&#xff1a;以前面发的node.js解析post请求方法一为模板&#xff0c;具体见 http://t.csdnimg.cn/ABaIn 此文我们运用第二种方法&#xff1a;使用第三方模块formidable对post请求进行解析。 1》代码难点 *** 在Node.js中使用formidable模块来解析POST请求主要涉及到处理…

读(用知云翻译)gaitedge论文

文章目录 前言摘要一、介绍二、相关工作2.1步态识别2.2端到端学习 三、跨域问题四、我们的框架4.1步法合成4.2步态对准模块 五、实验5.1设置5.2性能比较5.3消融实验5.4可视化 六、结论 前言 本篇博客仅为个人学习&#xff0c;全文均为知云翻译&#xff0c;如有翻译不当&#x…

Android中的屏幕刷新机制(动画视频形象说明机制)

一&#xff0c;刷新率和帧率&#xff0c;60hz和60fps的区别 在Android系统中&#xff0c;刷新率和帧率是两个不同的概念&#xff0c;它们各自在显示过程中扮演着不同的角色。以下是对它们的详细解释&#xff1a; 刷新率&#xff0c;单位是Hz&#xff0c;是指屏幕在一秒内刷新…

Python来计算 1,2,3,4 能组成多少个不相同且不重复的三位数?

我们今天的例子是 有 1&#xff0c;2&#xff0c;3&#xff0c;4 四个数字&#xff0c;它们能组成多省个互不相同且无重复的三位数&#xff1f;都分别是多少&#xff1f; 话不多说&#xff0c;我们先上代码 num 0 # 我们写了三个for循环&#xff0c;表示生成的三位数 for i…

ROS 2边学边练(41)-- 使用基于tf2_ros::MessageFilter带标记(位姿、时间...)的数据类型

前言 此篇将介绍如何利用tf2来使用传感器数据&#xff08;如单声道和立体声摄像机以及雷达&#xff09;。 假设我们创建了一只海龟叫turtle3&#xff0c;它的里程计不大好用&#xff0c;为了监视turtle3的活动轨迹&#xff0c;有台头顶摄像机被安装到该海龟的背上&#xff08;负…

arp欺骗详解

目录 arp攻击原理 arp协议简介 arp攻击原理 arp实验 实验环境 实验步骤 1、使用ipconfig命令查看靶机&#xff08;window10&#xff09;的IP地址为下一步攻击做好准备&#xff0c;这一步是模拟你获取对方IP的过程 2、使用ifconfig查询查看攻击者&#xff08;kali&#x…

【华为 ICT HCIA eNSP 习题汇总】——题目集19

1、&#xff08;多选&#xff09;以下选项中&#xff0c;FTP 常用文件传输类型有&#xff08;&#xff09;。 A、ASCII 码类型 B、二进制类型 C、EBCDIC 类型 D、本地类型 考点&#xff1a;应用层 解析&#xff1a;&#xff08;AB&#xff09; 文件传输协议&#xff08;FTP&…

Win10无法合并分区?尝试以下2种解决方法吧

若Win10无法合并分区&#xff0c;导致C盘无法扩容&#xff0c;该如何解决呢&#xff1f;本文将介绍如何利用磁盘管理工具和傲梅分区助手轻松解决这个问题&#xff01; 为什么要合并硬盘分区&#xff1f; 合并硬盘分区是指将同一硬盘上的两个分区合并成一个&#xff0c;或者将…

K8S controller编写之Informer的原理+使用[drift]

概念 核心思想&#xff08;重点&#xff09;watch-list 机制 Watch 通过 HTTP 协议与 Kubernetes API Server 建立长连接&#xff0c;接收 Kubernetes API Server 发来的资源变更事件。Watch 操作的实现机制使用 HTTP 协议的分块传输编码——当 client-go 调用 Kubernetes API…
最新文章