设计模式学习笔记 - 项目实战三:设计实现一个支持自定义规则的灰度发布组件(实现)
概述
上两篇文章,我们讲解了灰度组件的需求和设计的思路。不管之前讲的限流、幂等框架,还是现在讲的灰度组件,功能性需求都不复杂,相反,非功能性需求是开发的重点。
本章,按照上篇文章的灰度组件的设计思路,讲解如何进行编码实现。不过,本章对实现的讲解,前前面两个实战项目有所不同。在前两个项目中,我们都是手把手从最基础的 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小型证券术语解释及翻译系统的设计与开发
摘 要 在系统设计上,综合各种翻译类型网站优缺点,设计出具有任何使用者都可添加术语信息的且只有管理员能够实现术语修改及删除等独特方式的术语查看管理系统。此方式能够使术语量快速增大,并且便于使用者及管理员操作,满足相互…...

【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...
Spring Boot 实现流式响应(兼容 2.7.x)
在实际开发中,我们可能会遇到一些流式数据处理的场景,比如接收来自上游接口的 Server-Sent Events(SSE) 或 流式 JSON 内容,并将其原样中转给前端页面或客户端。这种情况下,传统的 RestTemplate 缓存机制会…...

.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...

UDP(Echoserver)
网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...
在四层代理中还原真实客户端ngx_stream_realip_module
一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡(如 HAProxy、AWS NLB、阿里 SLB)发起上游连接时,将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后,ngx_stream_realip_module 从中提取原始信息…...

ServerTrust 并非唯一
NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...

给网站添加live2d看板娘
给网站添加live2d看板娘 参考文献: stevenjoezhang/live2d-widget: 把萌萌哒的看板娘抱回家 (ノ≧∇≦)ノ | Live2D widget for web platformEikanya/Live2d-model: Live2d model collectionzenghongtu/live2d-model-assets 前言 网站环境如下,文章也主…...

spring Security对RBAC及其ABAC的支持使用
RBAC (基于角色的访问控制) RBAC (Role-Based Access Control) 是 Spring Security 中最常用的权限模型,它将权限分配给角色,再将角色分配给用户。 RBAC 核心实现 1. 数据库设计 users roles permissions ------- ------…...