当前位置:首页 > 技术文章 > 正文内容

Go 1.25 发布:从性能飞跃到工具链革新

一、Go 1.25 版本概述

Go 1.25 是 Go 语言的第 25 个主要版本,于 2025 年 8 月 12 日正式发布。作为半年一次的常规更新,Go 1.25 延续了 Go 语言一贯的设计理念:在保证100% 向后兼容性的前提下,通过底层优化、工具链增强和标准库扩展,持续提升开发者体验和程序性能。

Go 1.25 版本在多个关键领域进行了改进:

  • 引入了新的实验性垃圾收集器,显著降低 GC 开销
  • 增强了容器环境下的资源管理能力
  • 改进了工具链,提供更强大的调试和分析功能
  • 更新了标准库,增加了新的 API 和功能
  • 优化了编译器和运行时,提升了程序性能

本文将全面解析 Go 1.25 的新特性、改进点和兼容性变化,帮助开发者了解如何充分利用这个新版本的优势。

二、新语言特性

2.1 核心类型概念的移除

Go 1.25 最显著的语言变化是正式从语言规范中移除了 "核心类型"(Core Types) 这一概念。这一变化源于 Go 1.18 引入泛型时为简化规范而引入的临时解决方案,现在随着对泛型理解的深入,这一概念已完成其历史使命。

"核心类型" 是 Go 1.18 引入泛型时的一个过渡性概念,用于简化泛型初期的规范定义和编译器实现。然而,这一概念逐渐暴露出诸多问题:

  • 过度限制:基于核心类型的规则往往比基于类型集的规则更严格,限制了泛型代码的灵活性
  • 增加认知负担:开发者需要理解这一额外概念,尤其是在学习切片表达式等非泛型操作时
  • 规则不一致:像索引、len、cap 等操作的规则是基于类型集设计的,与基于核心类型的规则形成不一致感
  • 阻碍未来发展:核心类型的存在限制了更灵活的切片操作和类型推断改进的可能性

Go 1.25 通过以下方式解决这些问题:

  • 重写规范描述:将所有依赖 "核心类型" 的规范改用更明确、独立的语言描述,非泛型操作回归到 Go 1.18 之前的基于具体类型的描述方式
  • 移除核心类型章节:从规范中彻底删除关于核心类型的定义和解释

这一变化不会对现有代码的行为产生任何影响,保证了 100% 的向后兼容性。同时,编译器输出的错误信息也将更新,不再提及 "核心类型",有望提供更具体、指向性更强的错误提示。

2.2 类型参数推导改进

Go 1.25 进一步优化了类型参数的推导机制,使开发者在调用泛型函数时更少需要显式指定类型参数。这一改进使得泛型代码更加简洁,提高了代码的可读性和可维护性。

具体改进包括:

  • 更智能的类型推断算法
  • 更广泛的类型参数推导范围
  • 更友好的错误提示,当类型推导失败时

这些改进使得 Go 泛型的使用体验更加流畅,减少了样板代码,使开发者能够更专注于业务逻辑的实现。

2.3 接口类型匹配改进

Go 1.25 改进了接口类型的匹配规则,使得接口类型的使用更加灵活和直观。这一改进对于编写更通用的代码和数据结构特别有用,尤其是在泛型编程中。

改进后的接口匹配规则更加直观,减少了需要显式类型断言的情况,提高了代码的可读性和安全性。

三、标准库更新

3.1 新增标准库包

Go 1.25 引入了多个新的标准库包和模块,为开发者提供了更多功能和工具:

3.1.1 testing/synctest 包

testing/synctest 包为测试并发代码提供支持。其核心功能包括:

  • 在独立的 "气泡"(bubble) 中运行测试函数
  • 为 time 包函数提供伪造的时钟
  • Wait 函数可以等待当前气泡中的所有 goroutine 阻塞

这一功能对于测试时间敏感和并发操作非常有用,特别是在需要控制时间流程或验证并发执行顺序的测试中。该包首次在 Go 1.24 中以实验形式提供,现已毕业为正式功能。

3.1.2 encoding/json/v2 包(实验性)

Go 1.25 包含一个新的实验性 JSON 实现,可以通过设置特定环境变量在构建时启用。启用后,会提供两个新包:

  • encoding/json/v2 包是对 encoding/json 包的重大修订
  • encoding/json/jsontext 包提供更底层的 JSON 语法处理

