GoCenter助力Golang全速前进

一、背景

Go语言是Google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的编程语言。为了方便搜索和识别,有时会将其称为Golang。自2009年11月Google正式宣布推出,成为开放源代码项目以来,Go语言已成为当今开发人员和DevOps领域最流行的语言之一, 它被用于设计和编写Kubernetes和Helm。但是,相比语言本身已经得到了广泛的普及和使用,Go语言的包管理方案却大大滞后了。
Go语言生态系统中缺少的是标准化——没有用于依赖关系管理的标准工具, 也没有标准的包格式或兼容的包仓库规范。这意味着开发人员无法使用Go语言创建可重现的构建, 这是一个相当大的问题。这些年来, 社区推出了诸如dep、godep、glide和govender等工具,试图用来处理Go语言的依赖管理, 但并未成功。2018年Google在Go1.11官方推出了Go modules,为Go语言提供了支持版本化的依赖管理方案。
Go modules现在已成为Go语言标准的依赖管理工具和包仓库规范。而GoCenter为Go modules的实现和推广提供了依赖包的公共仓库,使得Go语言的开发人员能够更为稳定和方便地开发可重复构建的Go应用程序。

二、Go语言的依赖管理

在介绍GoCenter之前,我们先简要地回顾一下Go语言依赖管理的发展历程。
Go语言在推出之初,并没有明确的依赖管理方案。只是在构建过程中通过go get命令,将用import声明的依赖从对应的源,通常是git上的项目,下载到$GOPATH/src目录下,和Go应用自身的代码放在一起。这种依靠GOPATH来管理依赖的机制带来的问题 是显而易见的,比如:
? 因源依赖包自身的变化,导致不同时间构建Go应用时go get得到的依赖实质上是不同的,即不能实现可重复的构建;
? 或者因源依赖包自身的变化,重新构建Go应用时会引入不兼容的新实现,导致Go应用无法通过编译。
因此在1.5版本以前,为了规避这个问题,通常需要将使用的依赖包手工拷贝出来。
为了实现Go应用的可重复构建,Go1.5引入了Vendor机制。Vendor机制的核心就是在GOPATH下面增加了vendor文件夹。Go应用所需的依赖都可以从依赖源fork出所需的分支,存放到vendor文件夹。当构建Go应用时,Go编译器会优先在vendor文件夹下搜索依赖的第三方包,vendor文件夹下没有才会再到$GOPATH/src下去找。这样只要开发者预先将特定版本的依赖包存放在vendor文件夹,并提交到Go 项目的code repo,那么所有人理论上都会得到同样的编译结果,从而实现可重复构建。 在Go1.5发布后的若干年,Go社区把注意力都集中在如何利用Vendor机制解决Go应用的依赖管理问题,并诞生了众多的依赖管理工具,如dep、golide、govendor等。然而,和Java的Maven、Python的Pypi、C/C++的Conan等业界成熟的依赖管理方案相比,Vender机制仍然存在许多问题,比如:
? Vendor文件夹中的依赖包没有版本信息。这样依赖包脱离了版本管理,对于一致性管理、升级,以及问题追溯等场景,都会难以处理;
? Vendor机制没有给出如何能够方便地得到Go项目依赖了哪些包,并将其拷贝到vendor文件夹下的方案,多数情况下还需要到不同的Git源项目里手工拷贝;
? 当依赖的包比较多的时候,vendor文件夹也会变得非常庞大。
2018年年初,Go核心Team的技术leader,也是Go Team最早期成员之一的Russ Cox在个人博客上连续发表了七篇文章,系统阐述了Go team解决“包依赖管理”的技术方案:vgo。vgo的主要思路包括:语义导入版本控制(Semantic Import Versioning)、最小版本选择(Minimal Version Selection)、以及引入Go module等。同年5月份,Russ Cox的提案“cmd/go: add package version support to Go toolchain”被社区接受,vgo的代码合并到Go主干,并将这套机制正式命名为“go modules”。由于vgo项目本身就是一个实验原型,merge到主干后,vgo这个术语以及vgo项目的使命也就此结束了。后续Go modules机制将直接在Go主干上继续演化。
Go modules机制的主要变化包括:
? 去除了饱受诟病的GOPATH的限制。Go编译器将不再到GOPATH下面的vendor或src文件夹下搜索Go应用构建依赖的第三方包;
? Go modules机制为在同一应用repo下面的包赋予了一个新的抽象概念: 模块(module),即可重用的Go代码包,并启用一个新的文件go.mod记录模块的元信息和依赖关系。而在go.mod里明确描述了依赖包的版本信息,同一个依赖包也可以记录多个不同的版本。除了精确版本外,go.mod还支持用表达式模糊地定义依赖版本;
? 在Go modules机制下,还支持创建Go依赖包的公共仓库。这是因为应用程序包含的Go模块,必须从数千个独立的源代码存储库中解析,而每个存储库的维护纪律可能各不相同。因此,需要存在一个可公开访问的存储库,通过Go modules提供的依赖描述、解析机制,为Go的开发者提供一致的、可分享的、支持重复构建的、稳定的Go依赖包源。GoCenter就是这种Go依赖包公共仓库的重要实现。

