Trying to run this repo with some suspend functions. Can someone please give some hints?
Let say we have one
suspend fun log(){
mLog.subscribeAlways<GroupMessageEvent> { event ->
if (event.message.content.contains("Error")) {
print("****")
} else if (event.message.content.contains("Warning")) {
print("Warning")
}
}
mLog.Listen()
}
How can we trigger this log from main
open class Application {
companion object {
#JvmStatic fun main(args: Array<String>) {
SpringApplication.run(Application::class.java, *args)
}
}
}
What have try, it can run without error, but it didn't work as expected,
call the log function from Controller class
class Controller {
#Value("\${spring.datasource.url}")
private var dbUrl: String? = null
#Autowired
lateinit private var dataSource: DataSource
#RequestMapping("/")
internal suspend fun index(): String {
mLog()
return "index"
}
Suspend functions should be called from a coroutine. There are several coroutine builder functions: launch, async, runBlocking.
Using these functions you can start a coroutine, for example:
runBlocking {
// call suspend methods
}
Coroutines launched with launch and async coroutine builders in a context of some CoroutineScope. They don't block the current thread. There are more info in the docs.
runBlocking blocks the current thread interruptibly until its completion.
Using launch coroutine builder you will not block the current thread if calling suspend function in it:
fun index(): String {
GlobalScope.launch {
log()
}
"index"
}
Note: in this case function index returns before log is executed. GlobalScope is discouraged to use. Application code usually should use an application-defined CoroutineScope. How to define a CoroutineScope is described in the docs and here and here.
If your intention is to block the current thread until suspend function completes use runBlocking coroutine builder:
fun index(): String = runBlocking {
log()
"index"
}
Some useful links: GlobalScope.launch vs runBlocking, Update UI async call with coroutines, Suspend function 'callGetApi' should be called only from a coroutine or another suspend function.
Related
I have a Spring Boot app and when handling a given request I need to call upstream services in a parallel and wait for the result to complete before returning them in my own response.
In the existing code base, I noticed that in order to do so, the pattern is to use runBlocking(IO) { ... }
#Service
class MyUpstreamService {
fun getSomething() = 1
}
#RestController
class MyController(
val upstream: MyUpstreamService
) {
#GetMapping("/foo")
fun foo() =
runBlocking(Dispatchers.IO) {
val a = async { upstream.getSomething() }
val b = async { upstream.getSomething() }
a.await() + b.await()
}
}
This works as expected.
Now for some reasons I need to set the scope of MyUpstreamService to #RequestScope and if I do so, I get the following exception as soon as I access MyUpstreamService from within the runBlocking(IO) { ... } block:
Caused by: java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131) ~[spring-web-5.3.22.jar:5.3.22]
If I do not use the Dispatchers.IO context, then everything works fine.
So the question is why would one use runBlocking(Dispatchers.IO) { .. } instead of just runBlocking { .. } when waiting for several async calls to complete?
For completeness, here is the entire snippet demonstrating the question.
GET /bar works
GET /foo throws the exception
#RequestScope
#Service
class MyUpstreamService(
// val currentUser: CurrentUser
) {
fun getSomething() = 1
}
#RestController
class MyController(
val upstream: MyUpstreamService
) {
#GetMapping("/foo")
fun foo() =
runBlocking(Dispatchers.IO) {
val a = async { upstream.getSomething() }
val b = async { upstream.getSomething() }
a.await() + b.await()
}
#GetMapping("/bar")
fun bar() =
runBlocking {
val a = async { upstream.getSomething() }
val b = async { upstream.getSomething() }
a.await() + b.await()
}
}
runBlocking without particular dispatcher means that all coroutines inside are launched in a special single-threaded event loop dispatcher backed by the thread that you're blocking. This, in turn, means your coroutines would not run in parallel.
runBlocking(Dispatchers.IO) means the nested coroutines run on the IO dispatcher, which is backed by a pool of threads of dynamic size, and thus the coroutines are effectively run in parallel (within some limit). At the same time, it's still a runBlocking, which means the calling thread would still be blocked while waiting for the nested coroutines to complete, but it would not be used to do any work.
for some reasons I need to set the scope of MyUpstreamService to #RequestScope
When you do this, Spring creates one service instance by request - and this is done based on the request's thread (by using some ThreadLocal machinery I assume). As we have just seen, runBlocking without dispatcher actually uses the calling thread, so the request thread, and that is why this mechanism still works. If you use runBlocking(IO) and dispatch on other threads, you're breaking this Spring mechanism.
Now I haven't done Spring dev in a while, so I'm not 100% sure how to fix your problem. But I believe a good start would be to stop using the thread-per-request model if you're using coroutines, and thus switch to suspend functions in your controllers using Spring WebFlux. I think it will still not allow to use #RequestScope, though, because you would be giving up the "request thread" concept altogether. See https://github.com/spring-projects/spring-framework/issues/28235
Note: the below function test is being called via a launch coroutine.
The following code works:
#Retryable(value=[RetryException::class])
suspend fun test() {
throw RetryException("blah")
}
However, the moment I add an async call, retry stops working:
#Retryable(value=[RetryException::class])
suspend fun test() {
val deferred = supervisorScope {
async { library.method() }
}
deferred.await()
throw RetryException("blah")
}
What could be wrong?
Below is code I have for a component that starts a Flux and subscribes to it, all within the constructor of the class. This particular flux comes from a mongoChangeStreams call. It does not terminate unless there is an error.
I want the subscription to stay alive constantly so I restart the subscription in the event in terminates due to an error.
It has occurred to me that calling subscribe within a constructor might be a bad idea. Also I should probably enable a way to shut down this app gracefully by calling cancel on the subscription during shutdown.
My guess is that I should be implementing SmartLifeCycle but I'm not sure how to do that. Is there a standard way of implementing SmartLifeCycle on a component backed by a Flux subscription?
#Component
class SubscriptionManager(
private val fooFluxProvider: FooFluxProvider, //calling foos() on this returns a Flux of foos
private val fooProcessor: FooProcessor
) {
private var subscription: BaseSubscriber<Foo> = subscribe() //called in constructor
private fun subscribe() = buildSubscriber().also {
fooFluxProvider.foos().subscribe(it)
}
private fun buildSubscriber(): BaseSubscriber<Foo> {
return object : BaseSubscriber<Foo>() {
override fun hookOnSubscribe(subscription: Subscription) {
subscription.request(1)
}
override fun hookOnNext(value: Foo) {
//process the foo
fooProcessor.process(value)//sync call
//ask for another foo
request(1)
}
override fun hookOnError(throwable: Throwable) {
logger.error("Something went wrong, restarting subscription", throwable)
//restart the subscription. We'll recover if we're lucky
subscription = subscribe()
}
}
}
}
Instead of creating a Subscriber subclass that resubscribes on exception, chain one of the retry* operators on the Flux before subscribing. The retry operators will resubscribe to the upstream Flux if it completes with an exception. For example, fooFluxProvider.foos().retry() will retry indefinitely. There are other variations of retry* for more advanced behavior, including an extremely customizable retryWhen that can be used with the reactor.retry.Retry class from reactor-extra.
Instead of passing a subscriber to subscribe(subscriber), call one of the subscribe methods that returns a Disposable. This gives you an object on which you can call dispose() later during shutdown to cancel the subscription.
To implement SmartLifecycle:
In the constructor (or in start()), create the Flux (but do not subscribe to it in the constructor)
In start(), call flux.subscribe() and save the returned Disposable to a member field. The start() method is much better suited for starting background jobs than a constructor. Consider also chaining .subscribeOn(Scheduler) before .subscribe() if you want this to run in the background (by default, the subscription occurs on the thread on which subscribe was called).
In stop(), call disposable.dispose()
Perhaps something like this:
class SubscriptionManager(
fooFluxProvider: FooFluxProvider, //calling foos() on this returns a Flux of foos
fooProcessor: FooProcessor
) : SmartLifecycle {
private val logger = LoggerFactory.getLogger(javaClass)
private val fooFlux = fooFluxProvider.foos()
// Subscribe on a parallel scheduler to run in the background
.subscribeOn(Schedulers.parallel())
// Publish on a boundedElastic scheduler if fooProcessor.process blocks
.publishOn(Schedulers.boundedElastic())
// Use .doOnNext to send the foo to your processor
// Alternatively use .flatMap/.concatMap/.flatMapSequential if the processor returns a Publisher
// Alternatively use .map if the processor transforms the foo, and you need to operate on the returned value
.doOnNext(fooProcessor::process)
// Log if an exception occurred
.doOnError{ e -> logger.error("Something went wrong, restarting subscription", e) }
// Resubscribe if an exception occurred
.retry()
// Repeat if you want to resubscribe if the upstream flux ever completes successfully
.repeat()
private var disposable: Disposable? = null
#Synchronized
override fun start() {
if (!isRunning) {
disposable = fooFlux.subscribe()
}
}
#Synchronized
override fun stop() {
disposable?.dispose()
disposable = null
}
#Synchronized
override fun isRunning(): Boolean {
return disposable != null
}
}
I have an app that consumes some messages from a message Queue and processes them. The processing is implemented as suspending functions and there is a service that will publish the events to a Channel<Event>, I have another service that will basically do:
for (event in channel) {
eventProcessor.process(event)
}
The problem is that this is also a suspending function, and I am really not sure what's the proper way to launch it within the context of Spring.
My initial solution was to do the following:
#Bean
fun myProcessor(eventProcessor: EventProcessor, channel: Channel<Event>): Job {
GlobalScope.launch {
eventProcessor.startProcessing(channel)
}
}
But it seems somehow hacky, and I am not sure what's the proper way to do it.
Launching anything on a GlobalScope is a really bad idea. You loose all advantages of structured concurrency this way.
Instead, make your EventProcessor implement CoroutineScope.
This will force you to specify coroutineContext, so you can use Dispatchers.Default:
override val coroutineContext = Dispatchers.Default
So, the full example will look something like this:
#SpringBootApplication
class SpringKotlinCoroutinesApplication {
#Bean
fun myProcessor(eventProcessor: EventProcessor, channel: Channel<Event>): Job {
return eventProcessor.startProcessing(channel)
}
#Bean
fun p() = EventProcessor()
#Bean
fun c() = Channel<Event>()
}
fun main(args: Array<String>) {
runApplication<SpringKotlinCoroutinesApplication>(*args)
}
class Event
class EventProcessor : CoroutineScope {
override val coroutineContext = Dispatchers.Default
fun startProcessing(channel: Channel<Event>) = launch {
for (e in channel) {
println(e)
}
}
}
My JavaFX app's UI freezes after consecutive times of executing webservice calls. Those process calls are asynchronous.
How do I fix this problem? Is there a way to "unfreeze" the UI?
Sorry for the newbie question. But I badly need anyone;'s help
Did you create a thread to execute it? JavaFX is executed on the EDT (event dispatch thread). That is why you experience GUI freeze. Here is what you do
import javafx.async.*
public class MyWebService extends RunnableFuture {
public var webserviceURL:String;
override run(): Void {
// your web service
}
}
public class MyWebServiceTask extends JavaTaskBase {
override create(): RunnableFuture {
return MyWebService {
webserviceURL: "http://...."
};
}
}
def webserviceTask: MyWebServiceTask = MyWebServiceTask { }
webserviceTask.start();