RESP 客户端

连接 Redis 兼容客户端,通过主键读取完整的 PostgreSQL 行。本页介绍键编码、身份验证、响应行为、错误和 TLS。

键与响应约定

RESP 键用于标识一个已附加的表和一个主键对象:

CRUD:<db>.<schema>.<table>:<json pk>
CRUD:app.public.items:{"id":42}
CRUD:app.public.orders:{"tenant_id":7,"id":42}

MGET key [key ...] 按请求顺序返回 RESP2 数组。重复键保留各自位置。存在的行以 JSON bulk string 返回,不存在的行为 nil 元素。

限制 行为
每条命令最多 1,024 个键 在请求字节未超限时,1,025 个键的命令返回 ERR MGET accepts at most 1024 keys;达到 1,026 个键或更多时,解析器返回 ERR invalid argument count。
每个编码请求最多 65,536 字节 若输入缓冲区在完整请求解析前已填满,服务器会关闭连接。
每行 JSON 最大 65,536 字节 超大行无法通过 RESP 返回。
编码后的 MGET 响应最大 66,560 字节 聚合响应超出时返回 ERR response exceeds limit。

批次大小同时受键数量、编码请求字节数和响应字节数限制。每个编码请求请控制在 65,536 字节以内;键或行较大、接近任一字节上限时请拆分批次。

AUTH

发送其他命令前,先发送 AUTH <token>。客户端也可以发送 AUTH <username> <token>;用户名必须与 pg_local_cache.role 一致。所有 RESP 客户端共用该令牌,它不会为每个客户端选择不同的 PostgreSQL 权限。将令牌保存在权限为 0400 或 0600 的 pg_local_cache.auth_token_file 中;内联 auth_token 仅用于开发。非 loopback 监听器要求令牌至少为 32 字节。

TLS

原生 RESP TLS 使用 pg_local_cache.tls_* 设置,与 PostgreSQL 的 SQL TLS 分开。它要求 PostgreSQL 使用 OpenSSL 构建,并配置服务器证书和私钥。设置 tls_ca_file 后还会要求并验证客户端证书(mTLS)。如果只需验证服务器,请省略客户端证书选项。默认最低版本为 TLS 1.2。

监听器默认绑定到 loopback。关闭 TLS 时,非 loopback 明文监听器必须设置 pg_local_cache.allow_plaintext_network=on。监听器和安全设置请参阅技术参考。

redis-cli

将 PGLC_RESP_TOKEN 设置为已配置的令牌。本地快速入门还提供了一个演示令牌。

export REDISCLI_AUTH="$PGLC_RESP_TOKEN"
redis-cli -2 -h 127.0.0.1 -p 56379 MGET 'CRUD:pglc_demo.public.items:{"id":42}'
redis-cli -2 --tls --cacert ./ca.crt --cert ./client.crt --key ./client.key -h cache.example -p 6380 MGET 'CRUD:app.public.items:{"id":42}'

Go

安装 github.com/redis/go-redis/v9。设置 PGLC_RESP_TOKEN;TLS 示例使用 CA 和客户端证书文件。

package main

import (
	"context"
	"fmt"
	"os"

	"github.com/redis/go-redis/v9"
)

func main() {
	client := redis.NewClient(&redis.Options{
		Addr: "127.0.0.1:56379", Password: os.Getenv("PGLC_RESP_TOKEN"), Protocol: 2,
	})
	defer client.Close()
	rows, err := client.MGet(context.Background(), `CRUD:pglc_demo.public.items:{"id":42}`).Result()
	if err != nil {
		panic(err)
	}
	fmt.Printf("%v\n", rows)
}

TLS 变体:

package main

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"os"

	"github.com/redis/go-redis/v9"
)

