当前位置: 首页 > 工具软件 > PostgREST > 使用案例 >

使用PostgREST的RestAPI操作之管理与优化

弘靖琪
2023-12-01

使用PostgREST的RestAPI操作之管理与优化

 

硬化PostgREST 

PostgREST是构建RESTful API的快速方法。它的默认行为非常适合开发中的脚手架。只要您采取预防措施,当需要投入生产时,它也会发挥很大的作用。PostgREST是一个小巧的工具,专注于执行API到数据库的映射。我们依靠像Nginx这样的反向代理来提供其他保护措施。

第一步是创建一个Nginx配置文件,该文件代理对基础PostgREST服务器的请求。

http {
  # ...
  # upstream configuration
  upstream postgrest {
    server localhost:3000;
  }
  # ...
  server {
    # ...
    # expose to the outside world
    location /api/ {
      default_type  application/json;
      proxy_hide_header Content-Location;
      add_header Content-Location  /api/$upstream_http_content_location;
      proxy_set_header  Connection "";
      proxy_http_version 1.1;
      proxy_pass http://postgrest/;
    }
    # ...
  }
}

注意

对于ubuntu,如果您已经通过安装了nginx,则apt可以将其添加到中的配置文件中 /etc/nginx/sites-enabled/default

块全表操作

管理员选择的架构中的每个表都作为顶级路由公开。客户端请求由某些数据库角色执行,具体取决于它们的身份验证。支持所有与角色允许的动作相对应的HTTP动词。例如,如果活动角色可以删除表的行,则客户端可以使用DELETE动词。这是一个API要求,用于从假设的日志表中删除旧行:

DELETE /logs?time=lt.1991-08-06 HTTP/1.1

但是,通过省略查询参数来删除整个表非常容易!

DELETE /logs HTTP/1.1

这可能是偶然发生的,例如通过将请求从GET切换到DELETE。为了防止意外操作,请使用pg-safeupdate PostgreSQL扩展。如果在未指定条件的情况下执行UPDATE或DELETE,则会引发错误。要安装它,您可以使用PGXN网络:

sudo -E pgxn install safeupdate

# then add this to postgresql.conf:
# shared_preload_libraries='safeupdate';

这不能防止恶意行为,因为有人可以添加不影响结果集的url参数。为避免这种情况,您必须求助于数据库权限,禁止错误的人删除行,并且在需要更好的访问控制时使用行级安全性

计数头

为了方便客户端分页控件,PostgREST支持在响应中计数和报告表的总大小。如“ 限制和分页”中所述,响应通常包括一个范围,但未指定总计,例如

