借老系统重构机会我写了个groovy规则引擎
公司老系统的重构计划早就有了,为了对Java硬编码的各种校验规则进行重构,特地参考了相关技术,最终选择了groovy进行了系统的学习,并编写了一个即插即用的轻量级规则引擎。
文章目录
- 项目背景
- 技术选型
- groovy的性能
- groovy脚本执行线程安全问题
- 统一Java运行环境
- dsl风格的规则声明
- 弱类型的便利
- 校验规则的维护
- 基于事实推断的规则引擎实现
项目背景
笔者上班负责的是一个很老的某业务平台的申报系统。并发量随不高,但是申报分了很多的阶段,且每个阶段的申报项比较多,表单的字段也很多,校验规则也比较杂。项目前期有多个团队先后负责,代码风格不同,且业务规则的校验实现都是堆砌代码的方式,导致后期的代码维护比较麻烦。一个类文件通常是5千行代码以上,一个校验方法也至少几百行。
因为业务操作主要是数据的保存和申报,而校验的代码占到很大的比例,为此笔者的重构,很自然就想到把各种杂七杂八的校验代码给抽取出来,以规则脚本的形式进行更好的维护。
技术选型
抽取校验规则的技术选型,考虑了基于rete算法的drools、基于mvel的easyRule以及jvm体系的groovy。因为只是把系统中的校验规则抽取出来,做成脚本的形式维护,传统的规则引擎也基本用不上,因为它们更多的使用场景是基于多实体的事实对象的分析、推断,且比较重量级,且有一定的学习成本。而groovy本身就是一门动态的脚本语言,很轻量级,可以在java环境无缝衔接的调用,语法非常的简洁易学,它的dsl特性还能编写出可读性更高的脚本声明形式。
groovy的性能
groovy非常的轻量级,编译速度很快,新版本对编译的内容做了缓存,而且gc方面也做了优化。笔者在这方面做了一些实验:通过spring boot的定时任务每隔一秒钟用30个线程同时跑groovy规则脚本,并对应用的jvm参数-XX:MetaspaceSize和-XX:MaxMetaspaceSize都做了限制。用groovy老版本和新版本做了下对比,发现老版本执行很快就oom了:

而新版本稳定发挥,执行的性能用visualVM监控了下:

笔者还测试了下规则执行的耗时,第一次访问时需要耗时几百毫秒,后续就很快了,说明重复执行一个规则脚本,会缓存一些编译的类,提高性能:

groovy脚本执行线程安全问题
groovy脚本在java环境中的执行有多种方式,具体可参考这篇技术博客Integrating Groovy into Java Applications。我们采用GroovyShell的方式来执行规则脚本,因为规则是设计为dsl声明方式,而不是类和方法的执行方式,用bingding作为执行的上下文。binding本身是线程不安全的,在多个请求线程用同一个GroovyShell实例执行时,binding上绑定的变量是共享的,为此在实现上需要进行线程同步处理。而我们的做法是每次都创建一个新的GroovyShell来避免这个问题,这种方式没有性能问题,因为groovy本身就有对编译脚本相应的类缓存机制,且gc方面做了优化。
统一Java运行环境
在植入groovy脚本代码到当前的java系统时,可以设置加载groovy的类加载器为当前java系统的类加载器,这样就可以直接使用当前环境中的类和类库了。比如可以在groovy脚本中直接用获取spring bean的工具类:

而外部需要传进来参与规则执行的对象可以通过设置为binding的变量。
在groovy脚本中抛出异常,和在java系统中抛出的异常类型信息一样,不会被包装,方便系统原有的异常处理机制统一处理:

dsl风格的规则声明
借助于groovy闭包和binding可以很轻松的实现dsl风格的声明:

这里的condition以及逻辑运算闭包还可以进一步优化成,不满足逻辑条件就不再执行,比如上面截图中的第一个condition满足条件,则后续的闭包不再解析执行,都可以自行控制实现。
弱类型的便利
原先在java代码中编写的各种校验规则,需要严格的静态类型检查还需要避免在运行时的空指针问题,而groovy天生就有类似于javascript的弱类型特性,且变量属性的访问,也不会有空指针问题,比如Integer类型的变量为null,使用<操作符;或者值为null的字符串变量调用equals。
在弱语言中有一个特别需要注意的问题,变量可能存在全局污染,我们只要遵循这样的原则:在一个groovy脚本的头部声明变量时一定加上def,因为不用类型声明的变量会作为全局变量,可以在shell工具运行的多个脚本之间传递使用,因此要避免这一点。以下是笔者的练习:

校验规则的维护
对于简单的参数校验:非空、长度、正则等可以编写统一的groovy函数或者闭包,这样字段校验的脚本会变得非常简单,而对于复杂的关联性的校验只需要用java或者groovy脚本,在我们定义的condition中自由发挥即可。

