规则引擎Drools,基于mysql实现动态加载部署

文章目录

  • 一、使用
    • 1、参考资料
    • 2、引包
    • 3、创建规则实体类
    • 4、实现drools动态规则
    • 5、模拟数据库,实现规则的CRUD
    • 6、创建控制层
    • 7、测试规则的动态添加
      • (1)添加规则
      • (2)修改规则
      • (3)删除规则
    • 8、模拟2个kbase
    • 9、并发安全测试
  • 二、优化点
  • 参考资料

一、使用

1、参考资料

参考文档:
https://www.cnblogs.com/huan1993/p/16319931.html
https://blog.csdn.net/fu_huo_1993/article/details/124998602
https://gitee.com/huan1993/spring-cloud-parent/tree/master/drools/drools-dynamic-crud-rule

2、引包

gradle引包:

implementation 'org.drools:drools-core:7.74.1.Final'
implementation 'org.drools:drools-compiler:7.74.1.Final'
implementation 'org.drools:drools-templates:7.74.1.Final'
implementation 'org.drools:drools-mvel:7.74.1.Final'
implementation 'org.kie:kie-api:7.74.1.Final'
implementation ('org.kie:kie-spring:7.74.1.Final') {
    exclude(group: 'org.springframework', module: 'spring-tx')
    exclude(group: 'org.springframework', module: 'spring-beans')
    exclude(group: 'org.springframework', module: 'spring-core')
    exclude(group: 'org.springframework', module: 'spring-context')
}

maven引包:
可以参照大佬写的引(我没试过),或者按照我上面的引入。

<dependencyManagement>
   <dependencies>
        <dependency>
            <groupId>org.drools</groupId>
            <artifactId>drools-bom</artifactId>
            <type>pom</type>
            <version>7.69.0.Final</version>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>2.6.7</version>
            <scope>import</scope>
            <type>pom</type>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.drools</groupId>
        <artifactId>drools-compiler</artifactId>
    </dependency>
    <dependency>
        <groupId>org.drools</groupId>
        <artifactId>drools-mvel</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.22</version>
    </dependency>
</dependencies>

3、创建规则实体类

此实体类和数据库中的表是一一对应的。

需要注意的属性:
kieBaseName: 创建的kbase的名字。
kiePackageName:该kbase的属性package的值。

import lombok.Getter;
import lombok.Setter;

import java.util.Date;

/**
 * drools 规则实体类
 *
 * @author huan.fu
 * @date 2022/5/27 - 10:00
 */
@Getter
@Setter
public class DroolsRule {

    /**
     * 规则id
     */
    private Long ruleId;
    /**
     * kbase的名字
     */
    private String kieBaseName;
    /**
     * 设置该kbase需要从那个目录下加载文件,这个是一个虚拟的目录,相对于 `src/main/resources`
     * 比如:kiePackageName=rules/rule01 那么当前规则文件写入路径为: kieFileSystem.write("src/main/resources/rules/rule01/1.drl")
     */
    private String kiePackageName;
    /**
     * 规则内容
     */
    private String ruleContent;
    /**
     * 规则创建时间
     */
    private Date createdTime;
    /**
     * 规则更新时间
     */
    private Date updateTime;

    public void validate() {
        if (this.ruleId == null || isBlank(kieBaseName) || isBlank(kiePackageName) || isBlank(ruleContent)) {
            throw new RuntimeException("参数有问题");
        }
    }

    private boolean isBlank(String str) {
        return str == null || str.isEmpty();
    }
}


4、实现drools动态规则

需要注意的是:

KieFileSystem 需要是单例的,即使用的是同一个。
KieContainer需要是单例的,即使用的是同一个。
通过updateToKieModule方法动态更新。

import com.huan.drools.entity.DroolsRule;
import lombok.extern.slf4j.Slf4j;
import org.drools.compiler.kie.builder.impl.InternalKieModule;
import org.drools.compiler.kie.builder.impl.KieContainerImpl;
import org.kie.api.KieBase;
import org.kie.api.KieServices;
import org.kie.api.builder.KieBuilder;
import org.kie.api.builder.KieFileSystem;
import org.kie.api.builder.Message;
import org.kie.api.builder.Results;
import org.kie.api.builder.model.KieBaseModel;
import org.kie.api.builder.model.KieModuleModel;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.springframework.stereotype.Component;

import java.util.Collection;
import java.util.List;