此外,当启用 "jsonv2" 实验时:

  • encoding/json 包将使用新的 JSON 实现,序列化和反序列化的行为不受影响,但错误文本可能会改变
  • encoding/json 包包含若干可用于配置序列化器和反序列化器的新选项
  • 在许多场景下,新实现的性能明显优于现有版本,总体上编码性能与旧实现相当,而解码在新实现中明显更快

3.1.3 其他新增包

  • crypto/ecdsa:新增了低级编码函数和方法,简化了 ECDSA 密钥的低级操作,减少了对 math/big 的依赖
  • hash/XOF 和 hash/Cloner:这些新接口支持可扩展输出函数,方便哈希状态的复制
  • io/fs/ReadLinkFS:统一了文件系统接口,支持符号链接读取

3.2 现有库的改进

Go 1.25 对多个现有标准库包进行了改进和优化:

3.2.1 crypto/tls 包增强

crypto/tls 包在 Go 1.25 中获得了多项改进:

  • 新增 Config.GetEncryptedClientHelloKeys 回调,支持 Encrypted Client Hello (ECH) 扩展,进一步提升 TLS 客户端的连接隐私
  • 默认禁用 TLS 1.2 握手中的 SHA-1 签名算法(但可以通过特定的 GODEBUG 选项重新启用)
  • 在 FIPS 140-3 模式下,允许使用更现代的 Ed25519 和 X25519MLKEM768 密钥交换算法
  • 新增 ConnectionState.CurveID 字段,用于诊断密钥交换机制

这些改进持续强化了 Go TLS 的安全性、隐私保护和合规性,为迎接未来的量子安全和更严格的安全标准做准备。

3.2.2 os 包改进

os 包在 Go 1.25 中得到了显著增强:

  • 在 Windows 上,NewFile 现在支持为异步 I/O 打开的句柄,这些句柄会与 Go 运行时的 I/O 完成端口关联,为 File 带来更好的性能和功能
  • DirFS 和 Root.FS 返回的文件系统实现了新的 io/fs.ReadLinkFS 接口,支持符号链接操作
  • CopyFS 在复制实现了 io/fs.ReadLinkFS 的文件系统时支持符号链接
  • Root 类型新增了多项文件系统操作方法,如 Chmod、Chown、Chtimes 等,提升了文件系统操作的灵活性和一致性

3.2.3 reflect 包改进

reflect 包新增了 TypeAssert 函数,允许将一个 Value 直接转换为指定类型的 Go 值。这类似于对 Value.Interface 的结果进行类型断言,但可避免不必要的内存分配,提升性能。

3.2.4 sync.WaitGroup 改进

sync.WaitGroup 新增了 Go 方法,为创建和计数 goroutine 提供了一个更便捷的封装,进一步简化了 Go 中常见的并发模式的写法。使用示例:

var wg sync.WaitGroup

for i := 0; i < 5; i++ {
    wg.Go(func(i int) {
        defer wg.Done()
        fmt.Println(i)
    }, i)
}

wg.Wait()

这一改进解决了 sync.WaitGroup 常见的使用模式,减少了潜在的并发错误,如在 Wait 之后调用 Add 导致的 panic。

3.2.5 runtime/trace 包改进

runtime/trace 包新增了 FlightRecorder API,提供轻量级执行跟踪能力。它将跟踪持续记录到一个内存环形缓冲区中,当发生重要事件时,程序可以调用 FlightRecorder.WriteTo,将最近几秒的跟踪快照写入文件。这种方法能够生成更小的跟踪文件,便于分析和调试。

3.3 文件系统接口改进

Go 1.25 对文件系统操作进行了重要改进,新增了 io/fs.ReadLinkFS 接口,提供在文件系统中读取符号链接的能力。这一接口定义如下:

type ReadLinkFS interface {
    FS
    ReadLink(name string) (string, error)
    Lstat(name string) (FileInfo, error)
}

同时,os.DirFS 和 os.Root.FS 现均已实现该接口,使得测试框架能够更好地模拟真实文件系统行为。这意味着在使用 os.DirFS 时,用户可以利用该接口来处理符号链接,提高了文件系统操作的灵活性和一致性。

四、性能改进

4.1 新实验性垃圾收集器

Go 1.25 引入了一个新的实验性垃圾收集器,可以通过设置特定环境变量在构建时启用。这个新 GC 的设计旨在改进小对象的标记和扫描性能,并提升 CPU 可扩展性。

