0

I use Angular with Spring for internal API communication. Now I need to call external api to get some data. That external API provides callback feature, so I trigger call from Angular, call Spring rest method which, at the end, calls external API. On the other hand I get data on my callback methods (Spring rest also), but I dont know how to transfer those data back to Angular.

Is websocket the only option?

Nikola
  • 624
  • 1
  • 14
  • 31
  • Can you keep the connection between the client and the webapp open until the external API responds? Opening a WebSocket connection would be a bit of a heavy lift for a single request. The typical way to do this would be to not have the server respond until it retrieves the information from the external API. If the external API uses a callback feature, you might consider having the thread wait until the callback is executed, although this could get expensive depending on your expected server load. – Mike Hill Jul 06 '17 at 19:50
  • App is deployed on Heroku, so I don't have an option to keep the connection, because response from external API is usually >30s and request timeout in Heroku is 30s. – Nikola Jul 07 '17 at 09:14

1 Answers1

1

If internal API calls are longer than the allowable client timeout, then you will need to find a WebSocket or WebSocket-like alternative. The alternative to WebSockets is to use long-polling (good example with source code here), which is essentially having the client repeatedly make requests until the original task is completed and the data can be sent. Either way, you'll need to use a pub/sub mechanism of some sort to handle multiple users, which is where things can get complicated.

Pub/sub can be complicated and I don't have an example on-hand, but essentially you must (1) have the client subscribe to a channel using a unique identifier (you can do this with CometD via a service channel), (2) evaluate the response, (3) publish the response to the client's subscribed channel, and finally (4) close the channel when it's no longer in use.

I've had some luck with CometD as a library to simplify pub/sub channel management, and it provides a good abstraction for asynchronous communication, although I haven't tried it with Spring and it might be heavy for what you want to do.

Another option that I'm less familiar with is to use Spring with STOMP. This seems to come recommended by others. Spring does provide ways to send messages to single users using STOMP: Sending message to specific user on Spring Websocket

I'd recommend following the above STOMP example.

Additional STOMP resources:

As a side note, throttling may be necessary here, too, as with any time that you can spawn long-running threads from clients.


If you choose not to use STOMP or CometD, then a lighter solution could be to roll your own pub/sub for this single case using Spring's DeferredResult (as in Roger Hughes example), tying the subscription request to the long-poll request via a UUID token, which might also be the session ID if you choose to disallow concurrent requests per user. On subscription, the system can associate the request with a UUID and return this UUID to the client. The client can then make long-poll requests (again, as in Roger Hughes example) but with the UUID attached. The server can wait until the request for the given UUID has completed and then return the result to the client via the client's active long-poll request.

Channel management (i.e., request/UUID-tracking) can be done by clearing the channel on DeferredResult result retrieval and removing orphan channels with a separate thread acting as a GC -- or, perhaps better yet, automatically clearing orphan channels by removing them on DeferredResult completion/timeout if no active listeners exist. If you opt for the latter option, you will want to make sure that the client won't have any delay between its long-poll requests so that the DeferredResult doesn't unintentionally complete with no listeners.

Mike Hill
  • 3,622
  • 23
  • 27