In meiner Play-Framework-basierten Web-Anwendung können Benutzer alle Zeilen verschiedener Datenbanktabellen in CSV oder JSON-Format herunterladen. Tabellen sind relativ groß (100k + Zeilen) und ich versuche, das Ergebnis mithilfe von Chunking in Play 2.2 zurückzustreamen.Slow Chunk Antwort in Play 2.2
Das Problem ist jedoch, obwohl println-Anweisungen zeigt, dass die Zeilen in das Chunks.Out-Objekt geschrieben werden, sie werden nicht auf der Clientseite angezeigt! Wenn ich die Anzahl der zurückgeschickten Zeilen limitiere, wird es funktionieren, aber es hat auch eine große Verzögerung am Anfang, die größer wird, wenn ich versuche, alle Zeilen zurückzusenden und eine Zeitüberschreitung verursacht oder der Server nicht mehr genügend Speicherplatz hat.
Ich benutze Ebean ORM und die Tabellen sind indiziert und Abfragen von psql dauert nicht viel Zeit. Hat jemand eine Idee, was das Problem sein könnte?
Ich schätze Ihre Hilfe sehr! Hier
ist der Code für einen der Controller:
@SecureSocial.UserAwareAction
public static Result showEpex() {
User user = getUser();
if(user == null || user.getRole() == null)
return ok(views.html.profile.render(user, Application.NOT_CONFIRMED_MSG));
DynamicForm form = DynamicForm.form().bindFromRequest();
final UserRequest req = UserRequest.getRequest(form);
if(req.getFormat().equalsIgnoreCase("html")) {
Page<EpexEntry> page = EpexEntry.page(req.getStart(), req.getFinish(), req.getPage());
return ok(views.html.epex.render(page, req));
}
// otherwise chunk result and send back
final ResultStreamer<EpexEntry> streamer = new ResultStreamer<EpexEntry>();
Chunks<String> chunks = new StringChunks() {
@Override
public void onReady(play.mvc.Results.Chunks.Out<String> out) {
Page<EpexEntry> page = EpexEntry.page(req.getStart(), req.getFinish(), 0);
ResultStreamer<EpexEntry> streamer = new ResultStreamer<EpexEntry>();
streamer.stream(out, page, req);
}
};
return ok(chunks).as("text/plain");
}
Und der Streamer:
public class ResultStreamer<T extends Entry> {
private static ALogger logger = Logger.of(ResultStreamer.class);
public void stream(Out<String> out, Page<T> page, UserRequest req) {
if(req.getFormat().equalsIgnoreCase("json")) {
JsonContext context = Ebean.createJsonContext();
out.write("[\n");
for(T e: page.getList())
out.write(context.toJsonString(e) + ", ");
while(page.hasNext()) {
page = page.next();
for(T e: page.getList())
out.write(context.toJsonString(e) + ", ");
}
out.write("]\n");
out.close();
} else if(req.getFormat().equalsIgnoreCase("csv")) {
for(T e: page.getList())
out.write(e.toCsv(CSV_SEPARATOR) + "\n");
while(page.hasNext()) {
page = page.next();
for(T e: page.getList())
out.write(e.toCsv(CSV_SEPARATOR) + "\n");
}
out.close();
}else {
out.write("Invalid format! Only CSV, JSON and HTML can be generated!");
out.close();
}
}
public static final String CSV_SEPARATOR = ";";
}
Und das Modell:
@Entity
@Table(name="epex")
public class EpexEntry extends Model implements Entry {
@Id
@Column(columnDefinition = "pg-uuid")
private UUID id;
private DateTime start;
private DateTime finish;
private String contract;
private String market;
private Double low;
private Double high;
private Double last;
@Column(name="weight_avg")
private Double weightAverage;
private Double index;
private Double buyVol;
private Double sellVol;
private static final String START_COL = "start";
private static final String FINISH_COL = "finish";
private static final String CONTRACT_COL = "contract";
private static final String MARKET_COL = "market";
private static final String ORDER_BY = MARKET_COL + "," + CONTRACT_COL + "," + START_COL;
public static final int PAGE_SIZE = 100;
public static final String HOURLY_CONTRACT = "hourly";
public static final String MIN15_CONTRACT = "15min";
public static final String FRANCE_MARKET = "france";
public static final String GER_AUS_MARKET = "germany/austria";
public static final String SWISS_MARKET = "switzerland";
public static Finder<UUID, EpexEntry> find =
new Finder(UUID.class, EpexEntry.class);
public EpexEntry() {
}
public EpexEntry(UUID id, DateTime start, DateTime finish, String contract,
String market, Double low, Double high, Double last,
Double weightAverage, Double index, Double buyVol, Double sellVol) {
this.id = id;
this.start = start;
this.finish = finish;
this.contract = contract;
this.market = market;
this.low = low;
this.high = high;
this.last = last;
this.weightAverage = weightAverage;
this.index = index;
this.buyVol = buyVol;
this.sellVol = sellVol;
}
public static Page<EpexEntry> page(DateTime from, DateTime to, int page) {
if(from == null && to == null)
return find.order(ORDER_BY).findPagingList(PAGE_SIZE).getPage(page);
ExpressionList<EpexEntry> exp = find.where();
if(from != null)
exp = exp.ge(START_COL, from);
if(to != null)
exp = exp.le(FINISH_COL, to.plusHours(24));
return exp.order(ORDER_BY).findPagingList(PAGE_SIZE).getPage(page);
}
@Override
public String toCsv(String s) {
return id + s + start + s + finish + s + contract +
s + market + s + low + s + high + s +
last + s + weightAverage + s +
index + s + buyVol + s + sellVol;
}
Dank Download für Ihre Antworte Viktor. Die Pufferung wird die Geschwindigkeit verbessern, aber die Verzögerung zwischen dem Schreiben und dem Erscheinen im Browser ist immer noch groß. Das Hinzufügen einfacher println-Anweisungen zeigt an, dass alle Zeilen in das out geschrieben werden und wenn es keine mehr gibt und out, wird das Laden in den Browser gestartet !! Und wenn die Anzahl der Zeilen zu groß ist, gibt es einen Timeout-Fehler wie folgt: – p00ya00
[ERROR] [22.10.2013 13: 57: 16.285] [application-akka.actor.default-dispatcher-5] [ActorSystem (Anwendung)] Fehler beim Ausführen des Terminierungsrückrufs aufgrund von [Terminüberschreitungen nach [5000 Millisekunden]] java.util.concurrent.TimeoutException: Terminüberschreitungen nach [5000 Millisekunden] bei scala.concurrent.impl.Promise $ DefaultPromise.ready (Promise.scala: 96) bei scala.concurrent.impl.Promise $ DefaultPromise.result (Promise.scala: 100) bei scala.concurrent.Await $$ anonfun $ Ergebnis $ 1.apply (package.scala: 107) at akka.dispatch.MonitorableThreadFactory $ AkkaForkJoinWorkerThread $$ anon $ – p00ya00
Könnten Sie bitte mehrere 'System.out.println (System.currentTimeMillis())' in Ihren Code einfügen und die Ausgabe hier anzeigen? Bitte plazieren Sie sie hinter 'static Result showEpex()', nach '// ansonsten chunk result und send back' Zeile, kurz vor der letzten Zeile von' public void stream (Out out, Seite page, UserRequest req) 'und kurz davor 'return ok (Chunks) .as (" text/plain ");'? Aus irgendeinem Grund wurde deine Chunk-Ausführung nicht beendet oder nahm so viel Zeit in Anspruch, sodass die Ausführung durch das Spiel-Framework beendet wurde. Haben Sie auch versucht, meinen Code auszuführen? Könnten Sie bitte bestätigen, wenn Sie die gleichen Probleme damit haben? Hier –