技术解析 | 适用于TeamCity的Unreal Engine支持插件,提升游戏构建效率

龙智是JetBrains授权合作伙伴、Perforce授权合作伙伴,为您提供TeamCity、Perforce Helix Core等热门的游戏开发工具及一站式服务

TeamCity 是游戏开发的热门选择,大家选择它的原因包括支持 Perforce、可以进行本地安装,并提供了多种配置选项。除了将它主要用作通用 CI/CD 解决方案之外,JetBrains 还努力为各种构建工具提供专门支持。JetBrains 已提供 Unity 插件数年,去年也正式推出了 Unreal Engine 支持插件

在这款新插件的开发过程中,JetBrains 一直与多位游戏开发客户保持密切联系,以确保它符合从事实际 Unreal Engine 项目工作的 DevOps 团队的需求。

为 Unreal Engine 游戏设置合适的构建管道可能是一项艰巨的任务,特别是对于那些在平台独特性方面经验有限的人来说。本文,我们将带您了解基本设置,同时会展示该插件的功能,并演示分布式 BuildGraph 支持等高级功能

 新 Unreal Engine 插件生成的示例构建管道

主要功能

以下是此版插件所提供功能的快速概览:

  • 专为最常见用例(BuildCookRun、BuildGraph 和自动化测试)定制的专用运行程序。

  • 基于 BuildGraph 描述构建发行版。

  • 即时自动化测试报告。

  • 自动发现构建计算机上的 Unreal Engine 安装。

  • 兼容最新的 5.x 版 Unreal Engine,还支持 4.x 版本。

演示

在本演示中,我们将为随 Unreal 提供的两款入门级游戏 Cropout 和 Lyra 设置管道:我们将为构建代理和安装了 Unreal Engine 插件的 TeamCity Cloud 使用 AWS 基础架构(EC2 和用于 FSx for OpenZFS)。

我们将介绍两个场景:引擎已安装在代理上,以及引擎与游戏一起通过源代码构建。目前,TeamCity Cloud 不为代理提供预装的 Unreal Engine,但您可以随时添加您自己的包含所有必需 SDK 的自托管代理。我们将在这篇博文中采用这种方式。

使用 BuildCookRun 构建 Cropout

构建 Unreal 项目通常涉及多个‍步骤,例如:

  • 采用指定配置针对目标平台进行编译。

  • 资源烘焙(将所有资源转换为可在目标平台上读取的资源)。

  • 将项目封装为合适的分发格式。

  • 等等。

当然,之后还要进行测试!

首先,我们来看一个简单的场景,并使用标准 BuildCookRun 命令在一台计算机上按顺序运行所有阶段。

对于此场景,我们将使用撰写这篇博文时可以获取的最新普通版 Unreal Engine,并通过 EGL(Epic Games 启动器)进行安装。

将代理成功连接到 TeamCity Cloud 服务器后,我们可以在 Agents(代理)标签页中看到它。

现在,我想简单探讨一下代理的一些属性。

查看上图,我们可以看到代理发现了引擎及其版本(如您所见,我们的计算机上安装了多个引擎)。请记下此信息,因为稍后我们会将此信息用于构建步骤配置。

现在,常用的方式是将您所有的配置以代码形式存储在源代码控制系统下(也就是“配置即代码”)。在 TeamCity 中,您可以使用 Kotlin DSL 执行此操作。当然,您还可以使用传统 UI 配置,但今天我们将使用第一种方式(由于 YAML 已得到广泛使用,并且已成为事实上的标准,我们已将它添加到最近发布的 TeamCity Pipelines – 如果您还没有查看,请查看)。

用于在 TeamCity 中构建 Cropout 的 Kotlin DSL 配置如下所示:

