低代码平台后端搭建-阶段完结
前言
最近又要开始为跳槽做准备了,发现还是写博客学的效率高点,在总结其他技术栈之前准备先把这个专题小完结一波。在这一篇中我又试着添加了一些实际项目中可能会用到的功能点,用来验证这个平台的扩展性,以及总结一些学过的知识。在这一篇中会增加如下功能点:增加Python执行组件、支持断点调试组件流、展示每个组件的详细运行信息。
Python组件
实现过程
在实际的应用中,有些复杂的需求可能没办法用现有的组件去实现,比如希望对组件A的结果进行函数计算、数据格式转换等,此时可以考虑引入一个Python组件,在这个组件的入参中直接写Python代码进行需要的操作。具体代码用gpt即可搞定,示例如下:
lowcode.application.properties——修改
python.interpreter.path=/Library/Frameworks/Python.framework/Versions/3.10/bin/python3.10
在配置文件中引入当前机器的Python环境的位置

com.example.lowcode.util.PythonUtil——新增
然后创建一个类用于解析Python代码
package com.example.lowcode.util;import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Map;
import java.util.concurrent.TimeUnit;@Service
public class PythonUtil {@Value("${python.interpreter.path}")private String pythonInterpreterPath;public String executePythonCode(String code, Map<String,Object> params) throws IOException, InterruptedException {String fullCode = buildFullPythonCode(code, params);ProcessBuilder processBuilder = new ProcessBuilder(pythonInterpreterPath, "-c", fullCode);Process process = processBuilder.start();// Handle the process's output stream (Python's stdout)String output = readFromStream(process.getInputStream());// Handle the process's error stream (Python's stderr)String errorOutput = readFromStream(process.getErrorStream());boolean finished = process.waitFor(30, TimeUnit.SECONDS);if (!finished) {throw new RuntimeException("Python process did not finish within the timeout period.");}if (process.exitValue() != 0) {throw new RuntimeException("Python execution error: " + errorOutput);}return output.replaceAll("\\n$", "");}private String buildFullPythonCode(String code, Map<String, Object> params) {// 构建参数传递的代码StringBuilder arguments = new StringBuilder();for (Map.Entry<String, Object> entry : params.entrySet()) {String key = entry.getKey();Object value = entry.getValue();if (value instanceof String) {// 字符串参数需要加引号arguments.append(String.format("%s = '%s'", key, value));} else {// 非字符串参数直接转换为字符串arguments.append(String.format("%s = %s", key, value));}// 在参数之间添加换行符arguments.append(System.lineSeparator());}return arguments + code;}private String readFromStream(InputStream inputStream) throws IOException {StringBuilder output = new StringBuilder();try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {String line;while ((line = reader.readLine()) != null) {output.append(line).append(System.lineSeparator());}}return output.toString();}
}
com.example.lowcode.component.PythonScript——新增
最后再创建Python组件即可
package com.example.lowcode.component;import com.example.lowcode.core.dto.ComponentInfo;
import com.example.lowcode.core.framework.AbstractComponent;
import com.example.lowcode.core.framework.ComponentContext;
import com.example.lowcode.core.model.*;
import com.example.lowcode.util.PythonUtil;
import com.google.common.collect.Maps;
import org.springframework.beans.factory.annotation.Autowired;import java.util.HashMap;
import java.util.Map;@ComponentDefinition(name = "PythonScript", type = ComponentTypeEnum.SERVICE_CALL, desc = "python组件")
@InputParamDefinition({@Param(name = "code", desc = "python函数模板", type = ParamTypeEnum.STRING, required = true),@Param(name = "params", desc = "函数入参", type = ParamTypeEnum.MAP, required = false)
})
@OutputParamDefinition({@Param(name = "result", desc = "http接口返回结果", required = true)})
public class PythonScript extends AbstractComponent {@Autowiredprivate PythonUtil pythonUtil;@Overridepublic Map<String, Object> execute(ComponentContext context, ComponentInfo componentInfo) throws Exception {String code = (String) parseInputParam("code", context, componentInfo);Map<String, Object> paramMap = (Map<String, Object>) parseInputParam("params", context, componentInfo);String output = parseOutputParam("result",componentInfo);HashMap<String, Object> result = Maps.newHashMap();String pythonResult = pythonUtil.executePythonCode(code,paramMap);result.put(output, pythonResult);return result;}}
测试
单独测试这个组件:
@Testpublic void testPythonScript() {try {// 调用方法并打印结果ComponentInfo componentInfo = new ComponentInfo();componentInfo.setInputs(new HashMap<>() {{String s = """def main(response: str, length: int) -> str:import reresponse = response[:length]match = re.search(r'[ABCDEFGH]', response)if match:return match.group()else:return 'other result'""";String s1 = """def main(content1, content2):return content1 + "" + content2""";String mainDef = """result = main(response, length)print(result, end='')""";put("code", new ComponentParam().setName("code").setValue(s+mainDef));HashMap<Object, Object> map = new HashMap<>();
// map.put("content1","Hello World!");
// map.put("content2","hehe");map.put("response","Hello World!");map.put("length",20);put("params", new ComponentParam().setName("params").setValue(map));}});componentInfo.setOutputs(new HashMap<>() {{put("result", new ComponentParam().setName("result").setValue("result"));}});Map<String, Object> execute = pythonScript.execute(new ComponentContext(), componentInfo);System.out.println(execute);} catch (Exception e) {e.printStackTrace();}}
运行结果:

断点调试组件流
实现过程
在使用低代码平台编辑组件流时,可能会遇到后面几个组件执行有问题或执行很慢的情况,可以考虑增加断点执行的能力,制定中间的某个组件为结束节点。

如上图所示,比如在调试的时候不想调用HttpClient组件,那就可以把PageFilter组件指定为结束节点,最下面的组件因为入参不够也会不执行。
实现的思路很简单,因为之前2.0版本的代码会根据组件之间的线去解析关联关系,只需要找到新的结束节点依赖的所有节点,把他们放到执行引擎中,不被依赖的节点自然就被剪掉了。
com.example.lowcode.core.dto2.FlowEngineBuilder——修改
剪枝部分的代码:
public DagEngine<O> buildDebug(String instanceName) {check();DagEngine<O> engineWithOpConfig = getEngineWithOpConfig(flow, instanceName);clear();return engineWithOpConfig;}private DagEngine<O> getEngineWithOpConfig(Flow flow, String instanceName) {DagEngine<O> engine = new DagEngine<>(executor);List<OperatorWrapper<?, ?>> operatorWrappers = getWrappersWithOpConfig(flow, engine);// 单节点执行逻辑,根据当前节点解析依赖节点Set<String> dependNode = new HashSet<>();resolveDependenciesForCut(flow, operatorWrappers, instanceName, dependNode);// 遍历wrapperMap,保留debug节点的所有依赖节点Map<String, OperatorWrapper<?, ?>> debugWrapperMap = new HashMap<>();engine.getWrapperMap().forEach((k, v) -> {if (dependNode.contains(k)) {debugWrapperMap.put(k, v);}});engine.setWrapperMap(debugWrapperMap);return engine;}private void resolveDependenciesForCut(Flow flow, List<OperatorWrapper<?, ?>> operatorWrappers, String instanceName, Set<String> dependNode) {final Map<String, OperatorWrapper<?, ?>> wrapperMap = operatorWrappers.stream().collect(Collectors.toMap(OperatorWrapper::getInstanceName, e -> e));final Map<String, List<Edge>> groupBySource = flow.getEdgeInstances().stream().collect(Collectors.groupingBy(Edge::getSourceName));groupBySource.forEach((id, followings) -> {for (Edge following : followings) {final OperatorWrapper<?, ?> targetOp = wrapperMap.get(following.getTargetName());targetOp.depend(id);}});Map<String, List<String>> sourceNameMap = new HashMap<>();groupBySource.forEach((k, v) -> {List<String> collect = v.stream().map(Edge::getTargetName).collect(Collectors.toList());sourceNameMap.put(k, collect);});dependNode.add(instanceName);// 查找当前节点的依赖节点findDependNode(instanceName, dependNode, sourceNameMap);}private void findDependNode(String start, Set<String> dependNode, Map<String, List<String>> sourceNameMap) {List<String> list = new ArrayList<>();list.add(start);while (!list.isEmpty()) {String node = list.remove(0);for (Map.Entry<String, List<String>> entry : sourceNameMap.entrySet()) {if (entry.getValue().contains(node)) {dependNode.add(entry.getKey());list.add(entry.getKey());}}}}
另外指定新的结束节点需要一个组件标志,可以随意选择只要保证唯一即可,这里为了方便演示选择用nodeName(组件的自定义名称)作为组件标志,同时ComponentInfo类也需要加上private String instanceName;属性。

然后引擎类DagEngine也需要加上set方法。
public void setWrapperMap(Map<String, OperatorWrapper<?, ?>> wrapperMap){this.wrapperMap = wrapperMap;}
最后修改接口层,把构建引擎的方法替换为新建的:

测试


详细运行信息
实现过程
实现这个功能需要小改动一下架构,之前的上下文类是用于存放每个组件的变量->变量值,是整个流层面的对象;现在需要保存每个节点的运行信息,且因为是并行需要线程安全。这里我加上原开源框架的DagContext类,来保存每个组件的运行信息。
作为示例,本篇实现展示每个组件的组件名、输入输出、耗时、异常报错,经过分析,组件名、输入、异常报错可以直接从flowNode中获取,而输出和耗时需要在执行组件时添加。
com.example.lowcode.core.framework2.DagContext——新增
引擎上下文,和ComponentContext不同,后者是整个流用一个ComponentContext对象,这个类是用于记录多线程环境每个组件的执行过程。
package com.example.lowcode.core.framework2;import com.example.lowcode.core.dto2.OperatorResult;import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;/*** DAG执行引擎上下文* 上下文的生命周期是引擎执行期间,即从开始节点到结束节点之间*/
public class DagContext<O> {/*** 保存每个节点返回的结果* key: 节点id* value: result*/private Map<String, OperatorResult> operatorResultMap = new ConcurrentHashMap<>();private OperatorResult<O> output;public void putOperatorResult(String wrapperId, OperatorResult<?> operatorResult) {operatorResultMap.put(wrapperId, operatorResult);}public OperatorResult getOperatorResult(String wrapperId) {return operatorResultMap.get(wrapperId);}public synchronized void setOutput(OperatorResult<O> endResult) {this.output = endResult;}public OperatorResult<O> getOutput () {return output;}public Map<String, OperatorResult> getOperatorResultMap() {return operatorResultMap;}
}
com.example.lowcode.core.framework2.DagContextHolder——新增
包装DagContext,线程安全
package com.example.lowcode.core.framework2;import com.alibaba.ttl.TransmittableThreadLocal;
import com.example.lowcode.core.dto2.OperatorResult;/*** 获取DagContext上下文的工具类*/
public class DagContextHolder {private static ThreadLocal<DagContext> holder = new TransmittableThreadLocal<>();protected static void set(DagContext dagContext) {holder.set(dagContext);}public static DagContext get() {return holder.get();}protected static void remove() {holder.remove();}public static void putOperatorResult(String instanceName, OperatorResult<?> operatorResult) {holder.get().putOperatorResult(instanceName, operatorResult);}public static OperatorResult getOperatorResult(String instanceName) {return holder.get().getOperatorResult(instanceName);}
}
com.example.lowcode.core.framework2.DagEngine——修改
然后修改引擎类的代码

↑初始化DagContext

↑在getRunningTask方法中更新上下文的运行结果

↑最后在执行run组件的前后记录耗时,OperatorResult类也需要加上duration属性。这个实现方式很不好,在下文会修改这段实现。
com.example.lowcode.core.service.RunServiceImpl——修改
下一步是在实现类中新写一个接口,把组件的运行信息都取出来:
@Overridepublic Map<String, Map<String, Object>> runFlowDebug(long flowId, Map<String, ComponentInfo> inputParams, String instanceName) {FlowSnapshot flowSnapshot = FlowSnapshotDO.selectByFlowId(flowId);assert flowSnapshot != null;Flow flow = JSON.parseObject(flowSnapshot.getJsonParam(), new TypeReference<>() {});DagEngine<Map<String, Object>> engine = new FlowEngineBuilder<Map<String, Object>>().setFlow(flow).setInputParams(inputParams).setExecutor(THREAD_POOL_EXECUTOR).buildDebug(instanceName);engine.runAndWait();
// if (engine.getEx() != null) {
// throw new FlowExecutionException(String.format("【%s:%s】执行异常,原因:%s", flow.getId(), flow.getName(), engine.getEx().getMessage()), engine.getEx());
// }Map<String, Map<String, Object>> flowResult = new HashMap<>();// 遍历存放每个组件的信息for(FlowNode node : flow.getNodeInstances()) {Map<String, Object> flowInfo = Maps.newHashMap();flowResult.put(node.getNodeName(), flowInfo);flowInfo.put("nodeName", node.getNodeName());flowInfo.put("componentName", node.getComponentName());OperatorWrapper<?, ?> operatorWrapper = engine.getWrapperMap().get(node.getNodeName());// 当且仅当node执行才设置详细信息if(operatorWrapper == null || operatorWrapper.getOperatorResult() == null|| Objects.equals(ResultState.DEFAULT, operatorWrapper.getOperatorResult().getResultState())) {continue;}// 设置input信息Map<String, Object> inputMap = Maps.newHashMap();inputMap.putAll(node.getComponentInfo().getInputs());flowInfo.put("input", inputMap);// 设置output信息OperatorResult operatorResult = engine.getDagContext().getOperatorResultMap().get(node.getNodeName());flowInfo.put("output", operatorResult.getResult());// 设置durationflowInfo.put("duration", operatorWrapper.getOperatorResult().getDuration());// 设置log信息Map<String, Object> logMap = Maps.newHashMap();if(operatorResult.getEx() != null) {logMap.put("stderr", operatorResult.getEx().getStackTrace());}flowInfo.put("log", logMap);}return flowResult;}
上面我把异常判断注释了,当发现组件流有异常时不再抛异常,而是返回结果。这里遍历的是flowNode,里面有写node可能没有被执行(断点执行)。
测试

修改测试代码


可以看到result中展示了每个组件的运行结果。
之后我手动造了一个异常,用于测试报错信息的展示↓

虽然在这里报错了,却没有执行到下面这块↓,也就没有记录异常信息。

原因是我之前写切面的时候把异常捕获了还没有抛出↓

把这块删掉后,再次运行即可看到报错信息:

补充
上面记录耗时的写法并不好,如果未来要进行一些复杂操作,会写的比较杂乱,不过目前来看其实不需要重构,可以当做参考看看。原开源代码中提供了两种思路:类似抽象类的execute方法,再抽象出start方法、success方法、error方法,分别对应组件的执行前、执行完成、执行异常,调用方式和执行的方法execute类似;另一个思路是用回调方法来实现。这里我用前者来扩展实现:
实现过程
com.example.lowcode.core.framework.ComponentInterface——修改

package com.example.lowcode.core.framework;import com.example.lowcode.core.dto.ComponentInfo;
import com.example.lowcode.core.dto2.OperatorResult;
import com.example.lowcode.core.framework2.OperatorWrapper;import java.util.Map;/*** @author llxzdmd* @version IComponent.java, v 0.1 2024年01月02日 19:00 llxzdmd*/
public interface ComponentInterface {default Object defaultValue(ComponentContext context, ComponentInfo componentInfo) {return null;}Map<String, Object> execute(ComponentContext context, ComponentInfo componentInfo) throws Exception;default void onStart(ComponentContext context, ComponentInfo componentInfo){};default void onSuccess(ComponentContext context, ComponentInfo componentInfo, OperatorResult<Object> result){};default void onError(ComponentContext context, ComponentInfo componentInfo, OperatorResult<Object> result){};
}
首先在接口和抽象类中增加三个准备监听阶段的方法。这几个方法是组件去执行时的调用,可以在里面写额外逻辑,但目前需求不需要,直接置空即可。
com.example.lowcode.core.framework2.IOperator——修改

package com.example.lowcode.core.framework2;import com.example.lowcode.core.dto2.OperatorResult;
import com.example.lowcode.core.framework.ComponentContext;/*** Operator接口** @author llxzdmd* @version IOperator.java, 2024年02月18日 16:13 llxzdmd*/
public interface IOperator<P, V> {/*** 自定义OP的默认返回值*/default V defaultValue(P param) {return null;}/*** 该方法实现OP的具体处理逻辑*/V execute(P param, ComponentContext context) throws Exception;void onStart(OperatorWrapper<?, ?> param, ComponentContext context);void onSuccess(OperatorWrapper<?, ?> param, ComponentContext context, OperatorResult<Object> result);void onError(OperatorWrapper<?, ?> param, ComponentContext context, OperatorResult<Object> result);
}
因为引擎在执行的过程中无法获取到组件对象去执行对应的方法,需要获取到封装的IOperator类,由这个类再去执行接口的方法,因此在此处也定义几个阶段。之后需要修改IOperator接口的实现类。
com.example.lowcode.core.framework2.DefaultInvokeMethodComponent——修改
package com.example.lowcode.core.framework2;import com.example.lowcode.core.dto2.OperatorResult;
import com.example.lowcode.core.exception.FlowConfigException;
import com.example.lowcode.core.framework.AbstractComponent;
import com.example.lowcode.core.framework.ComponentContext;
import com.example.lowcode.core.framework.SpringUtil;
import org.springframework.stereotype.Component;import java.util.Map;/*** @author llxzdmd* @version DefaultInvokeMethodComponent.java, 2024年02月18日 19:26 llxzdmd*/
@Component
public class DefaultInvokeMethodComponent implements IOperator<OperatorWrapper<?, ?>, Object> {@Overridepublic Object defaultValue(OperatorWrapper<?, ?> param) {return new Object();}@Overridepublic Object execute(OperatorWrapper<?, ?> param, ComponentContext context) throws Exception {return invokeMethod(param, context);}@Overridepublic void onStart (OperatorWrapper<?, ?> param, ComponentContext context) {invokeMethod(param, context, "onStart", null);}@Overridepublic void onSuccess (OperatorWrapper<?, ?> param, ComponentContext context, OperatorResult<Object> result) {invokeMethod(param, context, "onSuccess", result);}@Overridepublic void onError (OperatorWrapper<?, ?> param, ComponentContext context, OperatorResult<Object> result) {invokeMethod(param, context, "onError", result);}private Object invokeMethod(OperatorWrapper<?, ?> param, ComponentContext context) {OpConfig opConfig = param.getOpConfig();try {Class<?> aClass = Class.forName(opConfig.getClassName());AbstractComponent abstractComponent = (AbstractComponent) SpringUtil.getBean(aClass);return abstractComponent.execute(context, opConfig.getComponentInfo());} catch (Exception e) {throw new RuntimeException(e);}}private void invokeMethod(OperatorWrapper<?, ?> param, ComponentContext context, String methodName, OperatorResult<Object> result){OpConfig opConfig = param.getOpConfig();try {Class<?> aClass = Class.forName(opConfig.getClassName());AbstractComponent abstractComponent = (AbstractComponent) SpringUtil.getBean(aClass);switch (methodName) {case "onStart" -> abstractComponent.onStart(context, opConfig.getComponentInfo());case "onSuccess" -> abstractComponent.onSuccess(context, opConfig.getComponentInfo(), result);case "onError" -> abstractComponent.onError(context, opConfig.getComponentInfo(), result);default -> abstractComponent.defaultValue(context, opConfig.getComponentInfo());}} catch (Exception e) {throw new RuntimeException(e);}}
}
这里新写了一个多一个参数的invokeMethod方法,因为需要得到onSuccess和onError时的运行结果。invokeMethod方法也可以再抽象一层,用反射来执行对应的方法,尝试了一下由于需要获取到每个方法的入参类型,用枚举的话和上面的写法类似;否则需要再定义一个记录需要执行的方法的入参类型、入参值,再在此处解析,成本太大,就不继续抽象了。
com.example.lowcode.core.framework2.DagEngine——修改


在引擎类的对应位置让执行节点调用对应的方法,节点就会调用到组件的对应方法。

之后可以在切面中监听到组件执行这几个方法的动作,进行相应的处理。

之后再把这两行注释掉,就准备就绪可以测试了。
测试


效果符合预期

↑把之前制造的bug去掉,正常运行
总结



需要博客源码可私信免费获取,看到就会回复。
相关文章:
低代码平台后端搭建-阶段完结
前言 最近又要开始为跳槽做准备了,发现还是写博客学的效率高点,在总结其他技术栈之前准备先把这个专题小完结一波。在这一篇中我又试着添加了一些实际项目中可能会用到的功能点,用来验证这个平台的扩展性,以及总结一些学过的知识。…...
暑假考研集训营游记
文章目录 摘要:1.对各大辅导机构考研封闭集训营的一些个人看法:2.对于考研原因一些感想:结语 摘要: Ashy在暑假的时候参加了所在辅导班的为期一个月的考研封闭集训营,有了一些全新的感悟,略作记录。 1.对…...
C#中的报文(Message)
在C#中,报文(Message)通常是指在网络通信中交换的数据单元。报文可以由多种不同的组成部分构成,具体取决于通信协议和应用场景。 以下是一些常见的报文组成部分: 头部(Header):包含…...
Python知识点:如何使用Python与Java进行互操作(Jython)
开篇,先说一个好消息,截止到2025年1月1日前,翻到文末找到我,赠送定制版的开题报告和任务书,先到先得!过期不候! Jython 是一种完全兼容 Java 的 Python 实现,它将 Python 代码编译成…...
ffmpeg解封装解码
文章目录 封装和解封装封装解封装 相关接口解封装的流程图关于AVPacket的解释如何区分不同的码流,视频流,音频流?第一种方式av_find_best_stream第二种方式 通过遍历流 代码 封装和解封装 封装 是把音频流 ,视频流,字…...
golang学习笔记10-循环结构
注:本人已有C,C,Python基础,只写本人认为的重点。 go的循环只有for循环,但有多个语法,可以实现C/C中的while和do while。当然,for循环也有break和continue,这点和C/C相同。 语法1: f…...
Java高级编程——泛型(泛型类、泛型接口、泛型方法,完成详解,并附有案例+代码)
文章目录 泛型21.1 概述21.2 泛型类21.3 泛型方法21.4 泛型接口 泛型 21.1 概述 JDK5中引入的特性,在编译阶段约束操作的数据类型,并进行检查 泛型格式:<数据类型> 泛型只能支持引用数据类型,如果写基本数据类型需要写对…...
GPU硬件如何实现光栅化?
版权声明 本文为“优梦创客”原创文章,您可以自由转载,但必须加入完整的版权声明文章内容不得删减、修改、演绎本文视频版本:见文末 引言 大家好,我是老雷,今天我想从GPU硬件原理出发,给大家分享在图形渲…...
Python写入文件内容:从入门到精通
在日常编程工作中,我们常常会遇到需要将数据保存至磁盘的需求。无论是日志记录、配置文件管理还是数据持久化,掌握如何有效地使用Python来写入文件内容都是必不可少的一项技能。本文将从基础语法开始,逐步深入探讨Python中写入文件内容的各种…...
相亲交易系统源码详解与开发指南
随着互联网技术的发展,越来越多的传统行业开始寻求线上转型,其中就包括婚恋服务。传统的相亲方式已经不能满足现代人快节奏的生活需求,因此,开发一款基于Web的相亲交易系统显得尤为重要开发者h17711347205。本文将详细介绍如何使用…...
Golang | Leetcode Golang题解之第413题等差数列划分
题目: 题解: func numberOfArithmeticSlices(nums []int) (ans int) {n : len(nums)if n 1 {return}d, t : nums[0]-nums[1], 0// 因为等差数列的长度至少为 3,所以可以从 i2 开始枚举for i : 2; i < n; i {if nums[i-1]-nums[i] d {t}…...
汽车总线之----FlexRay总线
Introduction 随着汽车智能化发展,车辆开发的ECU数量不断增加,人们对汽车系统的各个性能方面提出了更高的需求,比如更多的数据交互,更高的传输带宽等。现如今人们广泛接受电子功能来提高驾驶安全性,像ABS防抱死系统&a…...
前端代替后端做分页操作
如果后端没有分页api,前端如何做分页一、使用computed 这个变量应该是计算之后的值,是一个状态管理变量,跟onMounted类似import {computed} from vue // 定义ref储存rolelist,这里是原始数据 const roleList ref([])// 定义页码…...
L3 逻辑回归
🍨 本文为🔗365天深度学习训练营 中的学习记录博客🍖 原作者:K同学啊 在周将使用 LogisticRegression 函数对经典的鸢尾花 (Iris) 数据集进行分类。将详细介绍逻辑回归的数学原理。 1. 逻辑回归的数学原理 逻辑回归是一种线性分…...
Flink系列知识之:Checkpoint原理
Flink系列知识之:Checkpoint原理 在介绍checkpoint的执行流程之前,需要先明白Flink中状态的存储机制,因为状态对于检查点的持续备份至关重要。 State Backends分类 下图显示了Flink中三个内置的状态存储种类。MemoryStateBackend和FsState…...
智算中心动环监控:构建高效、安全的数字基础设施@卓振思众
在当今快速发展的数字经济时代,智算中心作为人工智能和大数据技术的核心支撑设施,正日益成为各行业实现智能化转型的重要基石。为了确保这些高性能计算环境的安全与稳定,卓振思众动环监控应运而生,成为智算中心管理的重要组成部分…...
PyTorch VGG16手写数字识别教程
手写数字识别教程:使用PyTorch和VGG16 1. 环境准备 确保你已安装以下库: pip install torch torchvision2. 导入必要的库 import torch import torch.nn as nn import torch.optim as optim import torchvision.transforms as transforms import tor…...
安卓13删除下拉栏中的设置按钮 android13删除设置按钮
总纲 android13 rom 开发总纲说明 文章目录 1.前言2.问题分析3.代码分析4.代码修改5.编译6.彩蛋1.前言 顶部导航栏下拉可以看到,底部这里有个设置按钮,点击可以进入设备的设置页面,这里我们将更改为删除,不同用户通过这个地方进入设置。也就是下面这个按钮。 2.问题分析…...
FDA辅料数据库在线免费查询-药用辅料
在药物制剂的研制过程中,需要确定这些药用辅料的安全用量。而美国食品药品监督管理局(FDA)的辅料数据库(IID)提供了其制剂研发中的关键参考资源,使得更多的医药研发相关人员及企业单位节省试验环节及时间成…...
git pull 报错 refusing to merge unrelated histories
这个对我来说非常常见,因为我都是先由本地项目,再想着传到github上去。 在本地项目中执行 git init git add . git commit -m “xxx” 在github上创建项目,添加了 README.md 文件。 git remote add origin https://github.com/raoxiaoya/x…...
MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...
微信小程序之bind和catch
这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...
练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...
云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...
对WWDC 2025 Keynote 内容的预测
借助我们以往对苹果公司发展路径的深入研究经验,以及大语言模型的分析能力,我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际,我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测,聊作存档。等到明…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...
Spring Boot面试题精选汇总
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...
MySQL中【正则表达式】用法
MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现(两者等价),用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例: 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用
1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...