根据官方的基准测试,在实际应用中,垃圾回收的开销有望减少10% 到 40%!这一改进将显著降低 Go 程序的 GC 停顿和整体资源消耗,对于所有 Go 应用(尤其是内存密集型应用)来说,是巨大的性能红利。

这个新的垃圾收集器通过以下方式提升性能:

  • 批量扫描优化:升级为按 span(内存块)进行批量扫描,显著提高内存访问局部性,减少了每个 stop-the-world 阶段的持续时间
  • 并行队列管理优化:使用类似于 Go 调度器的工作窃取机制,进一步提升了多核扩展性
  • 更好的局部性和 CPU 可扩展性:优化了小对象的标记和扫描性能

4.2 容器感知的 GOMAXPROCS 自动调整

在容器化部署日益普及的今天,Go 程序在 Kubernetes 等环境中运行时常常会遇到一个问题:GOMAXPROCS(控制 Go 运行时使用的最大 CPU 核心数)默认值是宿主机逻辑 CPU 数,而非容器实际被分配的 CPU 限制。这可能导致 CPU 资源浪费,或程序试图抢占过多资源,进而引发调度问题。

Go 1.25 通过引入容器感知的 GOMAXPROCS 自动调整机制解决了这一问题:

在 Linux 系统上,Go 运行时现在会默认考虑 cgroup 的 CPU 限制(即容器的 CPU limit)来设置 GOMAXPROCS 的默认值。如果 CPU limit 低于宿主机核心数,GOMAXPROCS 将自动降到这个更低的限制。此外,Go 运行时还会定期更新 GOMAXPROCS,以适应 cgroup 限制的动态变化。

具体实现细节:

  • 在程序启动时,如果用户未通过环境变量 GOMAXPROCS 指定值,Go 运行时(仅在 Linux 上)将主动检测以下三项:
    a. 机器的总 CPU 核心数
    b. CPU 亲和性限制
    c. Cgroup CPU Quota 限制
  • 新的默认 GOMAXPROCS 计算方式:计算 Cgroup 限制值时,先向上取整,然后确保结果至少为 2(adjusted_cgroup_limit = max (2, ceil (effective_cpu_limit)))
  • 新的默认 GOMAXPROCS 值将是上述三者(机器核心数、CPU 亲和性限制、调整后的 Cgroup 限制)中的最小值
  • 自动更新机制:为了适应 CPU 限制或亲和性可能在运行时发生的变化,Go 运行时将引入一个后台机制定期重新检查 CPU 亲和性设置和 Cgroup 的 CPU quota 文件。如果检测到变化导致计算出的默认 GOMAXPROCS 值改变,运行时将自动更新

这一改进,直接解决了 Go 应用在容器环境中可能存在的资源配置不当问题,使得 Go 程序在 K8s 等云原生环境中运行时更加高效和 "智能"。

4.3 Profile-Guided Optimization (PGO) 稳定版

Go 1.25 提供了 Profile-Guided Optimization (PGO) 的稳定版本,这是一种编译器优化技术,允许根据应用程序在运行时的性能数据来优化代码。PGO 在 Go 1.21 中首次引入作为实验性功能,现在在 Go 1.25 中已成为稳定功能。

使用 PGO 构建 Go 程序时,编译器会查找名为 default.pgo 的 pprof CPU 配置文件。根据官方数据,典型程序在优化后 CPU 时间减少2-14%。这一稳定版本的发布使 PGO 成为 Go 开发者进行性能优化的重要工具,无需额外配置即可获得性能收益。

4.4 内存管理改进

Go 1.25 在内存管理方面进行了多项重要改进:

4.4.1 更高效的内存分配

Go 1.25 优化了内存分配策略,减少了内存碎片,提高了内存使用效率。这些改进使得程序在处理大量对象时能够更高效地利用内存资源,减少了内存分配和回收的开销。

4.4.2 更高效的 interned 值回收

Go 1.25 改进了 unique 包的实现,更积极、高效、并行地回收 interned 值,减少内存膨胀,提升内存回收效率。这对于 Go 编译器、LSP (Language Server Protocol) 等会大量使用 unique 包的场景,将带来显著的内存和性能优化。

4.4.3 内存泄漏检测增强

