一文读懂API原理

简介: 一文读懂API原理

1

前 言

In the world of software management there exists a dreaded place called “dependency hell.” The bigger your system grows and the more packages you integrate into your software, the more likely you are to find yourself, one day, in this pit of despair.

依赖管理是一个语言必须要解决的问题,而且随着项目依赖的模块量以及复杂程度不断增加会显得更加重要。Go依赖管理发展历史可以归纳如下:

goinstall(2010.02):将依赖的代码库下载到本地,并通过import引用这些库
go get(2011.12):go get代替goinstall
godep(2013.09):godep提供了一个依赖文件,记录所有依赖具体的版本和路径,编译时将依赖下载到workspace中,然后切换到指定版本,并设置GOPATH访问(解决go get没有版本管理的缺陷)
gopkg.in(2014.03):通过import路径中添加版本号来标示不同版本,而实际代码存放于github中,go通过redirect获取代码。例如(import gopkg.in/yaml.v1,实际代码地址为:https://github.com/go-yaml/yaml)
vendor(2015.06);Go 1.5版本引入vendor(类似godep),存放于项目根目录,编译时优先使用vendor目录,之后再去GOPATH,GOROOT目录查找(解决GOPATH无法管控依赖变更和丢失的问题)
dep(2016.08):dep期望统一Go依赖管理,虽然提供了兼容其它依赖管理工具的功能,但是本质上还是利用GOPATH和vendor解决依赖管理
Go Modules(2018.08):Go 1.11发布的官方依赖管理解决方案,并最终统一了Go依赖管理(by Russ Cox)。Go Modules以semantic version(语义版本化)和Minimal Version Selection, MVS(最小版本选择)为核心,相比dep更具稳定性;同时也解决了vendor代码库依赖过于庞大,造成存储浪费的问题
通过如上历史,我们可以看出:Go依赖管理的发展历史,其实就是Go去google的历史(google内部没有强烈的版本管理需求),同时也是典型的社区驱动开发的例子

接下来,我将详细探讨Go Modules的两大核心概念:semantic version(语义化版本)和Minimal Version Selection, MVS(最小版本选择)

2

原 理

●semantic version●

Go使用semantic version来标识package的版本。具体来说:

MAJOR version when you make incompatible API changes(不兼容的修改)
MINOR version when you add functionality in a backwards compatible manner(特性添加,版本兼容)
PATCH version when you make backwards compatible bug fixes(bug修复,版本兼容)

这里,只要模块的主版本号(MAJOR)不变,次版本号(MINOR)以及修订号(PATCH)的变更都不会引起破坏性的变更(breaking change)。这就要求开发人员尽可能按照semantic version发布和管理模块(实际是否遵守以及遵守的程度不能保证,参考Hyrum's Law)

●Minimal Version Selection●

A versioned Go command must decide which module versions to use in each build. I call this list of modules and versions for use in a given build the build list. For stable development, today's build list must also be tomorrow's build list. But then developers must also be allowed to change the build list: to upgrade all modules, to upgrade one module, or to downgrade one module.

The version selection problem therefore is to define the meaning of, and to give algorithms implementing, these four operations on build lists:

Construct the current build list.
Upgrade all modules to their latest versions.
Upgrade one module to a specific newer version.
Downgrade one module to a specific older version.
这里将一次构建(go build)中所依赖模块及其版本列表称为build list,对于一个稳定发展的项目,build list应该尽可能保持不变,同时也允许开发人员修改build list,比如升级或者降级依赖。而依赖管理因此也可以归纳为如下四个操作:

构建项目当前build list
升级所有依赖模块到它们的最新版本
升级某个依赖模块到指定版本
将某个依赖模块降级到指定版本
在Minimal version selection之前,Go的选择算法很简单,且提供了 2 种不同的版本选择算法,但都不正确:

第 1 种算法是 go get 的默认行为:若本地有一个版本,则使用此版本;否则下载使用最新的版本。这种模式将导致使用的版本太老:假设已经安装了B 1.1,并执行 go get 下载,那么go get 不会更新到B 1.2,这样就会导致因为B 1.1太老构建失败或有bug

第 2 种算法是 go get -u 的行为:下载并使用所有模块的最新版本。这种模式可能会因为版本太新而失败:若你运行 go get -u 来下载A依赖模块,会正确地更新到B 1.2。同时也会更新到C 1.3 和E 1.3,但这可能不是 A 想要的,因为这些版本可能未经测试,无法正常工作

这 2 种算法的构建是低保真构建(Low-Fidelity Builds):虽然都想复现模块 A 的作者所使用的构建,但这些构建都因某些不明确的原因而变得有些偏差。在详细介绍最小版本选择算法后,我们将明白为什么最小版本选择算法可以产生高保真的构建:

Minimal version selection assumes that each module declares its own dependency requirements: a list of minimum versions of other modules. Modules are assumed to follow the import compatibility rule—packages in any newer version should work as well as older ones—so a dependency requirement gives only a minimum version, never a maximum version or a list of incompatible later versions.

Then the definitions of the four operations are:

To construct the build list for a given target: start the list with the target itself, and then append each requirement's own build list. If a module appears in the list multiple times, keep only the newest version.
To upgrade all modules to their latest versions: construct the build list, but read each requirement as if it requested the latest module version.
To upgrade one module to a specific newer version: construct the non-upgrad

相关文章
|
7月前
|
消息中间件 分布式计算 Java
探究Kafka原理-3.生产者消费者API原理解析(上)
探究Kafka原理-3.生产者消费者API原理解析
71 0
|
4月前
|
API 开发者 Python
API接口:原理、实现及应用
本文详细介绍了API接口在现代软件开发中的重要性及其工作原理。API接口作为应用程序间通信的桥梁,通过预定义的方法和协议实现数据和服务的共享。文章首先解释了API接口的概念,接着通过Python Flask框架示例展示了API的设计与实现过程,并强调了安全性的重要性。最后,本文还讨论了API接口在Web服务和移动应用程序等领域的广泛应用场景。
|
4月前
|
Kubernetes 负载均衡 API
在K8S中,api-service 和 kube-schedule 高可用原理是什么?
在K8S中,api-service 和 kube-schedule 高可用原理是什么?
|
7月前
|
XML 搜索推荐 API
通义千问API:让大模型使用各种工具
本章我们将通过一个简单的例子,揭示基于LangChain的Agent开发的秘密,从而了解如何扩展大模型的能力。
通义千问API:让大模型使用各种工具
|
6月前
|
存储 缓存 Java
JavaSE 字符串String及相关API StringBuilder StringJoiner 底层原理 详解
JavaSE 字符串String及相关API StringBuilder StringJoiner 底层原理 详解
50 2
|
7月前
|
安全 算法 Java
Java Stream API:原理、应用与深入解析
Java Stream API:原理、应用与深入解析
318 2
|
JSON 测试技术 API
API接口:原理、实现及应用
API接口:原理、实现及应用
Qt 窗口常用位置API函数 & 绘图原理 & 双缓冲机制 总结
Qt 窗口常用位置API函数 & 绘图原理 & 双缓冲机制 总结
|
7月前
|
安全 Java API
Spring工厂API与原理
Spring工厂API与原理
70 10
|
XML JavaScript API
API接口:原理、设计与应用
API,全称应用程序接口,是一些预先定义的函数,目的是提供应用程序与开发人员以访问一组例程的能力,我们可以在不暴露各内部例程的情况下,使用这些例程。API是构建在操作系统或应用程序之间的接口,它充当一个桥梁或纽带,使得不同的应用程序或系统之间可以相互交流和交互。