unrealEngine {engineDetectionMode = automatic {identifier = "5.4"}command = buildCookRun {project = "cropout/CropoutSampleProject.uproject"buildConfiguration = standaloneGame {configurations = "Development+Shipping"platforms = "Mac"}cook = cookConfiguration {maps = "Village+MainMenu"cultures = "en"unversionedContent = true}stage = stageConfiguration {directory = "./staged"}archive = archiveConfiguration {directory = "./archived"}pak = truecompressed = trueprerequisites = true}additionalArguments = "-utf8output -buildmachine -unattended -noP4 -nosplash -stdout -NoCodeSign"
}

所有设置的作用都一目了然,但有几项设置需要进一步说明:

  • engine detection mode

    我们可以在此处指定“automatic”或“manual”。前者假定您已经在代理上安装引擎并在系统中注册。

    我借此机会澄清一下“注册 ”的含义:当您通过 EGL 安装 Unreal Engine 或通过源代码构建 Unreal Engine 时,此操作会写入目标计算机上的某些文件(如果使用的是 Windows,则会写入注册表)。

    这基本上就是自动检测模式的用途。我们读取这些文件并发布标识符作为代理属性,这样一来,您稍后便可使用这些属性为构建选择合适的代理。

    “手动”模式允许您设置 Unreal Engine 根文件夹的准确路径,当您通过源代码构建时可能会用到该路径。稍后我们将介绍这部分内容。

  • build configuration

    通过调整此参数,我们可以指定所创建构建的类型,它是单机游戏、客户端、服务器,还是既是客户端又是服务器组件。

    根据所选的值,插件将应用 -client-server-noserver 标志,并相应地管理 -clientconfig-serverconfig-config-targetplatform 和 -servertargetplatform 参数。

到现在为止都没问题。现在,该添加一些自动化测试并运行最终管道了。

unrealEngine {engineDetectionMode = automatic {identifier = "5.4"}command = runAutomation {project = "cropout/CropoutSampleProject.uproject"execCommand = runTests {tests = """StartsWith:JsonConfigInput.Triggers.Released""".trimIndent()}nullRHI = true}additionalArguments = "-utf8output -buildmachine -unattended -noP4 -nosplash -stdout -NoCodeSign"
}

如您所知,使用 Unreal Engine 自动化框架时,通常使用以下自动化“子命令”之一执行测试:

  • RunAll – 这是一个非常简单的命令,能够运行所有必需的测试。

  • RunFilter – 借助此命令,可以执行使用一个指定筛选器(包括 EngineStressSmoke 等值)标记的测试。

  • RunTests – 这是最通用的命令,因为您可以使用该命令指定要运行的测试列表(包括使用 StartsWith 的前缀筛选器、运行组筛选器和简单的子字符串匹配)。

我们尝试在 Kotlin DSL 中保留相同的用词,以便 Unreal 用户对此感到熟悉。

还要指出的是,插件会即时解析测试结果,并以 TeamCity 自带的正确显示的格式呈现测试结果。

以下是构建结果示例:

在下方,我们可以看到发布到工件存储的游戏二进制文件。默认情况下,工件会发布到内置存储;但 TeamCity 还支持多种外部存储选项。在云环境中,可以配置发布到 S3。

UI 中的最终配置将如下所示:

下面是测试运行的步骤:

由于我们已启用 Kotlin DSL 配置并禁止在 UI 中对其进行编辑,所有设置均被禁用。

使用 BuildGraph 构建 Lyra

我们将逐步过渡到使用 Lyra 的更复杂示例。由于它是一款多人游戏,我们来构建 Linux 服务器(据我们所知,一切高性能游戏都应在 Linux 上运行!)以及 Windows 和 Linux 游戏客户端。

在前面的示例中,我们在一台计算机上按顺序运行了所有步骤。这一过程通常需要大量时间。但还有一种更好的方式,即 BuildGraph。这是一种 Unreal 方法,以声明方式(或几乎以声明方式)将构建描述为一组具有依赖关系的任务。随后,构件图的不同部分可以共同执行,或拆分到不同的计算机上。后一种方式可以显著加快整个构建过程的速度,特别适合大型复杂项目。

