-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
issue 361 - async feign variant supporting CompleteableFutures #1174
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this change... like to the point that I'm curious if it's possible to actually made it the main implementation. If we can get Async and non-async sharing a bit more of code, I think would be possible.
CompletableFuture<Response> response(); | ||
|
||
@RequestLine("POST /") | ||
CompletableFuture<String> post() throws TestInterfaceException; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm looking at this and I like it.
I wonder, do you think it would be possible to handle CompletableFuture<String>
and String
on the same code base?
So, deal with async and synchronous code using the same classes.
This would open the possibility of having async behavious as part of default feign, which would be interesting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is possible, and easy to implement. The proxy ReflectiveAsyncFeign would need to detect these kinds of methods and use CompleteableFuture.join() in a try/catch block with appropriate exception handling.
I though would exercise some caution here.
- I think that interfaces should either be asynchronous or synchronous - I really can't think of a case where it makes sense to mix between the two.
- If this is to be used as a replacement for the synchronous approach today, then I think it does require adding in a "blessed" way for doing retries (as exists today). Note though that it would break existing integrations, which would need to implement AsyncClient, as well as client code, in particular because context is now explicit. One could get around this by having the client also implement the context supplier (and handle under the covers in the builder by checking if the client implements
Supplier<C>
). This isn't too bad, since the synchronous clients today handle this internally generally on a thread local anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that interfaces should either be asynchronous or synchronous - I really can't think of a case where it makes sense to mix between the two.
You may not, but our user's may, so we should consider it.
See my other comment on Retry. We will have to break it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is really only one reason that I can see to want a mixed interface and that's when wants to extend an existing synchronous interface to add on new async features (and one wants to be able to pass the instance to existing code).
Otherwise, it's generally trivial to do a join on the async call. The other way, changing client code from sync to async would likely take additional effort. But you're right that users could potentially have other constraints or reasons they need things a certain way.
However, it's not difficult to support - best practices can certainly be captured in documentation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that interfaces should either be asynchronous or synchronous - I really can't think of a case where it makes sense to mix between the two.
My point at supporting both at same time, would be mainly to force a single workflow inside feign.
I don't really expect anyone using async for 3 methods and having one not async. Maybe someone will.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added now sync through async. One thing that was necessary as part of that was coercing the SynchronousMethodHandler to explictly call the decoder (which is really the stageDecode in AsyncFeign) - this is because there were some edge cases in terms of Response and void return types which have correct special handling in AsyncResponseHandler which would be quite ugly to handle otherwise. I thought this way would be the most robust, absent a more extensive rewrite. To keep things consistent and simple, the coercion is also used for the async case, even though it works without it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome job here, really. We are very grateful for your work and spending the time to help us with this.
Before this can move forward, I think we have some foundational stuff to change. I agree with @velo that this should be our default way of processing requests. I've done some preliminary work on this in FeignX: https://github.com/OpenFeign/feignx/tree/master/core/src/main/java/feign/impl and I think we can combine the concepts here. Let's create a branch for us to collaborate on to allow us to work out these changes without blocking other bug fixes.
Here's what I think we can do
- Refactor
MethodMetadata
to be thread-safe - Refactor
handleResponse
to be independent functions - Refactor
Contract
to choose the rightInvocationHandler
based on return type.
If we do these three things, this will lay the groundwork for everything else.
One last thought on Retry, this we know is something that will break when we do this and we know that.
|
||
private static class LazyInitializedExecutorService { | ||
|
||
private static final ExecutorService instance = Executors.newCachedThreadPool(r -> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cached Thread Pools are OK to start, but I think we may be better off with a Fixed Thread Pool.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I chose a CachedThreadPool to avoid needing to define additional configuration. Users can of course elect to pass in their own executor here for converting a sync client for use in async code (although they may have issues there with the fact that context may be shared in strange ways).
|
||
public class ReflectiveAsyncFeign<C> extends AsyncFeign<C> { | ||
|
||
private static class MethodInfo { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MethodMetadata
already exists to serve this purpose. We should consider refactoring that component before creating additional components.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed - it was doing much too much for my needs, hence the duplication. Much preferable to refactor.
Thank you for the positive feedback, and yes, I think a branch to work through this is a great idea. There are several aspects of Feign internals that I'm not familiar enough with, so please be patient if some questions look "too basic".
|
Agree. I poke around it recently and it has mutable and immutable data in a single class. Moving all mutable data to a new I guess one side effect of that, would be us cutting a feign 11 release =) |
# Conflicts: # core/src/test/java/feign/AsyncFeignTest.java
I've now updated the commits to take into account some of the comments - in particular, AsyncResponseHandler is used internally by SynchronousMethodHandler for processing of the response. Next up will be expanding Async to support synchronous methods (as I said, this should be pretty easy) - for testing, the tests in FeignTest not dealing with Retry can be used, just going through AsyncFeign instead. Finally would be looking to take MethodInfo and pull it out into first class citizen and then having MethodMetadata reference it, or extend it as the case may be. I'm trying to get this pretty clean for all the non-retry before looking to do major rewriting. |
|
||
private final Builder builder; | ||
private Supplier<C> defaultContextSupplier = () -> null; | ||
private AsyncClient<C> client; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was also thinking in collapsing this functionality into feign core.
Feign would only include the AsyncClient
attribute with two setters:
client(AsyncClient<C> client)
: just useAsyncClient
leveraging the async behaviour for async methods or.join()
for sync methodsclient(Client client)
: regular client it gets warped around theAsyncClient
you made, running on local thread
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure - that could work - although there would need to be enforcement that then you're not using a context supplier (i.e., the client would always be called with an empty requestContext Optional)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I delivered AysncClient.Pseudo instead as the fully synchronous AsyncClient implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair point, that is an excellent delivery. We can expand on that later
BTW, is there any tasks you would like coding assistance with? Feel free to throw anything you need at me, I can make PRs to your branch to help out |
Originally, I had this added as a "clean" extension - it leveraged standard Feign internally, but didn't make any changes. Since we're unifying the sync path onto this, it does require changes, and so we should do some cleanup here which I'd appreciate assistance with. One thing is I wouldn't want to break any existing code using the Feign class right now. What I suggest is to do this in 2 steps:
|
You are right. I made a few minor changes to your code that should make travis CI happy The only think missing is some instructions on readme =) |
Thanks, I've merged those in. |
There are still some formatting problems on |
Can you please do that and I'll merge? Getting an error on my local where it can't find pomSortOrder.xml in the classpath. |
Could you please run maven with |
I seem to be able to work-around this (by running from the feign parent dir with -f to core/pom.xml) - but the change it made to this file seems odd, in that it moved the opening brace to its own indented line:
If that's what you want, then sure I can push that... |
Odd, but let's see if that fixes the build |
Looks like it did fix the build |
@kdavisk6 I'm happy to merge this as is and start working on feign 11 with
What do you think? |
I’ll take a look tonight, but I trust you. Let’s create a new 11.x branch
after this is merged and refactor there.
…On Mon, Feb 24, 2020 at 3:09 PM Marvin Froeder ***@***.***> wrote:
@kdavisk6 <https://github.com/kdavisk6> I'm happy to merge this as is and
start working on feign 11 with
- Refactor MethodMetadata to be thread-safe
- Refactor handleResponse to be independent functions
- Refactor Contract to choose the right InvocationHandler based on
return type.
What do you think?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1174?email_source=notifications&email_token=ABN4FOZEGAYS7DEI2EFBMA3REQSP5A5CNFSM4KWVBDMKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEMZLHGQ#issuecomment-590525338>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ABN4FO6HMRNNOUUFBHDNO4TREQSP5ANCNFSM4KWVBDMA>
.
|
Ok, once merged, I think we should release another 10.x |
I think the actual refactoring should look different... basically, the current method handler approach should be used. This depends then if you want to remove the current SynchronousMethodHandler or not. |
cool, I merged as is. Will work on releasing later today (social life permitting =] ) I will be sending a PR that added async capabilities to apache http 5 integration. @motinis lemme know if you would be interested in reviewing that one. |
Hi @velo, @motinis , is Async is available for Feign-Retry now. We are currently using OpenFeign3.0.0 and If we want to use the version of OpenFeign which support Async for Feign-Retry which version is that ? and what are all the changes between version 3.00 and 11 . Could you please help me with the info. |
Hi @nallabheem , feign 3 is very, very, very, veeery old. I can't start listing what would be the impact of such change. Most relevant thing that come to mind is that we dropped java 6 support. I would suggest you go ahead and try it. |
* issue 361 - async feign variant supporting CompleteableFutures * Update core/src/test/java/feign/AsyncFeignTest.java * remove duplication between synchronous and async cases * remove duplication between synchronous and async cases * support sync calls in async-feign * Add a synchronous "AsyncClient" * Added license header * Apply feign formatter * Add '@experimental' annotation to new async classes * "fix" formatting to try and appease build Co-authored-by: Marvin Froeder <velo@users.noreply.github.com>
* issue 361 - async feign variant supporting CompleteableFutures * Update core/src/test/java/feign/AsyncFeignTest.java * remove duplication between synchronous and async cases * remove duplication between synchronous and async cases * support sync calls in async-feign * Add a synchronous "AsyncClient" * Added license header * Apply feign formatter * Add '@experimental' annotation to new async classes * "fix" formatting to try and appease build Co-authored-by: Marvin Froeder <velo@users.noreply.github.com>
Adds CompleteableFuture support.
The targeted API must be an interface, where each method has a return type of CompleteableFuture for some type T which can be decoded by the configured decoder.
Context is explicit, providing support for sessions.
A default synchronous -> asynchronous client is provided, although the different modules should add their own implementations of AsyncClient.
Retry is not supported - in particular, there can be many different ways one could handle the threading (scheduling the try on an appropriate thread). resilience4j could be an option, in particular to provide a wrapper to AsyncClient.