LiteFlow逻辑流引擎集成验证

本文将介绍开源逻辑流组件LiteFlow的架构、设计思想和适用场景,如何基于springboot集成LiteFlow,并验证DSL多种逻辑流程,以及逻辑流设计器的开发思路。

一、逻辑流解决什么问题

在每个公司的系统中,总有一些拥有复杂业务逻辑的系统,这些系统承载着核心业务逻辑,几乎每个需求都和这些核心业务有关,这些核心业务业务逻辑冗长,涉及内部逻辑运算,缓存操作,持久化操作,外部资源调取,内部其他系统RPC调用等等。时间一长,项目几经易手,维护成本就会越来越高。

如果你要对复杂业务逻辑进行新写或者重构,用LiteFlow最合适不过。它是一个编排式的规则引擎框架,组件编排,帮助解耦业务代码,让每一个业务片段都是一个组件。利用LiteFlow,你可以将瀑布流式的代码,转变成以组件为核心概念的代码结构,这种结构的好处是可以任意编排,组件与组件之间是解耦的,组件可以用脚本来定义,组件之间的流转全靠规则来驱动。LiteFlow拥有开源规则引擎最为简单的DSL语法。十分钟就可上手。

LiteFlow适用于拥有复杂逻辑的业务,比如说价格引擎,下单流程等,这些业务往往都拥有很多步骤,这些步骤完全可以按照业务粒度拆分成一个个独立的组件,进行装配复用变更。使用LiteFlow,你会得到一个灵活度高,扩展性很强的系统。因为组件之间相互独立,也可以避免改一处而动全身的这样的风险。然而,对于基于角色任务流转的场景,LiteFlow并非最佳选择,推荐使用Flowable、Activiti、Camunda等专门的流程引擎。关于开源工作流引擎介绍参考:https://lowcode.blog.csdn.net/article/details/116405594

有的时候大家把LiteFlow叫做规则引擎,其实,逻辑引擎和规则引擎还是不一样,我认为LiteFlow是逻辑流引擎,它偏向于组件级接口的编排,粒度更细更底层,而规则引擎(比如:drools),它更偏向于业务规则计算,比如决策树、决策表等,解决某一个特定的业务需求,比如:保险行业投保规则计算。

二、LiteFlow的设计思想

LiteFlow是基于工作台模式进行设计的,何谓工作台模式?

n个工人按照一定顺序围着一张工作台,按顺序各自生产零件,生产的零件最终能组装成一个机器,每个工人只需要完成自己手中零件的生产,而无需知道其他工人生产的内容。每一个工人生产所需要的资源都从工作台上拿取,如果工作台上有生产所必须的资源,则就进行生产,若是没有,就等到有这个资源。每个工人所做好的零件,也都放在工作台上。

这个模式有几个好处:

每个工人无需和其他工人进行沟通。工人只需要关心自己的工作内容和工作台上的资源。这样就做到了每个工人之间的解耦和无差异性。

即便是工人之间调换位置,工人的工作内容和关心的资源没有任何变化。这样就保证了每个工人的稳定性。

如果是指派某个工人去其他的工作台,工人的工作内容和需要的资源依旧没有任何变化,这样就做到了工人的可复用性。

因为每个工人不需要和其他工人沟通,所以可以在生产任务进行时进行实时工位更改:替换,插入,撤掉一些工人,这样生产任务也能实时的被更改。这样就保证了整个生产任务的灵活性。

这个模式映射到LiteFlow框架里,工人就是组件,工人坐的顺序就是流程配置,工作台就是上下文,资源就是参数,最终组装的这个机器就是这个业务。正因为有这些特性,所以LiteFlow能做到统一解耦的组件和灵活的装配。

三、LiteFlow强大的编排能力


LiteFlow的编排语法强大到可以编排出任何你想要的逻辑流程。如下图复杂的语法,如果使用瀑布式的代码去写,那种开发以及维护难度可想而知,但是使用LiteFlow你可以轻松完成逻辑流程的编排,易于维护。

四、LiteFlow使用mysql持久化

LiteFlow支持本地yml文件、mysql等关系型数据库、zookeeper、nacos、Etcd、redis等多种数据持久化方式。

以下介绍用mysql数据库如何持久化LiteFlow的逻辑流。

在mysql数据库中创建2张表:liteflow_chain(逻辑流表)和liteflow_script(脚本节点表)

