当前位置: 首页 > article >正文

Drools规则引擎初体验

前言

假设有这样一个场景,订单管理系统需要根据用户的消费情况,来为每个用户发放不同程度的优惠券,这个发放规则复杂且多变,我们该怎么办?在代码中写死显然是不可取的,规则一变就要修改代码,频繁重启服务不说,还极易出错,用户体验就很差了。

那么有没有办法改善这一现状呢?当然是有的,把规则判定逻辑和具体业务分离,规则可以随意编辑替换,应用无需重启。Drools规则引擎就是专门为了解决上述应用场景的问题而开发出来的,下面笔者将详细介绍其基本概念规则。


一、Drools简介

‌Drools‌是一个开源的业务规则管理系统(BRMS),它使用RETE算法实现,特别适用于Java环境。Drools由JBoss开发,并作为JBoss Rules发布,其前身是Codehaus的一个开源项目。Drools支持XML和多种编程语言(如Java、Groovy等),使得业务规则能够以自然的方式表达和执行‌。

官网地址
中文教程地址


二、Drools的应用场景

  1. 动态业务规则管理‌:在需要频繁调整业务规则的场景中,Drools可以将业务规则与系统代码分离,使得规则的调整无需重新编译和部署应用程序,提高了系统的响应速度和灵活性‌

  2. 复杂决策逻辑‌:在涉及复杂决策逻辑的业务场景中,Drools可以帮助将决策逻辑抽象化,使得业务人员可以参与规则的定义和管理,降低了技术门槛‌

  3. 实时决策支持‌:在需要实时决策支持的应用中,Drools可以快速解释和执行业务规则,提供实时的决策支持。


三、Drools规则解析

常用规则

Drools规则是基于Java的业务规则管理系统的核心组成部分。

主要元素

  • 规则名称(rule):每个规则都有一个唯一的名称,用于标识该规则。
  • 命名空间(namespace):规则文件中的package声明定义了规则的命名空间。
  • 导入语句(import):用于导入Java类或模型对象,以便在规则中使用。
  • 全局变量(global):允许规则访问全局变量,这些变量通常由应用程序设置。
  • 条件部分(when):也称为Left Hand Side (LHS),定义了规则触发的条件。可以包含多个条件,使用逻辑运算符连接。
  • 动作部分(then):也称为Right Hand Side (RHS),定义了当条件满足时执行的动作。
  • 规则属性(attributes):如salience(优先级)、no-loop(防止循环触发)、enabled(是否启用规则)等,用于控制规则的行为。

高级特性

  • 查询(query):用于查询工作内存中的数据。
  • 函数(function):可以在规则中定义和调用自定义函数。
  • 事件处理(event processing):支持复杂的事件处理场景。
  • 规则流(ruleflow):用于定义规则的执行顺序和流程。

规则基本示例

  1. 规则名称(rule)
rule "Check Customer Discount"
when// 条件部分
then// 动作部分
end
  1. 命名空间(namespace)
package com.example.rules;
  1. 导入语句(import)
package com.drools.project.ruleService;import com.example.model.Customer;
import com.example.model.Order;rule "Check Customer Discount"
when$customer : Customer()
then// 动作部分
end
  1. 全局变量(global)
package com.example.rulesimport com.example.model.Customer;
global java.util.List discountList;rule "Check Customer Discount"
when$customer : Customer(discount > 0)
thendiscountList.add($customer);
end
  1. 条件部分(when)
rule "Check Customer Discount"
when$customer : Customer(discount > 0)$order : Order(customer == $customer, totalAmount > 1000)
then// 动作部分
end
  1. 动作部分(then)
rule "Check Customer Discount"
when$customer : Customer(discount > 0)
thenSystem.out.println("Customer " + $customer.getName() + " has a discount.");
end
  1. 规则属性(attributes)
rule "High Priority Rule"salience 100no-loop true
when$customer : Customer(discount > 0)
thenSystem.out.println("High priority rule triggered for customer: " + $customer.getName());
end
  1. 查询(query)
