当前位置:   article > 正文

SpringBoot3 GraalVM 原生镜像打包 搭建云原生环境

springboot3 graalvm

java发布到如今,已经过去几十年,如今微服务、云原生逐渐成为了主流,java原本的很多优势不再重要,而启动慢,耗内存等的缺点也越来越被放大.

java在新发布的很多相关技术中也做出了很多改变

其中SpringBoot3结合GraalVM,可以直接将java项目打包成原生可执行文件,提升运行速度并大大节省服务资源,

但是GraalVM通过静态分析提前编译来为Java应用程序构建高度优化的本机可执行文件,这就需要在编译时就知道所有的程序类型,而java中的反射、动态代理等功能,在编译时不确定具体的类型,甚至很多代码是在运行时才生成的,所以在使用GraalVm构建native image前需要通过配置列出反射可见的所有类型。反射的配置是一个json格式的文件。为了简化这种反射的配置,GraalVm提供agentlib工具,来辅助生成这个配置文件

而这也就意味着,有大量使用反射的库,在GraalVM编译后的文件使用时,将会异常,本人在后面的服务中,为了解决这个问题,写了一个工具类,利用GraalVM提供的工具类,将项目中的java代码通通反射了一遍,向GraalVM配置列出项目中可能需要反射的所有类型,虽然感觉这样不太合适,但是本就是新技术,先把服务跑起来

环境准备:

GraalVM 是 Oracle 发布的虚拟机,可以像正常的运行java程序,也可以让js,python,C等不同语言互相调用,集成在一起

后续的代码是基于 graalvm-jdk-20_windows-x64_bin.zip 运行的

下载GrralVM

Download GraalVM

按照以前JDK的方式一样,配置好java环境变量

用GRAALVM_HOME替换掉JAVA_HOME,或者两个一起配置,避免一些其奇奇怪怪的错误

windows下需要安装visualstudio:

下载 Visual Studio Tools - 免费安装 Windows、Mac、Linux

运行,选择C++桌面开发安装

语言包需要修改为英语,否则打包会异常

Linux Ubuntu下,需要安装

gcc

 apt install gcc

后续将java项目编译成可执行文件时,可能会报一些异常,例如

/usr/bin/ld: cannot find -lxxx

再补充安装对应的文件

apt-get install libxxx-dev

例如

异常 /usr/bin/ld: cannot find -lz

安装 apt-get install libz-dev

下面按照最基本到相对复杂完整的三个服务,对技术进行演示


代码演示:

编译后的文件太大,没有和服务一起上传到代码库,

GitHub - cjs199/springboot3-demo2

一个最简单的web服务

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <parent>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter-parent</artifactId>
  8. <version>3.1.1</version>
  9. <relativePath/> <!-- lookup parent from repository -->
  10. </parent>
  11. <groupId>com.example</groupId>
  12. <artifactId>demo2</artifactId>
  13. <version>0.0.1-SNAPSHOT</version>
  14. <name>demo2</name>
  15. <description>Demo project for Spring Boot</description>
  16. <properties>
  17. <java.version>20</java.version>
  18. </properties>
  19. <dependencies>
  20. <dependency>
  21. <groupId>org.springframework.boot</groupId>
  22. <artifactId>spring-boot-starter-web</artifactId>
  23. </dependency>
  24. </dependencies>
  25. <build>
  26. <plugins>
  27. <plugin>
  28. <groupId>org.graalvm.buildtools</groupId>
  29. <artifactId>native-maven-plugin</artifactId>
  30. </plugin>
  31. <plugin>
  32. <groupId>org.springframework.boot</groupId>
  33. <artifactId>spring-boot-maven-plugin</artifactId>
  34. </plugin>
  35. </plugins>
  36. </build>
  37. </project>
  1. package com.example.demo;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. @SpringBootApplication(proxyBeanMethods = false)
  5. public class Demo2Application {
  6. public static void main(String[] args) {
  7. SpringApplication.run(Demo2Application.class, args);
  8. }
  9. }
  1. package com.example.demo.control;
  2. import java.util.Random;
  3. import org.springframework.web.bind.annotation.GetMapping;
  4. import org.springframework.web.bind.annotation.RestController;
  5. @RestController
  6. public class TestController {
  7. @GetMapping("test1")
  8. public void test1(int num) {
  9. String str = "";
  10. long begin = System.currentTimeMillis();
  11. Random random = new Random();
  12. for (int i = 0; i < num; i++) {
  13. str += random.nextInt();
  14. }
  15. long end = System.currentTimeMillis();
  16. System.err.println("总共耗时" + (end - begin) + "ms");
  17. System.err.println(str.length());
  18. }
  19. }

