#iot #real-time #messaging #broker #json-rpc #rpc-api

bin+lib objtalk

适用于物联网项目的轻量级实时数据库

9 个版本

0.3.0 2022年11月23日
0.2.1 2022年11月22日
0.1.5 2021年6月6日

778数据库接口

26 每月下载次数

MIT 许可证

150KB
3.5K SLoC

Rust 2.5K SLoC // 0.0% comments JavaScript 1K SLoC // 0.0% comments

包含 (WOFF 字体, 41KB) icons.woff2

objtalk

Documentation Automated Builds

objtalk 是一个轻量级实时数据库,适用于物联网项目。它可以用于实时存储和查询设备状态,并通过事件和 RPC 在设备之间进行通信。它是 mqtt 的加强版。

objtalk 存储对象。每个对象都有一个名称、一个值(json)和一个时间戳,表示对象上次修改的时间。可以通过 API 创建、修改、删除和查询对象。查询还可以监视更改并在实时返回给客户端。

API 可以使用多种传输方式访问。该协议类似于 JSON-RPC,可以通过 TCP 或 WebSocket 使用。也可以使用 HTTP/REST 进行简单命令。

objtalk 中存储的所有对象也可以使用内置的管理面板在浏览器中查看。

安装

安装预编译的二进制文件或使用 cargo

$ cargo install objtalk

使用服务器

创建一个配置文件,例如命名为 objtalk.toml

[storage]
backend = "sqlite"
sqlite.filename = "objtalk.db"

[[http]]
addr = "127.0.0.1:3000"
admin.enabled = true
#admin.asset-overrides = "admin"
#allow-origin = "*"

[[tcp]]
addr = "127.0.0.1:3001"

启动服务器

$ objtalk-server --config objtalk.toml
http transport listening on http://127.0.0.1:3000
tcp transport listening on 127.0.0.1:3001

访问管理面板 http://127.0.0.1:3000

使用客户端

$ objtalk-cli -u http://127.0.0.1:3000 set foo 42
$ objtalk-cli -u http://127.0.0.1:3000 get foo
[
    {
        "name": "foo",
        "value": 42,
        "lastModified": "2021-05-07T17:53:29.066420Z"
    }
]
$ objtalk-cli -u http://127.0.0.1:3000 remove foo

将 objtalk 作为 Rust 库使用

objtalk crate 提供了 objtalk-serverobjtalk-cli 二进制文件,但您也可以将其作为库集成到您的 Rust 项目中。请参阅 文档 以获取所有可用方法的列表。您可以使用 serverclient 功能标志来缩减库。

其他语言的库

其他集成

API/协议

基础

设置 name value

set 创建或替换对象。

使用 objtalk-cli

$ objtalk-cli set sensor '{"temperature":20}'

通过 http

$ curl -X POST 127.0.0.1:3000/objects/sensor -d '{"temperature":20}'

通过 tcp 或 websocket

{
    "id": 1,
    "type": "set",
    "name": "sensor",
    "value": { "temperature": 20 }
}

{
    "requestId": 1,
    "result": {
        "success": true
    }
}

修补 name value

patch 创建或更新对象。如果已存在具有相同名称的对象,则值将合并(非深度)。

使用 objtalk-cli

$ objtalk-cli replace sensor '{"temperature":20}'

通过 http

$ curl -X PATCH 127.0.0.1:3000/objects/sensor -d '{"temperature":20}'

通过 tcp 或 websocket

{
    "id": 1,
    "type": "patch",
    "name": "sensor",
    "value": {
        "temperature": 20
    }
}

{
    "requestId": 1,
    "result": {
        "success": true
    }
}

获取 pattern

get 返回所有名称与 pattern 匹配的对象。

对象名称看起来像文件路径,由多个部分组成,这些部分通过正斜杠分隔。每个部分可以是字符串,或者是一个 +*+ 匹配下一个斜杠之前的任何内容,* 匹配字符串末尾之前的任何内容。可以通过逗号将多个子模式组合起来,并且它们是逻辑或(OR)组合的。