/**
 * drools 管理
 */
@Component
@Slf4j
public class DroolsManager {

    // 此类本身就是单例的
    private final KieServices kieServices = KieServices.get();
    // kie文件系统,需要缓存,如果每次添加规则都是重新new一个的话,则可能出现问题。即之前加到文件系统中的规则没有了
    private final KieFileSystem kieFileSystem = kieServices.newKieFileSystem();
    // 可以理解为构建 kmodule.xml
    private final KieModuleModel kieModuleModel = kieServices.newKieModuleModel();
    // 需要全局唯一一个,如果每次加个规则都新创建一个,那么旧需要销毁之前创建的kieContainer,如果此时有正在使用的KieSession,则可能有问题
    private KieContainer kieContainer;

    /**
     * 判断该kbase是否存在
     */
    public boolean existsKieBase(String kieBaseName) {
        if (null == kieContainer) {
            return false;
        }
        Collection<String> kieBaseNames = kieContainer.getKieBaseNames();
        if (kieBaseNames.contains(kieBaseName)) {
            return true;
        }
        log.info("需要创建KieBase:{}", kieBaseName);
        return false;
    }

    public void deleteDroolsRule(String kieBaseName, String packageName, String ruleName) {
        if (existsKieBase(kieBaseName)) {
            KieBase kieBase = kieContainer.getKieBase(kieBaseName);
            kieBase.removeRule(packageName, ruleName);
            log.info("删除kieBase:[{}]包:[{}]下的规则:[{}]", kieBaseName, packageName, ruleName);
        }
    }

    /**
     * 添加或更新 drools 规则
     */
    public void addOrUpdateRule(DroolsRule droolsRule) {
        // 获取kbase的名称
        String kieBaseName = droolsRule.getKieBaseName();
        // 判断该kbase是否存在
        boolean existsKieBase = existsKieBase(kieBaseName);
        // 该对象对应kmodule.xml中的kbase标签
        KieBaseModel kieBaseModel = null;
        if (!existsKieBase) {
            // 创建一个kbase
            kieBaseModel = kieModuleModel.newKieBaseModel(kieBaseName);
            // 不是默认的kieBase
            kieBaseModel.setDefault(false);
            // 设置该KieBase需要加载的包路径
            kieBaseModel.addPackage(droolsRule.getKiePackageName());
            // 设置kieSession
            kieBaseModel.newKieSessionModel(kieBaseName + "-session")
                    // 不是默认session
                    .setDefault(false);
        } else {
            // 获取到已经存在的kbase对象
            kieBaseModel = kieModuleModel.getKieBaseModels().get(kieBaseName);
            // 获取到packages
            List<String> packages = kieBaseModel.getPackages();
            if (!packages.contains(droolsRule.getKiePackageName())) {
                kieBaseModel.addPackage(droolsRule.getKiePackageName());
                log.info("kieBase:{}添加一个新的包:{}", kieBaseName, droolsRule.getKiePackageName());
            } else {
                kieBaseModel = null;
            }
        }
        String file = "src/main/resources/" + droolsRule.getKiePackageName() + "/" + droolsRule.getRuleId() + ".drl";
        log.info("加载虚拟规则文件:{}", file);
        kieFileSystem.write(file, droolsRule.getRuleContent());

        if (kieBaseModel != null) {
            String kmoduleXml = kieModuleModel.toXML();
            log.info("加载kmodule.xml:[\n{}]", kmoduleXml);
            kieFileSystem.writeKModuleXML(kmoduleXml);
        }

        KieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem);
        // 通过KieBuilder构建KieModule下所有的KieBase
        kieBuilder.buildAll();
        // 获取构建过程中的结果
        Results results = kieBuilder.getResults();
        // 获取错误信息
        List<Message> messages = results.getMessages(Message.Level.ERROR);
        if (null != messages && !messages.isEmpty()) {
            for (Message message : messages) {
                log.error(message.getText());
            }
            throw new RuntimeException("加载规则出现异常");
        }
        // KieContainer只有第一次时才需要创建,之后就是使用这个
        if (null == kieContainer) {
            kieContainer = kieServices.newKieContainer(kieServices.getRepository().getDefaultReleaseId());
        } else {
            // 实现动态更新
            ((KieContainerImpl) kieContainer).updateToKieModule((InternalKieModule) kieBuilder.getKieModule());
        }
    }

    /**
     * 触发规则,此处简单模拟,会向规则中插入一个Integer类型的值
     */
    public String fireRule(String kieBaseName, Integer param) {
        // 创建kieSession
        KieSession kieSession = kieContainer.newKieSession(kieBaseName + "-session");
        StringBuilder resultInfo = new StringBuilder();
        kieSession.setGlobal("resultInfo", resultInfo);
        kieSession.insert(param);
        kieSession.fireAllRules();
        kieSession.dispose();
        return resultInfo.toString();
    }
}

