You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

pgsql-http中CURLOPT_TIMEOUT设置无效,循环请求超时求助

问题:循环调用HappyWhale API时出现连接超时错误

使用PL/pgSQL批量从HappyWhale获取encounter记录并存储到本地数据库,循环发送请求时频繁触发连接超时,调整CURLOPT_TIMEOUT参数无法解决问题。

原代码

do
$$
declare
    fromTime timestamp := '2000-01-01'::timestamp;
    toTime timestamp := 'now'::timestamp;
    deltaTime interval := '1 day'::interval;
    
    currTime timestamp;
    response record;
begin
    perform http_set_curlopt('CURLOPT_TIMEOUT', '30');
    perform http_set_curlopt('CURLOPT_TCP_KEEPALIVE', '30');
    create table if not exists encounters(id integer primary key, region text, species text, startdate timestamp, mincount integer, maxcount integer, geom geometry(point, 4326));
    currTime := fromTime;
    while currTime <= toTime loop
         select * into response from http((
              'POST'
             ,'https://critterspot.happywhale.com/v1/cs/admin/encounter/search'
             ,array[http_header('Connection', 'keep-alive'), http_header('Host', 'critterspot.happywhale.com')]
             ,'application/json'
             ,jsonb_build_object('showConnections', false, 'encounter', jsonb_build_object('datesearch', jsonb_build_object('startdate', to_char(currTime, 'YYYY-MM-DD'), 'type', 0)))
         )::http_request);
         if response.status <> 200 then
             raise notice '[%] %', currTime, (response.content::json)->>'message';
         else
             insert into encounters select
                  (e->>'id')::int
                 ,e->>'region'
                 ,e->>'species'
                 ,((e#>>'{dateRange,startDate}')::date + (e#>>'{dateRange,startTime}')::time)
                 ,(e->>'minCount')::int minCount
                 ,(e->>'maxCount')::int maxCount
                 ,ST_Point((e#>>'{location,lng}')::float, (e#>>'{location,lat}')::float, 4326) geom
             from (select json_array_elements(response.content::json) e) on conflict (id) do nothing;
             
             raise notice '[%] %', currTime, response.status;
             currTime := currTime + deltaTime;
         end if;
    end loop;
end;
$$

错误信息

NOTICE:  [2000-01-01 00:00:00] 200
NOTICE:  [2000-01-02 00:00:00] 200
NOTICE:  [2000-01-03 00:00:00] 200
NOTICE:  [2000-01-04 00:00:00] 200
NOTICE:  [2000-01-05 00:00:00] 200
NOTICE:  [2000-01-06 00:00:00] 200
NOTICE:  [2000-01-07 00:00:00] 200
NOTICE:  [2000-01-08 00:00:00] 200
NOTICE:  [2000-01-09 00:00:00] 200
NOTICE:  [2000-01-10 00:00:00] 200
NOTICE:  [2000-01-11 00:00:00] 200
NOTICE:  [2000-01-12 00:00:00] 200
NOTICE:  [2000-01-13 00:00:00] 200
NOTICE:  [2000-01-14 00:00:00] 200
ERROR:  Failed to connect to critterspot.happywhale.com port 443 after 1001 ms: Timeout was reached
CONTEXT:  SQL statement "select *               from http((
              'POST'
             ,'https://critterspot.happywhale.com/v1/cs/admin/encounter/search'
             ,array[http_header('Connection', 'keep-alive'), http_header('Host', 'critterspot.happywhale.com')]
             ,'application/json'
             ,jsonb_build_object('showConnections', false, 'encounter', jsonb_build_object('datesearch', jsonb_build_object('startdate', to_char(currTime, 'YYYY-MM-DD'), 'type', 0)))
         )::http_request)"
PL/pgSQL function inline_code_block line 15 at SQL statement 

已尝试操作

设置curl选项CURLOPT_TIMEOUT和CURLOPT_TCP_KEEPALIVE,但问题未解决:

perform http_set_curlopt('CURLOPT_TIMEOUT', '30');
perform http_set_curlopt('CURLOPT_TCP_KEEPALIVE', '30');

解决方案

1. 修正curl参数设置

CURLOPT_TCP_KEEPALIVE是布尔值,需设为1启用;同时单独设置CURLOPT_CONNECTTIMEOUT控制连接阶段的超时时间,避免和整体请求超时混淆:

perform http_set_curlopt('CURLOPT_TIMEOUT', '30'); -- 整体请求超时时间(秒)
perform http_set_curlopt('CURLOPT_CONNECTTIMEOUT', '10'); -- 连接服务器的超时时间(秒)
perform http_set_curlopt('CURLOPT_TCP_KEEPALIVE', '1'); -- 启用TCP保活机制
perform http_set_curlopt('CURLOPT_TCP_KEEPIDLE', '60'); -- 60秒无数据传输后发送第一个保活包
perform http_set_curlopt('CURLOPT_TCP_KEEPINTVL', '10'); -- 保活包发送间隔(秒)

2. 添加请求间隔与重试机制

短时间高频请求易触发目标服务器限流或拒绝连接,需在请求之间增加间隔,并对超时/失败请求进行重试:

3. 优化请求批次(可选)

如果API支持,将按天请求改为按周/月的日期范围查询,减少总请求次数,降低触发限流的概率。

修改后的完整代码

do
$$
declare
    fromTime timestamp := '2000-01-01'::timestamp;
    toTime timestamp := 'now'::timestamp;
    deltaTime interval := '1 day'::interval;
    
    currTime timestamp;
    response record;
    retry_count integer := 3; -- 最大重试次数
    wait_interval interval := '2 seconds'; -- 请求间隔时间
begin
    -- 修正curl参数配置
    perform http_set_curlopt('CURLOPT_TIMEOUT', '30');
    perform http_set_curlopt('CURLOPT_CONNECTTIMEOUT', '10');
    perform http_set_curlopt('CURLOPT_TCP_KEEPALIVE', '1');
    perform http_set_curlopt('CURLOPT_TCP_KEEPIDLE', '60');
    perform http_set_curlopt('CURLOPT_TCP_KEEPINTVL', '10');
    
    create table if not exists encounters(id integer primary key, region text, species text, startdate timestamp, mincount integer, maxcount integer, geom geometry(point, 4326));
    currTime := fromTime;
    
    while currTime <= toTime loop
        retry_loop: for i in 1..retry_count loop
            begin
                select * into response from http((
                    'POST'
                   ,'https://critterspot.happywhale.com/v1/cs/admin/encounter/search'
                   ,array[http_header('Connection', 'keep-alive'), http_header('Host', 'critterspot.happywhale.com')]
                   ,'application/json'
                   ,jsonb_build_object('showConnections', false, 'encounter', jsonb_build_object('datesearch', jsonb_build_object('startdate', to_char(currTime, 'YYYY-MM-DD'), 'type', 0)))
                )::http_request);
                
                exit retry_loop; -- 请求成功,退出重试循环
            exception
                when others then
                    if i = retry_count then
                        raise notice '[%] 重试%d次后仍失败: %', currTime, retry_count, SQLERRM;
                        currTime := currTime + deltaTime;
                        continue;
                    else
                        raise notice '[%] 请求失败,第%d次重试...', currTime, i;
                        perform pg_sleep(wait_interval);
                    end if;
            end;
        end loop retry_loop;
        
        if response.status = 200 then
            insert into encounters select
                (e->>'id')::int
               ,e->>'region'
               ,e->>'species'
               ,((e#>>'{dateRange,startDate}')::date + (e#>>'{dateRange,startTime}')::time)
               ,(e->>'minCount')::int minCount
               ,(e->>'maxCount')::int maxCount
               ,ST_Point((e#>>'{location,lng}')::float, (e#>>'{location,lat}')::float, 4326) geom
            from (select json_array_elements(response.content::json) e) on conflict (id) do nothing;
            
            raise notice '[%] 请求成功,状态码: %', currTime, response.status;
        else
            raise notice '[%] 请求失败,状态码: %,信息: %', currTime, response.status, (response.content::json)->>'message';
        end if;
        
        currTime := currTime + deltaTime;
        perform pg_sleep(wait_interval); -- 增加请求间隔,避免高频访问
    end loop;
end;
$$

内容的提问来源于stack exchange,提问作者gokdumano

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 17:54:54