BuildGraph 的优点是它会自动管理作业之间的所有中间工件。您只需要共享存储。构建 Lyra 时,我们会将此共享存储用于两个目的:在单个构建中的节点之间共享数据,以及维护共享派生数据缓存 (DDC)。

我们之前提到过,我们还将通过源代码构建引擎。您通过源代码构建引擎可能是出于多种原因,但就我们的特定情况而言,我们这样做是因为无法使用任何方式通过引擎(从 EGL 安装)构建 Linux 服务器(至少在撰写这篇博文时无法实现)。这是因为此特定引擎版本未提供相应文件和 SDK。

好的,序言介绍完毕 — 我们来看看代码。

项目结构和 Perforce 中的相应流如下所示(假设您已从 Epic 的 Perforce 获取 Unreal Engine 源代码):

下面是我们将用于构建的 BuildGraph XML 脚本的节选。脚本的这一特定部分给出了编辑器的构建方式以及编译所需的工具, 其中还包含几项测试。在我们的简单示例中,这些测试采用硬编码。在真实场景中,您可能会将测试列表传递给脚本并采用更复杂的逻辑。 

...
<!-- Editors --><ForEach Name="Platform" Values="$(EditorPlatforms)" Separator="+"><Property Name="Compiler" Value="$(AgentPrefixCompile)$(Platform)" /><Agent Name="Build Editor and tools $(Platform)" Type="$(Compiler)">...<Property Name="ExtraToolCompileArguments" Value="$(ExtraToolCompileArguments) -architecture=arm64" If="'$(Platform)' == 'Mac'" /><Node Name="$(ToolsNodeName) $(Platform)" Requires="Setup Toolchain $(Platform)" Produces="#$(Platform)ToolBinaries"><Compile Target="CrashReportClient" Platform="$(Platform)" Configuration="Shipping" Arguments="$(ExtraToolCompileArguments)" Tag="#$(Platform)ToolBinaries"/><Compile Target="CrashReportClientEditor" Platform="$(Platform)" Configuration="Shipping" Arguments="$(ExtraToolCompileArguments)" Tag="#$(Platform)ToolBinaries"/><Compile Target="ShaderCompileWorker" Platform="$(Platform)" Configuration="Development" Arguments="$(ExtraToolCompileArguments)" Tag="#$(Platform)ToolBinaries"/><Compile Target="UnrealLightmass" Platform="$(Platform)" Configuration="Development" Tag="#$(Platform)ToolBinaries"/><Compile Target="InterchangeWorker" Platform="$(Platform)" Configuration="Development" Arguments="$(ExtraToolCompileArguments)" Tag="#$(Platform)ToolBinaries"/><Compile Target="UnrealPak" Platform="$(Platform)" Configuration="Development" Arguments="$(ExtraToolCompileArguments)" Tag="#$(Platform)ToolBinaries"/><Compile Target="BootstrapPackagedGame" Platform="$(Platform)" Configuration="Shipping" Arguments="$(ExtraToolCompileArguments)" Tag="#$(Platform)ToolBinaries" If="'$(Platform)' == 'Win64'"/></Node>...<Node Name="$(EditorNodeName) $(Platform)" Requires="$(ToolsNodeName) $(Platform)" Produces="#$(Platform)EditorBinaries"><Compile Target="$(ProjectName)Editor" Project="$(UProject)" Platform="$(Platform)" Configuration="Development" Arguments="$(ExtraEditorCompileArguments)" Tag="#$(Platform)EditorBinaries"/></Node><Property Name="AutomationTestsNodeName" Value="Run Tests $(Platform)" /><Node Name="$(AutomationTestsNodeName)" Requires="$(EditorNodeName) $(Platform)"><Property Name="TestArgs" Value="-Project=$(UProject) -NullRHI -Deterministic" /><Property Name="TestArgs" Value="$(TestArgs) -Test=UE.EditorAutomation -RunTest=&quot;StartsWith:Input&quot;" /><Property Name="TestArgs" Value="$(TestArgs) -Build=Editor -UseEditor" /><Command Name="RunUnreal" Arguments="$(TestArgs)" /></Node></Agent><Property Name="BuildNodes" Value="$(BuildNodes);$(EditorNodeName) $(Platform);$(AutomationTestsNodeName);" /></ForEach>...