func main() {
	ca, err := os.ReadFile("./ca.crt")
	if err != nil { panic(err) }
	roots := x509.NewCertPool()
	if !roots.AppendCertsFromPEM(ca) { panic("invalid CA") }
	cert, err := tls.LoadX509KeyPair("./client.crt", "./client.key")
	if err != nil { panic(err) }
	client := redis.NewClient(&redis.Options{
		Addr: "cache.example:6380", Password: os.Getenv("PGLC_RESP_TOKEN"), Protocol: 2,
		TLSConfig: &tls.Config{MinVersion: tls.VersionTLS12, ServerName: "cache.example", RootCAs: roots, Certificates: []tls.Certificate{cert}},
	})
	defer client.Close()
	rows, err := client.MGet(context.Background(), `CRUD:app.public.items:{"id":42}`).Result()
	if err != nil { panic(err) }
	fmt.Printf("%v\n", rows)
}

Node.js (redis v4)

使用 npm install redis@^4 安装。Redis v4 默认使用 RESP2。

import { createClient } from 'redis';

const client = createClient({
  socket: { host: '127.0.0.1', port: 56379 },
  password: process.env.PGLC_RESP_TOKEN,
});
client.on('error', console.error);
await client.connect();
try {
  console.log(await client.mGet(['CRUD:pglc_demo.public.items:{"id":42}']));
} finally {
  await client.quit();
}

TLS 变体:

import { readFileSync } from 'node:fs';
import { createClient } from 'redis';

const client = createClient({
  socket: {
    host: 'cache.example', port: 6380, tls: true, servername: 'cache.example',
    ca: [readFileSync('./ca.crt')],
    cert: readFileSync('./client.crt'), key: readFileSync('./client.key'),
  },
  password: process.env.PGLC_RESP_TOKEN,
});
client.on('error', console.error);
await client.connect();
try {
  console.log(await client.mGet(['CRUD:app.public.items:{"id":42}']));
} finally {
  await client.quit();
}

Python (redis-py)

使用 python -m pip install redis 安装。

import os
import redis

client = redis.Redis(host="127.0.0.1", port=56379,
                     password=os.environ["PGLC_RESP_TOKEN"],
                     decode_responses=True, protocol=2)
print(client.execute_command('MGET', 'CRUD:pglc_demo.public.items:{"id":42}'))
client.close()

TLS 变体:

import os
import redis

client = redis.Redis(host="cache.example", port=6380,
                     password=os.environ["PGLC_RESP_TOKEN"],
                     decode_responses=True, protocol=2, ssl=True,
                     ssl_ca_certs="./ca.crt", ssl_certfile="./client.crt",
                     ssl_keyfile="./client.key", ssl_cert_reqs="required",
                     ssl_check_hostname=True)
print(client.execute_command('MGET', 'CRUD:app.public.items:{"id":42}'))
client.close()

错误与边界

响应 原因
NOAUTH Authentication required 请先对连接进行身份验证。
WRONGPASS invalid authentication token 令牌或可选用户名不正确。
ERR MGET accepts at most 1024 keys 命令包含 1,025 个键;请拆分批次。
ERR invalid argument count 解析器因参数过多拒绝命令;MGET 达到 1,026 个键或更多时会超过参数上限。
ERR busy: relation locked, retry 关系锁定期间,延迟未命中队列已满;请重试请求。
ERR response exceeds limit 请缩小批次或行负载。
ERR PostgreSQL: … PostgreSQL 或 SPI 错误,包括 PostgreSQL 语句或锁超时及取消。
ERR MGET deadline exceeded 显式的 MGET 总期限已到,包括在队列中等待关系锁释放时;这与 PostgreSQL 语句或锁超时不同。
ERR KVik key targets a different database 使用 RESP 端点配置的数据库。
ERR unknown KVik table mapping 检查键中的模式和表是否已映射。
ERR key must use CRUD:database.schema.table:{primary-key-json} 使用完整的 CRUD 键格式。
ERR KVik key must end with a primary-key JSON object 在表范围后提供主键 JSON 对象。
ERR invalid CRUD cache scope 仅适用于 INVALIDATE:提供的范围不是受支持的 CRUD 范围。

RESP 使用独立连接,不参与调用方的 SQL 事务。SQL 事务请直接使用 PostgreSQL;参见失效指南。

清理演示环境

docker compose -f examples/compose.yaml -f examples/compose.resp.yaml down

2.x 中的 local_cache.mget(regclass, anyarray) 已在 3.0.0 中移除;请参阅升级指南。

更新于 .