LogNet / grpc-spring-boot-starter

Spring Boot starter module for gRPC framework.
Apache License 2.0
2.23k stars 433 forks source link

Spring native support? #199

Open linux-china opened 3 years ago

linux-china commented 3 years ago

gRPC was already supported by Spring native on https://github.com/spring-projects-experimental/spring-native/tree/main/samples/grpc . Andy plan to add Spring Native support smoothly?

jvmlet commented 3 years ago

https://github.com/spring-projects-experimental/spring-native/issues/710

jvmlet commented 3 years ago

@linux-china , do you experience any issue when running your app as spring-native ?

linux-china commented 3 years ago

@jvmlet I did some testing, and I found the problem is io.grpc:grpc-netty-shaded which is not supported by spring native, but grpc-netty is ok with spring native.

linux-china commented 3 years ago

@jvmlet with #203 support, I upgraded my demo and the link is https://github.com/linux-china/grpc-native-demo

Spring Native and grpc-spring-boot-starter almost work well, but a problem is Spel with Spring Native, and issue link is https://github.com/spring-projects-experimental/spring-native/issues/768

I just find one place to use Spel in grpc-spring-boot-starter, could you use @ConditionalOnProperty(name = "grpc.inProcessServerName", havingValue = "") to replace @ConditionalOnExpression() ?

    @Bean
    @ConditionalOnExpression("#{environment.getProperty('grpc.inProcessServerName','')!=''}")
    public GRpcServerRunner grpcInprocessServerRunner(@Qualifier("grpcInternalConfigurator") Consumer<ServerBuilder<?>> configurator) {

        return new GRpcServerRunner(configurator, InProcessServerBuilder.forName(grpcServerProperties.getInProcessServerName()));
    }

removeSpelSupport can make compiling faster and native binary file smaller.

jvmlet commented 3 years ago

@linux-china , can you please try with 4.4.8-SNAPSHOT ?

BTW, replacing @ConditionalOnExpression("#{environment.getProperty('grpc.inProcessServerName','')!=''}") with @ConditionalOnProperty(name = "grpc.inProcessServerName", havingValue = "") will brake backward compatibility for users who choose to call the in-process-server as literal false ;-) Anyway, users also ask to support @PreAuthorize and @PostAuthorize spring security annotations (#175), need to be careful with these as well...

linux-china commented 3 years ago

@jvmlet yes, it works with 4.4.8-SNAPSHOT. 👍

Now I use native-image-agent to get reflect and resource config for grpc-spring-boot-starter, if possible, could you add src/main/resources/META-INF/native-image/io.github.lognet/grpc-spring-boot-starter/reflect-config.json and src/main/resources/META-INF/native-image/io.github.lognet/grpc-spring-boot-starter/resource-config.json files for native-image support, you can refer json files on https://github.com/linux-china/grpc-native-demo/tree/master/src/main/resources/META-INF/native-image

I'm not sure that it's necessary to add reflect-config.json for grpc-client-spring-boot-starter module.

jvmlet commented 3 years ago

Sure, I'll add them. But I wonder how all original spring boot starters, like web and jpa, work with native app without these files? This is what I asked in https://github.com/spring-projects-experimental/spring-native/issues/710 but didn't get the clear answer.... Do you have a clue?

linux-china commented 3 years ago

From https://www.graalvm.org/reference-manual/native-image/BuildConfiguration/ you don't need to import org.springframework.experimental:spring-native and add native-image support, and it's good choice to add json config file because it works well with normal Java Apps and Spring native apps. grpc-spring-boot-starter is very not complicated to work with native image, and my suggestion is to add configuration files.

jvmlet commented 3 years ago

Yes, I will add it, just want to know how standard spring boot starters support spring native... They don't have such files in META-INF directory...

sdeleuze commented 3 years ago

Yes probably better to just provide JSON configuration, I will update the guidelines in Spring Native to make that more clear.

jvmlet commented 3 years ago

Thanks @sdeleuze, Can you please point me to json files in standard jpa/web spring boot starters? Or any other sources in these repositories that make jpa and web starters supported by spring native.

sdeleuze commented 3 years ago

I can't because those files are generated dynamically by Spring Native. JPA and web starters are supported out of the box by Spring Native. Do no need some sample reflect-config.json or something else?

jvmlet commented 3 years ago

So as far as I understand, they are generated and packaged in distributable jar, can you please confirm it @sdeleuze? I also don't see spring native dependencies in these projects (jpa/web starters)....

jvmlet commented 3 years ago

@linux-china , I've created branch here The build fails with

Execution failed for task ':grpc-spring-boot-starter-native-demo:generateAot'.
> Unable to find class file for com/ecwid/consul/v1/agent/model/NewService$Check

Can you please have a look ?

jvmlet commented 3 years ago

@linux-china , please try with io.github.lognet:grpc-spring-boot-starter:4.5.4-SNAPSHOT , I've also added demo here

jvmlet commented 3 years ago

@linux-china , does it work for you ?

linux-china commented 3 years ago

@linux-china , does it work for you ?

It works for me. But I should still add some reflect config for gRPC, and reflect config example is

https://github.com/linux-china/grpc-native-demo/blob/master/src/main/resources/META-INF/native-image/reflect-config.json

If I remove reflect config for gRPC and I will get following exception:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'grpcServerRunner' defined in class path resource [org/lognet/springboot/grpc/autoconfigure/GRpcAutoConfiguration.class]: Unsatisfied dependency expressed through method 'grpcServerRunner' parameter 1; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'io.grpc.ServerBuilder<?>' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:800) ~[na:na]
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:541) ~[na:na]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1334) ~[na:na]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1177) ~[na:na]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:564) ~[na:na]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524) ~[na:na]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[na:na]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[na:na]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[na:na]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[na:na]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:944) ~[na:na]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918) ~[na:na]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[na:na]
    at org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext.refresh(ReactiveWebServerApplicationContext.java:63) ~[na:na]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:782) ~[grpc-native-demo:2.4.5]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:774) ~[grpc-native-demo:2.4.5]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439) ~[grpc-native-demo:2.4.5]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:339) ~[grpc-native-demo:2.4.5]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1340) ~[grpc-native-demo:2.4.5]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1329) ~[grpc-native-demo:2.4.5]
    at com.example.SpringBootApp.main(SpringBootApp.java:9) ~[grpc-native-demo:na]
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'io.grpc.ServerBuilder<?>' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1790) ~[na:na]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1346) ~[na:na]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1300) ~[na:na]
    at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:887) ~[na:na]
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791) ~[na:na]
    ... 20 common frames omitted

