我正在使用在python中编写一个烧录网页,该网页维护来自Oracle DB(v11.2)的缓存查询结果。我希望使用连续查询通知(CQN)在我的缓存变为无效时获取通知。我可以注册CQN通知监听器,但是当表更改时我的回调没有被调用。请注意,数据库在AWS上进行外部托管,我的开发(最终生产)计算机位于公司路由器后面的LAN上。
我使用此代码注册CQN回调:
import cx_Oracle
def CQNCallback(message):
print("Callback triggered.")
ops = cx_Oracle.OPCODE_INSERT | cx_Oracle.OPCODE_DELETE | cx_Oracle.OPCODE_UPDATE
conn = cx_Oracle.connect('userID','passwd','IP:port/ID',events=True)
subs = conn.subscribe(callback = CQNCallback, operations=ops, rowids = False, timeout=3000)
subs.registerquery('select * from MYTABLE')
此代码执行成功,我在DB USER_CHANGE_NOTIFICATION_REGS表中看到一个新条目:
REGID: 269
REGFLAGS: 0
CALLBACK: net8://(ADDRESS=(PROTOCOL=tcp)(HOST=<gateway>)(PORT=56824))?PR=0
OPERATIONS_FILTER: 14
CHANGELAG: 0
TIMEOUT: 2987
TABLE_NAME: MYTABLE
“gateway”是我公司网关/路由器的公共IP地址,端口56824似乎是由cx_Oracle分配的。 为了验证我的开发机器正在侦听这些通知,我运行netstat并看到计算机正在侦听IPv4和6上的端口56824
Proto Local Address Foreign Address State
TCP 0.0.0.0:56824 <devComputer>:0 LISTENING
TCP [::]:56824 <devComputer>:0 LISTENING
但是,当我使用SQLDeveloper手动更新或插入(和Commit!)对MYTABLE的更改时,我的回调永远不会被调用,我不知道为什么(我确保在进行更改时注册没有超时) )。
我的猜测:我怀疑数据库似乎使用与本地进程相同的端口号 - 这表明存在NAT问题。通知的tcp连接可能是由数据库发起的,它正在向网关发送通知消息:56824。如果是这样,路由器可能不知道如何处理该消息,因此丢弃它。如果是这种情况,我该如何配置cx_Oracle以正确处理中间路由器?
我发现了一个OTN线程,暗示这是Oracle 11.2.0.1(https://community.oracle.com/thread/2292328?tstart=0)中的一个已知错误。我想我的新任务是说服数据库管理员升级数据库并再次尝试。我的问题现在变成了,“贿赂/说服数据库管理员在自2009年以来没有被考虑过的系统上升级Oracle的最佳方法是什么,而不是被告知'去砸沙'?”
答案 0 :(得分:0)
通过执行两项任务,我能够使用上述代码的测试版本接收更改通知:
上面的第2项证实了我最初的怀疑,即如果您的数据库位于LAN外部的网络上,则无法使用更改通知(不会破坏您的路由器)。原因是,不是本地客户端打开用于通信更改的长期连接,而是从数据库端启动更改通知连接,并为每个更改通知创建新连接
然后我能够在工作局域网上实现上述两个步骤并成功触发我的回调代码。我需要通过修改订阅创建来为订阅指定固定端口,以便我可以在公司防火墙上正确实现端口转发:
subs = conn.subscribe(callback = CQNCallback, operations=ops, rowids = False, timeout=3000, port=50000)
我学到的另一件可以帮助别人的事情就是你不能只关闭使用cx_Oracle注册更改通知的python shell或ctrl-c代码。你需要明确地调用&#34; del subs&#34;否则订阅将永久保留在数据库中(永远?),没有删除它的机制。要在开发期间解决此限制,请确保明确指定订阅的短暂超时。这样,如果您错误地按下了ctrl-c,您只需要等待一段时间才能使订阅超时,然后才能继续工作。我不打算对生产代码使用超时,但我还不确定如何清理因崩溃而产生的孤立订阅。
这有效地解决了这个问题。让这个工作仍然存在问题,例如Oracle拒绝在通知端口上处理FIN和RST,但这将成为另一个问题的主题。