DROP TABLE IF EXISTS `liteflow_chain`;
CREATE TABLE `liteflow_chain`  (
  `id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '主键',
  `application_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '应用名称',
  `chain_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '逻辑流程名称',
  `chain_desc` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '逻辑流描述',
  `el_data` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '逻辑流内容',
  `chain_flow` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '逻辑流图',
  `enable` tinyint NULL DEFAULT NULL COMMENT '是否有效',
  `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '创建人',
  `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
  `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '修改人',
  `update_time` datetime NULL DEFAULT NULL COMMENT '修改时间',
  `sys_org_code` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '所属部门',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

DROP TABLE IF EXISTS `liteflow_script`;
CREATE TABLE `liteflow_script`  (
  `id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '主键',
  `application_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '应用名称',
  `script_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '脚本ID',
  `script_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '脚本名称',
  `script_data` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '脚本内容',
  `script_type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '脚本类型',
  `script_language` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '脚本语言',
  `enable` tinyint NULL DEFAULT NULL COMMENT '是否有效',
  `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '创建人',
  `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
  `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '修改人',
  `update_time` datetime NULL DEFAULT NULL COMMENT '修改时间',
  `sys_org_code` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '所属部门',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

数据库表创建完成后,插入逻辑流测试数据:

INSERT INTO `liteflow_chain` VALUES ('1', 'demo', 'chain1', '串行流', '  THEN(a, b, c);', NULL, 1, NULL, '2024-03-22 09:45:15', NULL, NULL, NULL);
INSERT INTO `liteflow_chain` VALUES ('2', 'demo', 'chain2', '并行流', ' WHEN(a, b, c);', NULL, 1, NULL, '2024-03-22 09:48:07', NULL, NULL, NULL);
INSERT INTO `liteflow_chain` VALUES ('3', 'demo', 'chain3', '串行并行流', '        THEN(\r\n            a, b, WHEN(c,d)\r\n        );', NULL, 1, NULL, '2024-03-22 09:48:07', NULL, NULL, NULL);
INSERT INTO `liteflow_chain` VALUES ('4', 'demo', 'chain4', '串行并行流', '        THEN(\r\n            a,\r\n            WHEN(b, c),\r\n            d\r\n        );', NULL, 1, NULL, '2024-03-22 09:48:07', NULL, NULL, NULL);
INSERT INTO `liteflow_chain` VALUES ('5', 'demo', 'chain5', '串并串行流', '        THEN(\r\n            a,\r\n            WHEN(b, THEN(c, d)),\r\n            e\r\n        );', NULL, 1, NULL, '2024-03-22 09:48:07', NULL, NULL, NULL);
INSERT INTO `liteflow_chain` VALUES ('6', 'demo', 'chain6', '选择流', 'THEN(a, SWITCH(s1).to(b, c, d));', NULL, 1, NULL, '2024-03-22 09:48:07', NULL, NULL, NULL);
INSERT INTO `liteflow_chain` VALUES ('7', 'demo', 'chain7', '循环流', 'THEN(a, FOR(s2).DO(THEN(b, c)));', NULL, 1, NULL, '2024-03-22 09:48:07', NULL, NULL, NULL);
INSERT INTO `liteflow_chain` VALUES ('8', 'demo', 'chain8', '串并串流程', '        THEN(\r\n            a,\r\n            WHEN( THEN(b, c), d),\r\n            e\r\n        );', NULL, 1, NULL, '2024-03-22 14:56:32', NULL, NULL, NULL);
INSERT INTO `liteflow_script` VALUES ('1', 'demo', 's1', '选择脚本', '  var count = defaultContext.getData(\"count\");\r\n                if(count < 100){\r\n                    return \"b\";\r\n                }else if( count > 100 && count < 500 ){\r\n                    return \"c\";\r\n                }else{\r\n                  return \"d\";\r\n                }', 'switch_script', 'js', 1, NULL, '2024-03-22 11:35:39', NULL, NULL, NULL);
INSERT INTO `liteflow_script` VALUES ('2', 'demo', 's2', '循环脚本', '                var count = 2;\r\n                return count;', 'for_script', 'js', 1, NULL, '2024-03-22 11:36:35', NULL, NULL, NULL);

插入数据的表如下:

五、Springboot集成LiteFlow

  1. 新建一个springboot工程,在pom.xml中引入如下Jar:
<dependency>
    <groupId>com.yomahub</groupId>
    <artifactId>liteflow-spring-boot-starter</artifactId>
    <version>2.11.4.2</version>
</dependency>
<dependency>
    <groupId>com.yomahub</groupId>
    <artifactId>liteflow-rule-sql</artifactId>
    <version>2.11.4.2</version>
</dependency>
<dependency>
    <groupId>com.yomahub</groupId>
    <artifactId>liteflow-script-groovy</artifactId>
    <version>2.11.4.2</version>
</dependency>
<dependency>
    <groupId>com.yomahub</groupId>
    <artifactId>liteflow-script-graaljs</artifactId>
    <version>2.11.4.2</version>
</dependency>

2、application.yaml文件中增加对liteflow的配置

server:
  port: 8080
spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/camunda719?characterEncoding=UTF-8&useUnicode=true&useSSL=false&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
liteflow:
  rule-source-ext-data-map:
    applicationName: demo
    #以下是chain表的配置,这个一定得有
    chainTableName: liteflow_chain
    chainApplicationNameField: application_name
    chainNameField: chain_name
    elDataField: el_data
    chainEnableField: enable
    #以下是script表的配置,如果你没使用到脚本,下面可以不配置
    scriptTableName: liteflow_script
    scriptApplicationNameField: application_name
    scriptIdField: script_id
    scriptNameField: script_name
    scriptDataField: script_data
    scriptTypeField: script_type
    scriptLanguageField: script_language
    scriptEnableField: enable
    #以下是轮询机制的配置
    pollingEnabled: true
    pollingStartSeconds: 0
    pollingIntervalSeconds: 30


3、新建逻辑流节点处理类

liteflow中的组件分普通组件、选择组件、条件组件等,需要分别继承NodeComponent、NodeSwitchComponent、NodeIfComponent等类需要你自己去定义一个类去继承这些父类。这样一方面造成了耦合,另一方面由于java是单继承制,所以使用者就无法再去继承自己的类了,在自由度上就少了很多玩法。

LiteFlow提供了声明式组件方式。声明式组件这一特性允许你自定义的组件不继承任何类和实现任何接口,普通的类也可以依靠注解来完成LiteFlow组件的声明。以下的测试类采用了组件声明方式。

为了方便模拟多种逻辑测试,分别建ACmp、BCmp、CCmp、DCmp、ECmp共5个类,每个类中线程睡眠500毫秒,模拟实际业务耗时。

ACmp.java类:

package com.yuncheng.logicflow;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.slot.DefaultContext;

@LiteflowComponent
public class ACmp  {
    @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = "a", nodeName = "组件a",  nodeType = NodeTypeEnum.COMMON)
    public void process(NodeComponent bindCmp) {
        //do your business
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        DefaultContext context = bindCmp.getContextBean(DefaultContext.class);
        context.setData("key1","hello");
        context.setData("count",200);
        User user = new User();
        user.setName("张三");
        user.setAge(28);
        context.setData("user",user);
        System.out.println("==============执行a: " );
    }
}


 

BCmp.java类:

package com.yuncheng.logicflow;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.slot.DefaultContext;

@LiteflowComponent
public class BCmp {

    @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = "b", nodeName = "组件b",  nodeType = NodeTypeEnum.COMMON)
    public void process(NodeComponent bindCmp) {
        //do your business
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        DefaultContext context = bindCmp.getContextBean(DefaultContext.class);
        User user = context.getData("user");
        System.out.println("==============执行b: ");
        System.out.println("==============执行b:key1= " + context.getData("key1"));
        System.out.println("==============执行b:count= " + context.getData("count"));
        System.out.println("==============执行b:user= " + user.getName());
    }
}

