设计模式学习笔记 - 项目实战三:设计实现一个支持自定义规则的灰度发布组件(实现)
概述
上两篇文章,我们讲解了灰度组件的需求和设计的思路。不管之前讲的限流、幂等框架,还是现在讲的灰度组件,功能性需求都不复杂,相反,非功能性需求是开发的重点。
本章,按照上篇文章的灰度组件的设计思路,讲解如何进行编码实现。不过,本章对实现的讲解,前前面两个实战项目有所不同。在前两个项目中,我们都是手把手从最基础的 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小型证券术语解释及翻译系统的设计与开发
摘 要 在系统设计上,综合各种翻译类型网站优缺点,设计出具有任何使用者都可添加术语信息的且只有管理员能够实现术语修改及删除等独特方式的术语查看管理系统。此方式能够使术语量快速增大,并且便于使用者及管理员操作,满足相互…...
硬件知识积累 音频插座的了解,看音频插座的原理图来了解音频插座的引脚。
1. 音频接口 音频插座是一种用于连接音频信号线路的电子元件,常见于音频设备(如音响、耳机、话筒等)中。它的主要作用是将电子信号转化为声音信号,以满足人们对于音乐、电影、游戏等方面的需求。 根据插头形状的不同,音…...
error LNK2001: 无法解析的外部符号 “__declspec(dllimport) public: __cdecl ......
运行程序时,报如上图所示错误,其中一条是: ReflectionProbe.obj : error LNK2001: 无法解析的外部符号 "__declspec(dllimport) public: __cdecl osg::Object::Object(bool)" (__imp_??0ObjectosgQEAA_NZ) 报这个错误一般是因为…...
邮箱Webhook API发送邮件的性能怎么优化?
邮箱Webhook API发送邮件的步骤?如何用邮箱API发信? 随着业务规模的扩大,如何高效地通过邮箱Webhook API发送邮件,成为了许多企业面临的关键问题。下面,AokSend将探讨一些优化邮箱Webhook API发送邮件性能的方法。 邮…...
并发编程实现
一、并行编程 1、Parallel 类 Parallel类是System.Threading.Tasks命名空间中的一个重要类,它提供数据并行和任务并行的高级抽象。 For和ForEach Parallel类下的For和ForEach对应着普通的循环和遍历(普通的for和foreach),但执行时会尝试在多个线程上…...
基于EBAZ4205矿板的图像处理:12图像二值化(阈值可调)
基于EBAZ4205矿板的图像处理:12图像二值化(阈值可调) 我的项目是基于EBAZ4205矿板的阈值可调的图像阈值二值化处理,可以通过按键调整二值化的阈值,key1为阈值加1,key4为阈值减1,key2为阈值加10,key5为阈值…...
人大金仓数据库报com.kingbase8.util.KSQLException: 致命错误: 用户 “SYSTEM“ Password 认证失败
com.kingbase8.util.KSQLException: 致命错误: 用户 “SYSTEM” Password 认证失败 解决办法: 问题在于用户权限只不足,相关配置文件在一般在 /data/sys hba.conf,修改IPV4 local connections选项中的改为trust。...
文件加密软件哪个好?文件加密软件排行榜前十名(好用软件推荐)
文件加密软件哪个好?这是许多个人和企业用户在面临数据保护需求时所关心的问题。随着数字化时代的推进,数据安全问题日益凸显,文件加密软件成为了保护数据安全的重要手段。本文将为您介绍当前市场上排名前十的文件加密软件,帮助您…...
Netty的第一个简单Demo实现
目录 说明需求ClientServer写法总结 实现运行 说明 Netty 的一个练习,使用 Netty 连通 服务端 和 客户端,进行基本的通信。 需求 Client 连接服务端成功后,打印连接成功给服务端发送消息HelloServer Server 客户端连接成功后࿰…...
K8S 哲学 - 服务发现 services
apiVersion: v1 kind: Service metadata:name: deploy-servicelabels:app: deploy-service spec: ports: - port: 80targetPort: 80name: deploy-service-podselector: app: deploy-podtype: NodePort service 的 endPoint (ep) 主机端口分配方式 两…...
Springboot工程创建
目录 一、步骤 二、遇到的问题及解决方案 一、步骤 打开idea,点击文件 ->新建 ->新模块 选择Spring Initializr,并设置相关信息。其中组为域名,如果没有公司,可以默认com.example。点击下一步 蓝色方框部分需要去掉,软件包…...
网站规划与建设周正刚/互联网营销顾问是做什么的
- 基础: 下载安装 声明变量的方法 数据的三种基础类型:bool,数字,string 数据类型:数组和切片 数据类型:Maps 条件判断以及循环 函数 包管理 package 指针 结构体- 初步进阶&am…...
做网站维护师傅带要学多久/链接提交入口
php连接访问Oracle是用过oci函数,以下是整理的文档1.安装Apache和php包 yum install -y httpd php* 2.下载Oracle组件oracle-instantclient-basic-10.2.0.4-1.i386.rpmoracle-instantclient-sqlplus-10.2.0.4-1.i386.rpmoracle-instantclient-devel-10.2.0.4-1.i38…...
怎么做网站上面的那种卡通图片/电商培训机构排名前十
看到很多人提问非科班该如何学习编程,其实科班也基本靠自学。有句话叫“师傅领进门修行靠个人”,再厉害的老师 能教你的东西都是很有限的,真正的修行还是要靠自己。我本科是学数学的,虽然研究生是计算机专业,但研究生往…...
学编程可以建设网站吗/百度新闻搜索
1.找到页面元素obj 2.设置obj.style A. 直接写css属性,如:obj.style.height/width/color B. 改大写(驼峰),如:obj.style.fontSize/marginLeft C. 浮动需要注意:obj.style[cssFloat in obj.style?cssFloat…...
网站开发培训费多少/东莞百度快速优化排名
gzip/gunzip压缩 只能压缩文件不能压缩目录 不保留原来的文件 gzip文件 (压缩文件,只能将文件压缩为*.gz文件)gunzip文件.gz (功能描述:解压缩文件命令) zip/unzip压缩 zipzip twinkle.zip requirements.…...
28网站制作/推广策略都有哪些
需求:一开始返回顶部图标是隐藏的,当滚动到指定位置的时候,小图标就会显示出来,,点击图标,会缓动的返回顶部 技术点:window.scrollTo(x,y),浏览器显示区域跳转到指定的坐标 <!DOC…...