算法服务开发
本文将会详细介绍如何使用 Java SDK
开发算法服务, 实现算法集成. 示例项目目上传至 https://github.com/air-iot/sdk-java-examples/tree/master/algorithm-example.
介绍
算法服务
是扩展或将已有 算法
集成到平台的一种方式. 在平台现有的功能不满足需求时, 可以通过开发算法服务来实现自定义的功能.
开发步骤
1. 创建项目
该过程同 数据接入驱动开发-创建项目 中的创建项目过程一致.
2. 引入SDK
在 pom.xml
中引入依赖:
<dependency>
<groupId>io.github.air-iot</groupId>
<artifactId>sdk-algorithm-starter</artifactId>
<version>4.x.x</version>
</dependency>
3. 定义 schema
schema
定义描述了该算法程序支持哪些算法函数, 以及每个算法函数的输入和输出参数定义. 每个算法程序可以包含多个算法函数, 每个算法函数的名称必须唯一, 详细说明参考算法schema说明. 示例如下:
[
{
"title": "函数1-加法",
"function": "add",
"input": {
"type": "object",
"properties": {
"num1": {
"title": "参数1",
"type": "number"
},
"num2": {
"title": "参数2",
"type": "number"
}
},
"required": [
"num1",
"num2"
]
},
"output": {
"type": "object",
"properties": {
"num1": {
"title": "结果",
"type": "number"
}
}
}
},
{
"title": "函数2-绝对值",
"function": "abs",
"input": {
"type": "object",
"properties": {
"num1": {
"title": "参数1",
"type": "number"
}
},
"required": [
"num1"
]
},
"output": {
"type": "object",
"properties": {
"res": {
"title": "结果",
"type": "number"
}
}
}
},
{
"title": "函数3-获取当前系统时间",
"function": "now",
"input": {
"type": "object",
"properties": {},
"required": []
},
"output": {
"type": "object",
"properties": {
"sysdate": {
"title": "当前系统时间",
"type": "string"
}
}
}
},
{
"title": "函数4-接收Map",
"function": "recvMap",
"input": {
"type": "object",
"properties": {
"name": {
"title": "姓名",
"type": "string"
}
},
"required": ["name"]
},
"output": {
"type": "object",
"properties": {
"result": {
"title": "输出结果",
"type": "string"
}
}
}
},
{
"title": "函数4-接收字符串",
"function": "recvString",
"input": {
"type": "object",
"properties": {
"name": {
"title": "姓名",
"type": "string"
}
},
"required": ["name"]
},
"output": {
"type": "object",
"properties": {
"result": {
"title": "输出结果",
"type": "string"
}
}
}
}
]
4. 实现算法接口
SDK
中定义了 算法服务接口
, 开发者需要实现这个接口.
/**
* 算法服务接口定义. 该接口定义了算法应用的生命周期方法, 以及算法函数的执行方法. <br>
* <b>注: 该接口的实现必须注入到 Spring IoC 容器中, 否则 SDK 无法识别.</b>
* <br>
* 可以在该接口的实现类中, 使用 {@link AlgorithmFunction} 注解定义算法函数.
* 也可以不使用该注解, 而是在 {@link #run(String, String, Map)} 方法中根据 {@code function} 参数的值, 执行对应的算法函数.
*/
public interface AlgorithmApp {
/**
* 当算法服务启动时,会调用此方法.
* <br>
* 如果程序需要在算法服务启动时执行一些初始化操作, 可以重写该方法.
*/
default void start() {
}
/**
* 当算法服务停止时,会调用此方法
* <br>
* 如果程序需要在算法服务停止时执行一些清理工作, 可以重写该方法.
*/
default void stop() {
}
/**
* 获取算法的 schema 定义信息
*/
String schema();
/**
* 执行算法. 如果在当前类型中没有找到使用 {@link AlgorithmFunction} 定义方法, 则会调用此方法
*
* @param projectId 发起请求的项目ID
* @param function 函数名
* @param params 请求参数
* @return 算法执行结果
* @throws AlgorithmException 算法执行异常, 该异常消息会作为错误信息返回给调用方
*/
default Object run(String projectId, String function, Map<String, Object> params) throws AlgorithmException {
throw new IllegalArgumentException("未实现的算法 '" + function + "'");
}
}
AlgorithmFunction 注解
SDK
提供了 @AlgorithmFunction
注解, 用于标识实现类中的方法对应 schema 的算法函数. 该注解只能用在方法上.
使用该注解修饰的方法, 必须满足以下条件:
- 必须为
public
方法. - 必须带有 1 或 2 个参数, 且第 1 个参数必须为
String projectId
用于接收发起请求的项目ID. - 如果该算法函数有参数定义, 必须放在第 2 个参数位置, 且类型只为
String
,Map<String, Object>
或自定义类型. - 该函数的返回值. 不能是基本类型, 只能是
Map<String, Object>
或自定义类型.
算法调用请求执行流程
- 当平台调用该算法服务时,
SDK
会解析请求中的function
信息. - 根据
function
的值, 在实现类中查找是否有使用@AlgorithmFunction("算法函数名")
注解修饰的方法. 如果找到了, 则会调用该方法, 并将方法的返回值作为算法执行结果返回给平台. - 如果没有找到使用
@AlgorithmFunction("算法函数名")
修饰的方法时, 则会调用run
方法, 并将function
参数的值作为run
方法的第二个参数传入, 由开发者根据function
参数值执行对应的算法逻辑. 最后将run
方法的返回值作为算法执行结果返回给平台.
示例
public class MyAlgorithm implements AlgorithmApp {
/**
* 算法函数1, 与 schema 中的 algorithm1 对应.
*/
@AlgorithmFunction("algorithm1")
public Map<String, Object> algorithm1(String projectId, Map<String, Object> params) {
// 自定义算法函数1
}
/**
* 算法函数2, 与 schema 中的 algorithm2 对应. 该方法接收一个自定义类型的参数, 并且返回自定义类型对象.
*/
@AlgorithmFunction("algorithm2")
public MyResult algorithm2(String projectId, MyParams params) {
// 自定义算法函数2
}
/**
* 算法函数3, 与 schema 中的 algorithm3 对应. 该方法不接收任何参数.
*/
@AlgorithmFunction("algorithm3")
public Map<String, Object> algorithm3(String projectId) {
// 自定义算法函数3
}
/**
* 算法函数4, 与 schema 中的 algorithm4 对应. 该方法接收一个 String 类型的参数.
*/
@AlgorithmFunction("algorithm4")
public Map<String, Object> algorithm4(String projectId, String params) {
// 自定义算法函数4
}
/**
* 默认执行函数. 例如: 当 function 为 algorithm5 时, 会调用此方法.
*/
@Override
public Object run(String projectId, String function, Map<String, Object> params) throws AlgorithmException {
if("algorithm5".equals(function)) {
// 自定义算法函数5
} else if("algorithm6".equals(function)) {
// 自定义算法函数6
} else {
throw new IllegalArgumentException("未实现的算法 '" + function + "'");
}
}
}
5. 算法配置信息
算法配置信息定义了算法的基本信息以及平台算法服务的连接信息. 整体配置如下:
algorithm:
id: 自定义算法标识 # 必填
name: 自定义算法名称 # 必填
max-threads: 10 # 可选. 最大线程数, 默认: 0, 表示取 CPU 核心数
algorithm-grpc: # 平台算法服务连接配置
host: localhost # 必填. 算法服务地址
port: 9236 # 必填. 算法服务端口, 默认: 9236
6. 打包
具体打包步骤请参考 流程插件开发-打包 中的步骤.
windows系统打包发布时的算法配置
algorithm:
id: 自定义算法标识
name: 自定义算法名称
max-threads: 10
algorithm-grpc:
host: 127.0.0.1
port: 9236
linux系统打包发布时的算法配置
algorithm:
id: 自定义算法标识
name: 自定义算法名称
max-threads: 10
algorithm-grpc:
host: algorithm
port: 9236
7. 部署
具体部署步骤请参考 流程插件开发-部署 中的步骤.