一些示例

  • * 匹配所有对象
  • device/lamp/+ 匹配 device/lamp/livingroomdevice/lamp/bedroom,但不匹配 device/sensor/livingroom
  • device/+/livingroom 匹配 device/lamp/livingroomdevice/sensor/livingroom
  • device/* 匹配 device/lamp/livingroomdevice/lamp/bedroomdevice/sensor/livingroom
  • device/lamp/+,device/sensor/+ 匹配 device/lamp/livingroomdevice/lamp/bedroomdevice/sensor/livingroom

使用 objtalk-cli

$ objtalk-cli get '*'

通过 http

$ curl '127.0.0.1:3000/query?pattern=*'

通过 tcp 或 websocket

{
    "id": 1,
    "type": "get",
    "pattern": "*"
}

{
    "requestId": 1,
    "result": {
        "objects": [
            {
                "name": "sensor",
                "value": { "temperature": 20 },
                "lastModified": "YYYY-MM-DDTHH:MM:SS.SSSSSSSSSZ"
            }
        ]
    }
}

查询 pattern

query 返回所有匹配 pattern 的对象,并监视其变化。初始响应包含所有匹配 pattern 的对象。当创建、更改或删除具有匹配名称的新对象时,会触发一个 queryAdd 事件、queryChange 事件或 queryRemove 事件。

使用 objtalk-cli:不受支持

通过 http

$ curl '127.0.0.1:3000/query?pattern=*' -H "Accept: text/event-stream"

通过 tcp 或 websocket

{
    "id": 1,
    "type": "query",
    "pattern": "*"
}

{
    "requestId": 1,
    "result": {
        "queryId": "01234567-89ab-cdef-0123-456789abcdef",
        "objects": [
            {
                "name": "sensor",
                "value": { "temperature": 20 },
                "lastModified": "YYYY-MM-DDTHH:MM:SS.SSSSSSSSSZ"
            }
        ]
    }
}

{
    "type": "queryChange",
    "queryId": "01234567-89ab-cdef-0123-456789abcdef",
    "object": {
        "name": "sensor",
        "value": { "temperature": 21 },
        "lastModified": "YYYY-MM-DDTHH:MM:SS.SSSSSSSSSZ"
    }
}

{
    "type": "queryAdd",
    "queryId": "01234567-89ab-cdef-0123-456789abcdef",
    "object": {
        "name": "foo",
        "value": { "bar": true },
        "lastModified": "YYYY-MM-DDTHH:MM:SS.SSSSSSSSSZ"
    }
}

{
    "type": "queryChange",
    "queryId": "01234567-89ab-cdef-0123-456789abcdef",
    "object": {
        "name": "foo",
        "value": { "bar": false },
        "lastModified": "YYYY-MM-DDTHH:MM:SS.SSSSSSSSSZ"
    }
}

{
    "type": "queryRemove",
    "queryId": "01234567-89ab-cdef-0123-456789abcdef",
    "object": {
        "name": "foo",
        "value": { "bar": false },
        "lastModified": "YYYY-MM-DDTHH:MM:SS.SSSSSSSSSZ"
    }
}

取消订阅 queryId

unsubscribe 停止监视变化并删除查询。

使用 objtalk-cli:不受支持

通过 HTTP:当事件流关闭时隐式执行

通过 tcp 或 websocket

{
    "id": 1,
    "type": "unsubscribe",
    "queryId": "01234567-89ab-cdef-0123-456789abcdef"
}
{
    "requestId": 1,
    "result": {
        "success": true
    }
}

删除 name

remove 删除对象。返回值指示是否存在具有该名称的对象,并且是否已成功删除。

使用 objtalk-cli

$ objtalk-cli remove sensor

通过 http

$ curl -X DELETE 127.0.0.1:3000/objects/sensor

通过 tcp 或 websocket

{
    "id": 1,
    "type": "remove",
    "name": "sensor"
}

{
    "requestId": 1,
    "result": {
        "existed": true
    }
}

事件

对象还可以发出事件。您可以通过创建查询来监听事件。

发出 object event data

emit 在现有对象上发布一个事件。

使用 objtalk-cli

$ objtalk-cli emit gamepad buttonPress '{"button":"a"}'

通过 http

$ curl -X POST 127.0.0.1:3000/events/gamepad -d '{"event":"buttonPress","data":{"button":"a"}}'

通过 tcp 或 websocket

{
    "id": 1,
    "type": "emit",
    "object": "gamepad",
    "event": "buttonPress",
    "data": { "button": "a" }
}

{
    "requestId": 1,
    "result": {
        "success": true
    }
}

这将在所有匹配该对象的所有查询上产生以下事件

{
    "type": "queryEvent",
    "queryId": "01234567-89ab-cdef-0123-456789abcdef",
    "object": "gamepad",
    "event": "buttonPress",
    "data": { "button": "a" }
}

RPC

可以在对象上调用方法。一个客户端连接到 objtalk,监听方法调用,执行它们并将结果推送到 objtalk。其他客户端可以调用这些方法。

调用方法

invoke 在对象上调用方法。

使用 objtalk-cli

$ objtalk-cli invoke device/lamp/livingroom setState '{"on":true}'

通过 http

$ curl -X POST 127.0.0.1:3000/invoke/device/lamp/livingroom -d '{"method":"setState","args":{"on":true}}'

通过 tcp 或 websocket

{
    "id": 1,
    "type": "invoke",
    "object": "device/lamp/livingroom",
    "method": "setState",
    "args": { "on": true }
}

{
    "requestId": 1,
    "result": {
        "success": true
    }
}

提供方法调用

为了为对象提供 RPC 调用,客户端(“提供者”)必须连接到 objtalk,并使用 provideRpc 设置为 true 创建一个查询。一旦另一个客户端(“消费者”)尝试在对象上调用方法,查询上就会发出一个 queryInvocation 事件。提供者可以处理请求,并使用 invokeResult 命令将结果返回给消费者。

整个流程看起来是这样的

{
    "id": 1,
    "type": "query",
    "pattern": "device/lamp/livingroom",
    "provideRpc": true
}

{
    "requestId": 1,
    "result": {
        "queryId": "01234567-89ab-cdef-0123-456789abcdef",
        "objects": [
            {
                "name": "device/lamp/livingroom",
                "value": { "on": false },
                "lastModified": "YYYY-MM-DDTHH:MM:SS.SSSSSSSSSZ"
            }
        ]
    }
}

{
    "type": "queryInvocation",
    "queryId": "01234567-89ab-cdef-0123-456789abcdef",
    "invocationId": "fedcba98-7654-3210-fedc-ba9876543210",
    "object": "device/lamp/livingroom",
    "method": "setState",
    "args": { "on": true }
}

{
    "id": 2,
    "type": "invokeResult",
    "invocationId": "fedcba98-7654-3210-fedc-ba9876543210",
    "result": { "success": true }
}

{
    "requestId": 2,
    "result": {
        "success": true
    }
}

断开连接命令

客户端可以注册一组在客户端断开连接时执行的命令。这可以用来,例如,通过改变对象值来指示设备已离线。支持的命令有 setpatchremoveemit

setDisconnectCommands commands

setDisconnectCommands 设置在客户端断开连接时执行的命令列表。

使用 objtalk-cli:不受支持

通过 http:不支持

通过 tcp 或 websocket

{
    "id": 1,
    "type": "setDisconnectCommands",
    "commands": [
        {
            "type": "set",
            "name": "device/foo",
            "value": { "offline": true },
        },
        {
            "type": "patch",
            "name": "device/foo",
            "value": { "offline": true },
        },
        {
            "type": "remove",
            "name": "client",
        },
        {
            "type": "emit",
            "name": "device/foo",
            "event": "offline",
            "data": {},
        }
    ]
}

{
    "requestId": 1,
    "result": {
        "success": true
    }
}

依赖项

~7–25MB
~310K SLoC