运行结果:

另外通过这样的方式可以把规则成块的组织起来,包含了规则头部的声明块(变量的声明和初始化)、各个规则rule闭包声明的规则执行块。有了这样的结构后,可以把规则的定义从文件搬到数据库中,然后再通过web端的编辑权限进行修改维护(用支持groovy语法高亮的web编辑器),这样就可以做到不重启服务器的情况下,来动态更新业务规则,非常的省心。
基于事实推断的规则引擎实现
基于groovy强大的闭包语法特性,我们可以很轻松的实现笛卡尔积匹配方式的小型规则引擎,比如:

相关文章:
借老系统重构机会我写了个groovy规则引擎
公司老系统的重构计划早就有了,为了对Java硬编码的各种校验规则进行重构,特地参考了相关技术,最终选择了groovy进行了系统的学习,并编写了一个即插即用的轻量级规则引擎。 文章目录 项目背景技术选型groovy的性能groovy脚本执行线…...
C#利用ffmpeg借助NVIDIA GPU实现实时RTSP硬解码+硬编码录制MP4
目录 说明 效果 项目 代码 下载 说明 利用周杰的开源项目 Sdcb.FFmpeg 项目地址:https://github.com/sdcb/Sdcb.FFmpeg/ 代码实现参考:https://github.com/sdcb/ffmpeg-muxing-video-demo 效果 C#利用ffmpeg借助NVIDIA GPU实现实时RTSP硬解码硬…...
第4章 汇编语言和汇编软件
第4章 汇编语言和汇编软件 该章主要介绍了汇编语言和汇编语言编译器的安装和使用。 汇编语言程序 该小节主要介绍了为什么要有汇编语言和汇编语言程序的一些基础写法。 书中有提到CPU有不同的架构,汇编语言有不同的风格,那么不同的CPU架构和不同的汇…...
网络安全在2024好入行吗?
前言 024年的今天,慎重进入网安行业吧,目前来说信息安全方向的就业对于学历的容忍度比软件开发要大得多,还有很多高中被挖过来的大佬。 理由很简单,目前来说,信息安全的圈子人少,985、211院校很多都才建立…...
C++练习
要求 1. 函数命名清晰 使用描述性的命名,准确反映函数的功能。例如,使用 CalculateSum() 而不是 sum()。避免使用缩写或模糊不清的名字,确保变量和函数名有明确的含义。 2. 参数传递 根据需要选择按值传递、按引用传递或按指针传递。如果…...
3. GIS后端工程师岗位职责、技术要求和常见面试题
本系列文章目录: 1. GIS开发工程师岗位职责、技术要求和常见面试题 2. GIS数据工程师岗位职责、技术要求和常见面试题 3. GIS后端工程师岗位职责、技术要求和常见面试题 4. GIS前端工程师岗位职责、技术要求和常见面试题 5. GIS工程师岗位职责、技术要求和常见面试…...
Linux学习笔记(4)----Debian压力测试方法
使用命令行终端压力测试需要两个实用工具:s-tui和stress sudo apt install s-tui stress 安装完成后,在终端中启动 s-tui实用工具: s-tui 执行后如下图: 你可以使用鼠标或键盘箭头键浏览菜单,然后点击“压力选项(Str…...
xml详解
一、XML是什么 XML(可扩展标记语言)是一种非常常用的数据存储和交换格式。 二、XML 的基本结构 声明 XML 文件通常以 XML 声明开始,例如:<?xml version"1.0" encoding"UTF-8"?>。它指定了 XML 的版…...
C140 杨辉三角
C140 杨辉三角 题目题解(94)讨论(102)排行面经 new 简单 通过率:29.57% 时间限制:1秒 空间限制:256M 知识点C工程师牛客 校招时部分企业笔试将禁止编程题跳出页面,为提前适应,练习时请使用在线自测,…...
C++字符串操作中的陷阱
休对故人思故国,且将新火试新茶。诗酒趁年华。 ——《望江南超然台作》【宋】苏轼 目录 正文: 首先我们要明白出现问题的原因: 1. 缓冲区溢出 2. 错误的字符串声明方式 3. 缺乏对NULL指针的检查 解决方案: 下期预告:C字符串…...
最值求解 | 管理类联考数学专项
日期内容2024.9.5新建2024.9.6曦曦求最值完结 实数求最值至少至多抽屉原理工程问题线性规划一次性绝对值求最值 参考: b站跟着曦曦老师玩转【最值】...
C++_继承详解
继承的概念 继承(inheritance)机制是面向对象程序设计使代码可以复用的重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能。继承呈现了面向对象程序设计的层次结构,之前我们接触的复用都是函数复用,今天我们所讨…...
区块链开发解决方案有哪些
区块链开发解决方案概述 区块链开发解决方案旨在利用区块链技术构建和开发新型应用和系统,以解决各种业务问题和提升效率。区块链作为一种基于密码学的分布式账本技术,通过将交易和数据记录在不可篡改的区块中,并通过网络中的多个节点共同验…...
Express与SQLite集成教程:轻松实现数据库操作
Express使用SQLite的教程可以大致分为以下几个步骤。以下是一个详细的指南,帮助你在Express项目中集成SQLite数据库。 1. 安装必要的库 首先,你需要在你的Express项目中安装sqlite3库。打开终端或命令提示符,切换到你的项目目录,…...
Transforms的常见用法
文章目录 一、封装函数与普通函数的用法区别二、Image.open()打开图片的格式三、ToTensor打开图片格式四、ToTensor使用五、Normalize归一化使用六、Resize的使用七、Compose - Resize 使用八、RandomCrop() 随机裁剪用法 一、封装函数与普通函数的用法区…...
js 创建 React 项目
起因(目的): js 很久没写了。 react js 之前粗略看过, 最近又需要用到, 继续学习, 记录 积累。 1. 新建 React 项目 的几种方法。 官方建议使用 next 来创建 React 项目, 但是我觉得太复杂了。以后再看看. npx create-next-applatest # !!! 不建议使…...
WPF 中常用 `Transform` 类的介绍、使用示例和适用场景
WPF 中常用 Transform 类的介绍、使用示例和适用场景 使用场景解释代码示例示例代码解释 Transform 类描述使用示例适用场景TranslateTransform用于沿 X 轴或 Y 轴平移(移动)元素。xml <TranslateTransform X"50" Y"100" />移…...
ElasticSearch-DSL
查询所有 match_all 分页查询 from size深分页查询 Scroll指定字段排序 sort返回指定字段_sourcematch 短语查询 match_phrase多字段查询 multi_matchquery_string simple_query_string 关键词查询 Term 结构化搜索 前缀查询 prefix通配符查询 wildcard范围查询 range多 id 查…...
Learn ComputeShader 07 Post Processing
这次我们将使用计算机着色器对图像进行后处理。 要进行后处理需要将渲染图像从cpu传递给gpu,并在gpu对图像进行处理然后传回cpu。 首先创建一个后处理基类BasePP 首先声明需要用到的属性。 using System.Collections; using System.Collections.Generic; using …...
初始QT!
作业:了解QT文件夹初始代码的意义 QT core gui #QT工程所需得类库 core是核心库 gui图形化界面相关库类 greaterThan(QT_MAJOR_VERSION, 4): QT widgets #版本超过4.0会加上widgetsCONFIG c11 #该编辑器支持c11后的版本 # The following define makes you…...
linux之kylin系统nginx的安装
一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源(HTML/CSS/图片等),响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址,提高安全性 3.负载均衡服务器 支持多种策略分发流量…...
转转集团旗下首家二手多品类循环仓店“超级转转”开业
6月9日,国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解,“超级…...
质量体系的重要
质量体系是为确保产品、服务或过程质量满足规定要求,由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面: 🏛️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限,形成层级清晰的管理网络…...
基于TurtleBot3在Gazebo地图实现机器人远程控制
1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...
(一)单例模式
一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…...
python爬虫——气象数据爬取
一、导入库与全局配置 python 运行 import json import datetime import time import requests from sqlalchemy import create_engine import csv import pandas as pd作用: 引入数据解析、网络请求、时间处理、数据库操作等所需库。requests:发送 …...
Oracle11g安装包
Oracle 11g安装包 适用于windows系统,64位 下载路径 oracle 11g 安装包...
rknn toolkit2搭建和推理
安装Miniconda Miniconda - Anaconda Miniconda 选择一个 新的 版本 ,不用和RKNN的python版本保持一致 使用 ./xxx.sh进行安装 下面配置一下载源 # 清华大学源(最常用) conda config --add channels https://mirrors.tuna.tsinghua.edu.cn…...
Python常用模块:time、os、shutil与flask初探
一、Flask初探 & PyCharm终端配置 目的: 快速搭建小型Web服务器以提供数据。 工具: 第三方Web框架 Flask (需 pip install flask 安装)。 安装 Flask: 建议: 使用 PyCharm 内置的 Terminal (模拟命令行) 进行安装,避免频繁切换。 PyCharm Terminal 配置建议: 打开 Py…...
数据库正常,但后端收不到数据原因及解决
从代码和日志来看,后端SQL查询确实返回了数据,但最终user对象却为null。这表明查询结果没有正确映射到User对象上。 在前后端分离,并且ai辅助开发的时候,很容易出现前后端变量名不一致情况,还不报错,只是单…...