5、模拟数据库,实现规则的CRUD

此处是使用内存保存规则,也可以保存到数据库中。


import com.huan.drools.DroolsManager;
import com.huan.drools.entity.DroolsRule;
import com.huan.drools.service.DroolsRuleService;
import org.springframework.stereotype.Service;

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

@Service
public class DroolsRuleServiceImpl implements DroolsRuleService {

    @Resource
    private DroolsManager droolsManager;

    /**
     * 模拟数据库
     */
    private Map<Long, DroolsRule> droolsRuleMap = new HashMap<>(16);

    @Override
    public List<DroolsRule> findAll() {
        return new ArrayList<>(droolsRuleMap.values());
    }

    @Override
    public void addDroolsRule(DroolsRule droolsRule) {
        droolsRule.validate();
        droolsRule.setCreatedTime(new Date());
        droolsRuleMap.put(droolsRule.getRuleId(), droolsRule);
        droolsManager.addOrUpdateRule(droolsRule);
    }

    @Override
    public void updateDroolsRule(DroolsRule droolsRule) {
        droolsRule.validate();
        droolsRule.setUpdateTime(new Date());
        droolsRuleMap.put(droolsRule.getRuleId(), droolsRule);
        droolsManager.addOrUpdateRule(droolsRule);
    }

    @Override
    public void deleteDroolsRule(Long ruleId, String ruleName) {
        DroolsRule droolsRule = droolsRuleMap.get(ruleId);
        if (null != droolsRule) {
            droolsRuleMap.remove(ruleId);
            droolsManager.deleteDroolsRule(droolsRule.getKieBaseName(), droolsRule.getKiePackageName(), ruleName);
        }
    }
}

6、创建控制层

@RestController
@RequestMapping("/drools/rule")
public class DroolsRuleController {

    @Resource
    private DroolsRuleService droolsRuleService;
    @Resource
    private DroolsManager droolsManager;

    @GetMapping("findAll")
    public List<DroolsRule> findAll() {
        return droolsRuleService.findAll();
    }

    @PostMapping("add")
    public String addRule(@RequestBody DroolsRule droolsRule) {
        droolsRuleService.addDroolsRule(droolsRule);
        return "添加成功";
    }

    @PostMapping("update")
    public String updateRule(@RequestBody DroolsRule droolsRule) {
        droolsRuleService.updateDroolsRule(droolsRule);
        return "修改成功";
    }

    @PostMapping("deleteRule")
    public String deleteRule(Long ruleId, String ruleName) {
        droolsRuleService.deleteDroolsRule(ruleId, ruleName);
        return "删除成功";
    }

    @GetMapping("fireRule")
    public String fireRule(String kieBaseName, Integer param) {
        return droolsManager.fireRule(kieBaseName, param);
    }
}

7、测试规则的动态添加

(1)添加规则

curl --location --request POST 'http://localhost:8080/drools/rule/add' \
--header 'User-Agent: apifox/1.0.0 (https://www.apifox.cn)' \
--header 'Content-Type: application/json' \
--data-raw '{
    "ruleId": 1,
    "kieBaseName": "kieBase01",
    "kiePackageName": "rules.rule01",
    "ruleContent": "package rules.rule01 \n global java.lang.StringBuilder resultInfo \n rule \"rule-01\" when $i: Integer() then resultInfo.append(drools.getRule().getPackageName()).append(\".\").append(drools.getRule().getName()).append(\"执行了,前端传递的参数:\").append($i); end"
}'

package rules.rule01

global java.lang.StringBuilder resultInfo

rule "rule-01"
    when
        $i: Integer()
    then
        resultInfo.append(drools.getRule().getPackageName()).append(".").append(drools.getRule().getName()).append("执行了,前端传递的参数:").append($i);
end

运行:

~ curl http://localhost:8080/drools/rule/fireRule\?kieBaseName\=kieBase01\&param\=1
rules.rule01.rule-01执行了,前端传递的参数:1%~