有关完整语法的描述,请参阅 Epic 网站。这里,我们本质上要做的是通过选项 EditorPlatforms 迭代传递给脚本的平台列表,并为每个平台构建编辑器。这部分代码的一个有趣的功能是代理节点的 Type 属性。

<Agent ... Type="">

在文档中查看此表:

因此,为了在特定代理上运行一组任务,您可以修改此字段。撰写这篇博文时,为了确保一切正常运行,本部分需要对构建代理进行一些配置。也就是说,您应当在代理配置文件中定义两个属性:

  • unreal-engine.build-graph.agent.type:此属性可以是任意值(或由 ; 分隔的值列表)。相应的任务集随后将仅在至少有一个匹配项的代理上运行。

  • unreal-engine.build-graph.agent.shared-dir:前文中讨论过,为了运行分布式 BuildGraph 构建,我们需要一个共享存储位置。通过此属性,我们可以在特定代理上设置该存储位置的路径。

以下是我们的 EC2 构建代理设置:

  • 对于 Linux:

unreal-engine.build-graph.agent.type = CompileLinux;CookLinux;Linux
unreal-engine.build-graph.agent.shared-dir = /mnt/agent-shared-dir/intermediate
  • 对于 Windows(由于某些原因,将文件夹作为单独的驱动器进行装载导致一次 Windows 系统调用失败,因此我们在配置中选择了网络共享):
unreal-engine.build-graph.agent.type = CompileWin64;CookWin64;Win64
unreal-engine.build-graph.agent.shared-dir = \\\\fs-040b8d6dab476baf1.fsx.eu-west-1.amazonaws.com\\fsx\\

 在真实场景中,可能需要区分执行烘焙的代理和执行编译的代理,并确保它们采用合适的规范。

我们来快速浏览一下游戏的编译、烘焙和封装过程。在我们的脚本中,我们已将构建客户端的逻辑和构建服务器的逻辑分开(因为它们传递的标志不同):