在这里我们可以看到使用了注解,那么Spring是如何解决GraalVM 反射的问题呢?

spring是通过spring-aot将代码在编译前生成的

在springboot3下,服务依旧可以直接运行main方法启动

当然最重要的是如何打包成指定平台的运行程序呢?运行下面的maven打包命令

mvn -Pnative -DskipTests clean native:compile

在eclipse下,运行打包

最终程序生成了如下一个demo2.exe文件

访问测试

http://localhost:8080/test1?num=10000

包含AOP的服务演示

服务代码库

GitHub - cjs199/springboot3-demo3

在上述代码的基础上增加一个AOP注解

  1. package com.example.demo.annotation;
  2. import java.lang.annotation.ElementType;
  3. import java.lang.annotation.Retention;
  4. import java.lang.annotation.RetentionPolicy;
  5. import java.lang.annotation.Target;
  6. @Target(ElementType.METHOD)
  7. @Retention(RetentionPolicy.RUNTIME)
  8. public @interface TestAnnotation {
  9. }
  1. package com.example.demo.annotation.aspect;
  2. import org.aspectj.lang.ProceedingJoinPoint;
  3. import org.aspectj.lang.annotation.Around;
  4. import org.aspectj.lang.annotation.Aspect;
  5. import org.springframework.stereotype.Component;
  6. import com.example.demo.annotation.TestAnnotation;
  7. /**
  8. * 处理RedisLock注解的逻辑</br>
  9. *
  10. * @author Robert 2020-8-17 11:34:10
  11. */
  12. @Aspect
  13. @Component
  14. public class RedisLockAspect {
  15. /**
  16. * 环绕加redis锁
  17. *
  18. * @param pjp
  19. * @param redisLock 切点
  20. * @return
  21. * @throws Throwable
  22. */
  23. @Around(value = "@annotation(testAnnotation)")
  24. public Object around(ProceedingJoinPoint pjp, TestAnnotation testAnnotation) throws Throwable {
  25. System.err.println("执行TestAnnotation注解");
  26. return pjp.proceed();
  27. }
  28. }
  1. @TestAnnotation
  2. @GetMapping("test1")
  3. public void test1(int num) {
  4. String str = "";
  5. long begin = System.currentTimeMillis();
  6. Random random = new Random();
  7. for (int i = 0; i < num; i++) {
  8. str += random.nextInt();
  9. }
  10. long end = System.currentTimeMillis();
  11. System.err.println("总共耗时" + (end - begin) + "ms");
  12. System.err.println(str.length());
  13. }

直接IDE运行 main服务访问,控制台会执行AOP的代码,在执行test1的代码 

http://localhost:8080/test1?num=1

在不处理反射相关问题时,直接打包,执行GraalVm的原生代码,再访问,就会报如下异常

