1、试用环境准备
- 准备好
centos7
系统 - 安装好
jdk17
- 安装好
docker20
2、编写项目并构建native
镜像
- 项目准备
从start.spring.io
创建一个新的项目,项目名为mynative
,在ADD DEPENDENCIES
中可以看到排在第一位的依赖就是spring-native
,不过目前还处理试验阶段,要谨慎使用;
添加几个基础依赖,主要是用来简单测试一下一个http
请求到web
应用,并将请求信息保存到内存数据库h2
中,然后打开内存数据库的console
控制台查询校验数据,看数据是否正常;
mynative
项目源码结果如下
- 代码片断
RequestLogController类
---
@Slf4j
@RestController
@RequestMapping("/")
public class RequestLogController {
@Autowired
private RequestLogRepository requestLogRepository;
@GetMapping("/log")
public String log(HttpServletRequest request) {
RequestLog entity = new RequestLog();
entity.setId(UUID.randomUUID().toString().toUpperCase());
Enumeration<String> names = request.getHeaderNames();
StringBuilder builder = new StringBuilder(request.getRequestURL()).append("\r\n");
while (names.hasMoreElements()) {
String name = names.nextElement();
builder.append(name).append(" = ").append(request.getHeader(name)).append("\r\n");
}
entity.setContent(builder.toString());
entity.setLogDate(new Date());
requestLogRepository.save(entity);
return HttpStatus.OK.toString();
}
}
---
RequestLog类
---
@Getter
@Setter
@Entity
public class RequestLog {
@Id
private String id;
@Column(length = 1024)
private String content;
private Date logDate;
}
---
RequestLogRepository类
---
@Repository
public interface RequestLogRepository extends JpaRepository<RequestLog, String> {
}
---
MyNativeApplication类
---
@EnableJpaRepositories
@SpringBootApplication
public class MyNativeApplication {
public static void main(String[] args) {
SpringApplication.run(MyNativeApplication.class, args);
}
}
---
application.yaml配置文件
---
server:
port: 8888
logging:
level:
root: info
spring:
h2:
console:
enabled: true
settings:
web-allow-others: true
jpa:
show-sql: true
---
简单起见,需要在配置文件中开启内存数据库h2
的管理控制台,并且允许web
访问(不仅是通过localhost
),如上面的application.yaml
配置
- 编译项目
mvn spring-boot:buid-image
首次编译期间会去下载以下镜像,耗时较长,而且这个构建过程比较耗cpu
资源,其使用率经常保持在较高的水平,大概10分钟左右完成镜像的构建;
docker.io/paketobuildpacks/builder:tiny
docker.io/paketobuildpacks/run:tiny-cnb
构建镜像片断
[INFO] > Pulling builder image 'docker.io/paketobuildpacks/builder:tiny' 59%
[INFO] > Pulling builder image 'docker.io/paketobuildpacks/builder:tiny' 71%
[INFO] > Pulling builder image 'docker.io/paketobuildpacks/builder:tiny' 84%
[INFO] > Pulling builder image 'docker.io/paketobuildpacks/builder:tiny' 100%
[INFO] > Pulled builder image 'paketobuildpacks/builder@sha256:782b8eff60d91ae734bd82c40c973b0f002c16ca881e91aeb977984699af666b'
[INFO] > Pulling run image 'docker.io/paketobuildpacks/run:tiny-cnb' 100%
[INFO] > Pulled run image 'paketobuildpacks/run@sha256:ecc1c097c43a313151bbcb544b4b6c17ec808e93707a777e01c1e5b37d4dd6bc'
......
[INFO] [creator] ===> EXPORTING
[INFO] [creator] Reusing layer 'paketo-buildpacks/ca-certificates:helper'
[INFO] [creator] Adding 1/1 app layer(s)
[INFO] [creator] Adding layer 'launcher'
[INFO] [creator] Adding layer 'config'
[INFO] [creator] Reusing layer 'process-types'
[INFO] [creator] Adding label 'io.buildpacks.lifecycle.metadata'
[INFO] [creator] Adding label 'io.buildpacks.build.metadata'
[INFO] [creator] Adding label 'io.buildpacks.project.metadata'
[INFO] [creator] Adding label 'org.opencontainers.image.title'
[INFO] [creator] Adding label 'org.opencontainers.image.version'
[INFO] [creator] Adding label 'org.springframework.boot.version'
[INFO] [creator] Setting default process type 'web'
[INFO] [creator] Saving docker.io/library/native:0.0.1...
[INFO] [creator] *** Images (9ebd0ad2764f):
[INFO] [creator] docker.io/library/native:0.0.1
[INFO] [creator] Reusing cache layer 'paketo-buildpacks/bellsoft-liberica:native-image-svm'
[INFO] [creator] Reusing cache layer 'paketo-buildpacks/syft:syft'
[INFO] [creator] Adding cache layer 'paketo-buildpacks/native-image:native-image'
[INFO]
[INFO] Successfully built image 'docker.io/library/native:0.0.1'
编译成功后生成了一个native:0.0.1镜像如下,可以看到镜像大小有145M,可以看到created时间不对,这里是42 years age
相应的解析https://medium.com/buildpacks/time-travel-with-pack-e0efd8bf05db
,大意是构建镜像的sha
会随文件时间戳而变化
3、运行native并观察结果
- 运行native镜像
docker run -it --rm -p 8888:8888 native:0.0.1
可以看到启动速度非常快,在1秒内完成了启动,正常通过java -jar target/native-0.0.1.jar
启动需要约5秒钟,有显著的差异,启动的过程中有一个细节可以看到H2 console available at '/h2-console'. Database available at 'jdbc:h2:mem:f3f1583f-1323-4dbe-914d-cc342d73a3d3'
,由于在spring.datasource.url
没有指定路径,所以默认会随机生成一个uuid
字符作为数据库名称,若指定一个数据库名则会直接使用指定的名称;
- 模拟数据
通过curl http://192.168.91.132:8888/log
模拟访问先造一些数据,可以看到控制台会有一些insert sql
语句打印出来,数据可以正常写入了。然后打开H2控制台 http://192.168.91.132:8888/h2-console
,发现报异常了,不能正常打开页面,报错如下
4、解决native镜像运行h2-console
无法访问的问题
根据报错的提示跟踪一下源码org.h2.server.web.WebServer
此处报空指针异常是trans
为null
,即说明getFile
返回为null
,跟踪getFile
方法,可以看到是通过Utils.getResource("/org/h2/server/web/res/" + file)
获取,继续跟踪getResource
方法可以发现资源是从RESOURCES
中获取,而这个资源是loadResource
方法从Utils.class.getResourceAsStream("data.zip")
此处获取,即在Utils
包的目录下拿到一个data.zip
文件。
接着打开h2-1.4.200.jar
包,进到目录org\h2\util
中可以找到data.zip
结果就清晰了,可能是spring-native
构建镜像的时候将此资源文件排除了,将其放到项目的resources/org/h2/util
中,再一次构建镜像运行,就可以正常看到h2
数据库的控制台了,多请求几次测试url
,即可查到数据。
5、最新版支持AOP
切面
- 编写一个日志切面
用此切面拦截Controller
中的日志log
请求,查看效果
package com.minxyz.mynative.aspect;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Slf4j
@Aspect
@Component
public class LogAspect {
@Around("execution(* com.minxyz.mynative.controller.RequestLogController.log(..))")
public Object processInsertRoleConfig(ProceedingJoinPoint point) throws Throwable {
log.info("enter LogAspect function");
Object[] args = point.getArgs();
return point.proceed(args);
}
}
同时需在启动类中增加@AotProxyHint
注解配置,指定类
@AotProxyHint(targetClass=com.minxyz.mynative.controller.RequestLogController.class,proxyFeatures = ProxyBits.IS_STATIC)
附:源码 https://gitee.com/viturefree/spring-native-test.git