go build -asan 选项在 Go 1.25 中默认启用了内存泄漏检测机制。如果 C 分配的内存未被释放且未被其他 C 或 Go 分配的内存引用,它将报告错误。开发者可以通过设置特定环境变量来禁用此功能。这对调试 Go 程序中 C/C++ 互操作性问题(特别是使用 cgo 的程序)来说是一个重要的增强。

4.4.4 切片操作优化

编译器现在在更多情况下能够将切片的底层存储分配到栈上,从而提高性能。此更改可能会放大对不正确使用 unsafe.Pointer 的影响,但通过更高效的内存管理,整体性能得到了提升。

4.5 编译器优化

Go 1.25 对编译器进行了多项重要改进:

4.5.1 改进的逃逸分析

Go 1.25 改进了逃逸分析,允许编译器更有效地优化 goroutines 的内存分配调用。这使得编译器能够更准确地确定变量应该分配在栈上还是堆上,从而减少不必要的堆分配,提高程序性能。

4.5.2 更高效的代码生成

Go 1.25 的编译器生成的代码更加高效,特别是在处理循环和条件语句时。这一改进使得程序执行速度更快,资源消耗更少。

4.5.3 更好的错误提示

编译器改进了错误提示,提供更详细、更有用的信息,帮助开发者更快地定位和解决问题。这一改进提高了开发效率,减少了调试时间。

4.5.4 修复 nil 指针检查延迟问题

编译器修复了长期存在的 nil 指针检查延迟问题。以下典型错误代码在 1.21-1.24 版本中能异常运行,但在 1.25 中将正确触发 panic:

package main

import "os"

func main() {
    f, err := os.Open("nonExistentFile")
    name := f.Name() // 此处触发nil指针解引用
    if err != nil {
        return
    }
    println(name)
}

正确的做法应始终先检查错误:

f, err := os.Open("nonExistentFile")
if err != nil {
    return
}
name := f.Name()

该修复强制要求开发者遵循 Go 语言规范,确保代码的健壮性。

4.6 并发模型改进

Go 1.25 对并发模型进行了多项改进:

4.6.1 更高效的 goroutine 调度

Go 1.25 改进了 goroutine 调度器的实现,减少了调度延迟,提高了并发性能。这一改进使得 Go 应用在高并发场景下能够更高效地利用 CPU 资源,减少了上下文切换的开销,提高了吞吐量。

4.6.2 并发代码分析器增强

Go 1.25 引入了多个新的静态分析器,帮助开发者检测并发代码中的常见错误:

  • 新增 waitgroup 分析器:报告对 sync.WaitGroup.Add 的错误调用位置。sync.WaitGroup 是常见的并发 bug 来源,如果 Add 在 Wait 之后或在可能在 Wait 之后启动的 goroutine 中调用,就可能导致微妙的错误
  • 新增 hostport 分析器:报告使用 fmt.Sprintf ("% s:% d", host, port) 构造 net.Dial 地址(不适用于 IPv6)的情况,并建议改用 net.JoinHostPort

这些分析器作为 go vet 工具的一部分,能够帮助开发者在早期发现并发代码中的潜在问题,提高代码的稳定性和可靠性。

五、工具链增强

5.1 编译器改进

Go 1.25 对编译器进行了多项重要改进:

5.1.1 DWARF 调试信息升级

编译器工具链现在默认生成 DWARF 5 格式的调试信息。相较于旧版本,新格式可节省约 30% 的调试信息存储空间。该特性通过特定环境变量启用,开发者可使用对应变量暂时禁用此功能。

5.1.2 更高效的函数内联

改进了函数内联算法,能够更智能地决定哪些函数应该被内联,从而减少函数调用开销。这一改进提高了程序的执行速度,特别是对于频繁调用的小型函数。

5.1.3 链接器改进

链接器现在接受-funcalign=N命令行选项,用于指定函数入口的对齐方式。默认值依赖于平台,在本次发布中保持不变。这一改进为特定平台和应用场景提供了更多的优化选项。

5.2 调试工具改进

Go 1.25 对调试工具进行了多项改进:

5.2.1 FlightRecorder API

运行时执行跟踪长期以来是理解和调试应用程序底层行为的强大但代价高昂的手段。Go 1.25 新增的
runtime/trace.FlightRecorderAPI 提供了一种轻量级的方式来捕获运行时执行跟踪:它将跟踪持续记录到一个内存环形缓冲区中。当发生重要事件时,程序可以调用FlightRecorder.WriteTo,将最近几秒的跟踪快照写入文件。通过让应用只捕获关心的跟踪片段,这种方法能够生成更小的跟踪文件。