可以看到我们动态加载的规则执行了。

(2)修改规则

需求: 在上面(1)测试规则的动态添加的基础上,修改规则。

之前的规则:

package rules.rule01

global java.lang.StringBuilder resultInfo

rule "rule-01"
    when
        $i: Integer()
    then
        resultInfo.append(drools.getRule().getPackageName()).append(".").append(drools.getRule().getName()).append("执行了,前端传递的参数:").append($i);
end

修改后的规则:

package rules.rule01

global java.lang.StringBuilder resultInfo

rule "rule-01"
    when
        $i: Integer(intValue() > 3) // 注意此处修改了
    then
        resultInfo.append(drools.getRule().getPackageName()).append(".").append(drools.getRule().getName()).append("执行了,前端传递的参数:").append($i);
end

可以看到修改的地方为$i: Integer(intValue() > 3),即增加了一个条件判断。

运行修改规则:

~ curl --location --request POST 'http://localhost:8080/drools/rule/update' \
--header 'User-Agent: apifox/1.0.0 (https://www.apifox.cn)' \
--header 'Content-Type: application/json' \
--data-raw '{
    "ruleId": 1,
    "kieBaseName": "kieBase01",
    "kiePackageName": "rules.rule01",
    "ruleContent": "package rules.rule01 \n global java.lang.StringBuilder resultInfo \n rule \"rule-01\" when $i: Integer(intValue() > 3) then resultInfo.append(drools.getRule().getPackageName()).append(\".\").append(drools.getRule().getName()).append(\"执行了,前端传递的参数:\").append($i); end"
}'

此处修改了规则内存中Integer的值必须>3时才执行。

运行:

~ curl http://localhost:8080/drools/rule/fireRule\?kieBaseName\=kieBase01\&param\=1~ curl http://localhost:8080/drools/rule/fireRule\?kieBaseName\=kieBase01\&param\=6
rules.rule01.rule-01执行了,前端传递的参数:6%~

从上面可以看到,当我们传递的param=1时,没有结果数据,当param=6时有结果输出。

(3)删除规则

~ curl --location --request POST 'http://localhost:8080/drools/rule/deleteRule?ruleId=1&ruleName=rule-01'

删除成功%~

运行结果

~ curl http://localhost:8080/drools/rule/fireRule\?kieBaseName\=kieBase01\&param\=6~ curl http://localhost:8080/drools/rule/fireRule\?kieBaseName\=kieBase01\&param\=1~

可以看到删除成功了。

8、模拟2个kbase

1、添加规则并执行

~ curl --location --request POST 'http://localhost:8080/drools/rule/add' \
--header 'Content-Type: application/json' \
--data-raw '{
    "ruleId": 1,
    "kieBaseName": "kieBase01",
    "kiePackageName": "rules.rule01",
    "ruleContent": "package rules.rule01 \n global java.lang.StringBuilder resultInfo \n rule \"rule-01\" when $i: Integer() then resultInfo.append(drools.getRule().getPackageName()).append(\".\").append(drools.getRule().getName()).append(\"执行了,前端传递的参数:\").append($i); end"
}'
添加成功%~ curl --location --request POST 'http://localhost:8080/drools/rule/add' \
--header 'Content-Type: application/json' \
--data-raw '{
    "ruleId": 2,
    "kieBaseName": "kieBase02",
    "kiePackageName": "rules.rule02",
    "ruleContent": "package rules.rule02 \n global java.lang.StringBuilder resultInfo \n rule \"rule-01\" when $i: Integer() then resultInfo.append(drools.getRule().getPackageName()).append(\".\").append(drools.getRule().getName()).append(\"执行了,前端传递的参数:\").append($i); end"
}'
添加成功%~ curl http://localhost:8080/drools/rule/fireRule\?kieBaseName\=kieBase01\&param\=1
rules.rule01.rule-01执行了,前端传递的参数:1%~ curl http://localhost:8080/drools/rule/fireRule\?kieBaseName\=kieBase02\&param\=1
rules.rule02.rule-01执行了,前端传递的参数:1%~

2、执行
在这里插入图片描述

9、并发安全测试

经测试,并发修改规则+执行规则,并不会出现问题!

二、优化点

1、加一个DroolsManager加载时,从数据源中读取规则,然后依次添加。

2、实际用到的是KieSession,可以添加一个方法:

