Go语言中支持的internal目录配置与组织内私网包配置详解

Go 中的内部包

  • 这里可能会有歧义
    • 可能是 Go 的 internal 目录中的包
    • 也可能是指内部开发的包

函数和变量的可见性

  • 对于函数和变量而言,有如下规则:
  • 1 )小写字母开头的函数变量结构体只能在本包内访问
  • 2 )大写字母开头的函数变量结构体可以在其他包访问
  • 注意
    • 如果, 结构体是大写字母开头,字段或方法名是小写字母开头
    • 这些字段和方法也只能在本包内访问

示例

  • pkg-demo/ 工程目录
    • pkg/
      • pkg.go
    • main.go

pkg.go

package pkgimport "fmt"var 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 ("fmt""demo/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.go
      • b/
        • b.go
        • c/
          • c.go
          • f/
            • f.go
          • internal/ 注意看这里
            • d/
              • d.go
              • e/
                • e.go
                • f/
                  • 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 ("fmt""xxx.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.mod
        module 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 等语句
      • 这里看一下 exclude
        exclude (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 GO111MODULE=on
  • 设置 GOPROXY
    • 即:$ export GOPROXY=https://goproxy.cn/,https://mirrors.aliyun.com/goproxy/,direct
    • 或:$ go env -w GOPROXY=https://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
      • GOPROXY="off" 表示不许从任何源下载依赖包
    • 需要注意的是
      • 工作中,不是所有包都可以从公网的代网网站去拉取
      • 有些包我们不要要走代理,比如内部git平台(内网)
      • 这个时候,可以通过配置 GOPRIVATE 来实现
  • GOPRIVATE: 组织内部的源,非公网上
    • 从go的1.13版本开始支持的
    • 作用是:让指定的域名不走代理也不进行 Go Modules 校验
    • 配置方式和这个 GOPRIVATE 是类似的
      • 可以使用域名,也可以使用域名的通配符
      • 比如: *.gitlab.com
      • 就是说,凡是通过这个域名拉取的包,都是不走 GOPROXY 代理的

其他环境变量

  • GONOPROXY: 与 GOPRIVATE 有相同作用

    • 他们都是从go 1.13版本开始支持的
    • 都可以用来配置让指定域名的包不走 GOPROXY 设置的代理
  • GONOSUMDB

    • 用来控制是否走 Go Modules 校验
    • go 在拉取包的时候,会对包进行hash校验
    • 通常,我们可以让内部私有包,不去进行校验
  • GONOPROXYGOPROXYGOPRIVATE 区别

    • 1 )GONOPROXY + GONOSUMDB 可以让包走私有地址拉取并不进行包的校验
    • 2 )GOPRIVATE 走私有地址拉包,不走GOPROXY拉取,并不进行包校验
    • 实际上1和2是等效的,并且工作中 GOPRIVATE 更方便
    • 最佳实践是
      • 一般公网上的仓库版本代码容易被修改或删除的风险
      • 企业内部需要建立自己的镜像仓库,将项目使用的包同步到企业内部
      • 并对这些包进行安全审核,避免使用有重大安全漏洞的包
  • 如果要使用 GONOPROXYGONOSUMDB 配置的一般顺序是

    • GOPROXY=https://xxx…
    • GONOPROXY=*gitlab.com
    • GONOSUMDB=$GONOPROXY
  • 实际工作中,只需要配置 GOPROXY 和 GOPRIVATE

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

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

相关文章

移动应用开发:揭秘内侧APP封装台的高效

在数字化浪席卷下,移应用已经成连接企业与用户纽带。为了抢占市场先机,快速发布高质量的移动应用成为业竞争的关键。侧APP封装平因此而诞生,成为了应开发者的得助手。以下是内侧APP封装台的全面解读,助在应用开发海洋中乘风破浪。…

基于CMake的大型C++工程组织

此文适合大型C工程,涉及到多个自定义库,多个第三方库,以及还有给第三方用户进行二次开发的需求下,应对这种复杂编译环境下的工程组织方式的一些经验介绍,希望给大型工业软件的开发者一些参考 一个大型工程&#xff0c…

【JavaFX】JDK11 基于Gson、hutool、Jackson持久化存储实体类数据的解决方案 (读取、追加、去重json对象)

文章目录 开发环境效果前言一、Gson是什么?二、使用步骤1.引入依赖2.创建实体类创建 JsonFileService类创建JsonFileService的实现类 JsonFileServiceImpl三、实现效果开发环境 JDK11IDEA 2023.3Gson、hutool、JacksonJavaFX 11效果 前言 使用JDK1

【每日一题】【12.29】 - 【12.31】年终收尾

🔥博客主页: A_SHOWY🎥系列专栏:力扣刷题总结录 数据结构 云计算 数字图像处理 力扣每日一题_ 这三天的题目难度相对较小,基本都为模拟题,但是第二三的题目年份贡献类型很有代表性。2023年最后三天年终收…

MongoDB 数据类型

目录 BSON 类型 二进制数据(Binary Data) ObjectId ObjectId定义 文档中的ObjectId ObjectId的单调性 字符串(String) 时间戳(Timestamps) 日期(Date) BSON类型的排序 数…

I/O多路复用

I/O就是对缓冲区的操作 I/O多路复用使得程序能同时监听多个文件描述符能够提高程序的性能 I/O多路复用是一种有效的处理多个I/O事件的机制,允许一个单独的进程或线程来监视多个文件描述符(sockets、文件、设备等),并在其中任何一个…

Python编程技巧 – format格式化文本

Python编程技巧 – format格式化文本 Python Programming Essentials - Using format() to format texts By JacksonML 本文简要介绍Python语言的format()方法(也即函数)相关实例和技巧,希望对读者有所帮助。 1. format定义和方法 forma…

如何处理并下载Sentinel-5数据

SENTINEL-5是欧洲空间局(European Space Agency,ESA)Copernicus计划中的一颗地球观测卫星。SENTINEL-5的主要任务是监测大气成分,特别是臭氧、氮二氧化物、二氧化硫、甲烷和其他气体的分布。这些观测对于了解大气污染、气候变化和…

php接口优化 使用curl_multi_init批量请求

PHP使用CURL同时抓取多个URL地址 抓取多个URL地址是Web开发中常见的需求,使用PHP的curl库可以简化这个过程。本文将详细介绍如何使用PHP的curl库同时请求多个URL地址,并提供具体的代码案例和注释。 curl库介绍 curl是一个常用的开源网络传输工具&…

图像分割实战-系列教程5:unet医学细胞分割实战3(医学数据集、图像分割、语义分割、unet网络、代码逐行解读)

🍁🍁🍁图像分割实战-系列教程 总目录 有任何问题欢迎在下面留言 本篇文章的代码运行界面均在Pycharm中进行 本篇文章配套的代码资源已经上传 上篇内容: unet医学细胞分割实战2 下篇内容: unet医学细胞分割实战4 5、损…

《Linux详解:深入探讨计算机基础》

《Linux详解:深入探讨计算机基础》 引言: 在计算机科学领域,操作系统是一个至关重要的概念,而Linux作为一种开源的Unix-like操作系统,不仅在服务器领域广泛应用,也在嵌入式系统、超级计算机等多个领域发挥…

Hermite矩阵

Hermite矩阵 文章目录 Hermite矩阵一、正规矩阵【定义】A^H^矩阵【定理】 A^H^的运算性质【定义】正规矩阵、特殊的正规矩阵【定理】与正规矩阵酉相似的矩阵也是正规矩阵【定理】正规的上(下)三角矩阵必为对角矩阵【定义】复向量的内积【定理】Schmitt正交化 二、酉矩阵&#x…

dijkstra和prim算法

最初看严蔚敏老师的《数据结构》一书时,便感觉Dijkstra (读作[daik stra])和Prim这两个算法几乎一摸一样,后来看到李清勇老师的《算法设计与问题求解》一书中对两中算法的比较,才知道两种算法的异同点。 关于Dijkstra的念法,请看…

Wireshark-win64

前言 Wireshark是使用最广泛的一款「开源抓包软件」,常用来检测网络问题、攻击溯源、或者分析底层通信机制。 它使用WinPCAP作为接口,直接与网卡进行数据报文交换。 Wireshark-win64 前言1 下载地址2 安装 1 下载地址 https://download.csdn.net/down…

k8s搭建(五、k8s可视化管理工具Dashboard配置)

天行健,君子以自强不息;地势坤,君子以厚德载物。 每个人都有惰性,但不断学习是好好生活的根本,共勉! 文章均为学习整理笔记,分享记录为主,如有错误请指正,共同学习进步。…

linux 网络系统管理 技能大赛 nginx fastCGI配置

WEB服务 安装 nginx 软件包 配置文件名为 ispweb.conf,放置在/etc/nginx/conf.d/目录下 网站根目录为/mut/crypt(目录不存在需创建) 启用 FastCGI 功能,让 nginx 能够解析 php 请求 ndex.php 内容使用 Welcome to 2022 Compu…

【mysql】数据处理是否超时、处理时长

数据处理 是否超时 -- Mysql如何计算两个时间字段的差值?可用函数 TIMESTAMPDIFF() -- 0否,1是,-1未知,“处理时限”超48小时则视为超时 select case when TIMESTAMPDIFF(HOUR, tpp.ARCHIVE_DATE,tpp.DEADLINE_TIME) > 48 t…

2023.12.30 pymysql操作

目录 1.Pymysql 1.1连接pymysql 1.2 查询数据 1.3增删改操作 2. Pymysql数据库练习 2.1 代码实现 3.数据库练习2 假设之前已有用户登录的方法,但是该方法没有记录日志。现在想通过装饰器来增强原有功能 1.Pymysql 总结: fetchall(): 获取所有查询…

【Linux】修复 Linux 错误 - 打开文件过多

修复 Linux 错误 - 打开文件过多 在使用 Linux 操作系统时,有时会遇到一个常见的错误 - "打开文件过多"。这个错误通常发生在一个进程打开了太多的文件描述符,超过了系统的限制。本文将介绍如何识别和修复这个问题。 识别问题 要识别是否出现了"打开文件过…

什么是 JavaScript 中的生成器

JavaScript中的生成器代码是一种特殊的函数,它可以生成迭代器对象。生成器代码通过使用yield关键字来指示生成器的状态。每次调用生成器函数时,它都会返回一个迭代器对象,该对象可以用于以惰性的方式逐步产生值。 生成器代码的语法类似于普通…