http://localhost:8080/test1?num=1

  1. PS D:\eclipse_file\new_ws\springboot3-demo3\target> .\springboot3-demo3.exe
  2. . ____ _ __ _ _
  3. /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
  4. ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
  5. \\/ ___)| |_)| | | | | || (_| | ) ) ) )
  6. ' |____| .__|_| |_|_| |_\__, | / / / /
  7. =========|_|==============|___/=/_/_/_/
  8. :: Spring Boot :: (v3.1.1)
  9. 2023-07-19T18:03:31.975+08:00 ERROR 16212 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler dispatch failed: org.graalvm.nativeimage.MissingReflectionRegistrationError: The program tried to reflectively invoke method public java.lang.Object com.example.demo.annotation.aspect.TestAnnotationAspect.around(org.aspectj.lang.ProceedingJoinPoint,com.example.demo.annotation.TestAnnotation) throws java.lang.Throwable without it being registered for runtime reflection. Add it to the reflection metadata to solve this problem. See https://www.graalvm.org/latest/reference-manual/native-image/metadata/#reflection for help.] with root cause
  10. ....
  11. org.graalvm.nativeimage.MissingReflectionRegistrationError: The program tried to reflectively invoke method public java.lang.Object com.example.demo.annotation.aspect.TestAnnotationAspect.around(org.aspectj.lang.ProceedingJoinPoint,com.example.demo.annotation.TestAnnotation) throws java.lang.Throwable without it being registered for runtime reflection. Add it to the reflection metadata to solve this problem. See https://www.graalvm.org/latest/reference-manual/native-image/metadata/#reflection for help.
  12. ....
  13. at com.example.demo.control.TestController$$SpringCGLIB$$0.test1(<generated>) ~[springboot3-demo3.exe:na]
  14. at java.base@20.0.1/java.lang.reflect.Method.invoke(Method.java:578) ~[springboot3-demo3.exe:na]

怎么解决这个问题?需要利用GraalVm提供的工具,将服务中使用到的反射配置好,执行如下的命令,绿色的部分,执行时需要替换为自己服务真实的名称和路径

java -agentlib:native-image-agent=config-output-dir=D:\eclipse_file\new_ws\springboot3-demo3\src\main\resources\META-INF\native-image  -jar  .\springboot3-demo3-0.0.1-SNAPSHOT.jar

但是上述命令还有一个坑,他能够生成的反射,是要代码被调用执行以后才能够生成使用了的类的反射配置,而其他没有被执行的代码对应的配置类,就不会执行,为了解决这个问题,我写了另一个类,扫描自己服务包下所有的类,将所有类通通反射一遍

  1. package com.example.demo.config;
  2. import java.lang.reflect.Constructor;
  3. import java.lang.reflect.Field;
  4. import java.lang.reflect.Method;
  5. import java.util.Arrays;
  6. import java.util.List;
  7. import java.util.Set;
  8. import org.springframework.beans.factory.annotation.Autowired;
  9. import org.springframework.beans.factory.annotation.Value;
  10. import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
  11. import org.springframework.stereotype.Component;
  12. import cn.hutool.core.util.ClassUtil;
  13. import jakarta.annotation.PostConstruct;
  14. /**
  15. * 反射将所有项目类扫描加入到服务, 大力出奇迹的操作,感觉不太合适,不过先让服务跑起来
  16. *
  17. * @author PC
  18. *
  19. */
  20. @Component
  21. public class ClassReflectConfig {
  22. static boolean begin = true;
  23. @Value("${scanclass}")
  24. private Boolean scanclass;
  25. @Autowired
  26. private ThreadPoolTaskExecutor executorService;
  27. @PostConstruct
  28. public void init() {
  29. if (scanclass) {
  30. System.err.println("配置文件下 scanclass 开启了生成反射类");
  31. } else {
  32. System.err.println("配置文件下 scanclass 关闭了生成反射类");
  33. }
  34. synchronized (ClassReflectConfig.class) {
  35. if (begin && scanclass) {
  36. begin = false;
  37. executorService.submit(() -> {
  38. // {
  39. // // 先抓取上一次的文件,生成
  40. // try {
  41. // BufferedReader utf8Reader = ResourceUtil
  42. // .getUtf8Reader("classpath:/META-INF/native-image/reflect-config.json");
  43. // String res = utf8Reader.lines().collect(Collectors.joining());
  44. // List object = ProJsonUtil.toObject(res, List.class);
  45. // for (Object object2 : object) {
  46. // try {
  47. // Map object22 = (Map) object2;
  48. // handlerClass(Class.forName(ProMapUtil.getStr(object22, "name")));
  49. // } catch (Exception e) {
  50. // }
  51. // }
  52. // } catch (Exception e) {
  53. // log.error("生成文件异常", e);
  54. // }
  55. // }
  56. {
  57. // 扫描系统第二级开始的包
  58. String packageName = ClassReflectConfig.class.getPackageName();
  59. String proPackageName = packageName.substring(0,
  60. packageName.indexOf(".", packageName.indexOf(".") + 1));
  61. // 可以在这个地方,添加除了服务以外其他的包,将会加入反射,以供graalvm生成配置
  62. List<String> asList = Arrays.asList(proPackageName);
  63. for (String spn : asList) {
  64. try {
  65. Set<Class<?>> doScan = ClassUtil.scanPackage(spn);
  66. for (Class clazz : doScan) {
  67. handlerClass(clazz);
  68. }
  69. } catch (Throwable e) {
  70. e.printStackTrace();
  71. }
  72. }
  73. }
  74. // handlerClass(RedisMessageListenerContainer.class);
  75. });
  76. }
  77. }
  78. }
  79. private void handlerClass(Class clazz) {
  80. if (clazz.equals(ClassReflectConfig.class)) {
  81. // 跳过自己,避免形成循环
  82. return;
  83. }
  84. executorService.submit(() -> {
  85. try {
  86. System.err.println("反射注入:" + clazz.getName());
  87. // 生成所有的构造器
  88. Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
  89. // 找到无参构造器然后实例化
  90. Constructor declaredConstructor = clazz.getDeclaredConstructor();
  91. declaredConstructor.setAccessible(true);
  92. Object newInstance = declaredConstructor.newInstance();
  93. Method[] methods = clazz.getDeclaredMethods();
  94. for (Method method : methods) {
  95. try {
  96. // 实例化成功,那么调用一下
  97. method.setAccessible(true);
  98. // graalvm必须需要声明方法
  99. method.invoke(newInstance);
  100. } catch (Throwable e) {
  101. }
  102. }
  103. Field[] fields = clazz.getDeclaredFields();
  104. for (Field field : fields) {
  105. try {
  106. field.setAccessible(true);
  107. field.getType();
  108. String name = field.getName();
  109. field.get(newInstance);
  110. } catch (Throwable e) {
  111. }
  112. }
  113. System.err.println("反射注入完成:" + clazz.getName());
  114. } catch (Throwable e) {
  115. }
  116. });
  117. }
  118. }