CCmp.java类:

package com.yuncheng.logicflow;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.slot.DefaultContext;

@LiteflowComponent
public class CCmp{

    @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = "c", nodeName = "组件c",  nodeType = NodeTypeEnum.COMMON)
    public void process(NodeComponent bindCmp) {
        //do your business
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        DefaultContext context = bindCmp.getContextBean(DefaultContext.class);
        System.out.println("==============执行c: ");
        System.out.println("==============执行c:key1= " + context.getData("key1"));
        System.out.println("==============执行c:count= " + context.getData("count"));
    }
}

DCmp.java类:

package com.yuncheng.logicflow;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
import com.yomahub.liteflow.enums.NodeTypeEnum;

@LiteflowComponent
public class DCmp {

    @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = "d", nodeName = "组件d",  nodeType = NodeTypeEnum.COMMON)
    public void process(NodeComponent bindCmp) {
        //do your business
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("==============执行d: " );
    }
}

ECmp.java类:

package com.yuncheng.logicflow;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
import com.yomahub.liteflow.enums.NodeTypeEnum;

@LiteflowComponent
public class ECmp {

    @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = "e", nodeName = "组件e",  nodeType = NodeTypeEnum.COMMON)
    public void process(NodeComponent bindCmp) {
        //do your business
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("==============执行e: ");
    }
}

