使用 Spring Boot 和 GraalVM 的原生镜像

🧑 博主简介:CSDN博客专家历代文学网(PC端可以访问:历代文学,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,精通Java编程高并发设计Springboot和微服务,熟悉LinuxESXI虚拟化以及云原生Docker和K8s,热衷于探索科技的边界,并将理论知识转化为实际应用。保持对新技术的好奇心,乐于分享所学,希望通过我的实践经历和见解,启发他人的创新思维。在这里,我希望能与志同道合的朋友交流探讨,共同进步,一起在技术的世界里不断学习成长。
技术合作请加本人wx(注明来自csdn):foreast_sea

在这里插入图片描述


1. 概述

在本文中,我们将了解原生镜像,以及如何从 Spring Boot 应用程序和 GraalVM 的原生镜像构建器创建原生镜像。我们指的是 Spring Boot 3,但我们将在本文末尾解决与 Spring Boot 2 的差异。

2. 原生镜像

本机映像是一种将 Java 代码构建为独立可执行文件的技术。此可执行文件包括应用程序类、来自其依赖项的类、运行时库类以及来自 JDK 的静态链接本机代码。JVM 已打包到本机映像中,因此目标系统上不需要任何 Java 运行时环境,但构建工件取决于平台。因此,我们需要为每个支持的目标系统构建一个版本,当我们使用 Docker 等容器技术时,这会更容易,我们可以将容器构建为可以部署到任何 Docker 运行时的目标系统。

2.1. GraalVM 和原生镜像构建器

通用递归应用程序和算法语言虚拟机 (Graal VM) 是为 Java 和其他 JVM 语言编写的高性能 JDK 发行版,同时支持 JavaScript、Ruby、Python 和其他几种语言。它提供了一个 Native Image 构建器 – 一种从 Java 应用程序构建原生代码并将其与 VM 一起打包成独立可执行文件的工具。它由 Spring Boot Maven 和 Gradle Plugin 官方支持,但有一些例外(最糟糕的是 Mockito 目前不支持原生测试)。

2.2. 特殊功能

在构建原生镜像时,我们遇到了两个典型特征。

预先 (AOT) 编译是将高级 Java 代码编译为本机可执行代码的过程。通常,这是由 JVM 的 Just-in-time 编译器 (JIT) 在运行时完成的,它允许在执行应用程序时进行观察和优化。在 AOT 编译的情况下,此优势将丢失。

通常,在 AOT 编译之前,可以选择有一个单独的步骤,称为 AOT 处理,即从代码中收集元数据并将其提供给 AOT 编译器。划分为这两个步骤是有意义的,因为 AOT 处理可以是特定于框架的,而 AOT 编译器更通用。下图给出了一个概述:

Java 平台的另一个特点是,只需将 JAR 放入 Classpath 中,即可在目标系统上实现可扩展性。由于启动时的反射和注释扫描,我们在应用程序中获得了扩展行为。

遗憾的是,这会减慢启动时间,并且不会带来任何好处,尤其是对于云原生应用程序,其中甚至服务器运行时和 Java 基类也被打包到 JAR 中。因此,我们省去了这个功能,然后可以使用 Closed World Optimization 构建应用程序。

这两项功能都减少了运行时需要执行的工作量。

2.3. 优势

本机映像具有各种优势,例如即时启动和减少内存消耗。它们可以打包到轻量级容器映像中,以便更快、更高效地部署,并且它们减少了攻击面。

2.4. 限制

由于 Closed World Optimization,在编写应用程序代码和使用框架时,我们必须注意一些限制。不久:

  • 类初始值设定项可以在构建时执行,以实现更快的启动和更好的峰值性能。但我们必须意识到,这可能会破坏代码中的一些假设,例如,当加载一个必须在构建时可用的文件时。
  • 反射和动态代理在运行时成本高昂,因此在 Closed World 假设下在构建时进行了优化。在构建时执行时,我们可以在类初始化器中不受限制地使用它。任何其他用法都必须向 AOT 编译器公布,Native Image 构建器会尝试通过执行静态代码分析来访问该编译器。如果失败,我们必须提供此信息,例如,通过配置文件。
  • 这同样适用于所有基于反射的技术,例如 JNI 和 Serialization。
  • 此外,本机映像生成器还提供了自己的本机接口,该接口比 JNI 简单得多,开销也较低。
  • 对于本机映像构建,字节码在运行时不再可用,因此无法使用针对 JVMTI 的工具进行调试和监控。然后,我们必须使用本机调试器和监控工具。

关于 Spring Boot,我们必须意识到,运行时不再完全支持配置文件、条件 bean 和 .enable 属性等功能。如果我们使用 profiles,则必须在构建时指定它们。

3. 基本设置

在构建原生镜像之前,我们必须安装这些工具。

3.1. GraalVM 和原生镜像

首先,我们按照安装说明安装当前版本的 GraalVM 和原生映像构建器。(Spring Boot 需要 22.3 版本)我们应该确保安装目录可以通过 GRAALVM_HOME 环境变量获得,并且 “<GRAALVM_HOME>/bin” 已添加到 PATH 变量中。

3.2. 原生编译器

在构建过程中,Native Image 构建器会调用特定于平台的原生编译器。因此,我们需要这个原生编译器,按照我们平台的 “Prerequisite” 说明进行操作。这将使构建平台相关。我们必须知道,只能在特定于平台的命令行中运行构建。例如,使用 Git Bash 在 Windows 上运行构建将不起作用。我们需要改用 Windows 命令行。

3.3. Docker

作为先决条件,我们将确保安装 Docker,稍后需要运行本机映像。Spring Boot Maven 和 Gradle 插件使用 Paketo Tiny Builder 构建容器。

4. 使用 Spring Boot 配置和构建项目

将本机构建功能与 Spring Boot 一起使用非常简单。例如,通过使用 Spring Initializr 并添加应用程序代码来创建我们的项目。然后,要使用 GraalVM 的原生映像构建器构建原生映像,我们需要使用 GraalVM 本身提供的 Maven 或 Gradle 插件来扩展我们的构建。

4.1. Maven 浏览器

Spring Boot Maven 插件的目标是 AOT 处理(即,不是 AOT 编译自身,而是为 AOT 编译器收集元数据,例如,在代码中注册反射的使用)和构建可与 Docker 一起运行的 OCI 映像。我们可以直接调用这些目标:

mvn spring-boot:process-aot
mvn spring-boot:process-test-aot
mvn spring-boot:build-image

我们不需要这样做,因为 Spring Boot 父 POM 定义了一个将这些目标绑定到构建的本机配置文件。我们需要使用此激活的配置文件进行构建:

mvn clean package -Pnative

如果我们还想执行本机测试,则可以激活第二个配置文件:

mvn clean package -Pnative,nativeTest

如果我们想要构建原生镜像,就必须添加 native-maven-plugin 的相应目标。因此,我们也可以定义一个原生配置文件。因为这个插件是由父 POM 管理的,所以我们可以保留版本号:

<profiles><profile><id>native</id><build><plugins><plugin><groupId>org.graalvm.buildtools</groupId><artifactId>native-maven-plugin</artifactId><executions><execution><id>build-native</id><goals><goal>compile-no-fork</goal></goals><phase>package</phase></execution></executions></plugin></plugins></build></profile>
</profiles>

目前,本机测试执行不支持 Mockito。因此,我们可以排除 Mocking 测试,或者通过将以下内容添加到我们的 POM 中来跳过本机测试:

<build><pluginManagement><plugins><plugin><groupId>org.graalvm.buildtools</groupId><artifactId>native-maven-plugin</artifactId><configuration><skipNativeTests>true</skipNativeTests></configuration></plugin></plugins></pluginManagement>
</build>

4.2. 在没有父 POM 的情况下使用 Spring Boot

如果我们不能从 Spring Boot Parent POM 继承,而是将其用作导入范围的依赖项,则必须自己配置插件和配置文件。然后,我们必须将以下内容添加到我们的 POM 中:

<build><pluginManagement><plugins><plugin><groupId>org.graalvm.buildtools</groupId><artifactId>native-maven-plugin</artifactId><version>${native-build-tools-plugin.version}</version><extensions>true</extensions></plugin></plugins></pluginManagement>
</build>
<profiles><profile><id>native</id><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><image><builder>paketobuildpacks/builder:tiny</builder><env><BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE></env></image></configuration><executions><execution><id>process-aot</id><goals><goal>process-aot</goal></goals></execution></executions></plugin><plugin><groupId>org.graalvm.buildtools</groupId><artifactId>native-maven-plugin</artifactId><configuration><classesDirectory>${project.build.outputDirectory}</classesDirectory><metadataRepository><enabled>true</enabled></metadataRepository><requiredVersion>22.3</requiredVersion></configuration><executions><execution><id>add-reachability-metadata</id><goals><goal>add-reachability-metadata</goal></goals></execution></executions></plugin></plugins></build></profile><profile><id>nativeTest</id><dependencies><dependency><groupId>org.junit.platform</groupId><artifactId>junit-platform-launcher</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><executions><execution><id>process-test-aot</id><goals><goal>process-test-aot</goal></goals></execution></executions></plugin><plugin><groupId>org.graalvm.buildtools</groupId><artifactId>native-maven-plugin</artifactId><configuration><classesDirectory>${project.build.outputDirectory}</classesDirectory><metadataRepository><enabled>true</enabled></metadataRepository><requiredVersion>22.3</requiredVersion></configuration><executions><execution><id>native-test</id><goals><goal>test</goal></goals></execution></executions></plugin></plugins></build></profile>
</profiles>
<properties><native-build-tools-plugin.version>0.9.17</native-build-tools-plugin.version>
</properties>

4.3. 格拉德尔

Spring Boot Gradle 插件提供用于 AOT 处理的任务(即,不是 AOT 编译自身,而是为 AOT 编译器收集元数据,例如,在代码中注册反射的使用情况)和构建可与 Docker 一起运行的 OCI 映像:

gradle processAot
gradle processTestAot
gradle bootBuildImage

如果我们想构建原生镜像,我们必须添加 Gradle 插件来构建 GraalVM 原生镜像:

plugins {// ...id 'org.graalvm.buildtools.native' version '0.9.17'
}

然后,我们可以通过调用

gradle nativeTest
gradle nativeCompile

目前,本机测试执行不支持 Mockito。因此,我们可以通过配置 graalvmNative 扩展来排除 Mocking 测试或跳过原生测试,如下所示:

graalvmNative {testSupport = false
}

5. 扩展本机映像生成配置

如前所述,我们必须为 AOT 编译器注册反射、类路径扫描、动态代理等的每次用法。因为 Spring 的内置原生支持是一个非常年轻的功能,目前并不是所有的 Spring 模块都有内置支持,所以我们目前需要自己添加这个。这可以通过手动创建 build configuration 来完成。尽管如此,使用 Spring Boot 提供的接口还是更容易,这样 Maven 和 Gradle 插件都可以在 AOT 处理期间使用我们的代码来生成构建配置。

指定其他本机配置的一种可能性是 Native Hints。那么,让我们看看目前缺少的内置支持的两个示例,以及如何将其添加到我们的应用程序中以使其正常工作。

5.1. 示例:Jackson 的 PropertyNamingStrategy

在 MVC Web 应用程序中,REST 控制器方法的每个返回值都由 Jackson 序列化,并自动将每个属性命名为 JSON 元素。我们可以通过在应用程序属性文件中配置 Jackson 的 PropertyNamingStrategy 来全局影响名称映射:

spring.jacksonproperty-naming-strategy=SNAKE_CASE

SNAKE_CASE 是 PropertyNamingStrategies 类型的静态成员的名称。不幸的是,此成员通过反射解决了。因此,AOT 编译器需要知道这一点,否则,我们将收到一条错误消息:

Caused by: java.lang.IllegalArgumentException: Constant named 'SNAKE_CASE' not foundat org.springframework.util.Assert.notNull(Assert.java:219) ~[na:na]at org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$Jackson2ObjectMapperBuilderCustomizerConfiguration$StandardJackson2ObjectMapperBuilderCustomizer.configurePropertyNamingStrategyField(JacksonAutoConfiguration.java:287) ~[spring-features.exe:na]

为此,我们可以通过如下简单的方式实现和注册 RuntimeHintsRegistrar

@Configuration
@ImportRuntimeHints(JacksonRuntimeHints.PropertyNamingStrategyRegistrar.class)
public class JacksonRuntimeHints {static class PropertyNamingStrategyRegistrar implements RuntimeHintsRegistrar {@Overridepublic void registerHints(RuntimeHints hints, ClassLoader classLoader) {try {hints.reflection().registerField(PropertyNamingStrategies.class.getDeclaredField("SNAKE_CASE"));} catch (NoSuchFieldException e) {// ...}}}}

注意:从 3.0.0-RC2 版本开始,在 Spring Boot 中解决此问题的拉取请求已经合并,因此它可以与 Spring Boot 3 一起开箱即用。

5.2. 示例:GraphQL 架构文件

如果我们想实现 GraphQL API,我们需要创建一个架构文件并将其定位在“classpath:/graphql/*.graphqls”下,Springs GraphQL 自动配置会自动检测到它。这是通过 Classpath scanning 以及集成的 GraphiQL 测试客户端的欢迎页面完成的。因此,要在本机可执行文件中正常工作,AOT 编译器需要了解这一点。我们可以用同样的方式注册它:

@ImportRuntimeHints(GraphQlRuntimeHints.GraphQlResourcesRegistrar.class)
@Configuration
public class GraphQlRuntimeHints {static class GraphQlResourcesRegistrar implements RuntimeHintsRegistrar {@Overridepublic void registerHints(RuntimeHints hints, ClassLoader classLoader) {hints.resources().registerPattern("graphql/**/").registerPattern("graphiql/index.html");}}}

Spring GraphQL 团队已经在努力解决这个问题,因此我们可能会在未来版本中内置它。

6. 编写测试

要测试 RuntimeHintsRegistrar 实现,我们甚至不需要运行 Spring Boot 测试,我们可以创建一个简单的 JUnit 测试,如下所示:

@Test
void shouldRegisterSnakeCasePropertyNamingStrategy() {// arrangefinal var hints = new RuntimeHints();final var expectSnakeCaseHint = RuntimeHintsPredicates.reflection().onField(PropertyNamingStrategies.class, "SNAKE_CASE");// actnew JacksonRuntimeHints.PropertyNamingStrategyRegistrar().registerHints(hints, getClass().getClassLoader());// assertassertThat(expectSnakeCaseHint).accepts(hints);
}

如果我们想通过集成测试来测试它,我们可以检查 Jackson ObjectMapper 是否具有正确的配置:

@SpringBootTest
class JacksonAutoConfigurationIntegrationTest {@AutowiredObjectMapper mapper;@Testvoid shouldUseSnakeCasePropertyNamingStrategy() {assertThat(mapper.getPropertyNamingStrategy()).isSameAs(PropertyNamingStrategies.SNAKE_CASE);}}

要使用 native 模式对其进行测试,我们必须运行一个 native test:

# Maven
mvn clean package -Pnative,nativeTest
# Gradle
gradle nativeTest

如果我们需要为 Spring Boot Tests 提供特定于测试的 AOT 支持,我们可以使用 AotTestExecutionListener 接口实现TestRuntimeHintsRegistrar或TestExecutionListener。我们可以在官方文档中找到详细信息。

7. Spring Boot 2

Spring 6 和 Spring Boot 3 在原生镜像构建方面迈出了一大步。但是对于之前的主要版本,这也是可能的。我们只需要知道目前还没有内置支持,即有一个补充的 Spring Native 计划来处理这个主题。因此,我们必须在我们的项目中手动包含和配置它。对于 AOT 处理,有一个单独的 Maven 和 Gradle 插件,该插件未合并到 Spring Boot 插件中。当然,集成库提供的原生支持程度不如现在(将来会更多)。

7.1. Spring 原生依赖

首先,我们必须为 Spring Native 添加 Maven 依赖项:

<dependency><groupId>org.springframework.experimental</groupId><artifactId>spring-native</artifactId><version>0.12.1</version>
</dependency>

但是,对于 Gradle 项目,Spring Native 是由 Spring AOT 插件自动添加的。

需要注意的是,每个 Spring Native 版本只支持一个特定的 Spring Boot 版本——比如 Spring Native 0.12.1 只支持 Spring Boot 2.7.1。因此,我们应该确保在pom.xml中使用兼容的 Spring Boot Maven 依赖项。

7.2. 构建包

要构建 OCI 映像,我们需要显式配置构建包。

使用 Maven,我们需要使用 Paketo Java buildpacks 的带有本机映像配置的 spring-boot-maven-plugin:

<build><pluginManagement><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><image><builder>paketobuildpacks/builder:tiny</builder><env><BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE></env></image></configuration></plugin></plugins></pluginManagement>
</build>

在这里,我们将使用各种可用构建器(如 base 和 full)中的小型构建器来构建原生镜像。此外,我们还通过向 BP_NATIVE_IMAGE 环境变量提供 true 值来启用 buildpack。

同样,在使用 Gradle 时,我们可以将 tiny 构建器以及 BP_NATIVE_IMAGE 环境变量添加到 build.gradle 文件中:

bootBuildImage {builder = "paketobuildpacks/builder:tiny"environment = ["BP_NATIVE_IMAGE" : "true"]
}

7.3. Spring AOT 插件

接下来,我们需要添加 Spring AOT 插件,该插件执行提前转换,有助于改善本机映像的占用空间和兼容性。

因此,让我们将最新的 spring-aot-maven-plugin Maven 依赖项添加到我们的 pom.xml中:

<plugin><groupId>org.springframework.experimental</groupId><artifactId>spring-aot-maven-plugin</artifactId><version>0.12.1</version><executions><execution><id>generate</id><goals><goal>generate</goal></goals></execution></executions>
</plugin>

同样,对于 Gradle 项目,我们可以在 build.gradle 文件中添加最新的 org.springframework.experimental.aot 依赖项:

plugins {id 'org.springframework.experimental.aot' version '0.10.0'
}

此外,正如我们之前提到的,这会自动将 Spring Native 依赖项添加到 Gradle 项目中。

Spring AOT 插件提供了几个选项来确定源生成。例如,removeYamlSupport 和 removeJmxSupport 等选项分别删除 Spring Boot Yaml 和 Spring Boot JMX 支持。

7.4. 构建并运行镜像

就是这样!我们已准备好使用 Maven 命令构建 Spring Boot 项目的本机映像:

$ mvn spring-boot:build-image

7.5. 原生镜像构建

接下来,我们将添加一个名为 native 的配置文件,该配置文件支持一些插件,例如 native-maven-plugin 和 spring-boot-maven-plugin:

<profiles><profile><id>native</id><build><plugins><plugin><groupId>org.graalvm.buildtools</groupId><artifactId>native-maven-plugin</artifactId><version>0.9.17</version><executions><execution><id>build-native</id><goals><goal>build</goal></goals><phase>package</phase></execution></executions></plugin><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><classifier>exec</classifier></configuration></plugin></plugins></build></profile>
</profiles>

此配置文件将在打包阶段从构建中调用 native-image 编译器。

但是,在使用 Gradle 时,我们会将最新的 org.graalvm.buildtools.native 插件添加到 build.gradle 文件中:

plugins {id 'org.graalvm.buildtools.native' version '0.9.17'
}

就是这样!我们已准备好通过在 Maven package 命令中提供本机配置文件来构建本机映像:

mvn clean package -Pnative

8. 总结

在本教程中,我们探索了使用 Spring Boot 和 GraalVM 的原生构建工具构建原生镜像。我们了解了 Spring 的内置原生支持。所有代码实现都可以在 GitHub 上找到(Spring Boot 2 示例)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/65895.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机网络-L2TP VPN基础实验配置

一、概述 上次大概了解了L2TP的基本原理和使用场景&#xff0c;今天来模拟一个小实验&#xff0c;使用Ensp的网卡桥接到本地电脑试下L2TP拨号&#xff0c;今天主要使用标准的L2TP&#xff0c;其实在这个基础上可以加上IPSec进行加密&#xff0c;提高安全性。 拓扑说明&#xf…

Linux | 零基础Ubuntu解压RaR等压缩包文件

目录 介绍 案例分析 安装工具 解压实践 介绍 RAR是一种专利文件格式&#xff0c;用于数据压缩与归档打包&#xff0c;开发者为尤金罗谢尔&#xff08;俄语&#xff1a;Евгений Лазаревич Рошал&#xff0c;拉丁转写&#xff1a;Yevgeny Lazarevich R…

Postman接口测试05|实战项目笔记

目录 一、项目接口概况 二、单接口测试-登录接口&#xff1a;POST 1、正例 2、反例 ①姓名未注册 ②密码错误 ③姓名为空 ④多参 ⑤少参 ⑥无参 三、批量运行测试用例 四、生成测试报告 1、Postman界面生成 2、Newman命令行生成 五、token鉴权&#xff08;“…

网络分析工具-tcpdump

文章目录 前言一、tcpdump基础官网链接命令选项详解常规过滤规则tcpdump输出 一、tcpdump实践HTTP协议ICMP状态抓包 前言 当遇到网络疑难问题的时候&#xff0c;抓包是最基本的技能&#xff0c;通过抓包才能看到网络底层的问题 一、tcpdump基础 tcpdump是一个常用的网络分析工…

可编辑31页PPT | 大数据湖仓一体解决方案

荐言分享&#xff1a;在当今数字化时代&#xff0c;大数据已成为企业决策和业务优化的关键驱动力。然而&#xff0c;传统的数据处理架构&#xff0c;如数据仓库和数据湖&#xff0c;各自存在局限性&#xff0c;难以满足企业对数据高效存储、灵活处理及实时分析的综合需求。因此…

STM32中断详解

STM32中断详解 NVIC 中断系统中断向量表相关寄存器中断优先级中断配置 外部中断实验EXTI框图外部中断/事件线映射中断步骤初始化代码实现 定时器中断通用定时器相关功能标号1&#xff1a;时钟源标号 2&#xff1a;控制器标号 3&#xff1a;时基单元 代码实现 NVIC 中断系统 STM…

【LeetCode】200、岛屿数量

【LeetCode】200、岛屿数量 文章目录 一、并查集1.1 并查集1.2 多语言解法 二、洪水填充 DFS2.1 洪水填充 DFS 一、并查集 1.1 并查集 // go var sets int var father [90000]intfunc numIslands(grid [][]byte) int {n, m : len(grid), len(grid[0])build(grid, n, m)for i …

SOME/IP 协议详解——序列化

文章目录 0. 概述1.基本数据序列化2.字符串序列化2.1 字符串通用规则2.2 固定长度字符串规则2.3 动态长度字符串规则 3.结构体序列化4. 带有标识符和可选成员的结构化数据类型5. 数组5.1 固定长度数组5.2 动态长度数组5.3 Enumeration&#xff08;枚举&#xff09;5.4 Bitfield…

【AndroidAPP】权限被拒绝:[android.permission.READ_EXTERNAL_STORAGE],USB设备访问权限系统报错

一、问题原因 1.安卓安全性变更 Android 12 的安全性变更&#xff0c;Google 引入了更严格的 PendingIntent 安全管理&#xff0c;强制要求开发者明确指定 PendingIntent 的可变性&#xff08;Mutable&#xff09;或不可变性&#xff08;Immutable&#xff09;。 但是&#xf…

C之(14)gcov覆盖率

C之(14)gcov覆盖率 Author: Once Day Date: 2024年12月30日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 全系列文章可参考专栏: C语言_Once-Day的博客-CSDN博客 前些天…

简易屏幕共享工具-基于WebSocket

前面写了两个简单的屏幕共享工具&#xff0c;不过那只是为了验证通过截屏的方式是否可行&#xff0c;因为通常手动截屏的频率很低&#xff0c;而对于视频来说它的帧率要求就很高了&#xff0c;至少要一秒30帧率左右。所以&#xff0c;经过实际的截屏工具验证&#xff0c;我了解…

Paperlib(论文管理工具)

Paperlib 是一个简单好用的论文管理工具。软件接入各学科数据库用于匹配论文元数据&#xff0c;逐步为每一个学科&#xff08;例如计算机科学&#xff0c;物理学等&#xff09;定制化数据库组合提高检索精度。尤其是精准的会议论文元数据检索能力。还可以管理你的论文&#xff…

c# 2024/12/27 周五

6《详解类型、变量与对象》36 详解类型、变量与对象 _1_哔哩哔哩_bilibili

Formality:匹配(match)是如何进行的?

相关阅读Formalityhttps://blog.csdn.net/weixin_45791458/category_12841971.html?spm1001.2014.3001.5482 匹配点、对比点和逻辑锥 匹配指的是Formality工具尝试将参考设计中的每个匹配点与实现设计中的相应匹配点进行配对&#xff0c;这里的匹配点包括对比点(Compare Point…

分布式算法(五):初识ZAB协议

文章目录 一、什么是Zookeeper二、ZAB与Zookeeper的关系为什么Zookeeper不直接使用Paxos 三、ZAB简介1.名词解释提案&#xff08;Proposal&#xff09;事务&#xff08;Transaction&#xff09;原子广播&#xff08;Atomic Broadcast&#xff09; 2.集群角色领导者&#xff08;…

Mybatis 01

JDBC回顾 select 语句 "select *from student" 演示&#xff1a; 驱动包 JDBC 的操作流程&#xff1a; 1. 创建数据库连接池 DataSource 2. 通过 DataSource 获取数据库连接 Connection 3. 编写要执⾏带 ? 占位符的 SQL 语句 4. 通过 Connection 及 SQL 创建…

tensorboard的界面参数与图像数据分析讲解

目录 1.基础概念&#xff1a; (a)精确率与召回率&#xff1a; (b)mAP: (c)边界框损失&#xff1a; (d)目标损失&#xff1a; (e)分类损失&#xff1a; (f):学习率&#xff1a; 2.设置部分&#xff08;最右边部分&#xff09;&#xff1a; GENERAL&#xff08;常规设置…

《计算机网络A》单选题-复习题库解析-2

目录 51、下列关于以太网网卡地址特点的说法中&#xff0c;不正确的是&#xff08; &#xff09;。 52、当一个Web Browser向一个使用标准服务器端口的Web Server提出请求时&#xff0c;那么在服务返回的响应包中&#xff0c;所使用的源端口是&#xff08; &#xff0…

Linux总结之CentOS Stream 9安装mysql8.0实操安装成功记录

Linux总结之CentOS Stream 9安装mysql8.0实操安装成功记录 由于网上很多的mysql8.0安装教程都是老版本或者安装过程记录有问题&#xff0c;导致经常安装到一半需要删除重新安装。所以将成功的实操安装过程记录一下&#xff0c;方面后面查阅&#xff0c;大家还有问题的可以在此讨…

高等数学学习笔记 ☞ 无穷小与无穷大

1. 无穷小 1. 定义&#xff1a;若函数当或时的极限为零&#xff0c;那么称函数是当或时的无穷小。 备注&#xff1a; ①&#xff1a;无穷小描述的是自变量的变化过程中&#xff0c;函数值的变化趋势&#xff0c;绝不能认为无穷小是一个很小很小的数。 ②&#xff1a;说无穷小时…