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

用 SPL 编写阿里云 FC2.0 函数

前言

在数字化转型持续加速的背景下,企业越来越多地将业务逻辑以服务化方式部署至云端。阿里云函数计算(Function Compute,简称FC)作为一种无服务器计算平台,屏蔽了底层资源运维的复杂性,使开发者能够专注于核心逻辑的开发与交付。只需上传代码,即可通过HTTP请求或事件驱动灵活触发函数执行,实现弹性伸缩、按需计费的函数式计算能力。

在实际应用中,函数计算往往需要处理复杂的数据处理任务,如多源数据的汇聚、清洗与分析。然而,使用传统Java编写这类逻辑不仅代码冗长、调试困难,还缺乏灵活性,微小的逻辑变更通常也需要重新构建、打包并上传整个函数代码包。

SPL(Structured Process Language)专注于结构化数据处理,具备简洁的语法、丰富的计算函数,特别适合表达各类复杂的数据逻辑。更重要的是,SPL支持将数据处理逻辑以外部脚本形式运行,修改逻辑时只需替换脚本文件,无需重新构建或部署函数代码包,即可在下一次调用时自动生效。这种“轻量级热切换”特性,非常契合函数计算轻量、快速迭代的特性,有效提升了云端函数逻辑的可维护性与敏捷性。

此外,SPL原生支持多种数据源(如支持JDBC的数据源、NAS文件、JSON等),并具备可视化分步调试能力,是Serverless架构中应对动态数据逻辑、数据服务编排的高效利器。

本文将介绍如何结合Micronaut框架与SPL脚本,以Fat-JAR方式部署至阿里云FC2.0,构建一个通过RESTful API调用、支持逻辑热更新的无服务器计算服务。示例将展示SPL如何访问并分析MySQL数据库和NAS文件系统,帮助读者快速构建可扩展、易维护的Serverless数据服务方案。

SPL在阿里云FC中的架构图

函数开发与部署

创建项目

使用 Micronaut CLI 快速创建 Maven 项目:

mn create-app micronaut-spl --build maven

创建完成后,可在集成开发环境(如 IntelliJ IDEA)中打开项目继续开发。

集成SPL

添加依赖

在 pom.xml 中引入 SPL 与数据库驱动:

<dependency><groupId>com.scudata.esproc</groupId><artifactId>esproc</artifactId><version>20250605</version>
</dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><version>8.3.0</version>
</dependency>

提示:中央仓库的更新频率有限,推荐从[SPL 下载地址]下载标准版,并通过私有Maven仓库同步更新。

相关jar包位于安装目录esProc/lib/,其中两个基础jar包为:

· esproc-bin-xxxx.jar:SPL 引擎及 JDBC 驱动

· icu4j-60.3.jar:国际化支持库

准备SPL配置文件

raqsoftConfig.xml 为 SPL 的核心配置文件,负责数据源定义、脚本路径管理等。本文中RDS MySQL数据源与脚本路径的示例配置如下:

……
<DBList encryptLevel="0"><DB name="rds_mysql"><property name="url" value="jdbc:mysql://rm-xxx.mysql.rds.aliyuncs.com:3306/spltest?useCursorFetch=true"></property><property name="driver" value="com.mysql.cj.jdbc.Driver"></property><property name="type" value="10"></property><property name="user" value="dms_user"></property><property name="password" value="password"></property><property name="batchSize" value="0"></property><property name="autoConnect" value="true"></property><property name="useSchema" value="false"></property><property name="addTilde" value="false"></property><property name="caseSentence" value="false"></property></DB>
</DBList>
<esProc>
……<splPathList><splPath>/opt</splPath></splPathList>
……
</esProc>
……

特别注意:需加上useCursorFetch=true,否则JDBC驱动可能将整个结果集加载至内存,容易在 FC 的内存限制下导致连接断开或错误。

与传统项目部署方式不同,这里我们先将raqsoftConfig.xml打成zip包(raqsoftConfig.xml.zip),以供后续作为自定义层上传使用。

SPL 通用接口实现

使用 SPL 提供的 JDBC 接口,在 Micronaut 中构建统一的函数调用入口。通过 HTTP POST 请求传入参数,执行指定 SPL 脚本,并将结果以 JSON 格式返回。

该接口接收两个参数:

· splxName:脚本文件名(不带扩展名)

· jsonParam:SPL脚本所需参数(JSON字符串)

返回值中包括状态码、消息提示及脚本执行结果数据。