<ForEach Name="Platform" Values="$(ClientPlatforms)" Separator="+"><!-- COMPILATION --><Property Name="Compiler" Value="$(AgentPrefixCompile)$(Platform)" /><Agent Name="Compile $(Platform) Client" Type="$(Compiler)"><Property Name="CompileNodeName" Value="Compile $(Platform) Client" /><Node Name="$(CompileNodeName)" Requires="$(ToolsNodeName) $(Platform)" Produces="#$(Platform) Client Binaries"><ForEach Name="TargetConfiguration" Values="$(TargetConfigurations)" Separator="+"><Compile Target="$(ProjectName)Client" Project="$(UProject)" Platform="$(Platform)" Configuration="$(TargetConfiguration)" Arguments="$(ExtraProjectCompileArguments)" /></ForEach></Node><Property Name="BuildNodes" Value="$(BuildNodes);$(CompileNodeName);" /></Agent><!-- COOKING --><Property Name="Cooker" Value="$(AgentPrefixCook)$(Platform)" /><Property Name="CookPlatformNodeName" Value="Cook $(Platform) Client" /><Agent Name="Cook $(Platform) Client" Type="$(Cooker)"><Property Name="CookPlatform" Value="$(Platform)" /><Property Name="CookPlatform" Value="Windows" If="'$(Platform)' == 'Win64'" /><Node Name="$(CookPlatformNodeName)" Requires="$(EditorNodeName) $(Platform)" Produces="#Cook $(Platform) Client Complete"><Cook Project="$(UProject)" Platform="$(CookPlatform)Client"/></Node></Agent><Property Name="BuildNodes" Value="$(BuildNodes);$(CookPlatformNodeName);" /><!-- PACKAGING --><Agent Name="Package $(Platform) Client" Type="$(Platform)"><Property Name="BCRArgs" Value="-Project='$(UProject)' -Platform=$(Platform) -NoCodeSign -Client" /><!-- Stage --><Node Name="Stage $(Platform) Client" Requires="Compile $(Platform) Client;Cook $(Platform) Client"><ForEach Name="TargetConfiguration" Values="$(TargetConfigurations)" Separator="+"><Command Name="BuildCookRun" Arguments="$(BCRArgs) -Configuration=$(TargetConfiguration) -SkipBuild -SkipCook -Stage -Pak" /></ForEach></Node><!-- Package --><Node Name="Package $(Platform) Client" Requires="Stage $(Platform) Client"><ForEach Name="TargetConfiguration" Values="$(TargetConfigurations)" Separator="+"><Command Name="BuildCookRun" Arguments="$(BCRArgs) -Configuration=$(TargetConfiguration) -SkipBuild -SkipCook -SkipStage -Package" /></ForEach></Node><!-- Publish (Packages) --><Node Name="Archive $(Platform) Client" Requires="Package $(Platform) Client"><ForEach Name="TargetConfiguration" Values="$(TargetConfigurations)" Separator="+"><Command Name="BuildCookRun" Arguments="$(BCRArgs) -Configuration=$(TargetConfiguration) -SkipBuild -SkipCook -SkipStage -SkipPak -SkipPackage -Archive" /></ForEach></Node><Node Name="Publish $(Platform) Client" Requires="Archive $(Platform) Client"><Property Name="PublishPlatform" Value="$(Platform)" /><Property Name="PublishPlatform" Value="Windows" If="'$(Platform)' == 'Win64'" /><Log Message="##teamcity[publishArtifacts 'game/ArchivedBuilds/$(PublishPlatform)Client=>$(PublishPlatform)Client.zip']" /></Node></Agent><Property Name="BuildNodes" Value="$(BuildNodes);Archive $(Platform) Client;Publish $(Platform) Client" />
</ForEach>

脚本本身不需要过多解释。我们会迭代希望客户端在其上运行的平台列表,该列表会再次作为选项传递。我们针对每个平台进行编译、烘焙资源,最后将一切封装起来。作为封装过程的最后一步,我们使用服务消息将构建的客户端作为 TeamCity 构建工件发布。

您可能已经注意到,在这部分脚本中,我们有三个代理代表构建过程的三个阶段(编译、烘焙和封装)。编译需要使用属于编辑器的工具,但不需要编辑器本身:

<Node Name="$(CompileNodeName)" Requires="$(ToolsNodeName) $(Platform)" Produces="#$(Platform) Client Binaries">

同时,烘焙过程需要实际的编辑器:

<Node Name="$(CookPlatformNodeName)" Requires="$(EditorNodeName) $(Platform)" Produces="#Cook $(Platform) Client Complete">

由于这两个过程之间没有依赖关系,它们可以在不同的计算机上并行运行(在有足够多的计算机可用的情况下)。这一简单示例演示了使用 BuildGraph 的强大功能:您只需描述共享依赖项的各项工作,然后它们就会同时运行。

为了简洁起见,我们不会显示包含服务器部分的脚本的其余部分,因为这部分内容与我们刚才描述的内容非常相似,只有标志集合不同。

最后,我们要介绍 TeamCity 中的插件配置。由于大部分工作已在 BuildGraph XML 脚本中描述,DSL 配置相当简单:

params {param("env.UE_SharedDataCachePath", "/mnt/agent-shared-dir/ddc")param("env.UE-SharedDataCachePath", "\\\\fs-040b8d6dab476baf1.fsx.eu-west-1.amazonaws.com\\fsx\\ddc")
}
steps {unrealEngine {id = "Unreal_Engine"name = "Build"engineDetectionMode = manual {rootDir = "engine"}command = buildGraph {script = "game/BuildProject.xml"targetNode = "BuildProject"options = """ProjectPath=%teamcity.build.checkoutDir%/gameProjectName=LyraClientPlatforms=Linux+Win64ServerPlatforms=LinuxEditorPlatforms=Linux+Win64TargetConfigurations=Shipping""".trimIndent()mode = UnrealEngine.BuildGraphMode.Distributed}additionalArguments = "-utf8output -buildmachine -unattended -noP4 -nosplash -stdout -NoCodeSign"}
}

