[go: nahoru, domu]

Skip to content
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

Add support for flagging an interface to declaratively expose it as an rsocket remote service #32827

Open
jeacott1 opened this issue May 15, 2024 · 13 comments
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) status: feedback-provided Feedback has been provided status: waiting-for-triage An issue we've not yet triaged or decided on

Comments

@jeacott1
Copy link

I really miss declarative service exporters.
I'm happy to see the declarative rsocket client option available, but the serverside still requires a ton of boilerplate. Creating controllers to mirror service interfaces is tedious.

To this end I'd like to see an option to trivially mark an interface and have it autogen the proxy etc required to expose it as an rsocket service. This should work with webflux and mvc worlds.

thoughts?

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label May 15, 2024
@snicoll snicoll changed the title feature request: An option to mark an interface to declaratively expose it as an rsocket remote service. Add support for flagging an interface to declaratively expose it as an rsocket remote service May 20, 2024
@snicoll snicoll added the in: web Issues in web modules (web, webmvc, webflux, websocket) label May 20, 2024
@rstoyanchev
Copy link
Contributor

It's not clear what you mean. On the one hand, you mention service exporters which reference the implementation to use. On the other, autogen and proxy which is to create a client, but we already have that with RSocketServiceProxyFactory.

@rstoyanchev rstoyanchev added the status: waiting-for-feedback We need additional information before we can continue label Jun 13, 2024
@jeacott1
Copy link
Author

@rstoyanchev sorry for the confusion.
afaik the RSocketServiceProxyFactory is for client creation which while helpful is not what I am looking for.
I want to restore a facility that works like the old service exporters to create rsocket endpoints via trivial interface definitions, and to be able to configure the serialisation. eg

 RSocketServiceServiceExporter exporter = new RSocketServiceServiceExporter();
     exporter.setService( new MyServiceImpl() );
     exporter.setServiceInterface( MyServiceInterface.class );
     exporter.setSerializer(new FurySerializer())

having to write all the controller boilerplate for backend services is ridiculous vs the above snippet.

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Jun 14, 2024
@rstoyanchev
Copy link
Contributor
rstoyanchev commented Jul 3, 2024

I understand now. Can you add @MessageMapping annotations on MyServiceInterface? The declarative service exporters were created in a very different time, and were a lot more viable as an option before Java 5 and annotations.

@jeacott1
Copy link
Author
jeacott1 commented Jul 4, 2024

@rstoyanchev imo the declarative service exporters were completely viable post java annotations, they worked!

@MessageMapping is method level only, so far from convenient:

@Target({ElementType.TYPE, ElementType.METHOD})

Also, afaik its stomp only. I like stomp well enough, but it's not rsocket.

It's also not really what I want. I have literally hundreds of interfaces I want to expose as services. The old apache cxf libraries do this for soap without pain, and without requiring modification, or annotation of those interfaces. The old declarative service exporters also could have supported my case with minimal effort.
I really think that spring should endeavour to replace the lost capability instead of having folks go back to inventing their own.
Remote invocation/listen is a pretty universal requirement, but nobody wants to create controllers for this stuff, they're just noise.

@rstoyanchev
Copy link
Contributor
rstoyanchev commented Jul 4, 2024

@jeacott1 have you looked at the documentation? @MessageMapping is what is use for RSocket https://docs.spring.io/spring-framework/reference/rsocket.html#rsocket-annot-messagemapping, and that's both method and type level, with the former inheriting and extending the latter as usual.

Annotations declare routes, which allows us to map incoming requests to specific methods. How would this be expressed and done otherwise? I suppose you're asking for us to also create a client that inserts metadata in the RSocket request that indicates the service/method to call on the server side, and of course this would only work if using that client we would provide.

@jeacott1
Copy link
Author
jeacott1 commented Jul 5, 2024

@rstoyanchev yes I have, and I think you are missing the point.
@MessageMapping defines a route and is intended for use on controllers with an event-ish nature. I certainly dont want to create controllers with thousands of routes to facilitate rpc - thats boilerplate that buys me nothing.
Its just not suitable for use to expose hundreds of interfaces each with 50+ methods as the basis for rpc.
I'd argue that having to create controllers at all is a waste of time generally when you already have well defined interfaces.

In order to practically use @MessageMapping I'd have to create myself a controller endpoint, and then manufacture myself (or leverage something) an RPC protocol, build myself a proxy that can map the incoming messages to the appropriate interface methods, and then build myself a client lib generator so that clients could use the rpc client. Spring remoting, particularly with hessian previously gave me all of this for the cost of a few lines of code! its simply not possible now without resurrecting and altering old spring code or DIY from scratch.

What I'd like to be able to do is in the most simple way possible define service endpoints without touching existing code.
That could look like an annotation I'd have to add onto a copy of an interface that also declared its intended delegate impl, which could look a bit like spring data repository declarations - but that seems very not IOC. The example RSocketServiceServiceExporter I offered earlier seems flexible, and relatively simple.
Spring needs something to fill this space now.

@rstoyanchev
Copy link
Contributor
rstoyanchev commented Jul 15, 2024

I'd have to create myself a controller endpoint, and then manufacture myself (or leverage something) an RPC protocol, build myself a proxy that can map the incoming messages to the appropriate interface methods, and then build myself a client lib generator so that clients could use the rpc client.

Right, for this to work, both client and server have to agree on the routing metadata used. That means not only a service exporter but also a declarative client proxy. You mentioned the declarative rsocket client option that's now available, but that's not the same.

What is available currently with the RSocket Interface expects an @RSocketExchange annotation to define the route, but with that in place it is supported for both client and server use. So it's not just a declarative client proxy. You can also use it for server handling, and that doesn't have to be a controller. Just an implementation of the interface to provide server handling.

There are some customizations possible. RSocketMessageHandler can be configured with a handler Predicate (e.g. no need for @Controller), and its getCondition method is protected (e.g. determine route without @RSocketExchange). The RSocketServiceProxyFactory is a less open right now in how it determines what methods to proxy and what route to use, but we could make that more flexible too.

In short, I think what we have with the RSocket service interface support is already quite close to what you are asking for. It just requires the @RSocketExchange annotation to be on interface methods. In the short term, we could make some changes to make it possible to customize this so that annotations are not even needed. In addition, we could explore first class support for a mode where you only need an interface, and routes are determined using default conventions.

@jeacott1
Copy link
Author

@rstoyanchev it sounds like you have something in mind here. I'm not entirely sure its what I'm looking for, but what you suggest sounds ok. " In addition, we could explore first class support for a mode where you only need an interface, and routes are determined using default conventions."
This sounds ideal. I would hope I would not have to add @RSocketExchange on every method on an interface nor declare routes. I'd want to add it at the interface level, and preferably not have to add it at all for situations where I'm dealing with generated code for example. For me, creating a mirror api of my target interfaces with annotations and delegating to my target whilst being a bit verbose is probably a better pattern for the cases I'm thinking of anyway. Still need a client lib generator (or something) to match and pluggable serialisation and I think we're home. A client via proxy is ok but it's not going to work out of the box for other language clients (javascript, python etc).
Thank you.

@MrWangGang
Copy link

I have already implemented an RPC framework for RSocket in my framework. You can check out my code repository.

I am starting a business and this is my open-source framework. The README.md is not up-to-date, but I will update it once the project is finished, as my team needs the documentation.
"In this framework, the focus is on user authorization and authentication, and propagating authorization information across the microservice chain. To retrieve user information for any service, it’s as simple as:

securityPrincipalHolder.fetchPrincipal2Object(UserPO.class).flatMap(seller -> { ... });

Moreover, this information is not stored in Redis cache and does not require network retrieval every time. Instead, it is included in the socket setup when establishing the connection through the RPC framework and then extracted into the contentView. The RPC framework utilizes a service registry such as Nacos or Zookeeper, or even direct TCP connections, for service calls."

https://github.com/MrWangGang/lambda-framework

Service Provider:


@RSocketRpcDiscorvery("ace-microservices-social")
public interface SocialRelationApi {
    @RSocketRpcType
    public Mono<Void> establishCompadre(EstablishCompadreDTO dto);
}


@RSocketRpcApi
public class RelationApi implements SocialRelationApi {

    @Resource
    private CompadreFunction compadreFunction;

    public Mono<Void> establishCompadre(EstablishCompadreDTO dto) {
        return compadreFunction.establishCompadre(dto);
    }
}


Service Consumer

    @RSocketRpc
    private SocialRelationApi socialRelationApi;
    
              EstablishCompadreDTO establishCompadreDTO = EstablishCompadreDTO.builder()
                             .compadreId($trade.getBuyerId())
                             .origin(ENUM_RELATION_ORIGIN_TRADE_SKILL)
                             .build();
            return socialRelationApi.establishCompadre(establishCompadreDTO);

@MrWangGang

This comment was marked as off-topic.

@MrWangGang

This comment was marked as off-topic.

@rstoyanchev
Copy link
Contributor

@MrWangGang I think it's useful to have a reference to such framework. However, this issue is not the right place for further details on how it works.

@rstoyanchev
Copy link
Contributor
rstoyanchev commented Jul 25, 2024

I would hope I would not have to add @RSocketExchange on every method on an interface nor declare routes. I'd want to add it at the interface level, and preferably not have to add it at all for situations where I'm dealing with generated code for example.

Firstly, I'm pointing out that the @RSocketExchange is for both client and server use. Secondly that there is almost enough flexibility for you to be able to customize it so that all you need is an interface, e.g. transparently plugging in a route based on the interface name and method, and applying the route for mapping on the server side. For 6.2, I can make sure that flexibility is in place.

Still need a client lib generator (or something) to match and pluggable serialisation and I think we're home. A client via proxy is ok but it's not going to work out of the box for other language clients (javascript, python etc).

Yes, this is what I was also alluding to earlier. We can only help with Java peer interactions, not with other languages. There is no codegen involved, and this is not different from declarative service exporters.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) status: feedback-provided Feedback has been provided status: waiting-for-triage An issue we've not yet triaged or decided on
Projects
None yet
Development

No branches or pull requests

5 participants