package micronaut.spl;import io.micronaut.http.annotation.*;
import io.micronaut.http.MediaType;import java.sql.*;
import java.util.*;@Controller("/spl")
public class SPLExecutionController {@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://?config=/opt/raqsoftConfig.xml");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<>();while (true) {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 LinkedHashMap<>();for (int i = 1; i <= columnCount; i++) {row.put(metaData.getColumnLabel(i), rs.getObject(i));}currentResult.add(row);}}if (!currentResult.isEmpty()) {allResults.add(currentResult);}}}if (!st.getMoreResults() && st.getUpdateCount() == -1) {break;}hasResult = true;}if (!allResults.isEmpty()) {response.put("code", 200);response.put("message", "success");response.put("data", allResults.size() == 1 ? allResults.get(0) : allResults);} else {response.put("code", 404);response.put("message", "No result data");}}} catch (Exception e) {e.printStackTrace();response.put("code", 500);response.put("message", "Execution failed: " + e.getMessage());}return response;}
}

打包项目后,再打成zip包(micronaut-spl-0.1.jar.zip)

上传部署Fat-JAR

在阿里云 FC 控制台中创建函数,推荐配置如下:

· 创建方式:使用自定义运行时创建

· 运行环境:Java 21

· 上传方式:ZIP 上传(micronaut-spl-0.1.jar.zip)

· 启动命令:java -jar micronaut-spl-0.1.jar

· 认证方式:无需认证(测试用)

点击创建后,即可完成函数部署。

添加配置层

在函数详情中点击“编辑层”,上传打包好的 raqsoftConfig.xml.zip,新建名为 config 的自定义层。

编写SPL脚本

使用 MySQL 表数据计算

在阿里云 RDS 中创建 MySQL 实例及 orders 表,导入 TPC-H 示例数据(建库建表和导入过程略)。以下脚本 rds-mysql.splx 演示如何按订单年份与状态分组统计订单总额:

A
1=connect("rds_mysql")
2=A1.cursor@x("SELECT O_ORDERDATE, O_ORDERSTATUS, O_TOTALPRICE FROM ORDERS")
3=A2.groups(year(O_ORDERDATE):year,O_ORDERSTATUS:status;sum(O_TOTALPRICE):amount)

将脚本打zip包后,添加并创建自定义层(splx),即可在/opt下访问到该脚本。

调用示例

请求行
POST https://sss-fff-xxx.cn-xxx.fcapp.run/spl/call
Content-Type: application/json
请求体
{"splxName": "rds-mysql","jsonParam": ""
}
返回
{"code": 200,"data": [{"year": 1992,"status": "F","amount": 34330674052.43},{"year": 1993,"status": "F","amount": 34340410079.03},{"year": 1994,"status": "F","amount": 34416369052.97},{"year": 1995,"status": "F","amount": 6614961429.26},{"year": 1995,"status": "O","amount": 20822054361.33},{"year": 1995,"status": "P","amount": 7109117393.01},{"year": 1996,"status": "O","amount": 34609364760.86},{"year": 1997,"status": "O","amount": 34373633413.04},{"year": 1998,"status": "O","amount": 20212721905.53}],"message": "success"
}

使用 NAS 文件系统

通过阿里云函数配置挂载 NAS 路径(如 /mnt/nas),可在 SPL 中直接读写该路径下的文件,实现持久化与共享。

以下为 w.splx(写)和 r.splx(读)两个脚本,模拟数据生成与读取处理过程。

w.splx如下:

A
1=path=json(jsonParams).path
2=connect("rds_mysql")
3=A2.cursor@x("SELECT O_ORDERDATE, O_ORDERSTATUS, O_TOTALPRICE FROM ORDERS")
4=file(path/"orders.btx").export@b(A3)
5return "btx exported."

r.splx如下:

A
1>p=json(jsonParam),path=p.path,n=p.n,m=p.m
2=file(path/"orders.btx").cursor@b()
3=A2.skip(n)
4=A2.fetch(m)
5>A2.close()
6return A4

写脚本,通过数据库游标读取MySQL实例中的orders表,并写入orders.btx文件(path可使用配置的NAS路径,实现持久化或与其他服务共享数据)。

读脚本,通过读取指定路径文件的游标,跳过前n条记录,展示接下来的m条记录。

也可将脚本文件本身存放在 NAS 中,配合 raqsoftConfig.xml 中增加路径:

…
<splPathList><splPath>/opt;/mnt/nas</splPath>
</splPathList>
…

写入调用示例