5.2.2 未处理 panic 输出优化

在异常处理机制方面,运行时系统改进了重复 panic 的日志输出格式。原来的重复 panic 信息:

panic: PANIC [recovered]
    panic: PANIC

现已优化为更清晰的表述:

panic: PANIC [recovered, reraised]

这种改进使得异常日志的可读性显著提升,特别是在复杂调用链中定位问题时更为直观。

5.2.3 内存映射标注

针对 Linux 系统的匿名内存映射(VMA),运行时现在支持通过CONFIG_ANON_VMA_NAME内核特性标注内存用途。例如堆内存会被标记为[anon: Go: heap],这对内存分析工具的使用具有重要价值。开发者可通过特定GODEBUG选项关闭此功能。

5.3 新的和改进的命令行工具

Go 1.25 引入了多个新的命令行工具功能,并改进了现有的工具:

5.3.1 go build 改进

go build -asan选项现在默认在程序退出时进行泄漏检测。如果由 C 分配的内存既未被释放,又不被任何其他由 C 或 Go 分配的内存引用,则会报告错误。运行程序时可以通过在环境中设置特定变量来禁用这些新错误报告。

5.3.2 go.mod ignore 指令

新的go.mod ignore指令可用于指定go命令应忽略的目录。位于这些目录及其子目录中的文件在匹配包模式(例如all或./...)时会被go命令忽略,但仍会包含在模块zip文件中。这对于管理包含大量非 Go 代码、文档、或子模块的超大型代码仓库(Monorepo)非常有用,可以减少构建和扫描时间,提高 Go Modules 的灵活性。

5.3.3 go doc -http 选项

新的go doc -http选项将启动一个文档服务器,展示所请求对象的文档,并在浏览器窗口中打开该文档。这极大地提升了开发者的体验,使得无需离开终端即可更快、更方便地浏览本地 Go 文档。

5.3.4 go version -m -json 选项

新的go version -m -json选项会打印给定 Go 二进制文件中嵌入的runtime/debug.BuildInfo结构的 JSON 编码。它提供了一种以编程方式从已编译二进制文件中提取构建信息(如模块版本、Go 版本、构建标志)的方法。

5.3.5 子目录模块根支持

go命令现在支持在解析模块路径时将仓库的子目录作为模块根路径,通过使用类似repo-url//subdir的语法来指示root-path对应于repo-url中的subdir子目录(由版本控制系统vcs管理)。这增强了 Go 模块托管的灵活性,允许单个仓库包含多个模块,或模块位于大型项目的特定子目录中。

5.3.6 work 包模式

新的work包模式会匹配work(原名为main)模块中的所有包:在module模式下为单个work模块,或在workspace模式下为工作区内的一组模块。这简化了跨 Go 工作区中所有模块的操作,简化了诸如go test ./work...或go build ./work...等命令。

5.3.7 工具链行的简化

当go命令更新go.mod或go.work文件中的go行时,不再添加一行toolchain来指定当前命令的版本。这减少了go.mod和go.work文件中的噪音和不必要的修改,简化了版本控制。

5.4 分析器和静态检查工具

Go 1.25 对分析器和静态检查工具进行了多项改进:

5.4.1 go vet 增强

go vet命令包含了新的分析器:

  • waitgroup:报告对sync.WaitGroup.Add的错误位置调用(例如在启动 goroutine 之后调用Add)
  • hostport:报告使用fmt.Sprintf("%s:%d", host, port)来构建传给net.Dial的地址的情况,因为这种方式在 IPv6 下不可用;建议改用net.JoinHostPort

这些分析器能够帮助开发者在早期发现代码中的潜在问题,提高代码质量和可靠性。

5.4.2 其他分析工具改进

Go 1.25 还改进了其他静态分析工具和编译器警告,提供更准确、更有用的诊断信息,帮助开发者编写更健壮、更安全的代码。

六、兼容性变化

6.1 平台支持变化

Go 1.25 对多个平台的支持进行了调整:

6.1.1 Darwin/macOS 平台变化

如同在 Go 1.24 的发布说明中所述,Go 1.25 需要 macOS 12 Monterey 或更高版本,不再支持更早的系统版本。这一变化是 Go 持续推进对现代操作系统支持的一部分,有助于简化开发和测试工作,确保 Go 在最新的系统环境中能够充分发挥性能。