在这里,指定我们希望在分布式模式下运行提供的 BuildGraph 脚本。但我们可以根据自己的意愿随时选择在一台计算机上按顺序运行所有节点。我们还指定了一些环境变量,如果引擎要启用共享 DDC,则需要使用这些变量。这些文件夹应当已装载到连接至 TeamCity 的代理。

现在,您可以为我们的游戏传递以下包含三个平台选项的列表:ClientPlatforms、ServerPlatforms 和 EditorPlatforms。

UI 中的显示如下:

启动构建时,我们会获得此构建链:

因此,如我们所见,插件已将构建图转换为适当的 TeamCity 构建链。

我们可以在指定构建的相应标签页上查看已发布的工件。Linux 服务器的显示如下:

在结束之前,我们想强调一些要点,请您务必牢记:

  • 为了确保 BuildGraph 分发过程正常运行,您的构建配置应仅包含一个有效构建步骤。

  • 目前,该插件不会以任何方式管理共享存储中生成的工件的保留期。您负责正确设置保留期。

  • 这篇博文中的示例绝不可用于生产。您的真实场景可能会更加复杂。但这些示例可作为一个良好的起点。

您可以从 GitHub 仓库中获取这篇博文中展示的演示代码。如果您想尝试用于 TeamCity 的 Unreal Engine 插件,可从 JetBrains Marketplace 下载,并在 TeamCity On-Premises 服务器上安装。

对于 TeamCity Cloud 用户,我们已预装了 Unreal Engine 插件,您只需添加 Unreal Engine 构建步骤便可使用此插件。需要提醒您的是,TeamCity Cloud 代理没有预装 Unreal Engine,因此您需要使用自托管代理运行 Unreal Engine 构建。

针对一直使用预览版插件(从 0.x.x 开始的任何版本)的用户的重要说明:请注意,由于我们在 1.0.0 版中进行了更改并重新设计了多个功能,您的现有配置将被破坏。您可能需要使用新版插件重新创建这些配置。

后续计划

首先,我们期待收到您的反馈。您可以随时联系我们:通过我们的问题跟踪器提交工单或对这篇博文发表评论。

我们对今后的工作还有一些想法,包括:

  • 提供更加深入的构建日志分析。

  • 包含与 UnrealGameSync (UGS) 的集成,添加构建状态发布等功能,以及提供开箱即用的元数据服务器。

  • 添加对构建计算机上可用 SDK 的检测,可能会使用整体解决方案。

  • 了解 Gauntlet 自动化框架以及我们可以在其中执行的操作。

  • 了解我们如何利用 Epic Games 推出的构建过程中的最新进展,即查看 Unreal Build Accelerator (UBA) 并通过 TeamCity 在代理上进行协调。

  • 将插件开源。我们相信这将提高透明度,并从整体上创建更好的插件。此外,开源会带来很多乐趣。

本博文英文原作者:Vladislav Grinin

关于 TeamCity

TeamCity 是一款强大的持续集成和部署服务器,面向以 DevOps 为中心的团队提供开箱即用的测试智能、构建问题的实时报告以及无与伦比的可扩展性。安装和部署 TeamCity,几分钟之内即可开始构建您的 DevOps 管道。TeamCity 提供本地部署和基于云的版本。

进一步了解 TeamC‍ity:

JetBrains中国授权合作伙伴-龙智

官网:www.shdsd.com

电话:400-666-7732

邮箱:marketing@shdsd.com

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

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

相关文章

Three.js 快速入门教程【二】透视投影相机