query "findCustomersWithDiscount"$customer : Customer(discount > 0)
endrule "Print Customers With Discount"
when$customers : List() from query "findCustomersWithDiscount"
thenfor (Customer customer : $customers) {System.out.println("Customer with discount: " + customer.getName());}
end
  1. 函数(function)
package com.example.rulesimport com.example.model.Customer;function boolean isEligibleForDiscount(Customer customer) {return customer.getDiscount() > 0 && customer.getTotalSpent() > 1000;
}rule "Check Customer Eligibility"
when$customer : Customer(isEligibleForDiscount($customer))
thenSystem.out.println("Customer " + $customer.getName() + " is eligible for discount.");
end
  1. 事件处理(event processing)
declare Event@role(event)type : Stringtimestamp : long
endrule "Process Login Event"
when$event : Event(type == "LOGIN", timestamp > now - 60000)
thenSystem.out.println("Login event detected within the last minute.");
end
  1. 规则流(ruleflow)
package com.example.rulesimport com.example.model.Customer;ruleflow-group "Order Processing"rule "Step 1: Validate Customer"
ruleflow-group "Order Processing"
salience 10
when$customer : Customer()
thenSystem.out.println("Validating customer...");
endrule "Step 2: Process Order"
ruleflow-group "Order Processing"
salience 5
when$order : Order()
thenSystem.out.println("Processing order...");
end

咱说啊,上面这些规则,真到实际开发里头,未必都能派上用场。平时也就用那最基础的几样,您说是不是这个理儿?咱用规则引擎,图的就是个方便、能简化开发流程。要是把规则弄得太复杂,还不如直接写代码来得痛快呢!您几位觉得呢?(哈哈哈哈……),下面笔者通过一个实战案例来向各位逐一展示下基础用法


四、drools-project规则实战

gitee地址下载git clone https://gitee.com/buxingzhe/drools-project.git

笔者新建了一个Drools-Project的SpringBoot应用,用来测试这个drools的规则是如何使用的

在这里插入图片描述

环境准备:JDK17、Maven3.9.8、IDEA2024.1

这里是笔者的开发环境,读者朋友可以根据自己的环境自行修改依赖版本适配

pom.xml内容如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.4.1</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.drools</groupId><artifactId>Drools-Project</artifactId><version>0.0.1-SNAPSHOT</version><name>Drools-Project</name><description>Drools-Project</description><properties><java.version>17</java.version><kie.version>7.59.0.Final</kie.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!--drools相关依赖--><dependency><groupId>org.drools</groupId><artifactId>drools-core</artifactId><version>${kie.version}</version></dependency><dependency><groupId>org.drools</groupId><artifactId>drools-compiler</artifactId><version>${kie.version}</version></dependency><dependency><groupId>org.drools</groupId><artifactId>drools-decisiontables</artifactId><version>${kie.version}</version></dependency></dependencies><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><annotationProcessorPaths><path><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></path></annotationProcessorPaths></configuration></plugin><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build></project>

可以看到drools相关依赖的版本是 7.59.0.Final


application.yml

server:port: 8099
spring:application:name: Drools-Project# drools 配置文件读取路径
drools:store:path: D:/drools

这个是配置文件内容,核心是这个drools 配置文件读取路径,因为笔者的电脑是windows操作系统,笔者把规则文件都放在了D盘下的一个drools文件夹中了,规则配置要求外部化,可以随时加载修改,当然读者也可以把配置路径放在Nacos等配置中心统一管理更方便修改。实际应用中我们的服务一般部署在linux操作系统中,自行新建一个文件路径,比如 /opt/drools,那就在Nacos配置文件里配置即可。


DroolsFilePathConfig 规则文件路径配置

package com.drools.project.config;import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;/*** drools规则文件存放路径*/
@Component
@ConfigurationProperties(prefix = "drools.store")
public class DroolsFilePathConfig {/*** drools规则文件存放路径,默认从D盘根目录读取(linux服务器,则自行决定规则文件存放路径)*/@Value("${drools.store.path:D:/drools}")private String path;public String getPath() {return path;}public void setPath(String path) {this.path = path;}
}