6.1.2 Windows 平台变化

Go 1.25 是最后一个包含有缺陷的 32 位 Windows/ARM 端口(GOOS=windows GOARCH=arm)的版本,该端口将在 Go 1.26 中被移除。这一变化是为了提高 Go 的整体质量和一致性,鼓励开发者迁移到更现代、更稳定的平台和架构。

6.1.3 RISC-V 平台改进

Linux/riscv64 端口现在支持plugin构建模式。GORISCV64环境变量现在接受一个新值rva23u64,用于选择RVA23U64用户模式应用程序配置文件。这一改进提高了 Go 在 RISC-V 架构上的可用性和功能。

6.1.4 Loong64 平台改进

Linux/loong64 端口现在支持数据竞争检测器(race detector),可以通过runtime.SetCgoTraceback从 C 代码收集回溯信息,并支持使用internal链接模式链接cgo程序。这使得 Go 在 Loong64 架构上的开发和调试更加便捷和高效。

6.2 行为变化和兼容性注意事项

Go 1.25 引入了一些行为变化,可能会影响现有的代码:

6.2.1 nil 指针检查行为变化

Go 1.25 修复了一个在 Go 1.21 中引入的编译器错误,该错误可能会错误地延迟对 nil 指针的检查。以下代码在 Go 1.21 到 1.24 中可能会错误地成功执行,但在 Go 1.25 中将正确触发 panic:

package main

import "os"

func main() {
    f, err := os.Open("nonExistentFile")
    name := f.Name() // 这里访问了nil指针
    if err != nil {
        return
    }
    println(name)
}

正确的做法是始终先检查错误:

f, err := os.Open("nonExistentFile")
if err != nil {
    return
}
name := f.Name()

这一变化提高了 Go 程序的可靠性,确保 nil 指针检查会及时且准确地执行,但可能会影响一些现有的代码。

6.2.2 未处理 panic 输出变化

当程序因一个被recover后又重新panic的未处理panic而退出时,打印的消息不再重复panic值的文本。之前,一个先panic("PANIC"),recover后又用原值重新panic的程序会打印:

panic: PANIC [recovered]
    panic: PANIC

现在该程序将打印:

panic: PANIC [recovered, repanicked]

这一变化提高了错误信息的可读性,但可能会影响依赖于特定错误消息格式的代码或工具。

6.2.3 符号链接处理变化

Go 1.25 改进了对符号链接的处理,特别是在使用io/fs.ReadLinkFS接口时。这可能会影响依赖于特定符号链接行为的代码,尤其是在测试和文件系统操作中。

相关文章

Vue基础(vue基础组件文件名符合规范的是)

Vue 是什么,它的核心特点有哪些?Vue 是一款渐进式 JavaScript 框架,它基于标准 HTML、CSS 和 JavaScript 构建,并提供了一套声明式的、组件化的编程模型,帮助开发者用...

Windows 下 Git 拉 Gitlab 代码(gitlab拉项目)

读者提问:『阿常你好,Windows 下 Git 拉 Gitlab 代码的操作步骤可以分享一下吗?』阿常回答:好的,总共分为五个步骤。一、Windows 下安装 Git官网下载链接:https://g...

同事git push到主分支上了,技术总监怒了

事情是这样的,同事前几天提交使用git提交代码的时候不小心提交到主分支上了,关键还提交成功了,这可是他自己开发的模块,还没测试的呢。技术总监也知道了,这下他慌乱了。最后还是技术总监给他兜底了。为了防止...

jenkins+gitlab 实现自动化部署(gitlab触发jenkins)

目录1、安装jdk,要记住安装路径2、安装maven,要记住安装路径3、安装git,要记住安装路径4、安装gitlab5、安装jenkins(centos7)创建安装目录下载通用war包启动和关闭Je...

基于Docker构建安装Git/GitLab,以及制作springboot工程镜像

今天给大家分享的是《领先的开源自动化服务器Jenkins的应用实战》之基于Docker安装构建Git/GitLab版本控制与代码云存储的场所;使用Git管理项目,springboot工程制作镜像知识体...

15款测试html5响应式的在线工具(测试类h5)

手机、平板灯手持设备的增多,网站要顺应变化,就必须要做响应式开发,响应式网站最大的特点在于可以在不同设备下呈现不同的布局,是基于html5+css3技术,目前越来越多的网站开始采用了响应式设计,而下面...