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

Micronaut 集成 SPL 实现微服务

微服务架构作为现代企业应用系统的主流趋势,常因其部署灵活、可扩展性强而备受推崇。然而,在实际落地中却面临“叫好不叫座”的尴尬局面。根源在于传统Java实现方式下,微服务代码冗长、业务逻辑难以解耦、更新困难,甚至很多企业最终不得不将复杂业务逻辑回写至数据库层,导致系统耦合度反而升高。

SPL(Structured Process Language)为这一困境提供了解法。SPL是纯Java实现的解释执行型脚本语言,擅长复杂数据逻辑处理,具备热切换能力,能够在不重启系统、不重新部署的前提下快速变更业务逻辑,天然适合作为微服务中的业务计算引擎。它与Micronaut这样的现代Java微服务框架结合,既不破坏微服务的原生特性,又提升了开发与运维效率。

微服务与SPL集成的架构图

以下将通过一个案例来演示SPL和Micronaut集成实现微服务的过程。

Micronaut环境准备

Micronaut CLI的安装可以参考官方文档(https://micronaut-projects.github.io/micronaut-starter/latest/guide/#installation),这里不再赘述。

创建项目

使用 Micronaut CLI可快速创建基于 Maven 的Micronaut Java项目,并在项目中启用服务发现(Consul)。

分别创建服务提供者和服务消费者:

mn create-app micronaut-pvovider --build maven --features discovery-consul
mn create-app micronaut-consumer --build maven --features discovery-consul

创建好后,在集成开发环境(本文使用IDEA)中打开之前创建的micronaut-pvovider项目

启动应用

启动应用之前,需要先启动注册中心(Consul)的服务端,安装运行Consul可以参考官方文档:https://developer.hashicorp.com/consul/docs/fundamentals/install。为了方便起见,本文使用docker镜像启动Consul,命令如下:

docker run -p 8500:8500 consul:1.15.4

启动Micronaut应用,可以直接run main calss,也可以在项目的根目录下通过命令启动。

./mvnw mn:run

微服务开发与部署

集成SPL

引入依赖(pom.xml)

<dependency><groupId>com.scudata.esproc</groupId><artifactId>esproc</artifactId><version>20250605</version>
</dependency><dependency><groupId>org.hsqldb</groupId><artifactId>hsqldb</artifactId><version>2.7.4</version>
</dependency>

实战中,由于中央仓库的代码更新频率较低,如需要包含最全最新功能的SPL版本,建议从官网下载标准版,并通过“私有Maven仓库”方案来同步更新jar。可以从[SPL 下载地址]下载标准版,安装。

需要用到两个 jar 包都可以在 [安装目录]\esProc\lib 目录下找到。

esproc-bin-xxxx.jar    //集算器计算引擎及JDBC驱动包
icu4j-60.3.jar    //处理国际化

部署SPL配置文件

raqsoftConfig.xml 是 SPL 的主要配置文件,数据源、主目录、SPL 脚本寻址路径等信息都在该文件中配置。将其复制到应用的类路径下即可。配置数据源部分的示例如下:

……
<DBList encryptLevel="0"><DB name="hsql"><property name="url" value="jdbc:hsqldb:hsql://127.0.01:9001/demo"></property><property name="driver" value="org.hsqldb.jdbcDriver"></property><property name="type" value="13"></property><property name="user" value="sa"></property><property name="password"></property><property name="batchSize" value="0"></property><property name="autoConnect" value="false"></property><property name="useSchema" value="false"></property><property name="addTilde" value="false"></property><property name="caseSentence" value="false"></property></DB>
</DBList>
……

服务提供者端

通过SPL提供的JDBC驱动实现对SPL脚本的调用。

package micronaut.pvovider;import io.micronaut.http.annotation.*;
import io.micronaut.http.MediaType;import java.sql.*;
import java.util.*;@Controller("/spl")
public class SPLProviderController {@Post(uri = "/call", consumes = MediaType.APPLICATION_JSON, produces = MediaType.APPLICATION_JSON)public Map<String, Object> execute(@Body Map<String, String> request) {Map<String, Object> response = new HashMap<>();String splxName = request.get("splxName");String jsonParam = request.get("jsonParam");if (splxName == null || splxName.isBlank()) {response.put("code", 400);response.put("message", "Missing splxName");return response;}try {Class.forName("com.esproc.jdbc.InternalDriver");try (Connection con = DriverManager.getConnection("jdbc:esproc:local://");CallableStatement st = con.prepareCall("call " + splxName + "(?)")) {if (jsonParam != null && !jsonParam.isEmpty()) {st.setString(1, jsonParam);} else {st.setNull(1, Types.VARCHAR);}boolean hasResult = st.execute();List<List<Object>> allResults = new ArrayList<>();int resultSetCount = 0;do {if (hasResult) {try (ResultSet rs = st.getResultSet()) {ResultSetMetaData metaData = rs.getMetaData();int columnCount = metaData.getColumnCount();List<Object> currentResult = new ArrayList<>();while (rs.next()) {if (columnCount == 1) {currentResult.add(rs.getObject(1));} else {Map<String, Object> row = new HashMap<>();for (int i = 1; i <= columnCount; i++) {row.put(metaData.getColumnLabel(i), rs.getObject(i));}currentResult.add(row);}}if (!currentResult.isEmpty()) {allResults.add(currentResult);resultSetCount++;}}}} while ((hasResult = st.getMoreResults()) || st.getUpdateCount() != -1);if (resultSetCount > 0) {response.put("code", 200);response.put("message", "success");response.put("data", resultSetCount == 1 ? allResults.get(0) : allResults);} else {response.put("code", 404);response.put("message", "No result data");}}} catch (Exception e) {response.put("code", 500);response.put("message", "Execution failed: " + e.getMessage());}return response;}
}

服务消费者端

打开之前创建的micronaut-consumer项目。因为与服务提供者一样,都是本地启动,所以在配置文件(application.properties)中改下应用的端口为8081(默认为8080),避免冲突

micronaut.server.port=${SERVER_PORT:8081}
package micronaut.consumer;import io.micronaut.core.type.Argument;
import io.micronaut.http.HttpRequest;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.*;
import io.micronaut.http.client.HttpClient;
import io.micronaut.http.client.annotation.Client;
import io.micronaut.scheduling.TaskExecutors;
import io.micronaut.scheduling.annotation.ExecuteOn;import java.util.Map;@Controller("/consumer")
@Produces(MediaType.APPLICATION_JSON)
@ExecuteOn(TaskExecutors.BLOCKING)
public class SPLConsumerController {private final HttpClient httpClient;public SPLConsumerController(@Client("micronaut-pvovider") HttpClient httpClient) {this.httpClient = httpClient;}@Post("/call-spl")public Map<String, Object> callSPL(@Body Map<String, Object> body) {return httpClient.toBlocking().retrieve(HttpRequest.POST("/spl/call", body),Argument.of(Map.class));}
}

至此,SPL 与 Micronaut 的集成已完整搭建完毕。服务侧代码和配置可作为稳定通用的底层执行框架,实际的业务计算将完全由可热更新的 SPL 脚本驱动,实现逻辑与服务解耦、灵活可控。

通过 SPL 实现微服务中的业务计算逻辑

业务用例示例:识别年度大客户

列出某年内销售额排名前若干的客户,其销售额累计达到全公司一半者,视为“大客户”。

SPL脚本名为top-customer-ids.splx,脚本内容如下:

AB
1=year=json(jsonParam).year/Parse the input parameter jsonParam as a JSON object and extract the year field
2=connect@l("hsql")/Establish a connection to the local HSQL database
3=A2.query@x("select customer_id,order_amount from orders where year(order_date) = ?",year)/Execute a parameterized SQL query to fetch customer_id and order_amount for the specified year
4=A3.groups(customer_id;sum(order_amount):total_amount).sort(total_amount:-1)/Group the results by customer_id, calculate total order_amount as total_amount, and sort in descending order
5=a=0,half=A4.sum(total_amount)*0.5,A4.pselect((a+=total_amount,a>=half))/Compute half of the total sales and find the index where the cumulative sum first reaches or exceeds this half

其中jsonParam为脚本参数,格式为:{year:2012}

这个代码只有短短5行,读者可以自行脑补Java实现同样功能的复杂度。

脚本要先用SPL的IDE来编写调试,但这不是本文的重点,感兴趣的读者可以去参考SPL的官方文档。

编写好后的SPL脚本需要放置于raqsoftConfig.xml 中设置的SPL 脚本寻址路径下,例如:

<splPathList><splPath>/home/raqsoft/SPL</splPath>
</splPathList>

服务消费者端请求测试

请求行
POST http://localhost:8081/consumer/call-spl
Content-Type: application/json
请求体
{"splxName": "top-customer-ids","jsonParam": "{year:2012}"
}
返回
{"code": 200,"data": ["ERNSH","BLONP","QUICK","QUEEN","RATTC","PICCO","FRANK","SPLIR","SAVEA","SUPRD"],"message": "success"
}

应对业务逻辑变更

我们将“大客户”判定标准从销售额占比50%提升至80%,以更好满足精细化客户分析与服务需求。
在 SPL 中,业务逻辑脚本的修改非常简单,只需将 A5 单元格中的 0.5 改为 0.8 即可。更重要的是,SPL 脚本采用解释执行机制,支持运行时热更新——只需将修改后的脚本重新上传至服务端,再次发起请求即可获得新的计算结果,无需编译,也无需重启应用,变更即时生效。

相比之下,Java 的灵活性明显不足。由于销售占比门槛等规则通常是硬编码在应用逻辑中的,哪怕只是一个参数调整,也必须重新编译应用并重启服务,部署过程繁琐,响应慢,难以适应频繁变化的业务需求。

总结

通过本案例可以看出,将SPL融入Micronaut微服务架构,能够有效解决传统Java微服务在复杂业务逻辑处理方面的诸多痛点。得益于SPL的解释执行特性,业务计算逻辑可以实现热更新,无需重启服务即可动态调整计算规则,极大提升了系统的灵活性与运维效率。

相比之下,传统Java实现将规则硬编码于服务中,任何调整都需重新编译和部署,导致开发成本高、响应周期长,不利于快速变化的业务场景。

SPL作为一个专注于结构化数据处理的脚本语言,不仅具备强大的计算表达能力,还能够与Micronaut等现代微服务框架无缝协作,实现“计算逻辑脚本化、服务框架标准化、部署运维最小化”的现代微服务设计理念。

在实际项目中,采用SPL驱动微服务计算逻辑,不仅提升了开发效率,也为业务规则的快速变更提供了高效通道,是面向灵活敏捷业务需求的理想解决方案。

http://www.dtcms.com/a/390934.html

相关文章:

  • 类加载的过程以及双亲委派模型
  • 将 RabbitMQ 与 .NET Core Web API 和 Worker Services 结合使用
  • 面试编程题(三)
  • SSD性能优化之4K对齐
  • dolphindb vscode更改连接配置的操作步骤
  • 船用配电盘在线绝缘监测故障定位系统
  • 爬虫逆向--Day22Day23--核心实战案例【四川农机购置与补贴信息】【Webpack国密SM3、SM4】
  • 计算机网络————第二章 物理层
  • uniapp usb调试鸿蒙一直连接不上。
  • MySQL独立表空间:优缺点与最佳实践
  • 直播预告 | P4 One正式发布:让创新协作更简单、更安全、更可靠
  • Zookeeper脑裂是什么原因导致的?
  • APK360加固与“脱壳”:移动应用安全的攻与防
  • langchain-routerchain
  • 第四章:VSCode编辑器使用介绍
  • Problem: lab-week3- exercise03 Selection sort
  • Vue 事件总线 主流的一些npm包推荐
  • 总线协议如何支持总线错误条件?
  • N-156基于springboot,vue小区物业管理系统
  • 物理心理学 (Psychophysics) - 阈值 (Threshold) 测量方法 - 人因工程笔记2
  • 从 “数据采集” 到 “性能跃迁”:Simcenter LMS 如何用振动噪声试验,激活高端装备核心竞争力?
  • 【STL vector的全面指南】基础操作与底层实现<1>
  • 微服务-基础知识(CAP、BASE)
  • 存储卷清理策略在vps环境磁盘空间维护的操作指南
  • Day46 ARM硬件体系 从计算机架构、处理器类型、指令集到内核寄存器与SoC总线结构
  • 【MySQL】从视图到用户和权限管理
  • 栈与队列:核心差异与应用场景
  • 【Hadoop】ZooKeeper:分布式系统的协调核心与一致性保障
  • AWS 全球机房延迟对比 区域选型经验分享
  • 免费插件分享 |Scene Switcher Pro