//根据kiebase 名字获取kiesession
public KieSession getKieSessionBySessionName(String kieBaseName) {
    KieSession kieSession = kieContainer.newKieSession(kieBaseName + "-session");
    return kieSession;
}

3、数据库部分没有完整实现,可以考虑实现多种数据源,其实mysql一种就够了。

参考资料

最新版本:8.44.2.FINAL

官方文档:
https://www.drools.org/learn/documentation.html
8版本:https://docs.drools.org/8.44.0.Final/drools-docs/drools/introduction/index.html
7版本:https://docs.drools.org/7.74.1.Final/drools-docs/html_single/index.html

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

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

相关文章

01PCB设计概述

PCB设计概述 EDA electronic design automatic 电子设计自动化&#xff08;利用计算机来实现电子设计&#xff09; 分为 &#xff1a; 微电子&#xff08;芯片设计&#xff09;、硬件板卡&#xff08;PCB设计&#xff09; 画原理图、画PCB布线 要会绘制原理图库、和封装图库 元…

短信发送验证码及邮件发送验证码

发送短信验证码 阿里云发送验证码 public Integer sendTelCode(String tel) {String url "https://dfsns.market.alicloudapi.com/data/send_sms";String appcode "a3198282fbdf443d97aa9f3cfbe1232e";int code RandomUtil.randomInt(1000,10000);emai…

Ubuntu系统安装

目录 安装准备 安装步骤 虚拟机配置 系统安装 安装准备 Ubuntu系统镜像&#xff0c;虚拟机环境 虚拟机环境 使用的虚拟机软件为VMware Workstation 系统镜像 阿里镜像站&#xff1a;阿里巴巴开源镜像站-OPSX镜像站-阿里云开发者社区 (aliyun.com)https://developer.aliyun.com…

掌握 NestJS 10.x:从零开始构建高效可扩展的服务器端应用详解

NestJS 是一个用于构建高效、可扩展的 Node.js 服务端应用的框架&#xff0c;基于 TypeScript 构建&#xff0c;并且受 Angular 的启发&#xff0c;提供了模块化、易测试、易维护的架构。NestJS 10.x 引入了一些新特性和改进&#xff0c;进一步提升了开发体验。本文将详细介绍如…

HarmonyOS鸿蒙学习笔记(27)resources目录说明

resources目录说明 目录结构目录说明base目录rawfile目录resfile目录资源组目录 参考资料 目录结构 在HarmonyOS的项目结构中&#xff0c;有resources目录&#xff0c;用于存放应用/服务所用到的资源文件&#xff0c;如图形、多媒体、字符串、布局文件等。关于资源文件&#x…

计网ppt标黄知识点整理第(1)章节——谢希仁版本、期末复习自用

大众熟知的三大网络&#xff1a;电信网络、有线电视网络、计算机网络。发展最快起到核心的是计算机网络。Internet是全球最大、最重要的计算机网络。互联网&#xff1a;流行最广、事实上的标准译名。互连网&#xff1a;把许多网络通过一些路由器连接在一起。与网络相连的计算机…

AI网络爬虫:无限下拉滚动页面的另类爬取方法

现在很多网页都是无限下拉滚动的。可以拉动到底部&#xff0c;然后保存网页为mhtml格式文件。 接着&#xff0c;在ChatGPT中输入提示词&#xff1a; 你是一个Python编程高手&#xff0c;要完成一个关于爬取网页内容的Python脚本的任务&#xff0c;下面是具体步骤&#xff1a; …

C++——list

目录 前言 一、list 1.1 list的介绍 1.2 list的使用 1.2.1 list的构造 1.2.2 list iterator的使用 1.2.3 list capacity 1.2.4 list element access 1.2.5 list modifiers 1.2.6 list的迭代器失效 二、list的模拟实现 2.1 模拟实现list 三、list与vector的对比 总结 前言 今天…

LNMP部署及应用

目录 1.LNMP概述 Nginx 特点 Nginx 作用 2.分布式部署LNMP操练 Nginx主机&#xff1a;CentOS 7-1 PHP主机: CentOS 7-2 1.LNMP概述 Nginx 是开源、高性能、高可靠的 Web 和反向代理服务器&#xff0c;而且支持热部署&#xff0c;几乎可以做到 7 * 24 小时不间断运行&…

基于广义极大极小凹惩罚的心电信号降噪方法(MATLAB R2021B)

凸优化是数学最优化的一个子领域&#xff0c;研究定义于凸集中的凸函数最小化问题。由于心电信号降噪的过程可以理解为求信号的稀疏近似解&#xff0c;因此基于凸优化和稀疏性表达的去噪方法可用于心电信号处理。在凸优化的数学模型中&#xff0c;惩罚项的选取对最终结果会产生…

LLVM技术在GaussDB等数据库中的应用

目录 LLVM和数据库 LLVM适用场景 LLVM对所有类型的SQL都会有收益吗&#xff1f; LLVM在OLTP中就一定没有收益吗&#xff1f; GaussDB中的LLVM 1. LLVM在华为应用于数据库的时间线 2. GaussDB LLVM实现简析 3. GaussDB LLVM支持加速的场景 支持LLVM的表达式&#xff1a…

python zip()函数(将多个可迭代对象的元素配对,创建一个元组的迭代器)zip_longest()

文章目录 Python zip() 函数深入解析基本用法函数原型基础示例 处理不同长度的迭代器高级用法多个迭代器使用 zip() 与 dict()解压序列 注意事项内存效率&#xff1a;zip() 返回的是一个迭代器&#xff0c;这意味着直到迭代发生前&#xff0c;元素不会被消耗。这使得 zip() 特别…

浅谈SpringBoot配置文件

文章目录 一、配置文件作用二、配置文件分类三、SpringBoot内置的配置文件格式3.1、.properties3.1.1、.properties配置语法3.1.2、.properties读取方式 3.2、.yml/.yaml3.2.1、.yml配置语法3.2.2、.yml读取形式 四、两种配置文件优缺点4.1、.properties4.2、.yml4.2.1、.yml支…

多门店小程序如何给各个门店进行结算

​有些商家业务扩张&#xff0c;会开设多个门店。其中有些门店是直营&#xff0c;有些门店是加盟。如果用一个小程序来涵盖所有门店的业务&#xff0c;那将有助于商家进行统一管理和建立品牌效应。但如何给各个门店进行资金结算&#xff0c;是一个重要的问题&#xff0c;本文将…

探索JavaScript函数---基础篇

目录 函数 声明和调用 声明&#xff08;定义&#xff09; 调用 参数 形参和实参 形参&#xff08;Formal Arguments&#xff09; 实参&#xff08;Actual Arguments&#xff09; 形参与实参的关系 返回值 作用域 全局作用域 局部作用域 匿名函数 函数表达式 立…

无限可能LangChain——开启大模型世界

什么是大语言模型&#xff1f; 大语言模型是一种人工智能模型&#xff0c;通常使用深度学习技术&#xff08;如神经网络&#xff09;来理解和生成人类语言。这些模型拥有非常多的参数&#xff0c;可以达到数十亿甚至更多&#xff0c;使得它们能够处理高度复杂的语言模式。 我…

【网络安全】Web安全基础 - 第二节:前置基础知识- HTTP协议,握手协议,Cookie及Session

本章节主要介绍一些基础知识 d(^_^o) HTTP协议 什么是HTTP 超文本传输协议&#xff08;HyperText Transfer Protocol&#xff09;是一种用于分布式、协作式和超媒体信息系统的应用层协议。 HTTP是一个基于请求与响应&#xff0c;无状态的&#xff0c;应用层协议&#xff0c;…

30 分钟内掌握 Mainnet、Testnet 和 Devnet。Devnet是什么??

在区块链技术领域&#xff0c;Mainnet、Testnet 和 Devnet 等术语经常被使用&#xff0c;但也经常被误解。 这三种环境在区块链应用的开发和部署中起着至关重要的作用&#xff0c;但它们的区别和目的却常常被混淆。 让我们踏上探索之旅&#xff0c;揭开 Mainnet、Testnet 和 De…

HTML5+CSS3回顾总结

一、HTML5新特性 1.语义化标签 <header> 头部标签<nav> 导航标签<article> 内容标签<section> 定义文档某个区域<aside> 侧边栏标签<footer> 尾部标签 2.多媒体标签 2.1视频标签vedio 》常规写法&#xff08;尽量都使用mp4&#xff0…

google的chromedriver最新版下载地址

Chrome for Testing availability (googlechromelabs.github.io) 复制对应的地址跳转进去即可下载&#xff0c;下载前先看下自己google浏览器版本&#xff0c;找到对应的版本号去下载&#xff0c;把解压缩的exe放到google浏览器目录下。