系列文章目录 系列文章目录 Three.js 快速入门教程【一】开启你的 3D Web 开发之旅 Three.js 快速入门教程【二】透视投影相机 Three.js 快速入门教程【三】渲染器 Three.js 快速入门教程【四】三维坐标系 Three.js 快速入门教程【五】动画渲染循环 Three.js 快速入门教程【六…

无人机仿真、感知、规划

文章目录 1.仿真环境1.1 博客教学1.2 教学视频1基础无人机仿真教学视频介绍2 XTDrone无人机仿真与控制技术全面教程3 ROS机器人集群仿真与实践教程 1.3 开源项目及插件1 ROS2-Gazebo Drone Simulation Plugin2 RotorS_UAV_Gazebo_Simulator3 自主无人机与Aruco导航教程4 基于 A…

php文件包含

文章目录 基础概念php伪协议什么是协议协议的格式php中的协议file协议http协议ftp协议php://input协议php://filter协议php://data协议 php文件上传机制高级文件包含nginx文件日志包含临时文件包含session文件包含pear文件包含远程文件包含 基础概念 文件包含&#xff0c;相当…

【超详细】神经网络的可视化解释

《------往期经典推荐------》 一、AI应用软件开发实战专栏【链接】 项目名称项目名称1.【人脸识别与管理系统开发】2.【车牌识别与自动收费管理系统开发】3.【手势识别系统开发】4.【人脸面部活体检测系统开发】5.【图片风格快速迁移软件开发】6.【人脸表表情识别系统】7.【…

微软CEO-纳德拉访谈-AGI计划

在与知名科技播客主播 Dwarkesh Patel 的深度访谈中,微软 CEO 萨提亚・纳德拉围绕 AI、量子计算、微软发展等多方面分享深刻见解,下面是针对访谈内容的介绍,其中还是有很多值得我们学习的地方。 1 AI 领域见解 影响力评估:纳德拉直言行业所标榜的 AGI 里程碑是 “无意义的基…

HAProxy介绍与编译安装

目录 1、HAProxy介绍 2、HAProxy编译安装 Centos 基础环境 Ubuntu 基础环境 编译安装HAProxy 验证HAProxy版本 HAProxy启动脚本 配置文件 启动haproxy 验证haproxy状态 查看haproxy的状态页面 1、HAProxy介绍 HAProxy是法国开发者 威利塔罗(Willy Tarreau) 在2000年…

细说STM32F407单片机2个ADC使用DMA同步采集各自的1个输入通道的方法

目录 一、示例说明 二、工程配置 1、RCC、DEBUG、CodeGenerator 2、USART6 3、TIM3 &#xff08;1&#xff09;Mode &#xff08;2&#xff09;参数设置 &#xff08;3&#xff09; TRGO &#xff08;4&#xff09;ADC1_IN0 1&#xff09;ADCs_Common_Settings 2&a…

从零开始用react + tailwindcs + express + mongodb实现一个聊天程序(一)

项目包含5个模块 1.首页 (聊天主页) 2.注册 3.登录 4.个人资料 5.设置主题 一、配置开发环境 建立项目文件夹 mkdir chat-project cd chat-project mkdir server && mkdir webcd server npm init cd web npm create vitelatest 创建前端项目时我们选择javascrip…

idea从远程gitee拉取项目

文章目录 从gitee上面拿到项目地址填写远程地址,并且设置项目保存位置拉取成功 从gitee上面拿到项目地址 填写远程地址,并且设置项目保存位置 拉取成功

大数据学习之PB级音乐数据中心数仓综合项目(1)-理论知识和项目需求、歌曲热度与歌手热度排行

一、理论知识和项目需求 1.课程介绍 2.数据库与ER建模_数据库三范式 3.数据库与ER建模_ER实体关系模型 4.数据库与维度建模_数据仓库(DATA WAREHOUSE) 5.数据库与维度建模_数据库与数据仓库区别 6.数据库与维度建模_数据仓库的发展历程 7.数据库与维度建模_维度建模 8.数据库与…

数据结构之队列