三、GoCenter简介

GoCenter通过创建Go模块的公共中央仓库,提供可重复和快速依赖解析的依赖包管理方案,解决了Go开发人员查找和获取Go依赖包的困难。GoCenter将直接从源代码存储库获取Go项目,转变为处理和验证不可变的、具备版本控制的Go模块, 并将其免费提供给Go应用的开发人员。
GoCenter(https://gocenter.io)提供了通过公共Go代理解析模块, 包括通过托管免费服务搜索模块的能力。从创建开始, GoCenter已经包括了数千个广受欢迎的 Go项目的模块, Go开发者可以立即使用这些项目进行自己的构建。

开发人员也可以提交自己的Go项目加入GoCenter,以便将其提供给Go社区开发者,从而得到更为广泛地应用。

GoCenter这个中央仓库,提供了预先打包,以及版本化的Go模块,使得Go开发人员或团队不再需要为使用公共模块而构建自己的模块库, 从而消减了使用 Go 语言的巨大成本。
此外,如果Go开发者或团队已经有了自己的JFrog Artifactory仓库,就可以通过配置指向GoCenter的远程仓库,为重复构建提供完全的本地化控制,并可以预防访问GoCenter的网络连接问题。

四、基于GoCenter构建Go应用

要构建Go应用项目,首先需要安装Go客户端(版本1.11.0 或更新的版本) 。而安装Go之后,有三种方法可以从GoCenter解析Go模块:使用goc、使用 go 客户端,或部署本地仓库(如Artifactory),以代理GoCenter。

1、使用goc

推荐在构建中使用GoCenter的方式是通过goc工具。goc工具包装了Go的客户端,器, 能够使用GoCenter中的包正确构建Go应用,而无需手动设置。
要安装goc,需要使用以下的curl命令,或按照goc的github主页(https://github.com/jfrog/goc)的说明
$ curl -fL https://getgoc.gocenter.io | sh
然后, 就可以从Go项目的根目录中运行任何命令, 就像运行Go命令一样。例如:
$ goc build
goc工具自动分配GOPROXY连接GoCenter,所以能够优先从该仓库解析Go的依赖包。对于在GoCenter找不到的包,goc将会试图通过源代码控制系统来解析它们,以更好地保证成功构建Go项目。
Go客户端自身不能执行这种辅助操作(请参阅下文), 因此至少在 GoCenter能够为大多数Go开发人员提供可能需要的所有依赖之前,仍然建议使用goc。

2、使用Go客户端

推荐在构建中使用GoCenter的方式是通过goc工具。goc工具包装了Go的客户端,器, 能够使用GoCenter中的包正确构建Go应用,而无需手动设置。
如上所述,使用GoCenter时并不建议直接利用Go客户端进行构建,因为当在GoCenter找不到相关依赖包时构建会失败。对于Go客户端这种限制的详细信息,可以参考相关的issue和修正信息(https://github.com/golang/go/issues/26334)。Go开发人员还是应该改用goc
当然,如果在充分了解这个限制还希望使用的情况下,也是可以使用Go客户端的。
如果希望构建Go项目时从GoCenter中获取相关依赖包,需要设置GOPROXY指向GoCenter的URL,https://gocenter.io
$ export GOPROXY=https://gocenter.io
现在就可以使用Go客户端构建Go应用了:
$ go build

3、部署代理GoCenter的私有仓库

如果使用的是如Artifactory这样的私有仓库,则只需设置GOPROXY指向该私有仓库,而把GoCenter创建为该私有仓库当中的远程仓库。
为了要在Artifactory里创建代理GoCenter的远程仓库,需要遵循以下步骤:

  1. 创建新的远程仓库,并设置包类型为Go;
  2. 设置远程仓库的名字,并在URL字段输入https://gocenter.io/;
  3. 点击“保存 & 完成”按键。
    还可以创建虚拟仓库,用以聚合同时从本地Go仓库和远程仓库获取的Go依赖包。
    一旦在Artifactory里配置好使用GoCenter,就可以使用标准的GOPROXY方式基于Artifactory进行构建。需要注意的是,根据Artifacotry上的设置,需要适当地处理客户端的认证信息,应为当前Go客户端在获取模块时是不会发送相关认证信息的,所以处理起来是有一定难度的。因此,当使用Artifactory代理GoCenter时,建议使用JFrog CLI来构建Go应用。当配置好JFrog CLI和Artifactory的关联之后,就可以使用类似于
    “jfrog rt go build”的命令来从Artifactory获取依赖,并构建Go应用。
    使用JFrog CLI的好处是可以方便地向Artifactory上传针对特定构建而创建的依赖包,也同时内置支持生成和发布与构建过程相关的元数据。详细信息,请参考JFrog CLI的相关文档。

    五、搜索Go模块

    GoCenter首页中的搜索框可帮助按特定模块名称(例如, "虹膜")进行搜索。当执行搜索时,GoCenter将列出与搜索名称匹配或部分匹配的模块。

点击列表中的某个模块,将会列出GoCenter中该模块的所有版本:

列出的版本都利用颜色编码来指示其当前的可用状态:
绿色,表示该模块版本已在GoCenter之中且处于可用状态;
红色,表示该模块版本不存在,而且不可用;
灰色,表示该模块版本正在引入的过程中,尚未可用。
搜索结果还会显示那些Go项目在相关Git代码库存在,而在GoCenter尚不存在的模块版本列表。如果有这样的缺失版本,可以通过单击“Add missing version(s)”把它们 添加到GoCenter。

六、提交自己的Go模块

如果希望将自己的Go项目添加到GoCenter,使其可被Go社区的开发人员使用,则需要提交相关的加入申请。
首先可以对希望加入的模块名执行搜索。如果相关模块并不存在,则可以单击“Add”图标来请求添加模块。一旦点击,将会看到加入申请表格。在表格中,可以输入申请加入的Go模块的URL。通过搜索该模块的结果可以查看该模块的加入进度。
GoCenter将依据以下最低标准来验证加入请求:
? Go模块位于gihub.com或gopkg.in上的公共项目(repo);
? 该项目没有被设置为存档状态(archived);
? 该项目至少拥有3颗星

七、总结

自从2007年首次在谷歌构想,并于2009年正式推出,Go语言很快就成为最流行的编程语言之一。事实上,Helm和Kubernetes都是用Go语言编写的。在2017年的一项调查中,Go语言在开发者的偏好中排名最高,67%的开发者都在利用Go语言编程。
为此, 我们期望GoCenter能够为不断增长的Go社区和开发人员提供必要的服务,并帮助Go语言更加符合DevOps的需求。
通过访问GoCenter,https://gocenter.io,可以发现经常使用的Go依赖包都已经包含在其中了。如果还没有,请提交相关的加入申请
GoCenter管理了版本化的Go模块,可以和Go应用构建使用的任何CI服务器或私有仓库进行对接。而使用JFrog CLI和Artifactory,可以使得这一过程更加便捷。
想要了解有关 GoCenter 更多深入的技术信息?请查看GoCenter的Github项目,https://github.com/jfrog/gocenter

八、参考文献

Golang:https://golang.org
Go & Versioning: https://research.swtch.com/vgo
GoCenter:https://gocenter.io/
https://github.com/jfrog/gocenter
goc: https://github.com/jfrog/goc
JFrog CLI:https://www.jfrog.com/confluence/display/CLI/CLI+for+JFrog+Artifactory

原文地址:https://blog.51cto.com/jfrogchina/2465270

时间: 2024-10-14 05:23:08

GoCenter助力Golang全速前进的相关文章

关注你所关注的 - Golang社区调研报告

Golang语言可以说现在炙手可热,大家熟悉的Kubernates 就是使用Golang开发的.我们在最近于伦敦和圣地亚哥举行的GopherCon大会上调查了1000多名开发者,以更好地了解Go 开发社区和对Go Module的总体看法.随着最近发布的Go 1.13版本,现在是向社区分享一些有趣数据的好时机. BTW, 在JFrog,我们也是Go开发者(JFrog CLI和Xray都是用Go编写的). 同时也是GoLang社区的贡献者, 并为社区维护贡献了公共注册中心 Gocenter(gopr

仿微信抢红包 Golang实战多版本抢红包系统

第1章 课程导学[征途*扬帆起航]没有梦想,何必远方?本小结将站在更高的起点,从项目演示开始 ,然后到本课程适用人群定位,再到通过思维导图介绍课程内容体系,接着到分析收获,最后到学习本课程的一些前置要求,为你的学习征途扬帆起航做保证. 第2章 红包业务概述&系统建模[磨刀不误砍柴工]或许大家体验过抢红包,但如何对现实世界的业务场景进行抽象,形成软件系统的需求,进行建模与技术选型,这是有一套“方法论”的.因此,本章分四点:红包系统业务知识:选用的技术框架:业务建模与数据库建模:红包算法&库存

golang []byte转string

golang中,字符切片[]byte转换成string最简单的方式是 package main import ( "fmt" _ "unsafe" ) func main() { bytes := []byte("I am byte array !") str := string(bytes) bytes[0] = 'i'//注意这一行,bytes在这里修改了数据,但是str打印出来的依然没变化, fmt.Println(str) } 打印信息:

golang实现Ringbuf

Ring buffer算法优点:高内存使用率,在缓冲buffer内存模型中,不太容易发生内存越界.悬空指针等 bug ,出了问题也容易在内存级别分析调试.做出来的系统容易保持健壮. package main import ( "bytes" "fmt" ) type Ringbuf struct { buf         []byte start, size int } func New(size int) *Ringbuf { return &Ringb

Golang Hash MD4

//Go标准包中只有MD5的实现 //还好,github上有MD4实现. package main import (     "golang.org/x/crypto/md4"     "encoding/hex"     "fmt" ) func get_md4(buf []byte) ([] byte) { ctx := md4.New() ctx.Write(buf) return ctx.Sum(nil) } func main() {

Java程序员的Golang入门指南(上)

Java程序员的Golang入门指南 1.序言 Golang作为一门出身名门望族的编程语言新星,像豆瓣的Redis平台Codis.类Evernote的云笔记leanote等. 1.1 为什么要学习 如果有人说X语言比Y语言好,两方的支持者经常会激烈地争吵.如果你是某种语言老手,你就是那门语言的"传道者",下意识地会保护它.无论承认与否,你都已被困在一个隧道里,你看到的完全是局限的.<肖申克的救赎>对此有很好的注脚: [Red] These walls are funny.

golang学习笔记:golang 语法篇(二)

在语法篇(一)中学习了go中基本的数据类型.变量.常量等组成语言的基本要素,在这一节中将会学习如何将这些元素组织起来,最终写成可以执行的代码. 在这一部分包括: go中的流程控制语句: go中函数的用法: go特殊的错误处理方式: Golang中的流程控制语句 在具体编程的时候免不了需要使用一些特殊的语句实现某些功能,比如使用循环语句来进行迭代,使用选择语句控制程序的执行方式等.这些语句在任何一门程序设计语言 中都会有支持,golang中除了支持常用的循环,条件选择语句以外,还支持跳转语句,下面

Golang关键字—— if/else

Golang中,if/else 关键字用于条件判断,如果满足条件就做某事,否则做另一件事: if age >= 18 { fmt.Println("成年人") } else { fmt.Println("未成年") } 多重判断: if score >= 90 { fmt.Println("优秀") } else if score >= 70 { fmt.Println("良好") } else if sco

golang控制channel的出入口

golang控制channel的出入口 我们常常使用channel来在多个goroutine之间做数据通讯,但是chan作为函数的入参我们应该怎么写呢?也许有人觉得这个问题比较傻,不过这个还真的是我今天才知道的. 首先我们看看下面的代码: func main() { c := make(chan int) go in(c) go out(c) time.Sleep(time.Second) } func in(c chan int) { for i := 0; i < 10; i++ { c <