LogicController.java,模拟测试的REST服务类:

package com.yuncheng.logicflow;

import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.Date;

@RestController
@RequestMapping
public class LogicController {
    @Resource
    private FlowExecutor flowExecutor;

    /**
     * 浏览器里访问:http://localhost:8080/logicTest/chain1
     * @param key 逻辑流定义的key
     * @return
     */
    @GetMapping(value = "/logicTest/{key}")
    public String logicTest(@PathVariable("key") String key) {
        long begin = new Date().getTime();
        LiteflowResponse response = flowExecutor.execute2Resp(key, "arg");
        long end = new Date().getTime();
        long hs = end - begin;
        System.out.println("====================执行耗时:" + hs);
        return response.getSlot().getExecuteStepStr() + ",执行耗时: " + hs;
    }

}

LogicFlowApplication.java,springboot应用启动类:

package com.yuncheng;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;

@SpringBootApplication
public class LogicFlowApplication {
    public static void main(String... args) {
        ConfigurableApplicationContext application =  SpringApplication.run(LogicFlowApplication.class, args);
        Environment env = application.getEnvironment();
        String port = env.getProperty("server.port");
        String path = env.getProperty("server.servlet.context-path");
        if (path == null || "".equals(path)) {
            path = "/";
        }
        System.out.println("\n----------------------------------------------------------\n" +
                "\tLogicFlowApplication is running!\n" +
                "\tPort:\t" + port + "\n" +
                "\tPath:\t" + path + "\n" +
                "----------------------------------------------------------");
    }
}

启动springboot工程:

启动成功,并发现LiteFlow定时查询liteflow_chain(逻辑流表)和liteflow_script(脚本节点表),说明LiteFlow使用mysql数据库持久化是成功的。

六、测试验证LiteFlow规则

1、串行流验证

逻辑流表达式:THEN(a, b, c);

浏览器地址栏输入:http://localhost:8080/logicTest/chain1

返回结果:a[组件a]==>b[组件b]==>c[组件c],执行耗时: 1528

2、并行流验证

逻辑流表达式: WHEN(a, b, c);

浏览器地址栏输入:http://localhost:8080/logicTest/chain2

返回结果:a[组件a]==>b[组件b]==>c[组件c],执行耗时: 550

3、串并流验证

逻辑流表达式: THEN( a, b, WHEN(c,d));

浏览器地址栏输入:http://localhost:8080/logicTest/chain3

返回结果:a[组件a]==>b[组件b]==>c[组件c]==>d[组件d],执行耗时: 1527

4、选择流验证

逻辑流表达式: THEN(a, SWITCH(s1).to(b, c, d));

浏览器地址栏输入:http://localhost:8080/logicTest/chain6

返回结果:a[组件a]==>s1[选择脚本]==>c[组件c],执行耗时: 2468

5、循环流验证

逻辑流表达式:THEN(a, FOR(s2).DO(THEN(b, c)));

浏览器地址栏输入:http://localhost:8080/logicTest/chain7

返回结果:a[组件a]==>s2[循环脚本]==>b[组件b]==>c[组件c]==>b[组件b]==>c[组件c],执行耗时: 2605

七、逻辑流设计器开发思路

开源逻辑流LiteFlow只有后端引擎,没有前端逻辑流设计器,需要使用者自行开发设计器。可使用滴滴开源的LogicFlow前端逻辑流设计器进行二次开发实现。

LogicFlow 是一款滴滴开源的流程图编辑框架,提供了一系列流程图交互、编辑所必需的功能和灵活的节点自定义、插件等拓展机制。LogicFlow支持前端研发自定义开发各种逻辑编排场景,如流程图、ER图、BPMN流程等。在工作审批配置、机器人逻辑编排、无代码平台流程配置都有较好的应用。体验地址:https://site.logic-flow.cn/