这段代码定义了一个配置类 DroolsFilePathConfig,用于管理Drools规则文件的存放路径。通过 @Component 注解使其成为Spring容器中的一个组件,并使用 @ConfigurationProperties 和 @Value 注解从配置文件中读取路径,默认路径为 D:/drools。

注意这里没有使用 @RefreshScope 支持配置动态刷新的注解,如果规则路径有变更,即使是在Nacos中配置的,也还是需要重启应用服务,重新加载配置才能生效,想要使用这个注解,必须引入如下依赖,一般微服务分布式应用可以直接使用这个注解进行配置的动态刷新功能,因为已经引入spring cloud相关依赖了

<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-config</artifactId>
</dependency>

DroolsConfig规则文件加载配置类

package com.drools.project.config;import jakarta.annotation.Resource;
import org.kie.api.KieServices;
import org.kie.api.builder.KieBuilder;
import org.kie.api.builder.KieFileSystem;
import org.kie.api.builder.KieModule;
import org.kie.api.runtime.KieContainer;
import org.kie.internal.io.ResourceFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.io.File;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;/*** @author hulei* 规则文件的配置类*/
@Configuration
public class DroolsConfig {/*** 外部规则文件路径配置类*/@Resourceprivate DroolsFilePathConfig droolsFilePathConfig;/*** classpath路径下的规则文件根路径*/private static final String CLASSPATH_RULES_PATH = "src/main/resources/";/*** 创建KieServices对象*/private static final KieServices kieServices = KieServices.Factory.get();//===========================================内部classPath下drools文件加载方式,不推荐此种方式==================================/*** 本方法规则为写死的,实际开发中不灵活,当规则文件需要动态变换时,则不适用*/@Beanpublic KieContainer kieContainer() throws IOException {KieFileSystem kfs = kieServices.newKieFileSystem();Files.walkFileTree(Paths.get(CLASSPATH_RULES_PATH), new SimpleFileVisitor<>() {@Overridepublic FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {if (file.toString().endsWith(".drl")) {kfs.write(ResourceFactory.newFileResource(file.toFile()));}return FileVisitResult.CONTINUE;}});KieBuilder kb = kieServices.newKieBuilder(kfs);kb.buildAll();KieModule kieModule = kb.getKieModule();return kieServices.newKieContainer(kieModule.getReleaseId());}//===========================================外部drools文件加载方式,推荐此种方式==============================================
//    /**
//     * 动态获取外部规则文件,可以获取多个规则文件,解析加载
//     * kfs.write("src/main/resources/rules/" + ruleFile.getName(),
//     * kieServices.getResources().newFileSystemResource(ruleFile));
//     * 这里的规则文件从ruleFile读取后写入到classpath路径下src/main/resources/rules/
//     * 是虚拟的,实际并不生成文件,需要注意的是虚拟路径的前缀必须是src/main/resources,否则报错
//     * 至于resources后面的路径可以随意写,也可以不写
//     */
//    @Bean
//    public KieContainer getKieContainer() {
//        KieFileSystem kfs = kieServices.newKieFileSystem();
//        // 获取指定目录下的所有规则文件
//        File directory = new File(droolsFilePathConfig.getPath());
//        if (directory.isDirectory()) {
//           processDirectory(directory, kfs, kieServices);
//        }
//        KieBuilder kieBuilder = kieServices.newKieBuilder(kfs);
//        kieBuilder.buildAll();
//        KieModule kieModule = kieBuilder.getKieModule();
//        return kieServices.newKieContainer(kieModule.getReleaseId());
//    }
//
//    /**
//     * 多级目录递归处理
//     */
//    private void processDirectory(File directory, KieFileSystem kfs, KieServices kieServices) {
//        File[] files = directory.listFiles();
//        if (files != null) {
//            for (File file : files) {
//                if (file.isDirectory()) {
//                    // 递归处理子目录
//                    processDirectory(file, kfs, kieServices);
//                } else if (file.getName().endsWith(".drl")) {
//                    kfs.write("src/main/resources/" + file.getName(),
//                            kieServices.getResources().newFileSystemResource(file));
//                }
//            }
//        }
//    }
}