添加这个类完成以后再执行

  1. PS D:\eclipse_file\new_ws\springboot3-demo3\target> java -agentlib:native-image-agent=config-output-dir=D:\eclipse_file\new_ws\springboot3-demo3\src\main\resources\META-INF\native-image -jar .\springboot3-demo3-0.0.1-SNAPSHOT.jar
  2. . ____ _ __ _ _
  3. /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
  4. ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
  5. \\/ ___)| |_)| | | | | || (_| | ) ) ) )
  6. ' |____| .__|_| |_|_| |_\__, | / / / /
  7. =========|_|==============|___/=/_/_/_/
  8. :: Spring Boot :: (v3.1.1)
  9. 2023-07-19T18:06:22.193+08:00 INFO 14292 --- [ main] c.e.demo.Springboot3Demo3Application : Starting Springboot3Demo3Application v0.0.1-SNAPSHOT using Java 20.0.1 with PID 14292 (D:\eclipse_file\new_ws\springboot3-demo3\target\springboot3-demo3-0.0.1-SNAPSHOT.jar started by PC in D:\eclipse_file\new_ws\springboot3-demo3\target)
  10. 2023-07-19T18:06:22.198+08:00 INFO 14292 --- [ main] c.e.demo.Springboot3Demo3Application : No active profile set, falling back to 1 default profile: "default"
  11. 2023-07-19T18:06:23.262+08:00 INFO 14292 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode
  12. 2023-07-19T18:06:23.264+08:00 INFO 14292 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data Redis repositories in DEFAULT mode.
  13. 2023-07-19T18:06:23.310+08:00 INFO 14292 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 22 ms. Found 0 Redis repository interfaces.
  14. 2023-07-19T18:06:24.451+08:00 INFO 14292 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
  15. 2023-07-19T18:06:24.463+08:00 INFO 14292 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
  16. 2023-07-19T18:06:24.463+08:00 INFO 14292 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.10]
  17. 2023-07-19T18:06:24.541+08:00 INFO 14292 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
  18. 2023-07-19T18:06:24.544+08:00 INFO 14292 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2252 ms
  19. 配置文件下 scanclass 开启了生成反射类
  20. 反射注入:com.example.demo.control.TestController$$SpringCGLIB$$0
  21. 反射注入:com.example.demo.annotation.aspect.TestAnnotationAspect
  22. 反射注入:com.example.demo.control.TestController
  23. 反射注入:com.example.demo.Springboot3Demo3Application$$SpringCGLIB$$0
  24. 反射注入:com.example.demo.annotation.TestAnnotation
  25. 反射注入:com.example.demo.Springboot3Demo3Application__BeanFactoryRegistrations
  26. 反射注入:com.example.demo.Springboot3Demo3Application__BeanDefinitions
  27. 反射注入:com.example.demo.annotation.aspect.TestAnnotationAspect__BeanDefinitions
  28. 反射注入:com.example.demo.control.TestController__BeanDefinitions
  29. 反射注入完成:com.example.demo.annotation.aspect.TestAnnotationAspect__BeanDefinitions
  30. 反射注入完成:com.example.demo.control.TestController
  31. 反射注入完成:com.example.demo.control.TestController__BeanDefinitions
  32. 反射注入完成:com.example.demo.annotation.aspect.TestAnnotationAspect
  33. 反射注入完成:com.example.demo.Springboot3Demo3Application__BeanFactoryRegistrations
  34. 反射注入完成:com.example.demo.Springboot3Demo3Application__BeanDefinitions
  35. 反射注入完成:com.example.demo.Springboot3Demo3Application$$SpringCGLIB$$0
  36. 反射注入完成:com.example.demo.control.TestController$$SpringCGLIB$$0
  37. 反射注入:com.example.demo.Springboot3Demo3Application__ApplicationContextInitializer
  38. 反射注入:com.example.demo.Springboot3Demo3Application
  39. 反射注入完成:com.example.demo.Springboot3Demo3Application__ApplicationContextInitializer
  40. 反射注入完成:com.example.demo.Springboot3Demo3Application
  41. 2023-07-19T18:06:27.476+08:00 INFO 14292 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
  42. 2023-07-19T18:06:27.486+08:00 INFO 14292 --- [ main] c.e.demo.Springboot3Demo3Application : Started Springboot3Demo3Application in 5.764 seconds (process running for 6.33)
  43. PS D:\eclipse_file\new_ws\springboot3-demo3\target>

