描述:我在linux上有一个没有任何框架的java web应用程序(jsp + tomcat)。我使用postgresql作为DBMS。
我有一个表( options )和一个封装它的Singleton类(选项)。 Options实例在应用程序启动时加载,并且无限期地保留在那里。
如果用户修改选项,方法( .refreshData())会更新内存中保存的实例。
现在出现问题:有一个远程服务可以直接访问数据库并更新选项表中的某些字段。我对这段代码没有控制权。
我想在外部服务更新选项表时触发刷新方法。我也知道该服务每天下午3点开始,但我不知道它什么时候结束。
postgresql(Postgres trigger to update Java cache)提供的 LISTEN - NOTIFY 功能在我看来是实现此目标的最佳方式。 在这个主题之后,我正在尝试一个简单的倾听者并根据我的需要“适应”(Sample of code in Postgress Documentation)。
在@Craig建议之后编辑:
public class OptionsListener extends Thread {
private int threadMills = 1000;
private Connection conn;
private org.postgresql.PGConnection pgconn;
private Options optionsInstance;
private static final String DB_URL;
private static final String DB_USERNAME;
private static final String DB_PASSWORD;
static {
try {
Context initContext = new InitialContext();
Context envContext = (Context) initContext.lookup("java:/comp/env");
DB_URL = (String) envContext.lookup("application/DB/url");
DB_USERNAME = (String) envContext.lookup("application/DB/username");
DB_PASSWORD = (String) envContext.lookup("application/DB/password");
} catch (NamingException e) {
throw new RuntimeException(e);
}
}
OptionsListener(Options instance, int threadMillis) {
optionsInstance = instance;
this.threadMills = threadMillis;
try {
Class.forName("org.postgresql.Driver");
conn = DriverManager.getConnection(DB_URL, DB_USERNAME, DB_PASSWORD);
pgconn = (PGConnection) DriverManager.getConnection(DB_URL, DB_USERNAME, DB_PASSWORD);
Statement stmt = conn.createStatement();
stmt.execute("LISTEN otionsUpdate");
stmt.close();
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
}
@Override
public void run() {
while (true) {
Log.addItem("Polling ?");
try {
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT 1");
rs.close();
stmt.close();
PGNotification notifications[] = pgconn.getNotifications();
if (notifications != null) {
Log.addItem("NOTIFY received");
optionsInstance.loadDbData();
}
Thread.sleep(threadMills); //tempo di attesa in millisecondi
} catch (Exception e) {
Log.addItem(getClass().getName() + " " + e.getMessage());
}
}
}
}
在Options Class中,我使用以下方法手动启动监听器:
public static void startExternalChangesListener(Options instance, int millis) {
OptionsListener listener = new OptionsListener(instance, millis);
listener.start();
}
最后
Options.startExternalChangesListener(options, 5000);
这是我第一次篡改线程......
我创建了一个 AFTER UPDATE触发器,它通知了通道,并通过PGAdmin3对其进行了测试。它就像一个魅力,但java似乎没有注意到......
答案 0 :(得分:2)
但是,问题似乎是在一个不必要的双重连接(名为pgcon)。
我正在使用 jdbc-postgresql 9.2_p1003 ,它就像一个魅力。
以下是最终代码:
OptionsListener(Options instance, int threadMillis) {
optionsInstance = instance;
this.threadMills = threadMillis;
try {
Class.forName("org.postgresql.Driver");
conn = DriverManager.getConnection(DB_URL, DB_USERNAME, DB_PASSWORD);
Statement stmt = conn.createStatement();
stmt.execute("SET application_name = 'myapp'; LISTEN optionsupdate");
stmt.close();
} catch (SQLException e) {
Log.addItem(getClass().getName() + " sql error:" + e.getMessage());
} catch (ClassNotFoundException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
@Override
public void run() {
while (true) {
try {
PGNotification notifications[] = ((PGConnection) conn).getNotifications();
if (notifications != null) {
optionsInstance.loadDbData();
}
Thread.sleep(threadMills);
} catch (SQLException sqle) {
Log.addItem(getClass().getName() + " sql error:" + sqle.getMessage());
} catch (InterruptedException ie) {
Log.addItem(getClass().getName() + " thread error: " + ie.getMessage());
}
}
}
答案 1 :(得分:0)
如果听众未收到预期通知,则此处的问题是您应该仔细整理的问题。注意我已经包含了为完整性而已经完成的步骤。
通知是否真的被提出?你能用psql验证这个吗?如果没有,那么您需要进行一些故障排除。听起来你已经这样做了,所以继续第2步。
其他会话实际上在听吗?数据包窥探器可能会对此有所帮助,因为您可以看到来自数据库的通知。如果它没有监听,那么你需要确保在你的应用程序中正确运行LISTEN命令并从那里进行调试。
您的应用程序中是否正确进行了轮询?如果您在投票过程中打印出标准错误的警告或通知,这也会有所帮助。
此时很难说问题出在哪里。但是,如果你按顺序解决这些问题(听起来你已经解决了#1并且可以从#2开始)那么你应该能够在不太长的时间内找到问题。