设计模式学习笔记 - 项目实战三:设计实现一个支持自定义规则的灰度发布组件(实现)
概述
上两篇文章,我们讲解了灰度组件的需求和设计的思路。不管之前讲的限流、幂等框架,还是现在讲的灰度组件,功能性需求都不复杂,相反,非功能性需求是开发的重点。
本章,按照上篇文章的灰度组件的设计思路,讲解如何进行编码实现。不过,本章对实现的讲解,前前面两个实战项目有所不同。在前两个项目中,我们都是手把手从最基础的 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_getUserByIdenabled: truerule: {893,342,1020-1120,%30}
- key: call_newapi_registerUserenabled: truerule: {1391198723, %10}
- key: newlog_loanenabled: truerule: {0-1000}
从 Demo 代码中,可以看出,对于业务系统来说,灰度组件的两个直接使用的类是 DarkLaunch
和 DarkFeature
。
我们先来看 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 secondsprivate DarkRule rule;private ScheduledExecutorService executor;public DarkLaunch(int ruleUpdateTimeInterval) {loadRule();this.executor = Executors.newSingleThreadScheduledExecutor();this.executor.scheduleAtFixedRate(new Runnable() {@Overridepublic 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 # 对应DarkFeatureConfigenabled: truerule: {893,342,1020-1120,%30}
- key: call_newapi_registerUser # 对应DarkFeatureConfigenabled: truerule: {1391198723, %10}
- key: newlog_loan # 对应DarkFeatureConfigenabled: truerule: {0-1000}
再来看下 DarkRule。DarkRule
包含所有要灰度的业务功能的灰度规则。它用来支持根据业务功能标识(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);}@VisibleForTestingprotected 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);}
}
添加、优化灰度组件
在第一步中,我们完成了灰度组件的基本功能。在第二步中,我们再实现基于编程的灰度规则配置方式,用来支持更复杂、更加灵活的灰度规则。
我们需要对第一步实现的代码,进行一些改造。改造之后的目录结构如下所示。其中,DarkFeature
、DarkRuleConfig
的代码基本不变, 新增了 IDarkFeature
接口,DarkLaunch
、DarkRule
的代码有所改动,用来支持编程实现灰度规则。
// 第一步的代码目录结构
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 secondsprivate DarkRule rule;private ScheduledExecutorService executor;public DarkLaunch(int ruleUpdateTimeInterval) {loadRule();this.executor = Executors.newSingleThreadScheduledExecutor();this.executor.scheduleAtFixedRate(new Runnable() {@Overridepublic 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_getUserByIdenabled: truerule: {893,342,1020-1120,%30}
- key: call_newapi_registerUserenabled: truerule: {1391198723, %10}
- key: newlog_loanenabled: truerule: {0-1000}// 编程实现的灰度规则
public class UserPromotionDarkRule implements IDarkFeature {@Overridepublic boolean isEnabled() {return true;}@Overridepublic boolean dark(long darkTarget) {// 灰度规则自己想怎么写就怎么写return false;}@Overridepublic 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));}
}
总结
到本章为止,项目实战环节就彻底结束了。在这一部分中,我们通过限流、幂等、灰度这三个实战项目,带你从需求分析、系统设计、代码实现三个环节,学习了如何进行功能性、非功能性需求分析,如何通过合理的设计,完成功能性需求,满足非功能性需求,以及如何编写高质量的代码实现。
实际上,项目本身的分析、设计、实现并不重要,不必对细节过于纠结。希望通过这三个例子,分享思考思路、开发套路,让你借鉴并举一反三地应用到你的项目开发中。这才是最有价值的,才是你学习的重点。
相关文章:
设计模式学习笔记 - 项目实战三:设计实现一个支持自定义规则的灰度发布组件(实现)
概述 上两篇文章,我们讲解了灰度组件的需求和设计的思路。不管之前讲的限流、幂等框架,还是现在讲的灰度组件,功能性需求都不复杂,相反,非功能性需求是开发的重点。 本章,按照上篇文章的灰度组件的设计思…...

BJFUOJ-C++程序设计-实验2-类与对象
A 评分程序 答案: #include<iostream> #include<cstring>using namespace std;class Score{ private:string name;//记录学生姓名double s[4];//存储4次成绩,s[0]和s[1]存储2次随堂考试,s[2]存储期中考试,s[3]存储期…...
数据库语法复习
总结: DDL(数据定义语言) CREATE DATABASE:创建一个新的数据库。DROP DATABASE:删除一个数据库。CREATE TABLE:创建一个新的表。DROP TABLE:删除一个表。ALTER TABLE:修改表的结构&a…...

Tomcat、MySQL、Redis最大支持说明
文章目录 一、Tomcat二、MySQL三、Redis1、最大连接数2、TPS、QPS3、key和value最大支持 一、Tomcat 查看SpringBoot内置Tomcat的源码,如下: 主要就是看抽象类AbstractEndpoint,可以看到默认的核心线程数10,最大线程数200 通过…...
MATLAB数值计算工具箱介绍
MATLAB是一个强大的数学计算平台,它提供了广泛的数值计算工具箱,这些工具箱覆盖了从基础的线性代数到复杂的数值分析和优化问题。以下是MATLAB中一些关键工具箱的详细介绍: 1. 线性代数工具箱(Linear Algebra Toolbox)…...
2023 广东省大学生程序设计竞赛(部分题解)
目录 A - Programming Contest B - Base Station Construction C - Trading D - New Houses E - New but Nostalgic Problem I - Path Planning K - Peg Solitaire A - Programming Contest 签到题:直接模拟 直接按照题目意思模拟即可,为了好去…...

ROS2学习——Docker环境下安装于使用(1)
目录 一、简要 二、ROS2和ROS1区别 三、环境搭建与安装 (2)拉取ubuntu22.04镜像 (2)安装ROS2 1. 基本设置 2.设置源 3.安装ROS2功能包 4.测试 四、相关指令学习 1.小海龟测试 2.ros2 node等指令 3.rqt 一、简要 随着R…...
数据仓库之Hologres
官方文档 简介 Hologres是阿里云推出的一种云原生的实时分析型数据仓库。它是基于开源项目Apache Hudi(Hadoop Upserts Deletes and Incrementals)进行扩展和优化的。Hologres提供了高性Hologres是阿里云推出的一种云原生的实时分析型数据仓库。它是基…...

MacOS搭建docker本地私有镜像库
相关环境 macOS: bigsur 11.7.8 docker desktop: 4.22.0 docker engine: 24.0.5 准备工作 本机已经安装好docker desktop,未安装的自行参考其他教程。如果不能翻墙,可以修改本地的镜像地址,可在docker desktop 设置中的docker engine中修…...

Unity Material(材质)、Texture(纹理)、Shader(着色器)简介
文章目录 一、概念二、Rendering Mode三、Main Maps三、参考文章 一、概念 Material(材质):物体的“色彩”、“纹理”、“光滑度”、“透明度”、“反射率”、“折射率”、“发光度”等,材质的本质是shader的实例(载体)Texture(贴图):附件到…...

《视觉十四讲》例程运行记录(1)—— 课本源码下载和3rdparty文件夹是空的解决办法
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、第二版十四讲课本源码下载1. 安装git工具 二、Pangolin下载和安装1. 源码下载2. Pangolin的安装(1) 安装依赖项(2) 源码编译安装(2) 测试是否安装成功 二、…...

VLM与基础分割模型的联合使用
最近做的项目里有涉及大模型,里面有一部分的功能是: 将图片输入VLM(视觉语言模型,我使用的是llava),询问图中最显著的物体,将其给出的答案作为基础分割模型(我使用的是Grounded-SAM)的text prom…...
JS数组去重的方法
目录 1、includes 2、indexOf 3、Set结合Array.from 4、filter 5、reduce 6、使用双重for循环 介绍一下数组常用的去重复方法 以以下数组为例子来介绍,一维的数字类型数组: const arr [1, 2, 2, 2, 3, 1, 6, 4, 4, 6, 5, 7] 1、includes funct…...

Go实战训练之Web Server 与路由树
Server & 路由树 Server Web 核心 对于一个 Web 框架,至少要提供三个抽象: Server:代表服务器的抽象Context:表示上下文的抽象路由树 Server 从特性上来说,至少要提供三部分功能: 生命周期控制&…...
C#中接口设计相关原则
在C#中,接口(Interface)是一种引用类型,它定义了一个契约,指定了一个类必须实现的成员(属性、方法、事件、索引器)。接口不提供这些成员的实现,只指定成员必须按照特定的方式被实现。…...

Pytorch学习笔记——卷积操作
一、认识卷积操作 卷积操作是一种数学运算,它涉及两个函数:输入函数(通常是图像)和卷积核(也称为滤波器或特征检测器)。卷积核在输入函数上滑动,将核中的每个元素与其覆盖的输入函数区域中的对应…...

探索鸿蒙开发:鸿蒙系统如何引领嵌入式技术革新
嵌入式技术已经成为现代社会不可或缺的一部分。而在这个领域,华为凭借其自主研发的鸿蒙操作系统,正悄然引领着一场技术革新的浪潮。本文将探讨鸿蒙开发的特点、优势以及其对嵌入式技术发展的深远影响。 鸿蒙操作系统的特点 鸿蒙,作为华为推…...

chrome extension插件替换网络请求中的useragent
感觉Chrome商店中的插件不能很好的实现自己想要的效果,那么就来自己动手吧。 本文以百度为例: 一般来说网页请求如下: 当前使用的useragent是User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safar…...
PHP基础【介绍,注释,更改编码,赋值,数据类型】
源码 <?php //单行注释 /* 多行注释 *///通过header()函数发送http头的请求信息用来指定页面的字符集编码 header("Content-type:text/html;Charsetutf-8"); //告诉浏览器,当前页面的内容类型是HTML,并且页面内容使用的是UTF-8编码。//ph…...

ASP.NET小型证券术语解释及翻译系统的设计与开发
摘 要 在系统设计上,综合各种翻译类型网站优缺点,设计出具有任何使用者都可添加术语信息的且只有管理员能够实现术语修改及删除等独特方式的术语查看管理系统。此方式能够使术语量快速增大,并且便于使用者及管理员操作,满足相互…...

《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》
在注意力分散、内容高度同质化的时代,情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现,消费者对内容的“有感”程度,正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中࿰…...

React19源码系列之 事件插件系统
事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...

屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...
数据链路层的主要功能是什么
数据链路层(OSI模型第2层)的核心功能是在相邻网络节点(如交换机、主机)间提供可靠的数据帧传输服务,主要职责包括: 🔑 核心功能详解: 帧封装与解封装 封装: 将网络层下发…...
Swagger和OpenApi的前世今生
Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”
2025年#高考 将在近日拉开帷幕,#AI 监考一度冲上热搜。当AI深度融入高考,#时间同步 不再是辅助功能,而是决定AI监考系统成败的“生命线”。 AI亮相2025高考,40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕,江西、…...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...
Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?
在大数据处理领域,Hive 作为 Hadoop 生态中重要的数据仓库工具,其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式,很多开发者常常陷入选择困境。本文将从底…...
Go语言多线程问题
打印零与奇偶数(leetcode 1116) 方法1:使用互斥锁和条件变量 package mainimport ("fmt""sync" )type ZeroEvenOdd struct {n intzeroMutex sync.MutexevenMutex sync.MutexoddMutex sync.Mutexcurrent int…...
MySQL 索引底层结构揭秘:B-Tree 与 B+Tree 的区别与应用
文章目录 一、背景知识:什么是 B-Tree 和 BTree? B-Tree(平衡多路查找树) BTree(B-Tree 的变种) 二、结构对比:一张图看懂 三、为什么 MySQL InnoDB 选择 BTree? 1. 范围查询更快 2…...