LiteFlow决策系统的策略模式,顺序、最坏、投票、权重
个人博客:无奈何杨(wnhyang)
个人语雀:wnhyang
共享语雀:在线知识共享
Github:wnhyang - Overview
想必大家都有听过或做过职业和性格测试吧,尤其是现在的毕业生,在投了简历之后经常会收到一个什么测评,那些测评真的是又臭又长,做的简直让人崩溃,很多时候都是边骂边做,都什么玩意!?
然而,本篇就由此出发,把整个测评作为一个策略的话,其中每一项都是一条规则,通常每一条规则(问答)需要我们输入一个类似1-9的分数,1和9分别代表两个极端,最终这个策略会结合所有的问答结果计算出我们的性格/职业。这是如何做的呢?其实就是一种分类算法,就拿二维平面直角坐标系举例吧!
如下二维平面直角坐标系下分出了4个区域,性格/职业测评的每条问答可以理解为其中一条经过原点的直线,1-9分别指示两个方向,你的答案最终会是一个由原点出发的n
条直线,这n
条直线可以绘成一个多边形,而这个多边形就构成了最终结果,长得有点类似雷达图。
当然这只是二维平面直角坐标系的例子,实际上现实往往比这个更复杂,高于三维的我也举不出例子啊🙂↔️
总之最后结果绝大多数情况下都会是一个不规则的东西(我实在不知道更高维的该怎么描述),这种测评会取出凸点作为我们的倾向性格/职业。
好吧,关于文章开篇就到这里了,下面就可以正式开始了。不过我还是想讲一个题外话,小时候接触的数学函数(方程)可以很轻易的表示在二维直角坐标系下,随着对于数学的深入探索,出现了越来越多的奇奇怪怪的字母和方程,有人也讲“数学的尽头是字母”🤔然而当我们换一个坐标系,这些是不是也会变个模样呢?所以说有时候换个角度看问题就会有不同收获,或者说换个角度问题就会迎刃而解。
策略
策略组件大致实现如下,编排时会使用p_cn.tag("code")
,运行FOR(p_fn).DO(r_cn).BREAK(p_bn);
或FOR(p_fn).parallel(true).DO(r_cn);
,前者适用于顺序模式,其他皆适用于后者。
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = LFUtil.POLICY_COMMON_NODE, nodeType = NodeTypeEnum.COMMON, nodeName = "策略普通组件")
public void policy(NodeComponent bindCmp) {PolicyContext policyContext = bindCmp.getContextBean(PolicyContext.class);PolicyContext.PolicyCtx policy = PolicyConvert.INSTANCE.convert2Ctx(policyMapper.selectByCode(bindCmp.getTag()));policyContext.addPolicy(policy.getCode(), policy);log.info("当前策略(code:{}, name:{}, code:{})", policy.getCode(), policy.getName(), policy.getCode());if (PolicyMode.ORDER.equals(policy.getMode())) {bindCmp.invoke2Resp(LFUtil.P_F, policy.getCode());} else {bindCmp.invoke2Resp(LFUtil.P_FP, policy.getCode());}
}
循环次数组件p_fn
如下,查询策略下的所有规则(规则还没做版本控制,后面再改),返回规则列表大小。
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_FOR, nodeId = LFUtil.POLICY_FOR_NODE, nodeType = NodeTypeEnum.FOR, nodeName = "策略for组件")
public int policyFor(NodeComponent bindCmp) {PolicyContext policyContext = bindCmp.getContextBean(PolicyContext.class);String policyCode = bindCmp.getSubChainReqData();List<PolicyContext.RuleCtx> ruleList = RuleConvert.INSTANCE.convert2Ctx(ruleMapper.selectByPolicyCode(policyCode));policyContext.addRuleList(policyCode, ruleList);return ruleList.size();
}
循环中断组件p_bn
如下,当策略上下文中有命中风险规则时就可以停止循环了。
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_BOOLEAN, nodeId = LFUtil.POLICY_BREAK_NODE, nodeType = NodeTypeEnum.BOOLEAN, nodeName = "策略break组件")
public boolean policyBreak(NodeComponent bindCmp) {PolicyContext policyContext = bindCmp.getContextBean(PolicyContext.class);String policyCode = bindCmp.getSubChainReqData();return policyContext.isHitRisk(policyCode);
}
另外在使用异步循环编排时需要注意并发操作问题,尤其是对上下文的操作。
剩下的规则组件r_cn
和策略上下文PolicyContext
请往下看。
顺序:按部就班、循序渐进
顺序模式是最好理解,就是顺序运行策略下的所有规则,默认在第一条设定的风险规则触发后结束,其实更准确的叫法应该是首次。如下表在顺序模式下执行,到规则2就结束了,因为默认pass
之外的才是风险规则。
规则 | 是否命中 | 处置方式 |
---|---|---|
1 | true | pass |
2 | true | reject |
3 | false | sms |
4 | true | review |
最坏:未雨绸缪,防患未然
与顺序模式不同,需要执行所有的规则,综合最坏的作为结果。如下表在最坏模式下,最终结果是reject
(因为reject
>review
>pass
,这个是配置的)。
规则 | 是否命中 | 处置方式 |
---|---|---|
1 | true | pass |
2 | true | reject |
3 | false | sms |
4 | true | review |
投票:集体智慧,共同决策
同上,需要执行完所有规则,以命中规则的结果最多的作为最终结果。如下表在投票模式下,结果是pass
。
规则 | 是否命中 | 处置方式 |
---|---|---|
1 | true | pass |
2 | true | reject |
3 | false | review |
4 | true | pass |
可以使用这样的计数器,但是考虑到策略集下有不一样的策略集,想必还要再包一层Map<String,ConcurrentHashMap<String, AtomicInteger>>
,以策略code
作为键。
private final ConcurrentHashMap<String, AtomicInteger> counters = new ConcurrentHashMap<>();/*** 增加指定 key 的计数值。* 如果 key 不存在,则初始化为 1;如果存在,则将当前值加 1。*/
public void increment(String key) {// 使用 computeIfAbsent 方法来确保只在第一次遇到该 key 时创建新的 AtomicIntegercounters.computeIfAbsent(key, k -> new AtomicInteger(0)).incrementAndGet();
}/*** 获取指定 key 的当前计数值。* 如果 key 不存在,则返回 0。*/
public int get(String key) {// 获取指定 key 的 AtomicInteger,并调用 get() 方法获取其值AtomicInteger counter = counters.get(key);return (counter != null) ? counter.get() : 0;
}
本来考虑的是在规则True
组件中根据策略不同做不同的事情,但后来放弃了,还是统一放在上下文中吧,且往下看。
权重:量化评估,科学分配
一样,需要运行完所有规则,综合权重模式阈值配置得出最终结论。如下表在权重模式下,结果是23
+21
+20
=64
,注意!!!这里只是得到一个数字,在策略设置为权重模式后额外还需要配置一个阈值表,拿这个数字去匹配对应的阈值区间得出最终结论。当然这只是个最简单例子,下面将展开,讨论其丰富的应用场景和更灵活的使用方法。
规则 | 是否命中 | 得分 |
---|---|---|
1 | true | 23 |
2 | true | 21 |
3 | false | 30 |
4 | true | 20 |
阈值配置表
得分 | 结果 |
---|---|
(-214,20] | pass |
(20,45] | review |
(45,70] | sms |
(70,900) | reject |
设计过程
阶段一:规则增加简单的分数属性,命中时累计就好,如:规则1命中时+10,规则2命中时-2,这样最为简单,也因此适用场景最少,最不灵活。
阶段二:固定公式计算,如下规则附加这些属性,在规则命中时计算一下,这种只适合简单的线性相关的规则计算。
/*** 计算公式为:base + al aOpType(加/减/乘/除) ${value}(取决于opType类型,是指标还是字段),结果范围[lowerLimit,upperLimit]** @author wnhyang* @date 2024/12/9**/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Weight {private Double base; // 基础权重private Double al; // 权重调整因子private String aOpType; // 操作类型:加/减/乘/除private String opType; // 指标还是字段private String value; // 指标名称或字段名称private Double upperLimit; // 上限private Double lowerLimit; // 下限/*** 计算最终权重** @param trueValue 实际值,当 opType 为 "zb" (指标) 时使用* @return 最终计算出的权重*/public double compute(double trueValue) {if (al == null || trueValue == 0 && ("/".equals(aOpType))) {throw new IllegalArgumentException("Invalid parameters for computation.");}double adjustment = 0;switch (aOpType) {case "+":adjustment = al + trueValue;break;case "-":adjustment = al - trueValue;break;case "*":adjustment = al * trueValue;break;case "/":adjustment = al / trueValue;break;default:throw new UnsupportedOperationException("Unsupported operation type: " + aOpType);}double result = base + adjustment;return Math.min(upperLimit, Math.max(lowerLimit, result));}public static void main(String[] args) {Weight weight = new Weight(10.41, -2.154, "*", "zb", "count", 5000.545, -56.654);double compute = weight.compute(25.21);System.out.println("Computed weight: " + compute);}
}
阶段三:灵活公式,使用QLExpress
实现。如下讲计算公式作为规则的一个属性,通过getOutVarNames
获取需要用到的外部变量名,在运行表达式之前通过LiteFlow
上下文取值塞到QLExpress
的上下文中。
当然还有可以优化的地方,1、设计上下文时实现QLExpress
的IExpressContext
接口,也就不用获取后在塞,直接拿LiteFlow
上下文作为QLExpress
上下文用就行;2、还有就是min(upperLimit, max({}, lowerLimit))
是否要放在表达式中,其实也是没必要,可以放在表达式计算完成之后嘛。3、是否要计算平均值,现在是权重之和,是否要做加权平均呢?4、等等
@Test
public void test3() throws Exception {ExpressRunner runner = new ExpressRunner();DefaultContext<String, Object> context = new DefaultContext<>();String fun = "base + al * value";String express = StrUtil.format("min(upperLimit, max({}, lowerLimit))", fun);log.info(express);String[] outVarNames = runner.getOutVarNames(express);log.info(Arrays.toString(outVarNames));context.put("base", 45.434);context.put("al", 3.352);context.put("value", 24.3264);context.put("lowerLimit", -35.342);context.put("upperLimit", 3463.57);Object r = runner.execute(express, context, null, true, false);log.info("{}", r);
}
注意点
对于像这种可输入的、脚本类的、在系统中运行的,一定要做好安全性校验,避免直接操作系统资源,稍微不注意控制就会有安全漏洞。在保证安全的前提下,再考虑如何优化用户使用体验,如用户需要使用一些系统字段时,在编辑器文本域输入特殊字符(像“@”或“#”),监听到输入后显示候选列表,可以关键词匹配并选择需要的字段,一旦选中,这个将作为一个整体,只能整体操作,就像我们在发邮件,或者聊天时输入“@”一样,另外再做一个内置运算符的提示符,这样编辑公式就更加便捷,且能降低出错率。再进一步就是做一个常用公式库,提示列表中有直接选中就行,剩下的就是填充需要的字段就行。
应用场景
比如在做密码登录时,设置了两条规则,一条正向规则y1=f(x1)
,x1
表示最近人脸登录成功次数,其与结果负相关,人脸登录成功次数越多得到的负数越大;一条反向规则y2=f(x2)
,x2
表示最近密码登录失败次数,其与结果正相关,密码登录失败次数越多得到的分数越大,而且保证其“增长率”大于f(x1)
。
可以大致表示为下面的曲线,最近人脸登录成功次数少时,风险高一些,多时也会存在上限,因为再多也没有意义了;最新密码登录错误次数少时风险低,但密码登录次数越多风险急剧增加,这样的话在整合y=y1+y2=f(x1)+f(x2)
后,风险受密码登录错误次数的影响更大。
当然将两个公式整合到一块做为一个规则也是可以的,差别就是是否需要独立的规则条件。
条件 | 公式 | |
---|---|---|
不合并 | condition1 | f(x1) |
condition2 | f(x2) | |
合并 | condition1 | f(x1)+f(x2) |
还有就是在信贷计算信用时,需要计算收入稳定性+信用历史+就业情况+债务水平+资产情况的场景时,当然这依赖多方数据,而且一般的信用评估不是简单的规则配置能解决的。
模型:智能学习,进化升级
最终都将到这一步的,虽然现在做的项目中还没有集成模型,但是我之后一定会做。先立flag
嘛,实现不是实现另说吧🧐可别连立flag
的勇气都没有了!
关于模型我也不是专业的,也是仅有一点点了解。我一直认为学习能力、看待、解决问题的思想是最重要的,特别是我看了几个关于机器学习的视频后,虽然其中很多的公式我都不懂,但是能理解到其看待、解决问题的思路方法,很受益,很有启发。
规则
以下分别是规则组件(包含isAccess
实现)、命中规则、未命中规则组件。在规则命中时额外计算规则配置的权重表达式。
@LiteflowMethod(value = LiteFlowMethodEnum.IS_ACCESS, nodeId = LFUtil.RULE_COMMON_NODE, nodeType = NodeTypeEnum.COMMON)
public boolean ruleAccess(NodeComponent bindCmp) {String policyCode = bindCmp.getSubChainReqData();int index = bindCmp.getLoopIndex();PolicyContext policyContext = bindCmp.getContextBean(PolicyContext.class);PolicyContext.RuleCtx rule = policyContext.getRule(policyCode, index);return !RuleStatus.OFF.equals(rule.getStatus());
}@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = LFUtil.RULE_COMMON_NODE, nodeType = NodeTypeEnum.COMMON, nodeName = "规则普通组件")
public void rulProcess(NodeComponent bindCmp) {String policyCode = bindCmp.getSubChainReqData();int index = bindCmp.getLoopIndex();PolicyContext policyContext = bindCmp.getContextBean(PolicyContext.class);PolicyContext.RuleCtx rule = policyContext.getRule(policyCode, index);bindCmp.invoke2Resp(StrUtil.format(LFUtil.RULE_CHAIN, rule.getCode()), rule);
}@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = LFUtil.RULE_TRUE, nodeType = NodeTypeEnum.COMMON, nodeName = "规则true组件")
public void ruleTrue(NodeComponent bindCmp) {PolicyContext policyContext = bindCmp.getContextBean(PolicyContext.class);PolicyContext.RuleCtx rule = bindCmp.getSubChainReqData();log.info("命中规则(name:{}, code:{})", rule.getName(), rule.getCode());if (RuleStatus.MOCK.equals(rule.getStatus())) {policyContext.addHitMockRuleVO(rule.getPolicyCode(), rule);} else {// 权重if (PolicyMode.WEIGHT.equals(policyContext.getPolicy(rule.getPolicyCode()).getMode())) {try {Double value = (Double) QLExpressUtil.execute(rule.getExpress(), bindCmp.getContextBean(FieldContext.class));rule.setExpressValue(value);} catch (Exception e) {log.error("规则表达式执行异常", e);}}policyContext.addHitRuleVO(rule.getPolicyCode(), rule);}
}@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = LFUtil.RULE_FALSE, nodeType = NodeTypeEnum.COMMON, nodeName = "规则false组件")
public void ruleFalse(NodeComponent bindCmp) {log.info("规则未命中");
}
策略上下文
直接上代码了。
/*** @author wnhyang* @date 2024/4/3**/
public class PolicyContext {/*** 处置方式集合*/private final Map<String, DisposalCtx> disposalMap = new ConcurrentHashMap<>();/*** 策略集*/private PolicySetCtx policySet;/*** 初始化** @param disposalCtxList 处置方式集合* @param policySet 策略集*/public void init(List<DisposalCtx> disposalCtxList, PolicySetCtx policySet) {for (DisposalCtx disposalCtx : disposalCtxList) {disposalMap.put(disposalCtx.getCode(), disposalCtx);}this.policySet = policySet;}/*** 策略集合*/private final Map<String, PolicyCtx> policyMap = new ConcurrentHashMap<>();/*** 添加策略** @param policyCode 策略code* @param policy 策略*/public void addPolicy(String policyCode, PolicyCtx policy) {policyMap.put(policyCode, policy);}/*** 获取策略** @param policyCode 策略code* @return 策略*/public PolicyCtx getPolicy(String policyCode) {return policyMap.get(policyCode);}/*** 规则集合*/private final Map<String, List<RuleCtx>> ruleListMap = new ConcurrentHashMap<>();/*** 添加规则集合** @param policyCode 策略code* @param ruleList 规则列表*/public void addRuleList(String policyCode, List<RuleCtx> ruleList) {ruleListMap.put(policyCode, ruleList);}/*** 获取规则** @param policyCode 策略code* @param index 规则索引* @return 规则*/public RuleCtx getRule(String policyCode, int index) {return ruleListMap.get(policyCode).get(index);}/*** 命中规则集合*/private final Map<String, List<RuleCtx>> hitRuleListMap = new ConcurrentHashMap<>();/*** 添加命中规则** @param policyCode 策略code* @param rule 规则*/public void addHitRuleVO(String policyCode, RuleCtx rule) {if (!hitRuleListMap.containsKey(policyCode)) {hitRuleListMap.put(policyCode, CollUtil.newArrayList());}hitRuleListMap.get(policyCode).add(rule);}/*** 是否命中风险规则** @param policyCode 策略code* @return true/false*/public boolean isHitRisk(String policyCode) {if (CollUtil.isNotEmpty(hitRuleListMap.get(policyCode))) {for (RuleCtx ruleCtx : hitRuleListMap.get(policyCode)) {if (!DisposalConstant.PASS_CODE.equals(ruleCtx.getDisposalCode())) {return true;}}}return false;}/*** 命中模拟规则集合*/private final Map<String, List<RuleCtx>> hitMockRuleListMap = new ConcurrentHashMap<>();/*** 添加命中模拟规则** @param policyCode 策略code* @param rule 规则*/public void addHitMockRuleVO(String policyCode, RuleCtx rule) {if (!hitMockRuleListMap.containsKey(policyCode)) {hitMockRuleListMap.put(policyCode, CollUtil.newArrayList());}hitMockRuleListMap.get(policyCode).add(rule);}/*** 转策略集结果** @return 策略集结果*/public PolicySetResult convert() {PolicySetResult policySetResult = new PolicySetResult(policySet.getName(), policySet.getCode(), policySet.getChain(), policySet.getVersion());for (Map.Entry<String, PolicyCtx> entry : policyMap.entrySet()) {PolicyCtx policy = entry.getValue();PolicyResult policyResult = new PolicyResult(policy.getName(), policy.getCode(), policy.getMode());// 最坏String maxDisposalCode = DisposalConstant.PASS_CODE;int maxGrade = Integer.MIN_VALUE;// 投票Map<String, Integer> votes = new HashMap<>();// 权重double weight = 0.0;List<RuleCtx> ruleList = hitRuleListMap.get(policy.getCode());if (CollUtil.isNotEmpty(ruleList)) {for (RuleCtx rule : ruleList) {if (PolicyMode.VOTE.equals(policy.getMode())) {// 投票votes.put(rule.getDisposalCode(), votes.getOrDefault(rule.getDisposalCode(), 0) + 1);} else if (PolicyMode.WEIGHT.equals(policy.getMode())) {// 权重weight += rule.getExpressValue();}RuleResult ruleResult = new RuleResult(rule.getName(), rule.getCode(), rule.getExpress());// 最坏和顺序DisposalCtx disposal = disposalMap.get(rule.getDisposalCode());if (null != disposal) {ruleResult.setDisposalName(disposal.getName());ruleResult.setDisposalCode(disposal.getCode());if (disposal.getGrade() > maxGrade) {maxGrade = disposal.getGrade();maxDisposalCode = disposal.getCode();}}// 模拟/正式规则区分开if (RuleStatus.MOCK.equals(rule.getStatus())) {policyResult.addMockRuleResult(ruleResult);} else {policyResult.addRuleResult(ruleResult);}}}if (PolicyMode.VOTE.equals(policy.getMode())) {String maxVoteDisposalCode = DisposalConstant.PASS_CODE;int maxVoteCount = Integer.MIN_VALUE;for (Map.Entry<String, Integer> entry1 : votes.entrySet()) {if (entry1.getValue() > maxVoteCount) {maxVoteCount = entry1.getValue();maxVoteDisposalCode = entry1.getKey();}}policyResult.setDisposalName(disposalMap.get(maxVoteDisposalCode).getName());policyResult.setDisposalCode(maxVoteDisposalCode);} else if (PolicyMode.WEIGHT.equals(policy.getMode())) {List<Th> thList = policy.getThList();// 排序thList.sort(Comparator.comparing(Th::getScore));for (Th th : thList) {if (weight <= th.getScore()) {policyResult.setDisposalName(disposalMap.get(th.getCode()).getName());policyResult.setDisposalCode(th.getCode());break;}}} else {policyResult.setDisposalName(disposalMap.get(maxDisposalCode).getName());policyResult.setDisposalCode(maxDisposalCode);}policySetResult.addPolicyResult(policyResult);}// TODO 入度大于1?考虑投票、加权平均等方法:不考虑policySetResult.setDisposalName(DisposalConstant.PASS_NAME);policySetResult.setDisposalCode(DisposalConstant.PASS_CODE);return policySetResult;}
}
策略集
策略集是用来编排策略的,即前面的策略组件p_cn.tag("code")
,从前面已知策略会有结果的,那么编排他们的策略集如何取这个结果呢?
这就要考虑如何设计策略集的编排了,两种情况,入度为1,入度大于1。
如下,图1并行运行策略1、2,并最终都返回到结束节点,这时就要考虑如何处理策略1、2的结果了,投票?加权平均?随机选一个?还是其他什么方法?图2通过分流结束节点只会接收到一个策略,那么此时就不会有冲突,分流到哪个就返回哪个。
当然这块还没想好怎么做,只是一些想法。
小结
本来还想分享一下项目进展的,但转眼一看好像写的已经有点多了,那就下次吧!
写在最后
拙作艰辛,字句心血,望诸君垂青,多予支持,不胜感激。
个人博客:无奈何杨(wnhyang)
个人语雀:wnhyang
共享语雀:在线知识共享
Github:wnhyang - Overview
相关文章:
LiteFlow决策系统的策略模式,顺序、最坏、投票、权重
个人博客:无奈何杨(wnhyang) 个人语雀:wnhyang 共享语雀:在线知识共享 Github:wnhyang - Overview 想必大家都有听过或做过职业和性格测试吧,尤其是现在的毕业生,在投了简历之后经…...
“AI换脸”骗过人脸识别?黑产攻击新手段应如何防御?
在著名美剧《权力的游戏》中,有一个神秘的刺客组织叫“无面者”,这个组织中的人可以通过某种神秘手段切换无数种不同的面孔,实现“一人千面”。 电视剧毕竟魔幻。但如今,基于人工智能的深度合成伪造技术正在让“一人千面”成为现…...
面试题整理6----什么是进程最大数、最大线程数、进程打开的文件数,怎么调整
什么是进程最大数、最大线程数、进程打开的文件数,怎么调整 1. 进程最大数1.1 调整方法: 2. 最大线程数2.1 调整方法: 3. 注意事项 #linux 1. 进程最大数 进程最大数是指操作系统允许同时运行的进程数量上限。这个限制通常由内核参数 ulimi…...
android RadioButton + ViewPager+fragment
RadioGroup viewpage fragment 组合显示导航栏 1、首先主界面的布局控件就是RadioGroup viewpage <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:tools…...
Zabbix6.0升级为6.4
为了体验一些新的功能,比如 Webhook 和问题抑制等,升级个小版本。 一、环境信息 1. 版本要求 一定要事先查看官方文档,确认组件要求的版本,否则版本过高或者过低都会出现问题。 2. 升级前后信息 环境升级前升级后操作系统CentOS…...
Unity 根据文本宽度自动移动图像位置
游戏中有时候需要变动的显示一个物品的数量,变化的文本宽度不停的变化,这时候需要将物品的icon随着文本的长度而改变位置。 实现思路:使用Content Size Fitter来动态改变内容的大小。 首先建立一个文本组件,添加Content Size Fi…...
spring @Mapper Converter转换泛型异常
spring Mapper Converter转换泛型异常 需要在每个list类型转换上面加Named 注解,否则会影响page生成的类型转换 比如: import org.mapstruct.Mapper; import org.mapstruct.Named;import com.baomidou.mybatisplus.core.metadata.IPage; import com.b…...
如何设计一个秒杀系统
开局一张图 结局要说清 对于设计一个秒杀系统,结合图片分层结构,根据每一层从访问层,负载层,服务层,业务层,支撑层,数据层,详细说明每一层应该怎么设计。 应该注意那些事项。比如访…...
SPL06 基于stm32F103 HAL库驱动(软件模拟IIC)
talk is cheap, show you my code SPL06.c #include "SPL06.h"//*************全局变量*************// Factor_List* b_list; //存储过采样率对应的系数KP,KT COEF_ValueStruct Coefficient { 0 }; //存储校准系数…...
arcgisPro将面要素转成CAD多段线
1、说明:正常使用【导出为CAD】工具,则导出的是CAD三维多线段,无法进行编辑操作、读取面积等。这是因为要素面中包含Z值,导出则为三维多线段数据。需要利用【复制要素】工具禁用M值和Z值,再导出为CAD,则得到…...
Cocos Creator 试玩广告开发
之前主要是使用Unity,这次刚好项目是试玩游戏的开发,所以临时学了Cocos来开发。所以这篇文章,更加关注从Unity转到Cocos开发的经历以及试玩的基本开发。 首先,我是没有使用过Cocos的,也没有接触过Ts语言,对于Ts的开发开…...
【Linux】解锁文件描述符奥秘,高效缓存区的实战技巧
fd和缓冲区 1. 文件描述符fd1.1. 概念与本质1.2. 打开文件的管理1.3. 一切皆文件的理解1.4. 分配规则1.5. 重定向的本质1.5.1. dup2 2. FILE中的缓冲区2.1. 概念2.2. 存在的原因2.3. 类型(刷新方案)2.4. 存放的位置2.4.1. 代码证明、现象解释 2.5. 模拟C标准库中的方法 1. 文件…...
MySQL基础笔记(五)
在此特别感谢尚硅谷-康师傅的MySQL精品教程 获取更好的阅读体验请前往我的博客主站! 如果本文对你的学习有帮助,请多多点赞、评论、收藏,你们的反馈是我更新最大的动力! 约束 1. 约束(constraint)概述 1.1 为什么需要约束 数据完整性&…...
夯实数字技术,培育创新人才:数据科学与大数据技术专业人才培养实践
近年来,得益于全球各国家和地区对大数据产业的政策扶持以及数字经济的蓬勃发展,大数据市场在全球范围内展现出了迅猛的增长态势。国家层面相继出台了诸如《“数据要素 ” 三年行动计划(2024—2026 年)》《数字中国建设整体布局规划…...
Java爬虫大冒险:如何征服1688商品搜索之巅
在这个信息爆炸的时代,数据就是力量。对于电商平台而言,数据更是金矿。今天,我们要踏上一场Java爬虫的冒险之旅,目标是征服1688这个B2B电商巨头,获取按关键字搜索的商品信息。这不仅是技术的挑战,更是智慧的…...
IEC 101/104 中为什么我们需要单点和双点信号
REDISANT 提供互联网与物联网开发测试套件 # 互联网与中间件: Redis AssistantZooKeeper AssistantKafka AssistantRocketMQ AssistantRabbitMQ AssistantPulsar AssistantHBase AssistantNoSql AssistantEtcd AssistantGarnet Assistant 工业与物联网࿱…...
01、NodeJS学习笔记,第一节:Node.js初识与内置模块
一、初识Node.js与内置模块 ##网址 https://nodejs.org##npm包 https://www.npmjs.com/ (搜索)https://registry.npmjs.org/ (下载)1、初识Node.js ##思考:为什么JavaScript可以在浏览器中被执行因为浏览器…...
ElasticSearch 自动补全
1、前言 当用户在搜索框输入字符时,我们应该提示出与该字符有关的搜索项,根据用户输入的字母,提示完整词条的功能,就是自动补全。 2、安装拼音分词器 Github地址:https://github.com/infinilabs/analysis-pinyin 插件…...
整点(枚举)
Hello!大家好!我是学霸小羊,今天分享一道c枚举题: 题目描述 在二维坐标系, 有一个圆,圆心在(0,0),圆的半径是r。问圆内有多少个整点(所谓的整点就是横坐标和纵坐标都是整数的点)。若点P的横坐标是整数a&a…...
【WRF安装】WRF编译错误:problems building executables look for error in the build log
WRF编译错误 错误内容:problems building executables, look for error in the build log原因1:gcc版本过低安装高版本gcc 原因2:netcdf版本和配置有误原因3:库冲突原因4:export NETCDF_classic1终极手段:重…...
2024年12月陪玩系统-仿东郊到家约玩系统是一种新兴的线上预约线下社交、陪伴系统分享-优雅草央千澈-附带搭建教程
2024年12月陪玩系统-仿东郊到家约玩系统是一种新兴的线上预约线下社交、陪伴系统分享-优雅草央千澈-附带搭建教程 产品介绍 仿东郊到家约玩系统是一种新兴的线上预约,线下社交、陪伴、助娱、助攻、分享、解答、指导等服务模式,范围涉及电竞、运动、音乐…...
社区版 IDEA 开发webapp 配置tomcat
1.安装tomcat 参考Tomcat配置_tomcat怎么配置成功-CSDN博客 2.构建webapp项目结构 新建一个普通项目 然后添加webapp的目录结构: main目录下新建 webapp 文件夹 webapp文件夹下新建WEB_INF文件夹 *WEB_INF目录下新建web.xml wenapp文件夹下再新建index.html …...
IDEA中解决Edit Configurations中没有tomcat Server选项的问题
今天使用IDEA2024专业版的时候,发现Edit Configurations里面没有tomcat Server,最终找到解决方案。 一、解决办法 1、打开Settings 2、搜索tomcat插件 搜索tomcat插件之后,找到tomcat 发现tomcat插件处于未勾选状态,然后我们将其勾选保存即可。 二、结果展示 最后,再次编…...
【Python】主成分分析PCA - 算法、问题与Python实现
【Python】主成分分析PCA - 算法、问题与Python实现 一、PCA 算法简介(一)概念及作用(二)基本原理(三)算法步骤1.数据预处理2.计算协方差矩阵3.进行特征值分解4.选择主成分5.完成数据降维 二、PCA 常见问题…...
Go怎么做性能优化工具篇之pprof
工欲善其事、必先利其器。这次我们来看看Go的性能优化工具有哪些吧 Go性能优化的工具 一、pprof 工具 pprof 是 Go 语言自带的性能分析工具,可以帮助开发者分析程序的 CPU 使用情况、内存使用情况、goroutine 调度情况等,从而定位性能瓶颈。通过 pprof…...
DataOps驱动数据集成创新:Apache DolphinScheduler SeaTunnel on Amazon Web Services
引言 在数字化转型的浪潮中,数据已成为企业最宝贵的资产之一。DataOps作为一种文化、流程和实践的集合,旨在提高数据管道的质量和效率,从而加速数据从源头到消费的过程。白鲸开源科技,作为DataOps领域的领先开源原生公司…...
递归读取指定目录下的文件
序言 需要读取sftp服务器上符合指定的文件名正则的文件列表,目前想到的最好的办法就是递归。 我这里引入的依赖是: <!-- jsch-sftp连接 --><dependency><groupId>com.jcraft</groupId><artifactId>jsch</artif…...
代码随想录算法训练营day46|动态规划part12
今天就结束动态规划章节了,以后还要多加练习。 今天的两道题都很有难度,647回文子串的思路非常巧妙,因为用一维dp数组比较难表示子串的起点和终点,所以需要用二维dp数组表示,dp[i][j]表示以i为起点,j为终点…...
ubuntu 24.04.1安装FTP流程
1、安装vsftpd: sudo apt update sudo apt install vsftpd 2、安装后重启查看vsftpd状态 sudo systemctl status vsftpd 输出如下所示,表明vsftpd服务处于活动状态并正在运行: * vsftpd.service - vsftpd FTP server Loaded: loaded (/…...
【Linux】UDP通信
udp使用的是数据报传输。可以一对一,一对多进行传输,用于快速,实时性高的场景 服务器端: 使用步骤: 1.创建socket 2.bind绑定可接收的客户端 3.while{ recv接收数据 send发送数据 } #include <stdio.h> #inclu…...
日期格式、JSR303校验
日期格式 public class Monster() {DateTimeFormat(pattern "yyyy-MM-dd")private Date birthday; } 输入:2024-11-12, 输出:Monster{birthdaySun Nov 12 00:00:00 CST 2024} public class Monster {JsonFormat(pattern &…...
ELK系列-(六)Redis也能作为消息队列?(上)
一、前文回顾 🔍 在前面的ELK系列中,我们已经搭建了ELK的核心组件,包括: ELK系列-(一)Docker部署ELK核心组件ELK系列-(二)LogStash数据处理的瑞士军刀ELK系列-(三&…...
点击展示大图预览
原文链接在table表格里能够实现,点击里面的图片实现大图预览的效果; 一、先安装viewer — 使用npm安装 npm install v-viewer --save二、在main.js中引入 import Viewer from v-viewer //点击图片大图预览 import viewerjs/dist/viewer.css Vue.use(…...
游戏AI实现-寻路算法(BFS)
广度优先搜索算法(英语:Breadth-first search,缩写:BFS),又译作宽度优先搜索,或横向优先搜索,是一种图形搜索算法。 寻路地图搭建: 游戏AI实现-寻路地图搭建-CSDN博客 …...
tryhackme-Pre Security-HTTP in Detail(HTTP的详细内容)
任务一:What is HTTP(S)?(什么是http(s)) 1.What is HTTP? (HyperText Transfer Protocol)(什么是 HTTP?(超文本传输协议)) http是你查看网站的时候遵循的…...
CNN和Transfomer介绍
文章目录 CNN和Transfomer介绍CNN和Transfomer的区别1. **基本概念**2. **数据处理方式**3. **模型结构差异**4. **应用场景区别** 自注意力机制1. **自注意力机制的概念**2. **自注意力机制的实现步骤**3. **自注意力机制的优势** Transformer结构组成1. **多头注意力层&#…...
37. Three.js案例-绘制部分球体
37. Three.js案例-绘制部分球体 实现效果 知识点 WebGLRenderer WebGLRenderer 是Three.js中的一个渲染器类,用于将3D场景渲染到网页上。 构造器 WebGLRenderer( parameters : Object ) 参数类型描述parametersObject渲染器的配置参数,可选。 常用…...
Latex+VsCode+Win10搭建
最近在写论文,overleaf的免费使用次数受限,因此需要使用本地的形式进行编译。 安装TEXLive 下载地址:https://mirror-hk.koddos.net/CTAN/systems/texlive/Images/ 下载完成直接点击iso进行安装操作。 安装LATEX Workshop插件 设置VsCode文…...
【ETCD】【Linearizable Read OR Serializable Read】ETCD 数据读取:强一致性 vs 高性能,选择最适合的读取模式
ETCD 提供了两种不同类型的读取操作方式,分别是 Linearizable Read(线性化读取)和 Serializable Read(可串行化读取)。这两种方式主要区分在读取数据时对一致性的要求不同。 目录 1. Linearizable Read(线…...
windows下搭建本地sofa-registry
官方介绍: SOFARegistry 是蚂蚁金服开源的一个生产级、高时效、高可用的服务注册中心。SOFARegistry 最早源自于淘宝的 ConfigServer,十年来,随着蚂蚁金服的业务发展,注册中心架构已经演进至第五代。目前 SOFARegistry 不仅全面服…...
什么是MyBatis
MyBatis 简介 MyBatis 是一个流行的 Java 持久层框架(Persistence Framework),它主要用于简化数据库操作,提供了对数据库的映射支持,使得开发人员能够通过简单的配置和映射文件来执行数据库操作(如增、删、…...
Docker如何运行一个Java的jar包程序
Docker如何运行一个Java的jar包程序 1、jar包程序 2、start.sh运行jar包脚本 #!/bin/bash #进入目录 cd /app #1.下载SDK并安装 java -jar SDKDown1.4.jar #2.加载环境变量 export LD_LIBRARY_PATH/opt/casb/CipherSuiteSdk_linux/lib echo $LD_LIBRARY_PATH #3.执行SDK java …...
c语言----顺序结构
顺序结构的基本概念 定义:顺序结构是C语言程序中最基本的结构,它按照语句的先后顺序依次执行。就像我们日常做事一样,一步一步地按照顺序来完成任务。在C语言程序中,从程序的第一条语句开始,逐句向下执行,…...
BERT采用双向训练
BERT采用双向训练 定义 BERT(Bidirectional Encoder Representations from Transformers)是一种基于Transformer架构的预训练语言模型。它在自然语言处理(NLP)领域具有极其重要的地位,由谷歌在2018年提出,能够对文本进行深度的语义理解,从而广泛应用于各种语言相关的任务…...
流程引擎Activiti性能优化方案
流程引擎Activiti性能优化方案 Activiti工作流引擎架构概述 Activiti工作流引擎架构大致分为6层。从上到下依次为工作流引擎层、部署层、业务接口层、命令拦截层、命令层和行为层。 基于关系型数据库层面优化 MySQL建表语句优化 Activiti在MySQL中创建默认字符集为utf8&…...
安卓 文件管理相关功能记录
文件管理细分为图片、视频、音乐、文件四类 目录 权限 静态声明权限 动态检查和声明权限方法 如何开始上述动态申请的流程 提示 图片 获取图片文件的对象列表 展示 删除 视频 获取视频文件的对象列表 获取视频file列表 按日期装载视频文件列表 展示 播放 删除…...
监控视频汇聚融合云平台一站式解决视频资源管理痛点
随着5G技术的广泛应用,各领域都在通信技术加持下通过海量终端设备收集了大量视频、图像等物联网数据,并通过人工智能、大数据、视频监控等技术方式来让我们的世界更安全、更高效。然而,随着数字化建设和生产经营管理活动的长期开展࿰…...
【jvm】主要参数
Java 虚拟机(JVM)有许多参数用于控制其行为和性能,下面是一些 主要的 JVM 启动参数,这些参数通常分为以下几类: 内存管理相关参数 这些参数主要用来配置 JVM 的内存分配策略、堆内存、栈内存等。 -Xms 设置 JVM 启动…...
在Ubuntu中配置mysql,并允许外部访问数据库
在虚拟机中安装 MySQL 并允许外部访问,可以按照以下步骤操作: 1. 更新系统包 首先,确保你的系统是最新的,使用以下命令更新包列表: sudo apt update sudo apt upgrade2. 安装 MySQL Server 安装 MySQL 服务&#x…...
Golang的向前兼容性和toolchain规则,Go1.21.0
在 Go 1.21 中,在工具上新增了两个变化:增强了向前兼容性;工具链管理。 向前兼容性 在以前的版本中,Go 工具链尝试编译依赖于新版本的代码时,可能会遇到兼容性问题。例如,如果你的代码依赖于 Go 1.18 引入…...