当配置文件执行完毕,停止服务,查看resources目录下,已经生成了对应的配置文件

 此时再次执行编译,运行生成后的文件

此时最好将配置文件和编译后的文件放在一起,关闭执行的反射配置类,此时这个类已经没有作用了

访问 http://localhost:8080/test1?num=1

  1. PS D:\eclipse_file\new_ws\springboot3-demo3\target> .\springboot3-demo3.exe
  2. . ____ _ __ _ _
  3. /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
  4. ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
  5. \\/ ___)| |_)| | | | | || (_| | ) ) ) )
  6. ' |____| .__|_| |_|_| |_\__, | / / / /
  7. =========|_|==============|___/=/_/_/_/
  8. :: Spring Boot :: (v3.1.1)
  9. ....
  10. 2023-07-19T18:12:19.231+08:00 INFO 4812 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 47 ms
  11. 配置文件下 scanclass 关闭了生成反射类
  12. ...
  13. 2023-07-19T18:12:21.648+08:00 INFO 4812 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms
  14. 执行TestAnnotation注解
  15. 总共耗时0ms
  16. 9

另外可以看一下,编译后的java服务,内存占用只有37.9m

 集成访问mysql和redis的服务

代码库:

GitHub - cjs199/springboot3-demo4

在测试中,spring-boot-starter-data-redis自带的lettuce-core在广播订阅消息时,会引发一些奇怪的错误,能够查询到的资料很少,不知道原因,于是只能将maven下的依赖从 lettuce 修改成 jedis

  1. ly configure spring.jpa.open-in-view to disable this warning
  2. 2023-07-21T12:25:42.063+08:00 INFO 16408 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
  3. Exception in thread "task-1" 2023-07-21T12:25:42.064+08:00 WARN 16408 --- [ main] w.s.c.ServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.context.ApplicationContextException: Failed to start bean 'RedisMessageListenerContainer'
  4. java.lang.UnsatisfiedLinkError: jdk.jfr.internal.JVM.isExcluded(Ljava/lang/Class;)Z [symbol: Java_jdk_jfr_internal_JVM_isExcluded or Java_jdk_jfr_internal_JVM_isExcluded__Ljava_lang_Class_2]
  5. at org.graalvm.nativeimage.builder/com.oracle.svm.core.jni.access.JNINativeLinkage.getOrFindEntryPoint(JNINativeLinkage.java:152)
  6. at org.graalvm.nativeimage.builder/com.oracle.svm.core.jni.JNIGeneratedMethodSupport.nativeCallAddress(JNIGeneratedMethodSupport.java:53)
  7. at jdk.jfr@20.0.1/jdk.jfr.internal.JVM.isExcluded(Native Method)
  8. ....
  9. at java.base@20.0.1/java.lang.Thread.runWith(Thread.java:1636)
  10. at java.base@20.0.1/java.lang.Thread.run(Thread.java:1623)
  11. at org.graalvm.nativeimage.builder/com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:807)
  12. at org.graalvm.nativeimage.builder/com.oracle.svm.core.windows.WindowsPlatformThreads.osThreadStartRoutine(WindowsPlatformThreads.java:179)
  13. 2023-07-21T12:25:46.217+08:00 INFO 16408 --- [ main] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
  14. 2023-07-21T12:25:46.217+08:00 INFO 16408 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
  15. 2023-07-21T12:25:46.242+08:00 INFO 16408 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
  16. 2023-07-21T12:25:46.246+08:00 INFO 16408 --- [ main] o.apache.catalina.core.StandardService : Stopping service [Tomcat]
  17. 2023-07-21T12:25:46.247+08:00 ERROR 16408 --- [ main] o.s.boot.SpringApplication : Application run failed
  18. org.springframework.context.ApplicationContextException: Failed to start bean 'RedisMessageListenerContainer'
  19. at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:181) ~[demo-1.exe:6.0.10]
  20. at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:356) ~[demo-1.exe:6.0.10]
  21. ...
  22. at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295) ~[demo-1.exe:3.1.1]
  23. at com.example.demo.Demo1Application.main(Demo1Application.java:11) ~[demo-1.exe:na]
  24. Caused by: java.lang.NoClassDefFoundError: Could not initialize class io.lettuce.core.event.connection.JfrConnectionCreatedEvent
  25. at java.base@20.0.1/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:500) ~[demo-1.exe:na]
  26. ....
  27. at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:178) ~[demo-1.exe:6.0.10]
  28. ... 13 common frames omitted
  29. PS D:\eclipse_file\ws3\demo-1\target>

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-data-redis</artifactId>
  4. <exclusions>
  5. <exclusion>
  6. <groupId>io.lettuce</groupId>
  7. <artifactId>lettuce-core</artifactId>
  8. </exclusion>
  9. </exclusions>
  10. </dependency>
  11. <dependency>
  12. <groupId>redis.clients</groupId>
  13. <artifactId>jedis</artifactId>
  14. </dependency>