请求行

POST https://sss-fff-xxx.cn-xxx.fcapp.run/spl/call

Content-Type: application/json

请求体
POST https://sss-fff-xxx.cn-xxx.fcapp.run/spl/call
Content-Type: application/json
返回
{"splxName": "w","jsonParam": "{path:/mnt/nas/}"
}

读取调用示例

请求行
POST https://sss-fff-xxx.cn-xxx.fcapp.run/spl/call
Content-Type: application/json
请求体
{"splxName": "r","jsonParam": "{path:/mnt/nas/,n:99,m:2}"
}
返回
{"code": 200,"data": [{"O_ORDERDATE": "1992-12-16","O_ORDERSTATUS": "F","O_TOTALPRICE": 198800.71},{"O_ORDERDATE": "1994-02-17","O_ORDERSTATUS": "F","O_TOTALPRICE": 2519.40}],"message": "success"
}

业务逻辑变更

我们将“使用 MySQL 表数据计算”的“按订单年份与状态分组统计订单总额”改为“按订单年月与状态分组统计订单总额”,只需要将 A3 中的 year(O_ORDERDATE):year 改为 month@y(O_ORDERDATE):YearMonth 即可。由于 SPL 支持将数据逻辑以外部脚本形式运行,此类修改无需重新构建和部署函数代码包,只需更新脚本内容即可在下一次函数调用时自动生效。这种“轻量级热切换”方式完美契合 Serverless 架构短生命周期、按需执行的特点,显著提升了云端数据服务的响应速度与维护效率,尤其适合频繁调整的数据分析类场景。

总结

借助 Micronaut 的轻量级框架特性与 SPL 的灵活脚本执行能力,我们成功构建了一个具备结构化数据处理能力的 Serverless 应用,并以 Fat-JAR 方式部署至阿里云函数计算 FC2.0。函数启动后可通过 REST 接口按需触发,访问 MySQL 或 NAS 等多种数据源,执行复杂的计算逻辑。

相比传统 Java 开发,SPL 显著简化了数据处理过程,使业务逻辑更清晰、更易维护。尤其在 Serverless 架构下,SPL 的“脚本热切换”能力无需重构或重新部署函数,即可在下次调用中自动生效,极大提升了系统的灵活性和运维效率。结合 NAS 文件系统,还能实现函数之间的数据共享与持久化,满足更多元的业务场景需求。

这种架构适用于需要快速迭代、规则频繁调整的数据服务场景,是企业在 Serverless 架构下构建高效、可扩展数据计算服务的有力方案。

http://www.xdnf.cn/news/19837.html

相关文章:

  • AntdesignVue 的月份区间组件用法
  • mysql分页SQL
  • Dubbo(分布式RPC调用和分布式文件储存)
  • 深入解析Django重定向机制
  • 2025React面试题集锦
  • Java 与 Docker 的最佳实践
  • wins中怎么用一个bat文件启动jar包和tomcat等多个服务
  • Linux tail 命令使用说明
  • 【C++详解】C++11(四) 包装器:function、bind、STL中⼀些变化
  • 【AI论文】UI-TARS-2技术报告:借助多轮强化学习推进图形用户界面(GUI)智能体发展
  • 20. 云计算-华为云-云服务
  • Linux Centos7搭建LDAP服务(解决设置密码生成密文添加到配置文件配置后输入密码验证报错)
  • 分享星空投影灯方案
  • 高效菜单管理页面:一键增删改查
  • Word 常用快捷键大全:提升文档处理效率的必备技巧​
  • FastGPT源码解析 Agent工作流编排后端详解
  • Ansible自动化运维:从入门到精通
  • 【面试题】词汇表大小如何选择?
  • React实现点击按钮复制操作【navigator.clipboard与document.execCommand】
  • Elasticsearch面试精讲 Day 6:Query DSL查询语法详解
  • 【JAVA】windows本地跑zookeeper,然后使用代码连接服务获取znode数据
  • 【leetcode】130. 被围绕的区域
  • NLP插曲番外 · 猫猫狐狐问答夜话
  • 分词器详解(一)
  • 信息融智学=信息哲学+信息科学+信息技术+信息系统工程+信息处理之智
  • 组长跟我说,她招人看重的是数据分析能力
  • 计算机视觉(七):膨胀操作
  • 机器学习 - Kaggle项目实践(8)Spooky Author Identification 作者识别
  • awk命令
  • GitHub 上那些值得收藏的英文书籍推荐(计算机 非计算机类)