HTTP/1.1 200 OK
Range-Unit: items
Content-Range: 0-14/*

但是,包括请求标头将计算并包括全部计数:Prefer: count=exact

HTTP/1.1 206 Partial Content
Range-Unit: items
Content-Range: 0-14/3573458

这在小型表中很好,但是由于PostgreSQL的MVCC体系结构,大型表的计数性能会下降。对于非常大的表,检索结果可能花费很长时间,这会导致拒绝服务攻击。解决方案是从所有请求中删除此标头:

-- Pending nginx config: Remove any prefer header which contains the word count

注意

在将来的版本中,我们将支持利用PostgreSQL统计表来获得快速(且相当准确)的结果。Prefer: count=estimated

HTTPS 

请参阅身份验证指南的HTTPS部分。

速率限制

Nginx支持“泄漏桶”速率限制(请参阅官方文档)。使用标准的Nginx配置,可以将路由分组到请求区域中以进行速率限制。例如,我们可以为登录尝试定义一个区域:

limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;

这将创建一个名为“登录”的共享内存区域,以存储访问限速网址的IP地址日志。保留的10 MB(10m)空间将为我们提供足够的空间来存储160k请求的历史记录。我们已选择每秒仅允许一个请求(1r/s)。

接下来,我们将该区域应用于某些路由,例如称为的假设存储过程login

location /rpc/login/ {
  # apply rate limiting
  limit_req zone=login burst=5;
}

如果来自特定IP的队列超过五个,则burst参数告诉Nginx开始丢弃请求。

Nginx速率限制是通用且不加区分的。要对每个已认证的请求分别进行速率限制,您将需要在“ 自定义验证”功能中添加逻辑。

调试

服务器版本

调试问题时,验证PostgREST版本很重要。您可以随时向运行中的服务器发出请求,并确切确定要部署的版本。查找ServerHTTP响应标头,其中包含版本号。

HTTP请求

PostgREST服务器将基本请求信息记录到stdout,包括请求的IP地址和用户代理,请求的URL和HTTP响应状态。但是,这为调试服务器错误提供了有限的信息。获取有关客户端请求和针对基础数据库执行的相应SQL命令的完整信息很有帮助。

检查包括标头和查询参数的传入HTTP请求的一种好方法是在运行PostgREST的端口上嗅探网络流量。例如,在绑定到本地主机上的端口3000的开发服务器上,运行以下命令:

# sudo access is necessary for watching the network
sudo ngrep -d lo0 port 3000

ngrep的选项取决于绑定服务器的地址和主机。绑定在“ 配置”部分中进行了描述。ngrep的输出不是特别漂亮,但是很清晰。

数据库日志

确认请求符合您的期望后,您可以通过查看数据库日志来获取有关服务器操作的更多信息。默认情况下,PostgreSQL不保留这些日志,因此您需要在下面进行配置更改。寻找postgresql.conf你的PostgreSQL数据目录中(地发现,发出命令)。找到分散在整个文件中的设置并将其更改为以下值,或者将此代码块附加到配置文件的末尾。show data_directory;

# send logs where the collector can access them
log_destination = "stderr"

# collect stderr output to log files
logging_collector = on

# save logs in pg_log/ under the pg data directory
log_directory = "pg_log"

# (optional) new log file per day
log_filename = "postgresql-%Y-%m-%d.log"

# log every kind of SQL statement
log_statement = "all"

重新启动数据库并实时查看日志文件,以了解如何将HTTP请求转换为SQL命令。

注意

在Docker上,您可以使用自定义启用日志init.sh

#!/bin/sh
echo "log_statement = 'all'" >> /var/lib/postgresql/data/postgresql.conf

之后,您可以启动容器并使用来检查日志。docker logs

docker run -v "$(pwd)/init.sh":"/docker-entrypoint-initdb.d/init.sh" -d postgres
docker logs -f <container-id>

架构重新加载

PostgREST的数据库架构缓存经常使用户感到困惑。之所以存在,是因为检测表之间的外键关系(包括这些关系如何通过视图)是必需的,但代价很高。API请求将参考模式缓存作为资源嵌入的一部分。但是,如果架构在服务器运行时发生更改,则会导致缓存过时,并导致错误,指出在表之间未检测到任何关系。

要刷新缓存而不重新启动PostgREST服务器,请向服务器进程发送SIGUSR1信号:

killall -SIGUSR1 postgrest

以上是手动操作的方法。要自动重载架构,请使用如下数据库触发器:

CREATE OR REPLACE FUNCTION public.notify_ddl_postgrest()
  RETURNS event_trigger
 LANGUAGE plpgsql
  AS $$
BEGIN
  NOTIFY ddl_command_end;
END;
$$;

CREATE EVENT TRIGGER ddl_postgrest ON ddl_command_end
   EXECUTE PROCEDURE public.notify_ddl_postgrest();

然后运行pg_listen实用程序以监视该事件,并在发生该事件时发送SIGUSR1:

pg_listen <db-uri> ddl_command_end $(which killall) -SIGUSR1 postgrest

现在,只要数据库模式的结构发生更改,PostgreSQL就会通知该ddl_command_end通道,这将导致pg_listen向PostgREST发送信号以重新加载其缓存。请注意,在上面的示例中,pg_listen需要可执行文件的完整路径。

Daemonizing 

对于使用systemd的 Linux发行(ubuntu,debian,archlinux),您可以按以下方式创建守护程序。

首先,在以下位置创建postgrest配置 /etc/postgrest/config

db-uri = "postgres://<your_user>:<your_password>@localhost:5432/<your_db>"
db-schema = "<your_exposed_schema>"
db-anon-role = "<your_anon_role>"
db-pool = 10

server-host = "127.0.0.1"
server-port = 3000

jwt-secret = "<your_secret>"

然后在中创建systemd服务文件 /etc/systemd/system/postgrest.service

[Unit]
Description=REST API for any Postgres database
After=postgresql.service

[Service]
ExecStart=/bin/postgrest /etc/postgrest/config
ExecReload=/bin/kill -SIGUSR1 $MAINPID

[Install]
WantedBy=multi-user.target

之后,您可以在启动时启用该服务,并使用以下命令启动它:

systemctl enable postgrest
systemctl start postgrest

## For reloading the service
## systemctl restart postgrest

备用网址结构

Singular或Plural中所述,PostgREST中没有用于单个资源的特殊URL形式,仅用于过滤的运算符。因此,没有类似的网址/people/1。它将被指定为

GET /people?id=eq.1 HTTP/1.1
Accept: application/vnd.pgrst.object+json

这允许使用复合主键,并使单数响应的意图独立于URL约定。

Nginx重写规则允许您模拟熟悉的URL约定。以下示例为所有表端点添加了重写规则,但您希望将其限制为具有名为“ id”的数字简单主键的表。

# support /endpoint/:id url style
location ~ ^/([a-z_]+)/([0-9]+) {

  # make the response singular
  proxy_set_header Accept 'application/vnd.pgrst.object+json';

  # assuming an upstream named "postgrest"
  proxy_pass http://postgrest/$1?id=eq.$2;

}
 类似资料: