Spring WebFlux rest controller serves only first two subscriptions - java

There's a flux created programmatically by Flux.create method:
Flux<Tweet> flux = Flux.<Tweet>create(emitter -> ...);
There's a rest controller:
#RestController
public class StreamController {
...
#GetMapping("/top-words")
public Flux<TopWords> streamTopWords() {
return topWordsStream.getTopWords();
}
}
There's a couple of web clients (in standalone processes):
Flux<TopWords> topWordsFlux = WebClient.create(".../top-words")
.method(HttpMethod.GET)
.accept(MediaType.TEXT_EVENT_STREAM)
.retrieve()
.bodyToFlux(TopWords.class)
.subscribe(System.out::println);
There's a couple of EventSource instances in JavaScript:
var eventSource = new EventSource(".../top-words");
eventSource.onmessage = function (e) {
console.log("Processing message: ", e.data);
};
Only the first two "subscribers" will start receiving the messages (no matter if it's a web client or EventSource instance). The other will open the connection, get HTTP 200 status, but the event stream stays empty. There're no errors on either client or server side.
I don't understand, where is the limit on "2 subscribers" imposed. What do I have to do, if I want to support more than 2 subscribers?
The application is built with Spring Boot 2.0.0.RELEASE and auto-configured with spring-boot-starter-webflux. The default configuration is not changed.

There is a limitation in the underlying API that I tried to adapt (Twitter streaming API).
The goal was to connect to Twitter once and process tweet stream by various different subscribers.
Originally I thought the emitter passed to Flux.create method always uses the same FluxSink for all subscribers. That of course doesn't make sense. Instead the FluxSink is provided per-subscriber, as the javadoc states clearly.
I implemented my use case with a Twitter listener that allows for registration (and un-registration) of a number of FluxSink instances. That way, the single tweet stream can be subscribed to by various different subsribers.
Flux<Tweet> flux = Flux.<Tweet>create(twitterListener::addSink);
My twitterListener implements org.springframework.social.twitter.api.StreamListener from spring-social-twitter project.

Related

calling 2 url with web client spring boot with in sequential order

This question is web flux spring boot related. I have 2 web clients, one that makes a call to a url to get accounts then I send those accounts to an object mapper to get mapped to a POJO. Then I use second web client to make a call to second url with those accounts. Can anyone tell me how I can make them execute in sequence ?
In current scenario by the time I get response back from first web client and response goes to object mapper, second web client fires up and accounts in request body goes as null.
Any input would be appreciated.
thanks.
Cast the WebClient output to Mono<YouPojo> and use flatMap to call the next service you want.
WebClient client1 = null;
WebClient client2 = null;
Mono<String> finalResult = client1.get()
.uri("https://example.com/service-1")
.retrieve()
.bodyToMono(String.class)
.flatMap(result -> client2.post()
.uri("https://example.com/service-2")
.body(BodyInserters.fromFormData("result", result))
.retrieve()
.bodyToMono(String.class));
As you see in the above, we have two clients client1 and client2 that we use them to call the corresponding services. You can combine the results of Monos with map or flatMap operations.
By default webclient is a non-blocking I/O. Considering you are using ObjectMapper to convert the response from the first call. You can use block() method to make it blocking call.
Example:
webClient.get().uri("http:/sdfsdf.com/sdsd").retrieve().bodyToMono(String.class).block();

how to make spring web flux wait until specified condition met in server and then return the response

I'm a starter in Spring Web-Flux. i want to have a reactive service for example named isConfirmed and this service must wait until another service of my server is called for example named confirm. both services are located in my server and the first reactive service must wait until the second service (confirm) is called and then return the confirm message. i want no threads to be blocked in my server until the second service is called. like an observer pattern. is it possible with spring web flux?
update: can we have this feature while server using distributed cache?
I think you could use a CompletableFuture between your 2 services, something like that:
CompletableFuture<String> future = new CompletableFuture<>();
public Mono<String> isConfirmed() {
return Mono.fromFuture(future);
}
public void confirm(String confirmation) {
future.complete(confirmation);
}

Best way to use Websocket with Spring Boot and Vuejs

I try to use Websocket with spring boot backend (as an API) and Vuejs frontend.
I take a simple use case to expose my question. Some users are logged on my website, and there is a messaging feature. User A send a message to User B. User B is actually logged, and I want to notify User B that a new message is arrived.
I see 3 ways to do it with websockets :
1 - When User A send message, an Axios post is call to the API for saving message, and, if the Axios response is success, I call something like
this.stompClient.send("/app/foo", JSON.stringify(bar), {})
2 - When User A send message, I only call something like
this.stompClient.send("/app/foo", JSON.stringify(bar), {})
and it's my controller's method (annotated with #MessageMapping("/xxxx") #SendTo("/topic/yyyy")) that call facade, service, dao to first, save message, then return message to subscribers
3 - I keep my actuals controllers, facade, services and DAO, and juste add when save is successfull something like :
#Autowired SimpMessagingTemplate webSocket;
...
#GetMapping("/send-message")
public ResponseEntity sendMessage(#AuthenticationPrincipal User user, ....) {
service.saveMessage(....);
webSocket.convertAndSend("/ws/message-from", message);
without a new controller contains #MessageMapping("/xxxx") #SendTo("/topic/yyyy"). User B is just subscibed to "/ws/message-from"
Could you help me.
In the 3 way there is a good method ?
Thanks you.
The one and two method has no much difference as you use axios from npm for sending request and the other one you can directly,while the third one you use controller,and facade dao at single place.it is about architecture and how you wanna send your requests for your framework,as a requirement.
They serve best at their level,till you come with specific requirement.
The suggestion would be to use axios.
It has advantages:
supports older browsers (Fetch needs a polyfill)
has a way to abort a request
has a way to set a response timeout
has built-in CSRF protection
supports upload progress
performs automatic JSON data transformation
works in Node.js

Webflux websocketclient, How to send multiple requests in same session[design client library]

TL;DR;
We are trying to design a WebSocket server using spring webflux WebSocket implementation. The server has usual HTTP server operations e.g. create/fetch/update/fetchall. Using WebSockets we were trying to expose one endpoint so the clients could leverage a single connection for all sort of operations, given WebSockets are meant for this purpose. Is it a right design with webflux and WebSockets?
Long Version
We are starting a project which is going to use reactive web sockets from spring-webflux. We need to build a reactive client library which can be used by consumers to connect to the server.
On the server, we get a request, read a message, save it and return a static response:
public Mono<Void> handle(WebSocketSession webSocketSession) {
Flux<WebSocketMessage> response = webSocketSession.receive()
.map(WebSocketMessage::retain)
.concatMap(webSocketMessage -> Mono.just(webSocketMessage)
.map(parseBinaryToEvent) //logic to get domain object
.flatMap(e -> service.save(e))
.thenReturn(webSocketSession.textMessage(SAVE_SUCCESSFUL))
);
return webSocketSession.send(response);
}
On the client, We want to make a call when someone calls save method and return the response from server.
public Mono<String> save(Event message) {
new ReactorNettyWebSocketClient().execute(uri, session -> {
session
.send(Mono.just(session.binaryMessage(formatEventToMessage)))
.then(session.receive()
.map(WebSocketMessage::getPayloadAsText)
.doOnNext(System.out::println).then()); //how to return this to client
});
return null;
}
We are not sure, how to go about designing this. Ideally, we think there should be
1) client.execute should be called only once and somehow hold the session. The same session should be used to send data in subsequent calls.
2) How to return the response from the server which we get in session.receive?
3) How about in case of fetch when the response is huge(not just a static string but list of events) in session.receive?
We are doing some research but we are unable to find proper resources for webflux-websocket-client documentation/implementation online. Any pointers on how to move ahead.
Please! Use RSocket!
It is absolutely correct design, and it worths to save resources and use only a connection per client for all possible ops.
However, don't implement a wheel and use the Protocol which gives you all of these kinds of communications.
RSocket has a request-response model which allows you to do the most common client-servert interaction today.
RSocket has a request-stream communication model so you can fulfill all your need and return a stream of events asynchronously reusing the same connection. RSocket does all maping of logical stream to phisical connection and back, so you will not feel the pain of doing that yourself.
RSocket has far more interaction models such as
fire-and-forget and stream-stream which could be useful in case of
sending a stream of data in both ways.
How to use RSocket in Spring
One of the options to do so is using RSocket-Java implementation of RSocket protocol. RSocket-Java is built on top of Project Reactor, so it naturally fits Spring WebFlux ecosystem.
Unfortunately, there is no featured integration with Spring ecosystem. Fortunately, I spent a couple of hours to provide a simple RSocket Spring Boot Starter that integrates Spring WebFlux with RSocket and exposes WebSocket RSocket server along with WebFlux Http server.
Why RSocket is a better approach?
Basically, RSocket hides the complexity of implementing the same approach yourself. With RSocket we don't have to care about interaction model definition as a custom protocol and as an implementation in Java. RSocket does for us delivering of the data to a particular logical channel. It provides a built-in client that sends messages to the same WS connection, so we don't have to invent a custom implementation for that.
Make it even better with RSocket-RPC
Since RSocket just a protocol it does not provide any message format, so this challenge is for business logic. However, there is an RSocket-RPC project which provides a Protocol Buffer as a message format and reuses the same code generation technique as GRPC does. So using RSocket-RPC we can easily build an API for the client and server and careless about transport and protocol abstraction at all.
The same RSocket Spring Boot integration provides an example of RSocket-RPC usage as well.
Alright, It has not convinced me, I wanna have a custom WebSocket server still
So, for that purpose, you have to implement that hell yourself. I have already done that once before, but I can't point to that project since it is an enterprise one.
Nevertheless, I can share a couple of code samples that can help you in building a proper client and server.
Server side
Handler and Open logical Subscribers mapping
The first point that must be taken into account is that all logical streams within one physical connection should be stored somewhere:
class MyWebSocketRouter implements WebSocketHandler {
final Map<String, EnumMap<ActionMessage.Type, ChannelHandler>> channelsMapping;
#Override
public Mono<Void> handle(WebSocketSession session) {
final Map<String, Disposable> channelsIdsToDisposableMap = new HashMap<>();
...
}
}
There are two maps in the sample above. The first one is your routes mapping which allows you to identify route based on the incoming message params, or so. The second one is created for request-streams usecase (in my case it was map of active subscriptions), so you can send a message-frame that creates a subscription, or subscribes you to a specific action and keep that subscription so once the unsubscribe action is executed you will be unsubscribed if a subscription exists.
Use Processor for messages multiplexing
In order to send back messages from all logical streams, you have to multiplex messages to one stream. For example, using Reactor, you can do that using UnicastProcessor:
#Override
public Mono<Void> handle(WebSocketSession session) {
final UnicastProcessor<ResponseMessage<?>> funIn = UnicastProcessor.create(Queues.<ResponseMessage<?>>unboundedMultiproducer().get());
...
return Mono
.subscriberContext()
.flatMap(context -> Flux.merge(
session
.receive()
...
.cast(ActionMessage.class)
.publishOn(Schedulers.parallel())
.doOnNext(am -> {
switch (am.type) {
case CREATE:
case UPDATE:
case CANCEL: {
...
}
case SUBSCRIBE: {
Flux<ResponseMessage<?>> flux = Flux
.from(
channelsMapping.get(am.getChannelId())
.get(ActionMessage.Type.SUBSCRIBE)
.handle(am) // returns Publisher<>
);
if (flux != null) {
channelsIdsToDisposableMap.compute(
am.getChannelId() + am.getSymbol(), // you can generate a uniq uuid on the client side if needed
(cid, disposable) -> {
...
return flux
.subscriberContext(context)
.subscribe(
funIn::onNext, // send message to a Processor manually
e -> {
funIn.onNext(
new ResponseMessage<>( // send errors as a messages to Processor here
0,
e.getMessage(),
...
ResponseMessage.Type.ERROR
)
);
}
);
}
);
}
return;
}
case UNSABSCRIBE: {
Disposable disposable = channelsIdsToDisposableMap.get(am.getChannelId() + am.getSymbol());
if (disposable != null) {
disposable.dispose();
}
}
}
})
.then(Mono.empty()),
funIn
...
.map(p -> new WebSocketMessage(WebSocketMessage.Type.TEXT, p))
.as(session::send)
).then()
);
}
As we can see from the sample above, there is a bunch of things there:
The message should include route info
The message should include a unique stream id to which it relates.
Separate Processor for message multiplexing where error should be a message as well
Each channel should be stored somewhere, in this case all we have a simple use case where each message can provide a Flux of messages or just a Mono (in case of mono it could be implemented simpler on the server side, so you don't have to keep unique stream ID).
This sample does not include messages encoding-decoding, so this challenge is left on you.
Client side
The client is not that simple as well:
Handle session
To handle connection we have to allocate two processors so further we can use them to multiplex and demultiplex messages:
UnicastProcessor<> outgoing = ...
UnicastPorcessor<> incoming = ...
(session) -> {
return Flux.merge(
session.receive()
.subscribeWith(incoming)
.then(Mono.empty()),
session.send(outgoing)
).then();
}
Keep all logical streams somewhere
All created streams whether it is Mono or Flux should be stored somewhere so we will be capable of distinguishing to which stream message relates:
Map<String, MonoSink> monoSinksMap = ...;
Map<String, FluxSink> fluxSinksMap = ...;
we have to keep two maps since MonoSink, and FluxSink does not have the same parent interface.
Message Routing
In the above samples, we just considered the initial part of the client side. Now we have to build a message routing mechanism:
...
.subscribeWith(incoming)
.doOnNext(message -> {
if (monoSinkMap.containsKey(message.getStreamId())) {
MonoSink sink = monoSinkMap.get(message.getStreamId());
monoSinkMap.remove(message.getStreamId());
if (message.getType() == SUCCESS) {
sink.success(message.getData());
}
else {
sink.error(message.getCause());
}
} else if (fluxSinkMap.containsKey(message.getStreamId())) {
FluxSink sink = fluxSinkMap.get(message.getStreamId());
if (message.getType() == NEXT) {
sink.next(message.getData());
}
else if (message.getType() == COMPLETE) {
fluxSinkMap.remove(message.getStreamId());
sink.next(message.getData());
sink.complete();
}
else {
fluxSinkMap.remove(message.getStreamId());
sink.error(message.getCause());
}
}
})
The above code sample shows how we can route incoming messages.
Multiplex requests
The final part is messages multiplexing. For that purpose we are going to cover possible sender class impl:
class Sender {
UnicastProcessor<> outgoing = ...
UnicastPorcessor<> incoming = ...
Map<String, MonoSink> monoSinksMap = ...;
Map<String, FluxSink> fluxSinksMap = ...;
public Sender () {
// create websocket connection here and put code mentioned earlier
}
Mono<R> sendForMono(T data) {
//generate message with unique
return Mono.<R>create(sink -> {
monoSinksMap.put(streamId, sink);
outgoing.onNext(message); // send message to server only when subscribed to Mono
});
}
Flux<R> sendForFlux(T data) {
return Flux.<R>create(sink -> {
fluxSinksMap.put(streamId, sink);
outgoing.onNext(message); // send message to server only when subscribed to Flux
});
}
}
Sumup of Custom implementation
Hardcore
No Backpressure support implemented so that could be another challenge
Easy to shoot yourself in the foot
Takeaways
PLEASE, use RSocket, don't invent protocol yourself, it is HARD!!!
To learn more about RSocket from Pivotal guys - https://www.youtube.com/watch?v=WVnAbv65uCU
To learn more about RSocket from one of my talks - https://www.youtube.com/watch?v=XKMyj6arY2A
There is a featured framework built on top of RSocket called Proteus - you might be interested in that - https://www.netifi.com/
To learn more about Proteus from core developer of RSocket protocol - https://www.google.com/url?sa=t&source=web&rct=j&url=https://m.youtube.com/watch%3Fv%3D_rqQtkIeNIQ&ved=2ahUKEwjpyLTpsLzfAhXDDiwKHUUUA8gQt9IBMAR6BAgNEB8&usg=AOvVaw0B_VdOj42gjr0YrzLLUX1E
Not sure if is this case your problem??
im seeing that you are sending a static flux response (this is a close-able stream)
you need a opend stream to send messages to that session for example you can create a processor
public class SocketMessageComponent {
private DirectProcessor<String> emitterProcessor;
private Flux<String> subscriber;
public SocketMessageComponent() {
emitterProcessor = DirectProcessor.create();
subscriber = emitterProcessor.share();
}
public Flux<String> getSubscriber() {
return subscriber;
}
public void sendMessage(String mesage) {
emitterProcessor.onNext(mesage);
}
}
and then you can send
public Mono<Void> handle(WebSocketSession webSocketSession) {
this.webSocketSession = webSocketSession;
return webSocketSession.send(socketMessageComponent.getSubscriber()
.map(webSocketSession::textMessage))
.and(webSocketSession.receive()
.map(WebSocketMessage::getPayloadAsText).log());
}

Java: How to handle an API call that can take around 10 seconds

I have a requirement and I am bit confused about its design.
Requirement: iOS makes a call to backend(java), backend makes a call to the cloud API which return a token for future calls. The cloud API might take approximately 6 to 10 seconds to return the actual result, so instead of waiting for 6 to 10 seconds it gives a token back and let the caller(in my case the backend java server) to pull the results.
Current Approach: iOS calls the backend(java server), the backend calls cloud API and get's the token, then it sleeps the thread for 1 second and once the thread is invoked it hit the cloud API to get the status, if the status is not completed thread.sleep is invoked again and this continues till the cloud API call give's the complete result. Once the cloud API returns the result the backend returns the result to iOS.
The approach is not scalable and was done to test the cloud API but now we need a more scalable approach.
This is what I am thinking about iOS calls backend, backend calls the API and send back the result to iOS(it displays some static screen just to keep users engaged) and in the mean time it puts the object in Spring Thread pool Executor. The executor hits the API every one second and update the iOS through push notification and this continues till we get the final result from cloud API.
This is better then existing approach but even this doesn't look scalable and thread pool executor will get exhausted after some time(making it slow) and also thread.sleep is also not a good option.
I thought about using AWS SQS but it doesn't provide real time processing and running background jobs every 1 second doesn't seem to be a good option.
I am also exploring Apache Kafka and trying to understand whether it can fit to my use case.
Let me know if someone has tacked the similar kind of use case.
Here #EventListener in tandem with #Scheduled can be utilized, if Spring 4.2 (or newer) version is used.
First Create an event object say APIResult which will hold the API result
public class APIResult extends ApplicationEvent {
public APIResult(Object result) {
super(source);
}
}
Next register a listener for the event published as APIResult
#Component
public class MyListener {
#EventListener
public void handleResult(APIResult result) {
// do something ...
}
}
Next create a scheduled process which will hold the token(s) for which result is not yet retrieved
#Component
public class MyScheduled {
private final ApplicationEventPublisher publisher;
private List<String> tokens = new ArrayList<>();
#Autowired
public MyScheduled (ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
#Scheduled(initialDelay=1000, fixedRate=5000) // modify it as per requirement
public void callAPIForResult() {
// call the API and get result for each token(s) ....
this.publisher.publishEvent(new APIResult(result));
}
// method to add & remove tokens
}
The overall process flow should be like
Application submit a request to API and collect the respective token.
Token is passed to scheduled service to fetch the result.
In its next run the scheduled service iterates over the available token(s) and call API to fetch the results (if result is available publish the event else continue)
The published event is intercepted by registered listener; which itself process the result or delegates as applicable
This approach will transparently fetch results without messing with the business logic and at same time leveraging the standard framework features viz. scheduling and asynchronous event publishing & processing.
Although I have not tested this but it should work, at least giving an idea on how to implement. The setup is tested with Spring boot ver. 1.5.1.RELEASE which is backed by Spring's 4.3.6.RELEASE
Do let know in comments if any further information is required.
Reference - Application Event in Spring (link)
I am thinking about using Spring ConcurrentTaskExecutor(let's call it cloudApiCall) and as soon as I received the token from Cloud API, I will submit a future job to the executor and return the token to the Mobile Client. The thread associated with ConcurrentTaskExecutor will pick the job, call the Cloud API and submit the response to the another ConcurrentTaskExecutor(let's call it pushNotification) which will be responsible for pushing the silent notification to the Mobile client. The thread associated ConcurrentTaskExecutor(cloudApiCall), will also check the status of the call, if the future call is required, it will submit the job back to ConcurrentTaskExecutor(cloudApiCall). This will continue till we get the complete response.

Categories

Resources