Maybe reflect config for gRPC should be included in reflection-config.json.

ArtyomGabeev commented 3 years ago

@jvmlet regarding spring boot way of doing native things: They implements NativeConfiguration and add NativeHint's for AoT. After that they use service locator to find all classes implemented this interface, e.g.: https://github.com/spring-projects-experimental/spring-native/blob/main/spring-native-configuration/src/main/java/LoggingInitHints.java

As for a 3rd party components, according to their documentation, we need to put NativeHints on library configuration classes. If Spring AoT decides that this configuration is active, these hints will be applied.

jvmlet commented 3 years ago

Thanks, @ArtyomGabeev ,I'm generating the reflect-config.yml by running test app in findepi/graalvm:java11-native container (with native-image-agent enabled) and then filtering out all starter-own classes to be included in final reflection-config.json which is bundled in starter's jar (4.5.4-SNAPSHOT already has it) The problem I'm experiencing now is that grpc related classes (io.grpc.netty.NettyServerBuilder)are not generated by AOT if grpc dependencies come from maven-hosted jar, but if switched to project dependencies, everything works as expected (see demo here). Waiting for spring-aot-gradle-plugin:0.10.0 release to complain about it to @sdeleuze ;-)

sdeleuze commented 3 years ago

We have released it ;-)

jvmlet commented 3 years ago

Thanks @sdeleuze, complaining ;-) As I already said, if I use project dependency in grpc-native demo application, the class io.grpc.netty.NettyServerBuilder appears in generated reflect-config.json, but if I switch to maven-hosted dependency jar, this class is missing from generated reflect-config.json.

I'm attaching the generated resources zip for both cases for your reference

sdeleuze commented 3 years ago

That could be something we need to fine tune in the Spring AOT Gradle plugin, if it still happens with 0.10.1 please create a related issue on https://github.com/spring-projects-experimental/spring-native with, if possible, a minimal reproducer for this "project versus Maven repo" dependency native configuration generation issue.

jvmlet commented 3 years ago

@sdeleuze , unfortunately even with 0.10.3 the issue is still reproducible. I've uploaded zip with generated aot resources for both cases : project vs maven dependency. io.grpc.netty.NettyServerBuilder is missing from reflect-config.json when referenced with maven hosted jar. To reproduce - switch to io.github.lognet:grpc-spring-boot-starter:4.5.4 dependency here , enable test task and run ./gradlew :grpc-spring-boot-starter-native-demo:test Back reference