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

Make the Spring and gRPC integration great #87

Closed
DanielLiu1123 opened this issue Dec 28, 2024 · 7 comments
Closed

Make the Spring and gRPC integration great #87

DanielLiu1123 opened this issue Dec 28, 2024 · 7 comments

Comments

@DanielLiu1123
Copy link

I am the author of the grpc-starter project. Here’s some background on grpc-starter: Java, Spring and gRPC.

I’m really excited to see Spring start integrating gRPC. I believe this is another big milestone for the Spring ecosystem.

Based on my experience developing grpc-starter and using Spring and gRPC extensively, I’d like to offer two suggestions:

  • Separate the client and server since their dependencies are different.
  • Provide client auto-configuration. From a user’s perspective, this would be a very valuable feature.

please let me know if there’s anything I can help!

@dsyer
Copy link
Collaborator

dsyer commented Jan 3, 2025

Thanks for reaching out. It really is helpful, and much appreciated.

Some questions and observations, about your suggestions...

What makes the dependencies different for client and server? Maybe I missed something, but that was for me a problem with the existing ecosystem projects - I don't need to decide if I'm a server or a client until I need that feature (the dependencies are identical). An exception is the servlet support, which I think is awesome, and would like to make propose has its own starter - you know if you are a server if you ask for a servlet.

Client autoconfiguration is already provided, in the sense that we provide an injectable GrpcChannelFactory. It's a bit like a RestTemplateBuilder or a WebClient.Builder as provided by Spring Boot for the vanilla HTTP clients. I don't think we need anything too different from that. What was the feature you had in mind exactly?

@DanielLiu1123
Copy link
Author

DanielLiu1123 commented Jan 5, 2025

About Separate Client and Server

For the client, the required dependencies are:

api("io.grpc:grpc-core")
api("io.grpc:grpc-inprocess")
api("io.grpc:grpc-protobuf")
api("io.grpc:grpc-stub")

For the server, the required dependencies are:

api("io.grpc:grpc-core")
api("io.grpc:grpc-inprocess")
api("io.grpc:grpc-protobuf")
api("io.grpc:grpc-services")

Some projects might only use the gRPC client and not provide any gRPC services themselves, so they don’t need the grpc-services dependencies.
Some projects might only use the gRPC server, only providing gRPC services and not using the gRPC client, so they don’t need the grpc-stub dependency.

Therefore, it makes sense to offer three starters:

spring-grpc-spring-boot-starter # includes both client and server
spring-grpc-client-spring-boot-starter # only client
spring-grpc-server-spring-boot-starter # only server

About Client Auto-Configuration

According to the documentation, I believe that adding a gRPC client bean is still done through manual registration rather than auto registration.

@Bean
SimpleGrpc.SimpleBlockingStub stub(GrpcChannelFactory channels) {
    return SimpleGrpc.newBlockingStub(channels.createChannel("local").build());
}

This is somewhat similar to the Spring HTTP interface:

@Bean
PostApi postApi(RestClient.Builder builder) {
    HttpServiceProxyFactory factory = HttpServiceProxyFactory
            .builderFor(RestClientAdapter.create(builder.build()))
            .build();
    return factory.createClient(PostApi.class);
}

Users might not be satisfied with this configuration method. You can refer to this discussion: Spring Boot Issue #31337. I believe the same problem will occur with gRPC clients. Users are looking for a simpler way to use clients without manually registering them, allowing auto registration through configuration.

The grpc-starter achieves auto registration in the following way (I believe this configuration is simple and clear):

grpc:
  client:
    base-packages: [ com.example ]
    channels:
      - authority: user-service:9090
        stubs: # All clients under this configuration share this channel
          - com.example.user.v1.** # Ant-style patterns
          - com.example.user.v2.**
      - authority: order-service:9090
        stubs:
          - com.example.order.**

Benefits of this approach:

  • For Users: No longer need to manually create beans. As spring-grpc matures, many will migrate from other starters. Providing configuration reduces the migration burden compared to manual bean creation.
  • For Developers: No need to provide abstractions like GrpcChannelFactory. gRPC already offers top-notch APIs, we might need more integration instead of abstraction.

@dsyer
Copy link
Collaborator

dsyer commented Jan 5, 2025

I see what you mean about the client and server, but only up to a point, because actually the only difference which is mandatory is the grpc-stub dependency. We could have the autoconfiguration arranged as it already is and make the grpc-stub optional, then add it back as a Boot-style starter, or we could just ask users with clients to add grpc-stub. I was kind of expecting we'd end up with a few more starters anyway, so that seems like a good direction. I'd also like one for the servlet support. Note however that most of the time Spring Boot does not bother to add a starter for just one additional dependency - we could argue this is an exception but we don't want to go crazy and add a starter per feature.

Autoconfiguration of stubs doesn't seem so easy to accept. Your argument about existing users of ecosystem libraries is true, and we did think about it for a while. The community projects had to do some weird things to get it to work though, and it doesn't fit with any existing Spring Boot autoconfiguration conventions. The parallel with HTTP clients is obvious, so I suppose the closest parallel we have for creating client beans is the HTTP Interface feature. I'm not sure I see how that would work with gRPC stubs, but it might be worth thinking about. Another possible parallel is the way that spring-amqp deals with queues and exchanges (I believe those are mostly hidden from users, but maybe worth a look).

@DanielLiu1123
Copy link
Author

I see what you mean about the client and server, but only up to a point, because actually the only difference which is mandatory is the grpc-stub dependency.

For applications that only require a gRPC client, the grpc-services dependency is also unnecessary.

Note however that most of the time Spring Boot does not bother to add a starter for just one additional dependency

I agree with this, but in this case, the reason I split into two starters is that I think the client and server are more like two different use cases, regardless of the number of dependencies.

If we are going to have multi starters, the following are some of my thoughts on how to build modular starters.

There are usually two approaches.

  1. Provide all dependencies in one autoconfigure module but make them optional, then have the starter module include these dependencies, similar to how Spring Boot does it.
  2. Split into multi separate autoconfigure modules.

The first approach has an issue: it might lead to “configuration hell.” Personally, I feel Spring Boot is already trending that way—for example, I can see spring.kafka.xxx configurations even though my project only depends on spring-boot-starter-jdbc. This doesn’t feel right.

Therefore, I personally prefer the second approach, which is why the grpc-starter project has two autoconfigure modules.

@DanielLiu1123
Copy link
Author

The parallel with HTTP clients is obvious, so I suppose the closest parallel we have for creating client beans is the HTTP Interface feature. I'm not sure I see how that would work with gRPC stubs, but it might be worth thinking about.

About autoconfiguration of stubs, we can look at how the HTTP interface solves this problem. The same pattern should work for gRPC stubs.

@dsyer
Copy link
Collaborator

dsyer commented Jan 6, 2025

the grpc-services dependency is also unnecessary.

That's why it is already marked as "optional". It's also optional for servers, which is why what I said is still true: the only mandatory dependency that is different for clients and servers is grpc-stub.

Therefore, I personally prefer the second approach

That's a good point and I actually agree, but Boot isn't going to change, so it's better for new projects to follow their lead. I'm afraid there's no way round that.

Thanks for the link to the HTTP interface registry thing. Definitely something to keep our eyes on, especially when Boot starts to adopt it. Would you mind raising a separate, more focused issue about that (and then we can close this one)?

@DanielLiu1123
Copy link
Author

I have split this issue into two more focused issues: #90, #91.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants