专业的佛山网站建设公司,深圳网站建设龙华,网站定制公司排行榜,网站建设运营的灵魂是Go 中的内部包
这里可能会有歧义 可能是 Go 的 internal 目录中的包也可能是指内部开发的包
函数和变量的可见性
对于函数和变量而言#xff0c;有如下规则#xff1a;1 #xff09;小写字母开头的函数变量结构体只能在本包内访问2 #xff09;大写字母开头的函数变量结…Go 中的内部包
这里可能会有歧义 可能是 Go 的 internal 目录中的包也可能是指内部开发的包
函数和变量的可见性
对于函数和变量而言有如下规则1 小写字母开头的函数变量结构体只能在本包内访问2 大写字母开头的函数变量结构体可以在其他包访问注意 如果, 结构体是大写字母开头字段或方法名是小写字母开头这些字段和方法也只能在本包内访问
示例
pkg-demo/ 工程目录 pkg/ pkg.go main.go
pkg.go
package pkgimport fmtvar TestVer1 TestVer1 // public
var tesVer2 tesVer2 // privateconst (TestConst1 TestConst1 // publictestConst2 testConst2 // private
)// public 结构体
type TestStruct1 struct {Field1 string // publicfield2 string // private 外部不可访问
}// private 结构体
type testStruct2 struct {Field1 stringfield2 string // private 外部不可访问
}// public 方法
func (ts1 TestStruct1) Test1() {fmt.Println(TestConst1)
}// private 方法
func (ts1 TestStruct1) test2() {fmt.Println(testConst2)
}// public 方法
func (ts2 testStruct2) Test21() {fmt.Println(testConst2)
}// private 方法
func (ts2 testStruct2) test21() {fmt.Println(testConst2)
}// 包内函数均可正常访问
func f() {fmt.Println(testConst2, tesVer2)// private 结构体t : testStruct2 {Field1: we,field2: lee, // private 字段}t.test21() // private 方法
}main.go
package mainimport (fmtdemo/pkg
)func main() {fmt.Println( pkg.TestConst1 ) // 正常fmt.Println( pkg.TestVer1 ) // 正常ts : pkg.TestStruct1{ Field1: test } // 正常ts.Test() // 正常ts.test2() // 报错ts2 : pkg.TestStruct1{ Field1: test, field2: dddd } // 报错
}在同一个包内大小写都可以正常访问在包外只能访问大写开头的大小写只能控制包内的常量变量方法是否可以被其他包所调用如果要限制整个包都不能被外部导入就需要用到 internal 目录
internal 目录
internal 目录是控制包的可见性的在go1.4版本呢可以使用 internal 目录限制包的导入权限用于分离应用中的共享和非共享代码编译的时候Go 会进行强行校验 (那internet文件夹内的代码包中声明的公开程序实体)比如说大写开头的常量变量函数方法等它只能被它父目录下面的包或者是子包所引用
目录结构嵌套示例 i-demo/ a/ a.gob/ b.goc/ c.gof/ f.go internal/ 注意看这里 d/ d.goe/ e.gof/ f.go g/ main.go 如上结构在 internal 目录中有 d包和e包 也就是说在这个 internal 文件夹下面这些包只能被 internal 的同级目录以及它下面的子目录里面的包所调用也就是说和 internal 目录平级的 c.go 中可调用 internal 目录下的程序比如 internal 下面的d包和e包和 internal 同级的 f包在其下的 f.go 中 也可调用 internal 目录下的程序同上 如果跨了一层目录也就是 internal 父目录的父目录 也就是这个b目录我们看一下这个b包下面有一个 b.go 的文件在这个b包下面调用 internal 目录内的程序实体就会报错 所以只要跨越了一个父目录就没办法使用 internal 下面的开的程序实体
internal目录的意义
在有些场景下一些包不被其他的工程导入是很有必要的比如我们的后端服务通常有用户层的业务代码和运营管理后台的代码如果用户层的业务代码不小心导入了管理后台的某些包而管理后台服务定义的一些方法的权限通常很大它能够对全部用户的数据进行操作这样可能会很容易出现安全隐患所以 我们通常会将工程的业务代码呢都放到 internal 目录下面那只有一些工具包或者是一些公用的包我们放在这个 internal 文件夹的外面
企业内部包
我们开发的包上传到企业内部的git平台上以供其他的业务组使用这种情况下我们应该怎么从内部的git平台上使用这些包呢
1 第一种方式: 通过本地包的方式导入
我们可以通过本地包的方式导入那这就需要用到 go mod 的另一个语法 replace将源码 import 的包替换成本地包的路径看下具体实现 1 初始化private-pkg工程 $ mkdir private-pkg$ cd private-pkg$ go mode init github.com/xxx/private-pkg 这里的 xxx 作为示例可替换成你们自己gitlab或gitee等真实的仓库这样初始化完成了一个 private-pkg 的包$ mkdir pkg touch pkg.go写一些程序到 pkg.go中, 例如package pkg
var Pkg string此时发现go.mod 中的 顶部一行 module github.com/xxx/private-pkg 2 初始化上面目录同级的 xxx-pkg 项目工程 $ touch main.go现在在这里的 main 包 调用上面 private-pkg 中的 pkg包内的属性或方法// main.go
package main
import (fmtxxx.gitlab.com/xxx/private-pkg // 注意这里是红色的说明目前有问题
)func main() {fmt.Println(pkg.Pkg) // 这里是 红的
}这时候就需要正确导入 private-pkg 包了$ go mod init xxx-pkg 同上这里的 xxx 也是随意举例的写法通过上面的执行生成了 go.mod 的文件$ go mod tidy 发现并没有在网络上拉取包并写入go.mod中我们需要修改 go.modmodule xxx-pkggo 1.20
require(xxx.gitlab.com/xxx/private-pkg latest
)再次执行 $ go mod tidy 直接报错 404 Not Found我们的包是没有上传到gitlab平台的所以找不到这里面提一点如果使用了go代理但是如果同时设置GOPRIVATE也就是设置了私有包地址凡是使用私有包的域名都会走相应的源码平台获取假如我们的 $GOPRIVATE 设置的是 *.gitlab.com所以凡是匹配到 *.github.com 的都会走源码获取 解决方案 在 go.mod 中module xxx-pkggo 1.20
require(xxx.gitlab.com/xxx/private-pkg ../private-pkg
)通过这样就可以把包导入进来之后回到 main.go 中点击红色的包选择 Sync dependencies of xxx-pkg之后就开始同步了这个过程相当于执行了 $ go mod tidy这时候包的依赖被我们解决了同时回到 go.mod 文件中可看到 原来的 require 变成了2行 replace, 并且源码地址后面多了一串尾版本号 这就是通过本地导入的方式使用远程地址的包名的方法 go.mod 除了 replace 还有 include(已在1.20时移除) 和 exclude 等语句 这里看一下 excludeexclude (dependency latest
)例如exclue (github.com/google/uuid v1.1.0
)可以排除指定的依赖包在实际项目中基本用不到除非我们知道某个版本有严重的bug, 可以用于排除指定包的某个版本注意replace, include, exclude 只有在当前模块为主模块的时候才会生效也就是说比如 xxx-pkg 工程目录是主模块在这个主模块中调用 同级的 private-pkg 模块如果 replace, include, exclude 在 private-pkg 中对主模块是不生效的
2 第二种方式: 通过私有仓库的方式来导入
通过本地replace方式导入呢存在一个很大的弊端由于replace引入的是本地环境的路径, 当其他人使用这个工程的时候他必须把这些代码拉取到本地放到与工程目录相对应的目录才能构建成功方式1中导入本地路径是有一个相对路径的只有放到这个相对路径下面这个工程呢才能构建成功当 replace 包发生变更或者是需要手动更新包里面的这些代码的时候呢就非常麻烦了我们就需要通过get命令将我们这个 private-pkg 的代码update到最新企业内部为了避免各个业务重复造轮也会考虑将内部开发的这些包在企业内实现共享 企业内部的这些包就不需要走Go的代理去拉取直接到企业内部的git平台上就可以拉这些包的源码了
示例
准备go版本在1.11, 在 go1.13之前需要开启我们的 Go Modules 即环境变量 GO111MODULE 设置为 on 或 $ go env -w GO111MODULEon 设置 GOPROXY 即$ export GOPROXYhttps://goproxy.cn/,https://mirrors.aliyun.com/goproxy/,direct或$ go env -w GOPROXYhttps://goproxy.cn/,https://mirrors.aliyun.com/goproxy/,direct 设置 GOPRIVATE 即$ export GOPRIVATE*.gitlab.com或$ go env -w GOPRIVATE*.gitlab.com 之后在公司内网的gitlab上创建私有包 比如把这个 private-pkg 同步上去并打一个tag注意没有tag就会使用最新的commit-id中的hash 在工程目录中移除之前处理的 go.mod 内的一些 require等配置在工程根目录中执行 $ go mod tidy 下载完成后go.mod 就生成了当前的依赖
GOPROXY 与 GOPRIVATE
GOPROXY: 顾名思义就是go用来下载依赖包的一个代理。 go在拉取包的时候根据这个环境变量设置的值地址里面来拉取我们的依赖包从go 1.11版本开始支持的也就是跟随 Go Modules 诞生的GOPROXY 默认地址是 https://proxy.golang.org这是个国外的一个代理地址在国内拉取包会比较慢我们通常推荐使用七牛云的地址 七牛云推出非盈利的代理网站免费可靠持续在线经过cdn加速的代理速度快且稳定 并且最后还使用 direct 这个关键词这个direct的含义是什么呢 也就是说当我们的包从这个 GOPROXY 取不到的时候就直接通过 go.mod中配置的地址去拉取 这样可以保证最大限度的能把这个包拉取下来同样GOPROXY 除了设置我们的地址, 还可以将它设置为 off GOPROXYoff 表示不许从任何源下载依赖包 需要注意的是 工作中不是所有包都可以从公网的代网网站去拉取有些包我们不要要走代理比如内部git平台(内网)这个时候可以通过配置 GOPRIVATE 来实现 GOPRIVATE: 组织内部的源非公网上 从go的1.13版本开始支持的作用是让指定的域名不走代理也不进行 Go Modules 校验配置方式和这个 GOPRIVATE 是类似的 可以使用域名也可以使用域名的通配符比如: *.gitlab.com就是说凡是通过这个域名拉取的包都是不走 GOPROXY 代理的
其他环境变量 GONOPROXY: 与 GOPRIVATE 有相同作用 他们都是从go 1.13版本开始支持的都可以用来配置让指定域名的包不走 GOPROXY 设置的代理 GONOSUMDB 用来控制是否走 Go Modules 校验go 在拉取包的时候会对包进行hash校验通常我们可以让内部私有包不去进行校验 GONOPROXY 与 GOPROXY 与 GOPRIVATE 区别 1 GONOPROXY GONOSUMDB 可以让包走私有地址拉取并不进行包的校验2 GOPRIVATE 走私有地址拉包不走GOPROXY拉取并不进行包校验实际上1和2是等效的并且工作中 GOPRIVATE 更方便最佳实践是 一般公网上的仓库版本代码容易被修改或删除的风险企业内部需要建立自己的镜像仓库将项目使用的包同步到企业内部并对这些包进行安全审核避免使用有重大安全漏洞的包 如果要使用 GONOPROXY 和 GONOSUMDB 配置的一般顺序是 GOPROXYhttps://xxx…GONOPROXY*gitlab.comGONOSUMDB$GONOPROXY 实际工作中只需要配置 GOPROXY 和 GOPRIVATE