【发布时间】:2025-12-07 22:00:02
【问题描述】:
我有一个在 Glassfish 4.1 下运行的 Web 应用程序,其中包含一些需要 JMS/MDB 的功能。 特别是我在使用 JMS/MDB 生成报告时遇到问题,即从表中获取数据并将它们转储到文件中。
这就是发生的事情,我有一个 JMS/MDB 消息,它在 Oracle 数据库中执行了几个任务,在表格中获得最终结果后,我想从该表(通常是 30M+ 记录)。
因此,在 JMS/MDB 中生成报告时会发生以下情况:
public boolean handleReportContent() {
Connection conn = null;
try {
System.out.println("Handling report content... " + new Date());
conn = DriverManager.getConnection(data.getUrl(), data.getUsername(), data.getPassword());
int reportLine = 1;
String sql = "SELECT FIELD_NAME, VALUE_A, VALUE_B, DIFFERENCE FROM " + data.getDbTableName() + " WHERE SET_PK IN ( SELECT DISTINCT SET_PK FROM " + data.getDbTableName() + " WHERE IS_VALID=? )";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setBoolean(1, false);
ResultSet rs = ps.executeQuery();
List<ReportLine> lst = new ArrayList<>();
int columns = data.getLstFormats().size();
int size = 0;
int linesDone = 0;
while (rs.next()) {
ReportLine rl = new ReportLine(reportLine, rs.getString("FIELD_NAME"), rs.getString("VALUE_A"), rs.getString("VALUE_B"), rs.getString("DIFFERENCE"));
lst.add(rl);
linesDone = columns * (reportLine - 1);
size++;
if ((size - linesDone) == columns) {
reportLine++;
if (lst.size() > 4000) {
appendReportContentNew(lst);
lst.clear();
}
}
}
if (lst.size() > 0) {
appendReportContentNew(lst);
lst.clear();
}
ps.close();
conn.close();
return true;
} catch (Exception e) {
System.out.println("exception handling report content new: " + e.toString());
return false;
}
这是有效的,我知道它很慢而且效率低下,很可能有更好的选择来执行相同的操作。 这个方法的作用是:
- 从ResultSet中收集数据;
- 将其转储到一个列表中;
- 对于每个 4K 对象都会调用方法appendReportContentNew()
-
将列表中的数据转储为 文件
public void appendReportContentNew(List<ReportLine> lst) { File f = new File(data.getJobFilenamePath()); try { if (!f.exists()) { f.createNewFile(); } FileWriter fw = new FileWriter(data.getJobFilenamePath(), true); BufferedWriter bw = new BufferedWriter(fw); for (ReportLine rl : lst) { String rID = "R" + rl.getLine(); String fieldName = rl.getFieldName(); String rline = rID + "," + fieldName + "," + rl.getValue1() + "," + rl.getValue2() + "," + rl.getDifference(); bw.append(rline); bw.append("\n"); } bw.close(); } catch (IOException e) { System.out.println("exception appending report content: " + e.toString()); }}
使用这种方法,在 20 分钟内写入 800k 行(30Mb 文件)通常会达到 4Gb 或更多。如果可能的话,这就是我想要改进的地方。
所以我决定尝试OpenCSV,我得到了以下方法:
public boolean handleReportContentv2() {
Connection conn = null;
try {
FileWriter fw = new FileWriter(data.getJobFilenamePath(), true);
System.out.println("Handling report content v2... " + new Date());
conn = DriverManager.getConnection(data.getUrl(), data.getUsername(), data.getPassword());
String sql = "SELECT NLINE, FIELD_NAME, VALUE_A, VALUE_B, DIFFERENCE FROM " + data.getDbTableName() + " WHERE SET_PK IN ( SELECT DISTINCT SET_PK FROM " + data.getDbTableName() + " WHERE IS_VALID=? )";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setBoolean(1, false);
ps.setFetchSize(500);
ResultSet rs = ps.executeQuery();
BufferedWriter out = new BufferedWriter(fw);
CSVWriter writer = new CSVWriter(out, ',', CSVWriter.NO_QUOTE_CHARACTER);
writer.writeAll(rs, false);
fw.close();
writer.close();
rs.close();
ps.close();
conn.close();
return true;
} catch (Exception e) {
System.out.println("exception handling report content v2: " + e.toString());
return false;
}
}
所以我正在收集 ResultSet 中的所有数据,并转储到 CSVWriter 中。这个操作同样的20分钟,只写了7k行。
但是同样的方法,如果我在JMS/MDB之外使用它,它有一个令人难以置信的不同,只是在前4分钟它写了3M行在文件。 在同样的 20 分钟内,它生成了一个 500Mb+ 的文件。
如果我想提高性能,显然使用 OpenCSV 是迄今为止最好的选择,我的问题是为什么它在 JMS/MDB 中的执行方式不同? 如果不可能,是否有任何可能的解决方案可以通过任何其他方式改进相同的任务?
感谢有关此问题的反馈和帮助,我正在尝试了解 JMS/MDB 内外行为/性能不同的原因。
**
编辑:
**
@MessageDriven(activationConfig = {
@ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
@ActivationConfigProperty(propertyName = "destinationLookup", propertyValue = "MessageQueue")})
public class JobProcessorBean implements MessageListener {
private static final int TYPE_A_ID = 0;
private static final int TYPE_B_ID = 1;
@Inject
JobDao jobsDao;
@Inject
private AsyncReport generator;
public JobProcessorBean() {
}
@Override
public void onMessage(Message message) {
int jobId = -1;
ObjectMessage msg = (ObjectMessage) message;
try {
boolean valid = true;
JobWrapper jobw = (JobWrapper) msg.getObject();
jobId = jobw.getJob().getJobId().intValue();
switch (jobw.getJob().getJobTypeId().getJobTypeId().intValue()) {
case TYPE_A_ID:
jobsDao.updateJobStatus(jobId, 0);
valid = processTask1(jobw);
if(valid) {
jobsDao.updateJobFileName(jobId, generator.getData().getJobFilename());
System.out.println(":: :: JOBW FileName :: "+generator.getData().getJobFilename());
jobsDao.updateJobStatus(jobId, 0);
}
else {
System.out.println("error...");
jobsDao.updateJobStatus(jobId, 1);
}
**boolean validfile = handleReportContentv2();**
if(!validfile) {
System.out.println("error file...");
jobsDao.updateJobStatus(jobId, 1);
}
break;
case TYPE_B_ID:
(...)
}
if(valid) {
jobsDao.updateJobStatus(jobw.getJob().getJobId().intValue(), 2); //updated to complete
}
System.out.println("***********---------Finished JOB " + jobId + "-----------****************");
System.out.println();
jobw = null;
} catch (JMSException ex) {
Logger.getLogger(JobProcessorBean.class.getName()).log(Level.SEVERE, null, ex);
jobsDao.updateJobStatus(jobId, 1);
} catch (Exception ex) {
Logger.getLogger(JobProcessorBean.class.getName()).log(Level.SEVERE, null, ex);
jobsDao.updateJobStatus(jobId, 1);
} finally {
msg = null;
}
}
private boolean processTask1(JobWrapper jobw) throws Exception {
boolean valid = true;
jobsDao.updateJobStatus(jobw.getJob().getJobId().intValue(), 0);
generator.setData(jobw.getData());
valid = generator.deployGenerator();
if(!valid) return false;
jobsDao.updateJobParameters(jobw.getJob().getJobId().intValue(),new ReportContent());
Logger.getLogger(JobProcessorBean.class.getName()).log(Level.INFO, null, "Job Finished");
return true;
}
因此,如果相同的方法,handleReportContent() 在generator.deployGenerator() 内部执行,结果会很慢。如果我等待该方法中的所有内容并使该 bean JobProcessorBean 中的文件更快。我只是想弄清楚这种行为为什么/如何起作用。
【问题讨论】:
-
消息驱动 bean 上的
@TransactionAttribute注释是什么?如何在 MDB 之外调用handleReportContentv2方法? -
@MaDa 我有一个类
JobProcessorBean具有以下注释:@MessageDriven(activationConfig = { @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"), @ActivationConfigProperty(propertyName = "destinationLookup", propertyValue = "MessageQueue") })这个bean 注入另一个bean,我在其中调用deploy()方法。handleReportContentv2()在进行部署的 bean 内部使用,我得到了这些结果。如果我复制相同的方法并在不同的 bean 中运行,我会得到不同的结果。我将添加那部分代码。 -
尝试在 bean 上添加
@TransactionAttribute(NOT_SUPPORTED)看看这是否对性能有影响。另外,请查看*.com/questions/19139426/…,了解将文件直接保存到 Web 应用程序文件夹中的其他问题。 -
@MaDa 会的,如果有什么不同我会告诉你的。
-
@MaDa 解决了这个问题,它在 MDB 中的执行方式相同。随意用你之前的评论回答这个问题,所以我可以接受。感谢您的时间和帮助。 :)
标签: java glassfish jms message-driven-bean opencsv