Обратите внимание: очевидным ответом здесь будет "исправить то, что занимает 20 минут". Это не тот ответ, который я ищу, потому что я не могу контролировать фактический механизм (см. WidgetProcessor
ниже), который здесь является узким местом.
У меня есть приложение Grails (2.4.3), которое выполняет большую часть обработки на стороне клиента через HTML5 и JS. Теперь мне нужно, чтобы пользователь нажал кнопку, и чтобы это событие запустило очень длинный (где-то от 3 до 20 минут) асинхронный процесс, который в конечном итоге должен привести к тому, что экран пользователя будет динамическим (без обновления страницы) обновлен с результатом.
На стороне клиента (jQuery):
$(".my-button").click(function(e){
var btn = $(this);
$.ajax({
type: 'GET',
url: "/myapp/fizz/kickOffBigJob",
data: {fizz: $(btn).attr('fizz')},
success: function (data) {
}
})
});
При щелчке это отправляется моему методу FizzController#kickOffBigJob()
:
class FizzController {
FizzServiceClient fizzServiceClient = FizzServiceFactory.newFizzServiceClient()
// ... lots of other stuff
// This is called when the button above is clicked.
def kickOffBigJob(params) {
// Send the request off to a RESTful web service. This service is what
// handles the asynchronous process and ultimately returns a result
// (a String). The service endpoint returns immediately ( < 500ms) but
// the actual result can take up to 20 minutes to be computed.
fizzServiceClient.kickOffBigJob(convertToWidget(params))
}
}
Внутри FizzService#kickOffBigJob()
:
// This code is deployed to a different JVM/WAR that exposes RESTful endpoints that
// respond to 'fizzServiceClient.kickOffBigJob(Widget)'.
class FizzService {
ExecutorService executor = initExecutor()
// This method submits the 'widget' to an executor and then returns an HTTP response
// to the service client. Note that this response is not the 'result' we're looking
// for, it's just a quick indication that the request was received OK.
def kickOffBigJob(Widget widget) {
WidgetJob widgetJob = new WidgetJob(widget)
executorService.submit(widgetJob) // WidgetJob extends Runnable
}
}
И наконец:
class WidgetJob implements Runnable {
Widget widget
WidgetProcessorFactory wpf = new WidgetProcessorFactory()
// Constructors, etc.
@Override
def run() {
WidgetProcessor processor = wpf.newWidgetProcess()
// Where the magic happens; this is what takes up to 20 minutes to
// compute the 'result'.
String result = processor.process(widget)
}
}
Итак, у меня сейчас 2 проблемы:
- Как передать '
result
', который мы вычисляем внутриWidgetJob#run()
, обратно в контроллер Grails (FizzController
); и - Как динамически передать значение "
result
" из контроллера Grails обратно на сторону клиента таким образом, чтобы без обновления страницы пользовательский интерфейс пользователя внезапно обновлялся со значением "result
".
Любые идеи относительно того, как я могу это сделать?