grpc-spring-boot-starter icon indicating copy to clipboard operation
grpc-spring-boot-starter copied to clipboard

Spring native support?

Open linux-china opened this issue 4 years ago • 23 comments

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?

linux-china avatar Apr 07 '21 07:04 linux-china

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

jvmlet avatar Apr 07 '21 09:04 jvmlet

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

jvmlet avatar Apr 19 '21 10:04 jvmlet

@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 avatar Apr 20 '21 01:04 linux-china

@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.

linux-china avatar May 04 '21 23:05 linux-china

@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...

jvmlet avatar May 05 '21 07:05 jvmlet

@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.

linux-china avatar May 05 '21 22:05 linux-china

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?

jvmlet avatar May 05 '21 22:05 jvmlet

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.

linux-china avatar May 06 '21 00:05 linux-china

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...

jvmlet avatar May 06 '21 03:05 jvmlet

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

sdeleuze avatar May 27 '21 08:05 sdeleuze

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.

jvmlet avatar May 27 '21 12:05 jvmlet

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?

sdeleuze avatar May 27 '21 14:05 sdeleuze

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 avatar May 27 '21 14:05 jvmlet

@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 avatar Jun 01 '21 12:06 jvmlet

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

jvmlet avatar Jun 09 '21 14:06 jvmlet

@linux-china , does it work for you ?

jvmlet avatar Jun 10 '21 10:06 jvmlet

@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.

linux-china avatar Jun 10 '21 23:06 linux-china

@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.

ArtyomGabeev avatar Jun 16 '21 07:06 ArtyomGabeev

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 ;-)

jvmlet avatar Jun 16 '21 08:06 jvmlet

We have released it ;-)

sdeleuze avatar Jun 17 '21 08:06 sdeleuze

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

jvmlet avatar Jun 23 '21 06:06 jvmlet

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.

sdeleuze avatar Jul 27 '21 10:07 sdeleuze

@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

jvmlet avatar Aug 23 '21 13:08 jvmlet