我正在使用psycopg2连接到远程主机上的PostgreSQL数据库。我打开一个连接并等待请求,然后对于每个请求,我在连接上运行查询并返回数据。
但是当连接打开后网络连接丢失时,下一个数据库查询会挂起,我必须手动终止该程序。
详细说明:
在运行查询之前,我需要一些可靠的方法来检测连接失败,因此我的程序不会挂起,或者使cursor.execute(..)
在失败的连接上引发异常的方法。
import psycopg2
import time
conn = psycopg2.connect("host='dbs' dbname='foo' user='joe' password='x'")
time.sleep(10) # I manually turn VPN off during this sleep..
cu = conn.cursor()
cu.execute('SELECT 1') # <- hangs here
print cu.fetchone()
cu.commit()
设置TCP超时“全局” - 在psycopg2导入之前,我添加了:
import socket
socket.setdefaulttimeout(10)
在psycopg.connection
的套接字上设置TCP超时:
..
conn = psycopg2.connect(...
s = socket.fromfd(conn.fileno(), socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(5)
..
为psycopg.connection
的套接字启用keepalive:
...
conn = psycopg2.connect(...
s = socket.fromfd(conn.fileno(), socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(5)
s.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
s.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 1)
s.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 3)
s.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 5)
...
答案 0 :(得分:2)
经过长期的艰苦奋斗,我认为我只是通过执行其他人正在谈论的策略来解决此问题,而是使用psycopg2 connect函数本身:
from psycopg2 import connect
conn = connect(
database=database,
user=username,
password=password,
host=hostname,
port=port,
connect_timeout=3,
# https://www.postgresql.org/docs/9.3/libpq-connect.html
keepalives=1,
keepalives_idle=5,
keepalives_interval=2,
keepalives_count=2)
我看到psycopg2在长时间运行的查询上始终挂起,但是现在问题似乎已完全解决。
请注意,这可能是新功能,因为这个问题很旧。
答案 1 :(得分:1)
看一下套接字超时,看完this和this后,这些设置对我有用
s = socket.fromfd(connection.fileno(),
socket.AF_INET, socket.SOCK_STREAM)
# Enable sending of keep-alive messages
s.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
# Time the connection needs to remain idle before start sending
# keepalive probes
s.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, int(ceil(time)))
# Time between individual keepalive probes
s.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 1)
# The maximum number of keepalive probes should send before dropping
# the connection
s.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 3)
答案 2 :(得分:1)
配置 KEEPALIVE 的 OP 和 Gabriel Salla 的解决方案不完整。此解决方案仅在连接空闲(在网络关闭前未发送数据)和网络关闭时有效。
如果某些数据已经通过网络发送,但尚未被 KEEPALIVE 功能检测到,则会挂起。发生这种情况是因为在发送某些数据时使用了 RTO 机制而不是 KEEPALIVE。
要为 RTO 设置超时,您必须为套接字设置 TCP_USER_TIMEOUT 超时(以毫秒为单位)。
完整的解决方案是(KEEPALIVE 和 RTO 超时都配置为 10 秒):
s = socket.fromfd(conn.fileno(), socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
s.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 6)
s.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 2)
s.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 2)
s.setsockopt(socket.IPPROTO_TCP, socket.TCP_USER_TIMEOUT, 10000)
答案 3 :(得分:0)
为了确保连接仍然有效,请阅读属性connection.isolation_level
。如果连接已经死亡,这将使OperationalError
引发pgcode == "57P01"
。
try:
connection.isolation_level
except OperationalError as oe:
conn = psycopg2.connect(dsn)