1. 队列的概念及结构 1.1 队列的概念 队列&#xff1a;只允许在一段进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性表&#xff0c;队列具有先进先出FIFO(First In First Out) 入队列&#xff1a;进行插入操作的一端称为队尾 出队列&#xff1a;进行删除操…

计算机网络-面试总结

计算机网络 从输入一个URL到页面加载完成的过程 整体流程 DNS查询过程SSL四次握手HTTP 的长连接与短连接 HTTP 的 GET 和 POST 区别浏览器访问资源没有响应&#xff0c;怎么排查? OSI七层参考模型 TCP/IP四层参考模型比较 TCP/IP 参考模型与 OSI 参考模型 TCP三次握手&四…

kafka消费能力压测:使用官方工具

背景 在之前的业务场景中&#xff0c;我们发现Kafka的实际消费能力远低于预期。尽管我们使用了kafka-go组件并进行了相关测试&#xff0c;测试情况见《kafka-go:性能测试》这篇文章。但并未能准确找出消费能力低下的原因。 我们曾怀疑这可能是由我的电脑网络带宽问题或Kafka部…

正式页面开发-登录注册页面

整体路由设计&#xff1a; 登录和注册的切换是切换组件或者是切换内容&#xff08;v-if和 v-else)&#xff0c;因为点击两个之间路径是没有变化的。也就是登录和注册共用同一个路由。登录是独立的一级路由。登录之后进到首页&#xff0c;有三个大模块&#xff1a;文章分类&…

Oracle 深入理解Lock和Latch ,解析访问数据块全流程

Oracle 锁机制介绍 根据保护对象的不同&#xff0c;单实例Oracle数据库锁可以分为以下几大类&#xff1a; DML lock&#xff08;data locks&#xff0c;数据锁&#xff09;&#xff1a;用于保护数据的完整性&#xff1b; DDL lock&#xff08;dictionary locks&#xff0c;字典…

Codes 开源免费研发项目管理平台 2025年第一个大版本3.0.0 版本发布及创新的轻IPD实现

Codes 简介 Codes 是国内首款重新定义 SaaS 模式的开源项目管理平台&#xff0c;支持云端认证、本地部署、全部功能开放&#xff0c;并且对 30 人以下团队免费。它通过创新的方式简化研发协同工作&#xff0c;使敏捷开发更易于实施。并提供低成本的敏捷开发解决方案&#xff0…

aws(学习笔记第二十九课) aws cloudfront hands on

aws(学习笔记第二十九课) 使用aws cloudfront 学习内容&#xff1a; 什么是aws cloudfront练习使用aws cloudfront 1. 什么是aws cloudfront aws cloudfront的整体架构 这里可以看出&#xff0c;aws引入了edge location的概念&#xff0c;用户的client与edge location进行是…

写大论文的word版本格式整理,实现自动生成目录、参考文献序号、公式序号、图表序号

前情提要&#xff1a;最近开始写大论文&#xff0c;发现由于内容很多导致用老方法一个一个改的话超级麻烦&#xff0c;需要批量自动化处理&#xff0c;尤其是序号&#xff0c;在不断有增添删减的情况时序号手动调整很慢也容易出错&#xff0c;所以搞一个格式总结&#xff0c;记…

AWS - Redshift - 外部表读取 Parquet 文件中 timestamp 类型的数据

问题&#xff1a; 通过 Redshift Spectrum 功能可以读取 S3 中的文件&#xff0c;当读取 Parquet 文件时&#xff0c;如果列格式设置为 timestamp&#xff0c; 通过 psql 客户端读取会出现以下错误&#xff1a; testdb# select * from myspectrum_schema_0219.test_ns; ERROR…

单片机总结【GPIO/TIM/IIC/SPI/UART】

一、GPIO 1、概念 通用输入输出口&#xff1b;开发者可以根据自己的需求将其配置为输入或输出模式&#xff0c;以实现与外部设备进行数据交互、控制外部设备等功能。简单来说&#xff0c;GPIO 就像是计算机或微控制器与外部世界沟通的 “桥梁”。 2、工作模式 工作模式性质特…