OpenCSV + JMS/MDB 行为 + 性能问题
OpenCSV + JMS/MDB behavior + performance issue
我有一个 Web 应用程序,它在 Glassfish 4.1 下运行,其中包含一些需要 JMS/MDB 的功能.
特别是我在使用 JMS/MDB 生成报告时遇到问题,即从 table 获取数据并将它们转储到文件中。
这就是发生的事情,我有一条 JMS/MDB 消息 在 Oracle 数据库中执行几项任务并在 [=102= 中获得最终结果之后],我想从 table(通常是 30M+ 条记录)中获取一份 csv 报告。
因此,在 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 中的执行方式不同?
如果不可能,是否有任何可能的解决方案可以通过任何其他方式改进同一任务?
感谢您对此事的反馈和帮助,我正在尝试了解 behavior/performance 与 JMS/MDB 的 in/out 不同的原因。
**
编辑:
**
@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;
}
因此,如果在 generator.deployGenerator()
中执行相同的方法,handleReportContent()
会产生缓慢的结果。如果我等待该方法中的所有内容并在此 bean 中创建文件 JobProcessorBean
会更快。我只是想弄清楚 why/how 行为会像这样执行。
在 bean 上添加 @TransactionAttribute(NOT_SUPPORTED)
注释可能会解决问题(正如您的评论所指出的那样)。
为什么会这样?因为如果您不在消息驱动的 bean 上放置任何事务注释,则默认值变为 @TransactionAttribute(REQUIRED)
(因此 bean 所做的一切都由事务管理器监督)。显然,这会减慢速度。
我有一个 Web 应用程序,它在 Glassfish 4.1 下运行,其中包含一些需要 JMS/MDB 的功能. 特别是我在使用 JMS/MDB 生成报告时遇到问题,即从 table 获取数据并将它们转储到文件中。
这就是发生的事情,我有一条 JMS/MDB 消息 在 Oracle 数据库中执行几项任务并在 [=102= 中获得最终结果之后],我想从 table(通常是 30M+ 条记录)中获取一份 csv 报告。
因此,在 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 中的执行方式不同? 如果不可能,是否有任何可能的解决方案可以通过任何其他方式改进同一任务?
感谢您对此事的反馈和帮助,我正在尝试了解 behavior/performance 与 JMS/MDB 的 in/out 不同的原因。
**
编辑:
**
@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;
}
因此,如果在 generator.deployGenerator()
中执行相同的方法,handleReportContent()
会产生缓慢的结果。如果我等待该方法中的所有内容并在此 bean 中创建文件 JobProcessorBean
会更快。我只是想弄清楚 why/how 行为会像这样执行。
在 bean 上添加 @TransactionAttribute(NOT_SUPPORTED)
注释可能会解决问题(正如您的评论所指出的那样)。
为什么会这样?因为如果您不在消息驱动的 bean 上放置任何事务注释,则默认值变为 @TransactionAttribute(REQUIRED)
(因此 bean 所做的一切都由事务管理器监督)。显然,这会减慢速度。