然后,mysql测试,数据库连接一个最简单的表

  1. CREATE TABLE `sys_user` (
  2. `id` varchar(255) NOT NULL,
  3. `password` varchar(255) DEFAULT NULL,
  4. `username` varchar(255) DEFAULT NULL,
  5. PRIMARY KEY (`id`) USING BTREE,
  6. KEY `username_index` (`username`) USING BTREE
  7. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC;

测试内容如下,这些功能足够满足大部分呢开发中使用场景了

  1. package com.example.demo.control;
  2. import java.util.List;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.data.redis.core.StringRedisTemplate;
  5. import org.springframework.web.bind.annotation.GetMapping;
  6. import org.springframework.web.bind.annotation.RestController;
  7. import com.example.demo.jpa.SysUser;
  8. import com.example.demo.jpa.SysUserRepository;
  9. import cn.hutool.core.util.RandomUtil;
  10. import cn.hutool.json.JSONUtil;
  11. @RestController
  12. public class TestControl {
  13. @Autowired
  14. private SysUserRepository sysUserRepository;
  15. @Autowired
  16. private StringRedisTemplate stringRedisTemplate;
  17. // 数据库保存
  18. @GetMapping("/test_save")
  19. public void test_save() throws Exception {
  20. System.err.println("save");
  21. SysUser sysUser = new SysUser();
  22. sysUser.setUsername(RandomUtil.randomString(3));
  23. sysUser.setPassword(RandomUtil.randomString(3));
  24. sysUserRepository.save(sysUser);
  25. }
  26. // 数据库所有查找
  27. @GetMapping("/test_find_all")
  28. public String test_find_all() throws Exception {
  29. List<SysUser> findAll = sysUserRepository.findAll();
  30. System.err.println(JSONUtil.toJsonPrettyStr(findAll));
  31. return JSONUtil.toJsonPrettyStr(findAll);
  32. }
  33. // 数据库id查找
  34. @GetMapping("/test_find_by_id")
  35. public String test_find_by_id(String id) throws Exception {
  36. SysUser sysUser = sysUserRepository.getOne(id);
  37. System.err.println(JSONUtil.toJsonPrettyStr(sysUser));
  38. return JSONUtil.toJsonPrettyStr(sysUser);
  39. }
  40. // redis 键值对数据设置
  41. @GetMapping("/test_redis_kv_set")
  42. public String test_redis_kv_set() throws Exception {
  43. stringRedisTemplate.opsForValue().set("123", "456");
  44. return "OK";
  45. }
  46. // redis 键值对数据获取
  47. @GetMapping("/test_redis_kv_get")
  48. public String test_redis_kv_get() throws Exception {
  49. System.err.println(stringRedisTemplate.opsForValue().get("123"));
  50. return "OK";
  51. }
  52. // redis广播消息测试
  53. @GetMapping("/test_redis_pubsub")
  54. public String test_redis_pubsub() throws Exception {
  55. stringRedisTemplate.convertAndSend("test_redis_pubsub", "a msg");
  56. return "OK";
  57. }
  58. // redis队列测试
  59. @GetMapping("/test_redis_queue")
  60. public String test_redis_queue() throws Exception {
  61. stringRedisTemplate.opsForList().rightPush("MsgQueue", "a msg");
  62. return "OK";
  63. }
  64. }

测试,所有功能都是正常的

  1. PS D:\eclipse_file\ws3\demo-1\target> .\demo-1.exe
  2. . ____ _ __ _ _
  3. /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
  4. ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
  5. \\/ ___)| |_)| | | | | || (_| | ) ) ) )
  6. ' |____| .__|_| |_|_| |_\__, | / / / /
  7. =========|_|==============|___/=/_/_/_/
  8. :: Spring Boot :: (v3.1.1)
  9. 2023-07-21T12:39:14.776+08:00 INFO 5156 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms
  10. save
  11. Hibernate: insert into sys_user (password,username,id) values (?,?,?)
  12. Hibernate: select s1_0.id,s1_0.password,s1_0.username from sys_user s1_0
  13. [
  14. {
  15. "id": "4028dc158976be5a018976bead320000",
  16. "password": "r58",
  17. "username": "rix"
  18. },
  19. ....
  20. ]
  21. Hibernate: select s1_0.id,s1_0.password,s1_0.username from sys_user s1_0 where s1_0.id=?
  22. {
  23. "id": "4028dc378973329b0189733358ce0002",
  24. "password": "swv",
  25. "username": "w1m"
  26. }
  27. MsgPubSub收到消息:a msg
  28. MsgPubSub2收到消息:a msg
  29. 收到队列消息:a msg
  30. 2023-07-21T12:40:01.024+08:00 INFO 5156 --- [ionShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
  31. 2023-07-21T12:40:01.024+08:00 INFO 5156 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
  32. 2023-07-21T12:40:01.025+08:00 INFO 5156 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
  33. PS D:\eclipse_file\ws3\demo-1\target>

服务资源占用还是很低的

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/68695
推荐阅读
相关标签
  

闽ICP备14008679号