DroolsConfig 是核心类了,主要是加载各种规则配置文件的,此类文件是以.drl为后缀,IDEA中可以下载一个插件Drools用以加载显示此类文件格式
在这里插入图片描述


上面的类中,笔者提供了两种方式加载.drl后缀的规则文件

第一种是 classpath路径下的规则文件路径

在这里插入图片描述
可以看到几个测试的规则文件是放在src/main/resources/rules下的,代码中是使用了递归遍历src/main/resources/路径下的所有.drl后缀的文件,所以不管有多少层都可以遍历到

在这里插入图片描述
这种一般是我们开发后测试规则文件是否生效时使用,一般就放在resources文件目录下,对于线上随时更改替换规则文件不方便,不建议使用。


第二种是外部drools文件加载方式

这是自己定义加载文件路径,方便随时替换的,推荐使用

在这里插入图片描述

不过笔者又发现可以直接按照第一种方式写,之不过换了下加载路径Paths.get(droolsFilePathConfig.getPath())。。。。java自己提供了遍历文件树的方法,笔者看了下,用的还不是递归,是自定义栈队列的方式,这样可以避免深层次递归造成的栈溢出问题,这种方式更好。递归是代码简单易于理解,但是树的层次比较深时,会发生栈溢出报错情况。

    /*** 动态获取外部规则文件,可以获取多个规则文件,解析加载* kfs.write("src/main/resources/rules/" + ruleFile.getName(),* kieServices.getResources().newFileSystemResource(ruleFile));* 这里的规则文件从ruleFile读取后写入到classpath路径下src/main/resources/rules/* 是虚拟的,实际并不生成文件,需要注意的是虚拟路径的前缀必须是src/main/resources,否则报错* 至于resources后面的路径可以随意写,也可以不写*/@Beanpublic KieContainer kieContainer() throws IOException {KieFileSystem kfs = kieServices.newKieFileSystem();// 获取指定目录下的所有规则文件,其实可以用后面的//======之间的内容替换,
//        File directory = new File(droolsFilePathConfig.getPath());
//        if (directory.isDirectory()) {
//           processDirectory(directory, kfs, kieServices);
//        }
//=================================================================================Files.walkFileTree(Paths.get(droolsFilePathConfig.getPath()), new SimpleFileVisitor<>() {@Overridepublic FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {if (file.toString().endsWith(".drl")) {kfs.write(ResourceFactory.newFileResource(file.toFile()));}return FileVisitResult.CONTINUE;}});
//=================================================================================KieBuilder kieBuilder = kieServices.newKieBuilder(kfs);kieBuilder.buildAll();KieModule kieModule = kieBuilder.getKieModule();return kieServices.newKieContainer(kieModule.getReleaseId());}/*** 多级目录递归处理*/private void processDirectory(File directory, KieFileSystem kfs, KieServices kieServices) {File[] files = directory.listFiles();if (files != null) {for (File file : files) {if (file.isDirectory()) {// 递归处理子目录processDirectory(file, kfs, kieServices);} else if (file.getName().endsWith(".drl")) {kfs.write("src/main/resources/" + file.getName(),kieServices.getResources().newFileSystemResource(file));}}}}

DroolsProjectApplication规则测试

至于各个规则关键词的测试呢,笔者是放在启动类里了,实现了CommandLineRunner,重写了其中的run方法

package com.drools.project;import com.drools.project.model.complex.Event;
import com.drools.project.model.complex.Student;
import com.drools.project.model.coupon.Order;
import com.drools.project.model.discount.OrderDiscount;
import com.drools.project.model.discount.OrderRequest;
import com.drools.project.model.coupon.User;
import com.drools.project.ruleService.ComplexService;
import com.drools.project.ruleService.CouponService;
import com.drools.project.ruleService.OrderDiscountService;
import jakarta.annotation.Resource;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;@SpringBootApplication
public class DroolsProjectApplication implements CommandLineRunner {@Resourceprivate CouponService couponService;@Resourceprivate OrderDiscountService orderDiscountService;@Resourceprivate ComplexService complexService;public static void main(String[] args) {SpringApplication.run(DroolsProjectApplication.class, args);}@Overridepublic void run(String... args) {System.out.println("=======================规则案例一--基本用法规则==============================");Calendar calendar = Calendar.getInstance();calendar.add(Calendar.YEAR, -1);Date oneYearAgo = calendar.getTime();Order order1 = new Order();order1.setCategory("Electronics");order1.setAmount(300);order1.setOrderDate(oneYearAgo);Order order2 = new Order();order2.setCategory("Books");order2.setAmount(200);order2.setOrderDate(oneYearAgo);Order order3 = new Order();order3.setCategory("Electronics");order3.setAmount(600);order3.setOrderDate(new Date());List<Order> orders = new ArrayList<>();orders.add(order1);orders.add(order2);orders.add(order3);User user = new User();user.setName("John Doe");user.setVip(true);user.setPurchaseCount(15);user.setTotalPurchaseAmount(2000);user.setOrders(orders);user.setCouponAmount(0);user.setLocation("Shanghai");user.setPoints(1500);couponService.applyCoupons(user);System.out.println("当前用户总优惠券金额为: " + user.getCouponAmount());System.out.println("=======================规则案例二--global规则==============================");OrderRequest orderRequest = new OrderRequest();orderRequest.setCustomerNumber("123456");orderRequest.setAge(55);orderRequest.setAmount(2000);orderRequest.setCustomerType(com.drools.project.enums.CustomerType.LOYAL);OrderDiscount orderDiscount = orderDiscountService.getDiscount(orderRequest);System.out.println("获得的折扣:" + orderDiscount.getDiscount());//===========================================================================================List<Student> studentList = new ArrayList<>();studentList.add(new Student("John",  20, 90,"Shanghai"));studentList.add(new Student("Jane",  25, 85,"Beijing"));studentList.add(new Student("Jim",   17, 95,"Shanghai"));studentList.add(new Student("Judy",  35, 80,"Beijing"));studentList.add(new Student("Jason", 15, 90,"Shanghai"));studentList.add(new Student("Julia", 45, 85,"Beijing"));studentList.add(new Student("James", 19, 76,"Shanghai"));studentList.add(new Student("Julie", 16, 80,"Beijing"));System.out.println("=======================规则案例三--query函数==============================");complexService.complexQuery(studentList);System.out.println("=======================规则案例四--function函数==============================");complexService.complexFunction(studentList);System.out.println("=======================规则案例五--priority规则==============================");complexService.complexPriority(studentList);System.out.println("=======================规则案例六--role,flow,group规则==============================");complexService.complexRoleFlowGroup();System.out.println("=======================规则案例七--event规则==============================");List<Event> eventList = new ArrayList<>();long timestamp = System.currentTimeMillis();eventList.add(new Event("LOGIN", timestamp-50000));eventList.add(new Event("LOGIN", timestamp-40000));eventList.add(new Event("LOGIN", timestamp-80000));eventList.add(new Event("LOGOUT", timestamp-30000));complexService.complexEvent(eventList);}
}

各规则测试结果如下

在这里插入图片描述


五、代码结构

在这里插入图片描述
上图展示的是三个测试的service,对应三个不同的drools文件

  • coupon.drl:使用了优惠卷发放规则来展示基本用法,对应规则服务类是CouponService
  • discount.drl:根据用户年龄、购买次数等情况计算打折规则,没什么特别的,注意dialect "mvel"用法,这个具体在规则文件中有解释
  • complex.drl:展示了query、event、ruleflow group等高级规则使用方式

注意:笔者代码中的实体类并没有使用lombok,因为笔者的jdk版本和drools较高,相关drools依赖版本不支持lombok写法了

到这里,关于Drools的介绍就结束了,主要展示了基本用法,笔者写这篇文章也仅仅是探索学习其用法,因为工作中遇到需要使用规则引擎的场景,读者朋友可以把代码拉下来跑一下看看,大部分都有注释,尤其是规则文件中,相信诸位都能看得明白,是在不会的可在评论区找我,我尽可能回答大家。

相关文章:

Drools规则引擎初体验

前言 假设有这样一个场景&#xff0c;订单管理系统需要根据用户的消费情况&#xff0c;来为每个用户发放不同程度的优惠券&#xff0c;这个发放规则复杂且多变&#xff0c;我们该怎么办&#xff1f;在代码中写死显然是不可取的&#xff0c;规则一变就要修改代码&#xff0c;频…...

Day36【AI思考】-表达式知识体系总览

文章目录 **表达式知识体系总览**回答1&#xff1a;**表达式知识体系****一、三种表达式形式对比****二、表达式转换核心方法****1. 中缀转后缀&#xff08;重点&#xff09;****2. 中缀转前缀** **三、表达式计算方法****1. 后缀表达式计算&#xff08;栈实现&#xff09;****…...

Tailwind CSS v4.0 升级与 Astro 5.2 项目迁移记录

本文博客链接 https://ysx.cosine.ren/tailwind-update-v4-migrate 自用小记。 Tailwind CSS v4.0 - Tailwind CSS 新的高性能引擎 - 完整构建的速度速度快 5 倍&#xff0c;增量构建的速度快于 100 倍以上 —— 以微秒为单位进行测量。为现代 Web 设计 - 建立在前沿的 CSS 特…...

K8S ReplicaSet 控制器

一、理论介绍 今天我们来实验 ReplicaSet 控制器&#xff08;也叫工作负载&#xff09;。官网描述如下&#xff1a; 1、是什么&#xff1f; ReplicaSet 副本集&#xff0c; 维护一组稳定的副本 Pod 集合。 2、为什么需要&#xff1f; 解决 pod 被删除了&#xff0c;不能自我恢…...

基于springboot校园点歌系统

基于Spring Boot的校园点歌系统是一种专为校园场景设计的音乐点播平台&#xff0c;它能够丰富学生的校园生活&#xff0c;提升学生的娱乐体验。以下是对该系统的详细介绍&#xff1a; 一、系统背景与意义 在校园环境中&#xff0c;学生们对于音乐有着浓厚的兴趣&#xff0c;传…...

【R语言】数据操作

一、查看和编辑数据 1、查看数据 直接打印到控制台 x <- data.frame(a1:20, b21:30) x View()函数 此函数可以将数据以电子表格的形式进行展示。 用reshape2包中的tips进行举例&#xff1a; library("reshape2") View(tips) head()函数 查看前几行数据&…...

【C++】2.高并发内存池 -- 如何设计一个定长内存池

博客主题&#xff1a;如何设计一个定长内存池 个人主页&#xff1a;https://blog.csdn.net/sobercq CSDN专栏&#xff1a;https://blog.csdn.net/sobercq/category_12884309.html Gitee链接&#xff1a;https://gitee.com/yunshan-ruo/high-concurrency-memory-pool 文章目录 前…...

Redis --- 使用Feed流实现社交平台的新闻流

要实现一个 Feed 流&#xff08;类似于社交媒体中的新闻流&#xff09;&#xff0c;通常涉及以下几个要素&#xff1a; 内容发布&#xff1a;用户发布内容&#xff08;例如文章、状态更新、图片等&#xff09;。内容订阅&#xff1a;用户可以订阅其他用户的内容&#xff0c;获…...

React中key值的正确使用指南:为什么需要它以及如何选择

React中key值的正确使用指南&#xff1a;为什么需要它以及如何选择 一、key值的基本概念二、如何选择合适的key值1. 数据来源决定key策略2. key值的三大核心要求 三、React为何需要key值&#xff1f;1. 虚拟DOM优化机制2. 状态维护机制 四、常见误区及解决方案1. 索引作为key的…...

在Debian 12上安装VNC服务器

不知道什么标题 可以看到这个文章是通过豆包从国外网站copy的&#xff0c;先这样写着好了&#xff0c;具体的我有时间再补充&#xff0c;基本内容都在这里了。 在Debian 12上安装VNC服务器 简介 VNC&#xff08;Virtual Network Computing&#xff0c;虚拟网络计算&#xf…...

游戏引擎学习第88天

仓库:https://gitee.com/mrxiao_com/2d_game_2 调查碰撞检测器中的可能错误 在今天的目标是解决一个可能存在的碰撞检测器中的错误。之前有人提到在检测器中可能有一个拼写错误&#xff0c;具体来说是在测试某个变量时&#xff0c;由于引入了一个新的变量而没有正确地使用它&…...

c++中priority_queue的应用及模拟实现

1.介绍 priority_queue 是一种数据结构&#xff0c;它允许你以特定的顺序存储和访问元素。在 C 标准模板库&#xff08;STL&#xff09;中&#xff0c;priority_queue 是一个基于容器适配器的类模板&#xff0c;它默认使用 std::vector 作为底层容器&#xff0c;并且默认使用最…...

深度学习篇---计算机视觉任务模型的剪裁、量化、蒸馏

文章目录 前言第一部分&#xff1a;计算机视觉任务图像分类特点 图像识别特点 目标检测特点 图像分割子任务特点 第二部分&#xff1a;模型剪裁、量化、蒸馏模型剪裁1.权重剪裁2.结构剪裁3.迭代剪裁 模型量化1.对称量化2.非对称量化3.动态量化4.静态量化 知识蒸馏1.训练教师网络…...

java-关键字(final,static)

关键字 final 和 static 是两个常用的关键字&#xff0c;它们分别用于不同的场景&#xff0c;具有不同的作用。 final final 关键字用于表示某个实体是不可变的。它可以应用于变量、方法和类。 final 变量 当 final 用于变量时&#xff0c;表示该变量一旦被初始化后&#…...

游戏引擎 Unity - Unity 设置为简体中文、Unity 创建项目

Unity Unity 首次发布于 2005 年&#xff0c;属于 Unity Technologies Unity 使用的开发技术有&#xff1a;C# Unity 的适用平台&#xff1a;PC、主机、移动设备、VR / AR、Web 等 Unity 的适用领域&#xff1a;开发中等画质中小型项目 Unity 适合初学者或需要快速上手的开…...

JDK17主要特性

JDK 17&#xff0c;也被称为Java 17或Java Platform, Standard Edition 17&#xff0c;是Java编程语言的第十七个主要版本&#xff0c;由Oracle公司在2021年9月发布。Java 17是一个长期支持&#xff08;LTS&#xff0c;Long-Term Support&#xff09;版本&#xff0c;这意味着它…...

将OneDrive上的文件定期备份到移动硬盘

背景&#xff1a; 我在oneDrive上存了很多文件&#xff0c;分布在多个文件夹中&#xff0c;也有套了好几层文件夹的情况。我希望每隔一段时间&#xff0c;将oneDrive上的所有文件向移动硬盘上拷贝一份&#xff0c;但是我只想将距离上一次向移动硬盘拷贝的文件相比&#xff0c;发…...

【Elasticsearch】geohex grid聚合

在 Elasticsearch 中&#xff0c;地理边界过滤是一种用于筛选地理数据的技术&#xff0c;它可以根据指定的地理边界形状&#xff08;如矩形、多边形等&#xff09;来过滤符合条件的文档。这种方法在地理空间数据分析中非常有用&#xff0c;尤其是在需要将数据限制在特定地理区域…...

crewai框架第三方API使用官方RAG工具(pdf,csv,json)

最近在研究调用官方的工具&#xff0c;但官方文档的说明是在是太少了&#xff0c;后来在一个视频里看到了如何配置&#xff0c;记录一下 以PDF RAG Search工具举例&#xff0c;官方文档对于自定义模型的说明如下&#xff1a; 默认情况下&#xff0c;该工具使用 OpenAI 进行嵌…...

算法 哈夫曼树和哈夫曼编码

目录 前言 一&#xff0c;二进制转码 二&#xff0c;哈夫曼编码和哈夫曼树 三&#xff0c;蓝桥杯 16 哈夫曼树 总结 前言 这个文章需要有一定的树的基础&#xff0c;没学过树的伙伴可以去看我博客树的文章 当我们要编码一个字符串转成二进制的时候&#xff0c;我们要怎么…...

TCP 丢包恢复策略:代价权衡与优化迷局

网络物理层丢包是一种需要偿还的债务&#xff0c;可以容忍低劣的传输质量&#xff0c;这为 UDP 类服务提供了空间&#xff0c;而对于 TCP 类服务&#xff0c;可以用另外两类代价来支付&#xff1a; 主机端采用轻率的 GBN 策略恢复丢包&#xff0c;节省 CPU 资源&#xff0c;但…...

Sumatra PDF:小巧免费,满足多样阅读需求

Sumatra PDF是一款完全免费的本地阅读器软件&#xff0c;以小巧的体积和全面的功能受到用户青睐。如今&#xff0c;它已经更新到3.3版本&#xff0c;带来了更多实用功能&#xff0c;尤其是新增的注释功能&#xff0c;值得我们再次关注。 软件特色 轻量级体积&#xff1a;压缩…...

vue2-给data动态添加属性

vue2-给data动态添加属性 1. 问题的来源 在VUe2中&#xff08;VUE3中使用了proxy&#xff0c;及时动态添加也能实现响应式&#xff09;&#xff0c;如果我们动态给data添加一个属性&#xff0c;会发现视图没有同步更新举个例子我们通过v-for遍历data中的一个属性list&#xf…...

TiDB 分布式数据库多业务资源隔离应用实践

导读 随着 TiDB 在各行业客户中的广泛应用 &#xff0c;特别是在多个业务融合到一套 TiDB 集群中的场景&#xff0c;各企业对集群内多业务隔离的需求日益增加。与此同时&#xff0c;TiDB 在多业务融合场景下的资源隔离方案日趋完善&#xff0c;详情可参考文章 《你需要什么样的…...

105,【5】buuctf web [BJDCTF2020]Easy MD5

进入靶场 先输入试试回显 输入的值成了password的内容 查看源码&#xff0c;尝试得到信息 什么也没得到 抓包&#xff0c;看看请求与响应里有什么信息 响应里得到信息 hint: select * from admin where passwordmd5($pass,true) 此时需要绕过MD5&#xff08;&#xff09;函…...

BFS(广度优先搜索)——搜索算法

BFS&#xff0c;也就是广度&#xff08;宽度&#xff09;优先搜索&#xff0c;二叉树的层序遍历就是一个BFS的过程。而前、中、后序遍历则是DFS&#xff08;深度优先搜索&#xff09;。从字面意思也很好理解&#xff0c;DFS就是一条路走到黑&#xff0c;BFS则是一层一层地展开。…...

33.Word:国家中长期人才发展规划纲要【33】

目录 NO1.2样式​ NO3​ 图表 ​ NO4.5.6​ 开始→段落标记视图→导航窗格→检查有无遗漏 NO1.2样式 F12/另存为&#xff1a;Word.docx&#xff1a;考生文件夹样式的复制样式的修改 样式的应用&#xff08;没有相似/超级多的情况下&#xff09;——替换 [ ]通配符&#x…...

gym-anytrading

参考&#xff1a;https://github.com/upb-lea/gym-electric-motor AnyTrading 是一组基于 reinforcement learning (RL) 的 trading algorithms&#xff08;交易算法&#xff09;的 OpenAI Gym 环境集合。 该项目主要用于foreign exchange (FOREX) 和 stock markets (股票市场)…...

如何自定义软件安装路径及Scoop包管理器使用全攻略

如何自定义软件安装路径及Scoop包管理器使用全攻略 一、为什么无法通过WingetUI自定义安装路径&#xff1f; 问题背景&#xff1a; WingetUI是Windows包管理器Winget的图形化工具&#xff0c;但无法直接修改软件的默认安装路径。原因如下&#xff1a; Winget设计限制&#xf…...

私有化部署 DeepSeek + Dify,构建你的专属私人 AI 助手

私有化部署 DeepSeek Dify&#xff0c;构建你的专属私人 AI 助手 概述 DeepSeek 是一款开创性的开源大语言模型&#xff0c;凭借其先进的算法架构和反思链能力&#xff0c;为 AI 对话交互带来了革新性的体验。通过私有化部署&#xff0c;你可以充分掌控数据安全和使用安全。…...