W mojej aplikacji sieciowej opartej na grze, użytkownicy mogą pobierać wszystkie wiersze różnych tabel bazy danych w formacie CSV lub JSON. Tabele są stosunkowo duże (100k + wiersze) i staram się odtwarzać wynik za pomocą chunkingu w Play 2.2.Powolna odpowiedź na porcję w Play 2.2
Jednak problem polega na tym, że instrukcje println pokazują, że wiersze są zapisywane do obiektu Chunks.Out, nie pojawiają się po stronie klienta! Jeśli ograniczę liczbę odesłanych wierszy, zadziała, ale na początku będzie duże opóźnienie, które będzie większe, jeśli spróbuję odesłać wszystkie wiersze i upłynie limit czasu lub serwerowi skończy się pamięć.
Używam ORMS Ebean, a tabele są indeksowane, a zapytanie z psql nie zajmuje dużo czasu. Czy ktoś ma pojęcie, co może być problemem?
Bardzo dziękuję za pomoc!
Oto kod dla jednego z kontrolerów:
@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");
}
A 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 = ";";
}
A model:
@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;
}
Dziękujemy za Twoje odpowiedz Viktor. Buforowanie poprawi prędkość, ale opóźnienie pomiędzy zapisywaniem i wyświetlaniem w przeglądarce jest wciąż ogromne. Dodanie prostych instrukcji println pokazuje, że wszystkie wiersze zostaną wypisane na zewnątrz, a gdy nie ma już żadnych i zostaną zamknięte, zaczynają ładowanie w przeglądarce !! A jeśli liczba wierszy jest zbyt duża, pojawia się błąd przekroczenia limitu czasu: – p00ya00
[BŁĄD] [22.10.2013 13: 57: 16.285] [application-akka.actor.default-dispatcher-5] [ActorSystem (application)] Nie udało się uruchomić wywołania zwrotnego z terminem z powodu [Futures przekroczono limit czasu po [5000 milisekund]] java.util.concurrent.TimeoutException: Futures przekroczono limit czasu po [5000 milisekund] na scala.concurrent.impl.Promise $ DefaultPromise.ready (Promise.scala: 96) na scala.concurrent.impl.Promise $ DefaultPromise.result (Promise.scala: 100) na scala.concurrent.Await $$ anonfun $ wynik $ 1.apply (package.scala: 107) na akka.dispatch.MonitorableThreadFactory $ AkkaForkJoinWorkerThread $$ anon $ – p00ya00
Czy mógłbyś wstawić kilka kodów "System.out.println (System.currentTimeMillis())" do swojego kodu i pokazać tutaj wyjście? Proszę umieścić je po 'static Result showEpex()', po '// w przeciwnym razie wynik porcji i odesłanie', tuż przed ostatnią linią 'public void stream (Out out, Page page, UserRequest req) i tuż przed 'return ok (chunks) .as (" text/plain ");'? Z jakiegoś powodu wykonanie fragmentów nie zakończyło się lub zajęło tak dużo czasu, więc wykonanie zostało zakończone przez strukturę gry. Czy próbowałeś uruchomić mój kod? Czy mógłbyś potwierdzić, czy masz takie same kłopoty? –