LogicFlow 虽然提供了可视化图形编排框架,但LiteFlow逻辑流设计器开发有个技术难点,就是如何把图形化的节点编排转换为LiteFlow可执行的逻辑流表达式。云程低代码平台(http://www.yunchengxc.com)开发团队目前正在开发LiteFlow逻辑流设计器,初步思路借鉴图论中的有向无环图算法解决,通过节点的流向和度数,计算生成LiteFlow逻辑流表达式,开发完成后考虑把逻辑流设计器开源。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/502045.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

喜报 | 攸信技术再获殊荣,被授予厦门攸信智能制造系统研发中心

近日&#xff0c;厦门攸信信息技术有限公司凭借其卓越的科技创新实力和突出的研发成果&#xff0c;经过厦门市科学技术局的严格筛选与评审&#xff0c;三月份被正式授予“厦门攸信智能制造系统研发中心”的荣誉称号。 2023年12月&#xff0c;厦门市科学技术局积极响应《厦门市关…

2024年阿里云无影云电脑具体价格,99元一年起

2024年阿里云无影云电脑具体价格99元一年起&#xff0c;配置可选4核8G和8核16G&#xff0c;使用时长可选800小时和1800小时&#xff0c;目前有四款无影云电脑可以享受优惠价格&#xff0c;阿里云服务器网aliyunfuwuqi.com整理2024年无影云电脑详细配置和优惠价格表&#xff0c;…

20240322-1-协同过滤面试题

协同过滤面试题 1. 协同过滤推荐有哪些类型 基于用户(user-based)的协同过滤 基于用户(user-based)的协同过滤主要考虑的是用户和用户之间的相似度&#xff0c;只要找出相似用户喜欢的物品&#xff0c;并预测目标用户对对应物品的评分&#xff0c;就可以找到评分最高的若干个物…

VS Code 安装

VS Code 安装文档 一、下载 进入VS Code官网&#xff1a;https://code.visualstudio.com&#xff0c;点击 DownLoad for Windows下载windows版本 当然也可以点击旁边的箭头&#xff0c;下载Windows版本 或 Mac OS 版本 Stable&#xff1a;稳定版Insiders&#xff1a;内测版 …

算法系列--动态规划--背包问题(4)--完全背包拓展题目

&#x1f495;"这种低水平质量的攻击根本就不值得我躲&#xff01;"&#x1f495; 作者&#xff1a;Lvzi 文章主要内容&#xff1a;算法系列–动态规划–背包问题(4)–完全背包拓展题目 大家好,今天为大家带来的是算法系列--动态规划--背包问题(4)--完全背包拓展题目…

Codeforces Round 838 (Div. 2) D. GCD Queries

题目 思路&#xff1a; #include <bits/stdc.h> using namespace std; #define int long long #define pb push_back #define fi first #define se second #define lson p << 1 #define rson p << 1 | 1 const int maxn 1e6 5, inf 1e9, maxm 4e4 5; co…

信息系统项目管理师——第11章项目成本管理(重要)

选择、本章节内容属于10大管理知识领域中的重中之重案例、论文都会考&#xff0c;需要完全掌握。 选择题大概考3分左右&#xff0c;理论和计算都会考。 案例题&#xff0c;必考内容&#xff0c;挣值相关的计算&#xff0c;必须得会。 论文题&#xff0c;考的比较多&#xff0c;…

STM32之HAL开发——DMA转运串口数据

DMA功能框图&#xff08;F1系列&#xff09; 如果外设要想通过 DMA 来传输数据&#xff0c;必须先给 DMA 控制器发送 DMA 请求&#xff0c; DMA 收到请求信号之后&#xff0c;控制器会给外设一个应答信号&#xff0c;当外设应答后且 DMA 控制器收到应答信号之后&#xff0c;就会…

【Linux】POSIX信号量{基于环形队列的PC模型/理解信号量的出现/参考代码}

文章目录 1.POSIX信号量1.1介绍1.2接口 2.基于环形队列的PC模型2.1环形队列常用计算2.2如何设计&#xff1f;2.3如何实现&#xff1f; 3.细节处理3.1空间资源和数据资源3.2push/pop3.3理解信号量的出现1.回顾基于阻塞队列的PC模型中条件变量的使用2.如何理解信号量的投入使用&a…

【活动预告】SLMOps 系列(一)|SLMOps 基础 - Azure AI Studio 的 SLM 应用构建

点击蓝字 关注我们 编辑&#xff1a;Alan Wang 排版&#xff1a;Rani Sun 2023年&#xff0c;Azure OpenAI Service 引领了 AI 2.0 时代的热潮&#xff0c;各行业企业都在 AI 模型的探索与应用中持续发力。相比复杂度更高的大模型&#xff0c;有时候轻量且高效的小模型&#xf…

时序预测 | MATLAB实现BiTCN双向时间卷积神经网络的时间序列预测

时序预测 | MATLAB实现BiTCN双向时间卷积神经网络的时间序列预测 目录 时序预测 | MATLAB实现BiTCN双向时间卷积神经网络的时间序列预测预测效果基本介绍程序设计参考资料预测效果 基本介绍 MATLAB实现BiTCN双向时间卷积

【C语言基础】:自定义类型(一)--> 结构体

文章目录 一、内置类型与自定义类型1.1 内置类型&#xff08;基本数据类型&#xff09;1.2 自定义类型 二、结构体2.1 结构体的声明2.2 结构体变量的创建和初始化2.3 结构体的特殊声明2.4 结构体的自引用 三、结构体内存对齐3.1 对齐规则3.2 为什么存在内存对齐3.3 修改默认对齐…

十七、InnoDB 一次更新事务的执行过程

一、InnoDB的一次更新事务是怎么实现的&#xff1f; InnoDB的一次更新事务涉及到多个组件和步骤&#xff0c;包括Buffer Pool、BinLog、UndoLog、RedoLog以及物理磁盘。 下面是一次完整的事务更新操作过程&#xff1a; 1. 加载数据到缓存中&#xff08;Buffer Pool&#xff0…

Huggingface模型下载

1. 基础信息 huggingface的模型排行榜&#xff08;需要翻墙&#xff09;&#xff1a;https://huggingface.co/spaces/mteb/leaderboard 2. 下载模型 2.1 手动一个个下载&#xff08;方式1&#xff09; 2. 使用huggingface-cli下载(方式2) pip install -U huggingface_hub h…

英文网站怎么推广,英文网站推广排名方案

英文网站的推广对于吸引国际用户、提升品牌知名度和增加业务收入至关重要。而在全球范围内&#xff0c;谷歌是最主要的搜索引擎之一&#xff0c;因此谷歌SEO排名优化是英文网站推广的重要手段之一。本文将介绍英文网站推广的方法&#xff0c;并重点探讨谷歌SEO排名优化推广策略…

STM32嵌套中断向量控制器NVIC

一、嵌套终端向量控制器NVIC 1.1NVIC介绍 NVIC&#xff08;Nest Vector Interrupt Controller&#xff09;&#xff0c;嵌套中断向量控制器&#xff0c;作用是管理中断嵌套 先级。 核心任务是管理中断优 管理中断嵌套&#xff1a;我们在处理某个中断的过程中还没处理完这个中…

Python环境下基于慢特征分析SFA的过程监控(TE数据)

近几年来&#xff0c;慢特征分析&#xff0c;作为一种新兴的非监督型特征提取算法&#xff0c;正在逐渐兴起。它以变量随时间的一阶导数的大小来衡量变量变化的快慢&#xff0c;并从建模数据中提取出变化最慢的潜在特征变量&#xff0c;称为“不变量”或“慢特征”。 因为工业…

在新能源充电桩、智能充电枪、储能等产品领域得到广泛应用的两款微功耗轨至轨运算放大器芯片——D8541和D8542

D8541和D8542是我们推荐的两款微功耗轨至轨运算放大器芯片&#xff0c;其中D8541为单运放&#xff0c; D8542为双运放&#xff0c;它特别适用于NTC温度采集电路、ADC基准电压电路、有源滤波器、电压跟随器、信号放大器等电路应用&#xff0c;在新能源充电桩、智能充电枪、…

显示器亮度调节,如何调屏幕亮度

我们常说的显示器亮度&#xff0c;其实就是屏幕亮度。在使用电脑的时候呢&#xff0c;屏幕亮度直接可以影响我们的视觉感官&#xff0c;太亮度或者过暗都会伤害视力&#xff0c;而且眼睛看着也不舒服。那么电脑如何调屏幕亮度呢?操作方法很简单。接下来小编为大家介绍&#xf…

2024测试员最佳跳槽频率是多少?进来看看你是不是符合!

最近笔者刷到一则消息&#xff0c;一位测试员在某乎上分享&#xff0c;从月薪5K到如今的20K&#xff0c;他总共跳了10次槽&#xff0c;其中还经历过两次劳动申诉&#xff0c;拿到了大几万的赔偿&#xff0c;被同事们称为“职场碰瓷人”。 虽说这种依靠跳槽式的挣钱法相当奇葩&…