Go 包操作之如何拉取私有的Go Module
Go 包操作之如何拉取私有的Go Module
在前面,我们已经了解了GO 项目依赖包管理与Go Module常规操作,Go Module 构建模式已经成为了 Go 语言的依赖管理与构建的标准。
在平时使用Go Module 时候,可能会遇到以下问题:
- 在某 module 尚未发布到类似GitHub 或 Gitee 这样的网站前,如何 import 这个本地的 module?
- 如何拉取私有 module?
文章目录
- Go 包操作之如何拉取私有的Go Module
- 一、导入本地 module
- 1.1 依赖本地尚未发布的 module
- 1.2 Go Module 开发中本地导入两种方式
- 1.2.1 使用 replace 指令
- 1.2.2 使用工作区模式
- 二、拉取私有 module 的需求与参考方案
- 2.1 方案一:通过直连组织公司内部的私有 Go Module 服务器拉取
- 2.2 方案二:将外部 Go Module 与私有 Go Module 都交给内部统一的 GOPROXY 服务去处理:
- 三、统一 Goproxy 方案的实现思路与步骤
- 3.1 goproxy 服务搭建
- 3.2 自定义包导入路径并将其映射到内部的 vcs 仓库
- 3.3 开发机 (客户端) 的设置
- 3.4 方案的“不足”
- 3.4.1 第一点:开发者还是需要额外配置 GONOSUMDB 变量
- 3.4.2 第二点:新增私有 Go Module,vanity.yaml 需要手工同步更新
- 3.4.3 第三点:无法划分权限
一、导入本地 module
1.1 依赖本地尚未发布的 module
如果我们的项目依赖的是本地正在开发、尚未发布到公共站点上的 Go Module,那么我们应该如何做呢?
例如:假设有个hello-module的项目,你的main包中依赖了moduleA,代码如下:
package mainimport "gitee.com/tao-xiaoxin/study-basic-go/hello-module/moduleA"func main() {moduleA.ModuleA()
}
并且,这个项目中的moduleA 依赖 moduleB,此时此刻,module A 和 moduleB 还没有发布到gitee
公共托管站点上,它的源码还在你的开发机器上。也就是说,Go 命令无法在gitee.com/user/
上找到并拉取 module A 和 module B,这时,使用go mod tidy
命令,就会收到类似下面这样的报错信息:
$go mod tidy
go: finding module for package gitee.com/user/moduleB
go: finding module for package gitee.com/user/moduleA
go: gitee.com/tao-xiaoxin/study-basic-go importsgitee.com/user/moduleA: module gitee.com/user: git ls-remote -q origin in /Users/thinkook/go/pkg/mod/cache/vcs/ff424152e6f6be73e07b96e5d8e06c6cd9f86dc9903058919a7b8737718a8418: exit status 128:致命错误:仓库 'https://gitee.com/user/' 未找到
go: gitee.com/tao-xiaoxin/study-basic-go/moduleA importsgitee.com/user/moduleB: module gitee.com/user: git ls-remote -q origin in /Users/thinkook/go/pkg/mod/cache/vcs/ff424152e6f6be73e07b96e5d8e06c6cd9f86dc9903058919a7b8737718a8418: exit status 128:致命错误:仓库 'https://gitee.com/user/' 未找到
所以,Go提供了两种方式可以导入本地正在开发的 Go Module
1.2 Go Module 开发中本地导入两种方式
1.2.1 使用 replace 指令
介绍: 使用replace
指令可以替代远程依赖模块的路径,将其指向本地的模块路径,便于本地开发和测试。
基本使用: 下面是一个示例replace指令的使用方式:
replace example.com/module@版本号 => 你的本地Module路径(可以使用相对路径或者绝对路径)
接着,我们继续回到上面的举例中,首先,我们需要在 module a 的 go.mod 中的 require
块中手工加上这一条并且替换为本地路径上的module A
和moduleB
:
replace (gitee.com/user/moduleA v1.0.0 => ../moduleAgitee.com/user/moduleB v1.0.0 => ../moduleB
)
这里的v1.0.0
版本号是一个“假版本号”,目的是满足go.mod
中require
块的语法要求。
或者使用go mod edit
命令编辑 go.mod
文件:
go mod edit -replace=gitee.com/user/moduleA@v1.0.0=../moduleA -replace=gitee.com/user/moduleB@v1.0.0=../moduleB
这样修改之后,Go 命令就会让module A
依赖你本地正在开发、尚未发布到代码托管网站的module B
的源码了,并且main
函数依赖你本地正在开发、尚未发布到代码托管网站的module B
的源码了。
虽然虽然这个方案可以解决上述问题,但是在平时开发过程中,go.mod
文件通常需要上传到代码服务器上,这意味着,另一个开发人员下载了这份代码后,很可能无法成功编译。在这个方法中,require
指示符将gitee.com/user/moduleA v1.0.0
替换为一个本地路径下的module A
的源码版本,但这个本地路径因开发者环境而异。为了成功编译module A
和主程序,该开发人员必须将replace
后面的本地路径更改为适应自己的环境路径。
于是,每当开发人员 pull 代码后,第一件事就是要修改go.mod
中的replace
块。每次上传代码前,可能还要将replace
路径还原,这是一个很繁琐的事情。于是,Go开发团队在Go 1.18 版本中加入了 Go 工作区(Go workspace,也译作 Go 工作空间)辅助构建机制。
上述举例代码仓库地址:点我进入
1.2.2 使用工作区模式
**介绍:**Go 工作区模式是 Go 语言 1.18 版本引入的新功能,允许开发者将多个本地路径放入同一个工作区中,这样,在这个工作区下各个模块的构建将优先使用工作区下的模块的源码。工作区模式具有以下优势:
- 可以将多个本地模块放入同一个工作区中,方便开发者管理。
- 可以解决“伪造 go.mod”方案带来的那些问题。
- 可以提高模块构建的性能。
常用命令:
Go 工具提供了以下命令来帮助开发者使用工作区模式:
go work edit
:提供了用于修改go.work
的命令行接口,主要是给工具或脚本使用。go work init
:初始化工作区文件 go.workgo work use
:将模块添加到工作区文件go work sync
:把go.work
文件里的依赖同步到workspace包含的Module的go.mod
文件中。
基本使用:
- 首先,我们初始化 Go workspace 使用命令
go work init
命令如下:
go work init [moddirs]
moddirs
是Go Module所在的本地目录。如果有多个Go Module,就用空格分开。如果go work init
后面没有参数,会创建一个空的workspace。
执行go work init
后会生成一个go.work
文件,go.work
里列出了该workspace需要用到的Go Module所在的目录,workspace目录不需要包含你当前正在开发的Go Module代码。
- 如果要给workspace新增Go Module,可以使用如下命令:
go work use [-r] moddir
如果带有-r
参数,会递归查找-r
后面的路径参数下的所有子目录,把所有包含go.mod
文件的子目录都添加到go work
文件中。
-
如果要同步依赖到workspace包含的Module的
go.mod
文件中,可以使用如下命令:go work sync
介绍完之后,我们回到上面的例子中,现在我们进入 gowork下面,然后通过下面命令初始化一个go.work:
go work init .
我们看到go work init
命令创建了一个go.work
文件,使用go env GOWORK
命令查看go.work
所在位置
$go env GOWORK
~/workspace/GolandProjects/study-basic-go/syntax/gowork/go.work
接着,我们在 module a
的go.work
中的 use
块中替换为本地路径上的module A
和moduleB
:
go 1.21.1use (../moduleA./moduleB
)
支持replace指示符:go.work还支持replace指示符,使用方法和上面一样
上面的代码地址:点我
二、拉取私有 module 的需求与参考方案
自从 Go 1.11 版本引入 Go Module 构建模式后,通过 Go 命令拉取项目依赖的公共 Go Module,已不再是一个“痛点”。现在,我们只需要在每个开发机上设置环境变量 GOPROXY,配置一个高效且可靠的公共 GOPROXY 服务,就可以轻松地拉取所有公共 Go Module 了。
但随着公司内 Go 使用者和 Go 项目的增多,“重造轮子”的问题就出现了。抽取公共代码放入一个独立的、可被复用的内部私有仓库成为了必然,这样我们就有了拉取私有 Go Module 的需求。
一些公司或组织的所有代码,都放在公共 vcs 托管服务商那里(比如 github.com),私有 Go Module 则直接放在对应的公共 vcs 服务的 private repository(私有仓库)中。如果你的公司也是这样,那么拉取托管在公共 vcs 私有仓库中的私有 Go Module,也很容易,见下图:
也就是说,只要我们在每个开发机上,配置公共 GOPROXY 服务拉取公共 Go Module,同时将私有仓库配置到 GOPRIVATE 环境变量,就可以了。这样,所有私有模块的拉取都将直接连接到代码托管服务器,不会通过 GOPROXY 代理服务,并且不会向 GOSUMDB 服务器发出 Go 包的哈希值校验请求。
当然,这个方案有一个前提,那就是每个开发人员都需要具有访问公共 vcs 服务上的私有 Go Module 仓库的权限,凭证的形式不限,可以是 basic auth 的 user 和 password,也可以是 personal access token(类似 GitHub 那种),只要按照公共 vcs 的身份认证要求提供就可以了。
不过,更多的公司 / 组织,可能会将私有 Go Module 放在公司 / 组织内部的 vcs(代码版本控制)服务器上,就像下面图中所示:
那么这种情况,我们该如何让 Go 命令,自动拉取内部服务器上的私有 Go Module 呢?这里给出两个参考方案。
2.1 方案一:通过直连组织公司内部的私有 Go Module 服务器拉取
在这个方案中,我们看到,公司内部会搭建一个内部 goproxy 服务(也就是上图中的 in-house goproxy)。这样做有两个目的,一是为那些无法直接访问外网的开发机器,以及 ci 机器提供拉取外部 Go Module 的途径,二来,由于 in-house goproxy 的 cache 的存在,这样做还可以加速公共 Go Module 的拉取效率。
另外,对于私有 Go Module,开发机只需要将它配置到 GOPRIVATE 环境变量中就可以了,这样,Go 命令在拉取私有 Go Module 时,就不会再走 GOPROXY,而会采用直接访问 vcs(如上图中的 git.yourcompany.com)的方式拉取私有 Go Module。
这个方案十分适合内部有完备 IT 基础设施的公司。这类型的公司内部的 vcs 服务器都可以通过域名访问(比如 git.yourcompany.com/user/repo),因此,公司内部员工可以像访问公共 vcs 服务那样,访问内部 vcs 服务器上的私有 Go Module。
2.2 方案二:将外部 Go Module 与私有 Go Module 都交给内部统一的 GOPROXY 服务去处理:
在这种方案中,开发者只需要把 GOPROXY 配置为 in-house goproxy,就可以统一拉取外部 Go Module 与私有 Go Module。
但由于 go 命令默认会对所有通过 goproxy 拉取的 Go Module,进行 sum 校验(默认到 sum.golang.org),而我们的私有 Go Module 在公共 sum 验证 server 中又没有数据记录。因此,开发者需要将私有 Go Module 填到 GONOSUMDB 环境变量中,这样,go 命令就不会对其进行 sum 校验了。
不过这种方案有一处要注意:in-house goproxy 需要拥有对所有 private module 所在 repo 的访问权限,才能保证每个私有 Go Module 都拉取成功。
在平时开发中,更推荐第二个方案。在第二个方案中,我们可以将所有复杂性都交给 in-house goproxy 这个节点,开发人员可以无差别地拉取公共 module 与私有 module,心智负担降到最低。
三、统一 Goproxy 方案的实现思路与步骤
3.1 goproxy 服务搭建
Go module proxy 协议规范发布后,Go 社区出现了很多成熟的 Goproxy 开源实现,比如最初的 Athens,还有国内的两个优秀的开源实现:goproxy.cn 和 goproxy.io 等。其中,goproxy.io 在官方站点给出了企业内部部署的方法,所以今天我们将基于 goproxy.io 来实现我们的方案。
我们在上图中的 in-house goproxy 节点上执行这几个步骤安装 goproxy:
$mkdir ~/.bin/goproxy
$cd ~/.bin/goproxy
$git clone https://github.com/goproxyio/goproxy.git
$cd goproxy
$make
编译后,我们会在当前的 bin 目录(~/.bin/goproxy/goproxy/bin)下看到名为 goproxy 的可执行文件。
然后,我们建立 goproxy cache 目录:
$mkdir /root/.bin/goproxy/goproxy/bin/cache
再启动 goproxy:
$./goproxy -listen=0.0.0.0:8081 -cacheDir=/root/.bin/goproxy/goproxy/bin/cache -proxy https://goproxy.io
goproxy.io: ProxyHost https://goproxy.io
启动后,goproxy 会在 8081 端口上监听(即便不指定,goproxy 的默认端口也是 8081),指定的上游 goproxy 服务为 goproxy.io。
不过要注意下:goproxy 的这个启动参数并不是最终版本的,这里我仅仅想验证一下 goproxy 是否能按预期工作。我们现在就来实际验证一下。
首先,我们在开发机上配置 GOPROXY 环境变量指向 10.10.20.20:8081:
// .bashrc
export GOPROXY=http://10.10.20.20:8081
生效环境变量后,执行下面命令:
$go get github.com/pkg/errors
结果和我们预期的一致,开发机顺利下载了 github.com/pkg/errors 包。我们可以在 goproxy 侧,看到了相应的日志:
goproxy.io: ------ --- /github.com/pkg/@v/list [proxy]
goproxy.io: ------ --- /github.com/pkg/errors/@v/list [proxy]
goproxy.io: ------ --- /github.com/@v/list [proxy]
goproxy.io: 0.146s 404 /github.com/@v/list
goproxy.io: 0.156s 404 /github.com/pkg/@v/list
goproxy.io: 0.157s 200 /github.com/pkg/errors/@v/list
在 goproxy 的 cache 目录下,我们也看到了下载并缓存的 github.com/pkg/errors 包:
$cd /root/.bin/goproxy/goproxy/bin/cache
$tree
.
└── pkg└── mod└── cache└── download└── github.com└── pkg└── errors└── @v└── list8 directories, 1 file
这就标志着我们的 goproxy 服务搭建成功,并可以正常运作了。
3.2 自定义包导入路径并将其映射到内部的 vcs 仓库
一般公司可能没有为 VCS 服务器分配域名,我们也不能在 Go 私有包的导入路径中放入 IP 地址,因此我们需要给我们的私有 Go Module 自定义一个路径,比如:mycompany.com/go/module1
。我们统一将私有 Go Module 放在 mycompany.com/go
下面的代码仓库中。
那么,接下来的问题就是,当 goproxy 去拉取 mycompany.com/go/module1
时,应该得到 mycompany.com/go/module1
对应的内部 VCS 上 module1
仓库的地址,这样,goproxy 才能从内部 VCS 代码服务器上下载 module1
对应的代码,具体的过程如下:
那么我们如何实现为私有 module 自定义包导入路径,并将它映射到内部的 vcs 仓库呢?
其实方案不止一种,这里我使用了 Google 云开源的一个名为 govanityurls 的工具,来为私有 module 自定义包导入路径。然后,结合 govanityurls 和 Nginx,我们就可以将私有 Go Module 的导入路径映射为其在 VCS 上的代码仓库的真实地址。具体原理你可以看一下这张图:
首先,goproxy 要想不把收到的拉取私有 Go Module(mycompany.com/go/module1)的请求转发给公共代理,需要在其启动参数上做一些手脚,比如下面这个就是修改后的 goproxy 启动命令:
$./goproxy -listen=0.0.0.0:8081 -cacheDir=/root/.bin/goproxy/goproxy/bin/cache -proxy https://goproxy.io -exclude "mycompany.com/go"
这样,凡是与 -exclude
后面的值匹配的 Go Module 拉取请求,goproxy 都不会将其转发给 goproxy.io,而是直接请求 Go Module 的“源站”。
而上面这张图中要做的,就是将这个“源站”的地址,转换为企业内部 VCS 服务中的一个仓库地址。然后我们假设 mycompany.com
这个域名并不存在(很多小公司没有内部域名解析能力),从图中我们可以看到,我们会在 goproxy
所在节点的 /etc/hosts
中添加这样一条记录:
127.0.0.1 mycompany.com
这样做了后,goproxy 发出的到 mycompany.com
的请求实际上是发向了本机。而上面这图中显示,监听本机 80
端口的正是 nginx
,nginx
关于 mycompany.com
这一主机的配置如下:
// /etc/nginx/conf.d/gomodule.confserver {listen 80;server_name mycompany.com;location /go {proxy_pass http://127.0.0.1:8080;proxy_redirect off;proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;proxy_http_version 1.1;proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection "upgrade";}
}
我们看到,对于路径为 mycompany.com/go/xxx
的请求,nginx
将请求转发给了 127.0.0.1:8080
,而这个服务地址恰恰就是 govanityurls
工具监听的地址。
govanityurls
这个工具,是前 Go 核心开发团队成员 Jaana B. Dogan 开源的一个工具,这个工具可以帮助 Gopher 快速实现自定义 Go 包的 go get
导入路径。
govanityurls 本身,就好比一个“导航”服务器。当 go 命令向自定义包地址发起请求时,实际上是将请求发送给了 govanityurls 服务,之后,govanityurls 会将请求中的包所在仓库的真实地址(从 vanity.yaml 配置文件中读取)返回给 go 命令,后续 go 命令再从真实的仓库地址获取包数据。
注:govanityurls 的安装方法很简单,直接 go install/go get github.com/GoogleCloudPlatform/govanityurls 就可以了。在我们的示例中,vanity.yaml 的配置如下:
host: mycompany.compaths:/go/module1:repo: ssh://admin@10.10.30.30/module1vcs: git
也就是说,当 govanityurls
收到 nginx
转发的请求后,会将请求与 vanity.yaml
中配置的 module
路径相匹配,如果匹配 OK,就会将该 module
的真实 repo 地址,通过 go
命令期望的应答格式返回。在这里我们看到,module1
对应的真实 VCS 上的仓库地址为:ssh://admin@10.10.30.30/module1
。
所以,goproxy
会收到这个地址,并再次向这个真实地址发起请求,并最终将 module1
缓存到本地 cache
并返回给客户端。
3.3 开发机 (客户端) 的设置
前面示例中,我们已经将开发机的 GOPROXY
环境变量,设置为 goproxy
的服务地址。但我们说过,凡是通过 GOPROXY
拉取的 Go Module,go
命令都会默认把它的 sum
值放到公共 GOSUM
服务器上去校验。
但我们实质上拉取的是私有 Go Module,GOSUM
服务器上并没有我们的 Go Module 的 sum
数据。这样就会导致 go build
命令报错,无法继续构建过程。
因此,开发机客户端还需要将 mycompany.com/go
,作为一个值设置到 GONOSUMDB
环境变量中:
export GONOSUMDB=mycompany.com/go
这个环境变量配置一旦生效,就相当于告诉 go
命令,凡是与 mycompany.com/go
匹配的 Go Module,都不需要再做 sum
校验了。
到这里,我们就实现了拉取私有 Go Module 的方案。
3.4 方案的“不足”
3.4.1 第一点:开发者还是需要额外配置 GONOSUMDB 变量
由于 Go 命令默认会对从 GOPROXY
拉取的 Go Module 进行 sum
校验,因此我们需要将私有 Go Module 配置到 GONOSUMDB
环境变量中,这就给开发者带来了一个小小的“负担”。
对于这个问题,我的解决建议是:公司内部可以将私有 Go 项目都放在一个特定域名下,这样就不需要为每个 Go 私有项目单独增加 GONOSUMDB
配置了,只需要配置一次就可以了。
3.4.2 第二点:新增私有 Go Module,vanity.yaml 需要手工同步更新
这是这个方案最不灵活的地方了,由于目前 govanityurls
功能有限,针对每个私有 Go Module,我们可能都需要单独配置它对应的 VCS 仓库地址,以及获取方式(git、svn 或 hg)。
关于这一点,我的建议是:在一个 VCS 仓库中管理多个私有 Go Module。相比于最初 Go 官方建议的一个 repo 只管理一个 module,新版本的 Go 在一个 repo 下管理多个 Go Module 方面,已经有了长足的进步,我们已经可以通过 repo 的 tag 来区别同一个 repo 下的不同 Go Module。
不过对于一个公司或组织来说,这点额外工作与得到的收益相比,应该也不算什么!
3.4.3 第三点:无法划分权限
在讲解上面的方案的时候,我们也提到过,goproxy
所在节点需要具备访问所有私有 Go Module 所在 VCS repo 的权限,但又无法对 Go 开发者端做出有差别授权。这样,只要是 goproxy
能拉取到的私有 Go Module,Go 开发者都能拉取到。
不过对于多数公司而言,内部所有源码原则上都是企业内部公开的,这个问题似乎也不大。如果觉得这是个问题,那么只能使用前面提到的第一个方案,也就是直连私有 Go Module 的源码服务器的方案了。
参考链接:
- 小厂内部私有Go module拉取方案3
- Go 1.18新特性前瞻:Go工作区模式
相关文章:

Go 包操作之如何拉取私有的Go Module
Go 包操作之如何拉取私有的Go Module 在前面,我们已经了解了GO 项目依赖包管理与Go Module常规操作,Go Module 构建模式已经成为了 Go 语言的依赖管理与构建的标准。 在平时使用Go Module 时候,可能会遇到以下问题: 在某 modul…...

VR酒店专业情景教学演示
VR酒店情景教学为学生带来的全新学习体验。在这个虚拟环境中,学生可以亲身经历各种酒店管理场景,从客房清洁、餐厅服务,到客人接待、突发事件处理,都能得到生动的模拟和实践。 客房清洁是酒店管理中最基础却也最重要的一环。通过V…...

odps函数
1、wm_concat 聚合函数,可以实现对分组后的列数据拼接成一行。 参数:第一个参数为分隔符,第二个参数为要聚合的列; select prov_code,wm_concat(-,city_name) from code_china_area group by prov_code; 2、datediff 日期函数…...

【golang】mysql默认排序无法实现 使用golang实现对时间字符串字段的排序
一、问题场景 1、mysql实现排序-性能低下 例如:某字段 finish_time 数据如下:6:13:27、 10:56:11、 21:56:11 会出现顺序如下的场景: 10:56:11、 21:56:11、6:13:27 二、解决方案 2、golang实现排序 package mainimport ("fmt"&…...

C语言学习笔记总结(一)
C语言基础 字节大小 char:1 字节 unsigned char:1 字节 short:2 字节 unsigned short:2 字节 int:通常为 4 字节(32 位平台)或 8 字节(64 位平台) unsigned int&#x…...

WPF:自定义按钮模板
1.WPF:自定义按钮模板 自定义封装的按钮属性可写在<Button.Template>中 Background"{TemplateBinding Background}"中的TemplateBinding代表使用按钮本身所使用的背景颜色 不在样式内修改背景颜色 例如: <Button Width"300" Height&q…...

Spring Boot集成Swagger接口分类与各元素排序问题
在上一篇中我们完成使用JSR-303校验,以及利用Swagger2得到相关接口文档,这节,我们在原先的基础之上,完成Swagger中关于对各个元素之间控制前后顺序的具体配置方法。 Swagger的接口的分组 首先我们需要对Swagger中的接口也就是以…...

盘点国产ChatGPT十大模型
什么是ChatGPT ChatGPT是一种基于OpenAI的GPT(Generative Pre-trained Transformer)模型的聊天机器人。GPT是一种基于深度学习的自然语言处理模型,它使用了Transformer架构来处理文本数据。GPT模型通过在大规模文本数据上进行预训练ÿ…...

【C++杂货铺】C++11特性总结:列表初始化 | 声明 | STL的升级
文章目录 一、C11简介二、统一的列表初始化2.1 { } 初始化2.2 列表初始化在内置类型上的应用2.3 列表初始化在内置类型上的应用2.4 initializer_list2.4.1 {1, 2, 3} 的类型2.4.2 initializer_list 使用场景2.4.3 模拟实现的 vector 中的 { } 初始化和赋值 三、声明3.1 auto3.1…...

doris的be其中失败的原因
doris的be其中失败的原因 修改limits文件 vim /etc/security/limits.conf1在文件的末尾处添加或者修改 * soft nofile 204800 * hard nofile 204800 * soft nproc 204800 * hard nproc 204800vim /etc/sysctl.conf#添加 fs.file-max 6553560 vm.max_map_count 2000000 1 2…...

LeetCode 面试题 16.04. 井字游戏
文章目录 一、题目二、C# 题解 一、题目 设计一个算法,判断玩家是否赢了井字游戏。输入是一个 N x N 的数组棋盘,由字符" ",“X"和"O"组成,其中字符” "代表一个空位。 以下是井字游戏的规则&#…...

Nodejs和Node-red的关系
NPM相关知识 npm概念 npm:Node Package Manager,Node包管理器。是Node.js默认的,以JavaScript编写的软件包管理系统。 npm工作原理 npm的操作原理是各个官网使用npm publish把代码提交到npm的服务器,其他人想要使用这些代码&am…...

Spring Boot整合OAuth2实现GitHub第三方登录
Spring Boot整合OAuth2,实现GitHub第三方登录 1、第三方登录原理 第三方登录的原理是借助OAuth授权来实现,首先用户先向客户端提供第三方网站的数据证明自己的身份获取授权码,然后客户端拿着授权码与授权服务器建立连接获得一个Access Token…...

Android [SPI,AutoSerivce,ServiceLoader]
记录一下在Android中使用SPI的过程。 1.项目gralde文件。 plugins {id kotlin-kapt } dependencies {implementation com.google.auto.service:auto-service:1.0-rc7 kapt "com.google.auto.service:auto-service:1.0-rc7" } 这个AutoServ…...

【Linux】ASCII码表-256个
ASCII码简介: ASCII码 是现今最通用的单字节编码系统,并等同于国际标准ISO/IEC 646 。在这个页面,你可以找到8位的256个字符、ASCII码表和Windows-1252 (code page 1252,它是国际标准ISO 8859-1的一个扩展字符集) 标准保持一致;ASCII码 是 A…...

node.js - 上传文件至阿里云oss
deploy.js const fs require(fs) const path require(path) const util require(util) const OSS require(ali-oss)/*因为,在Node.js中,许多核心模块的函数都是采用回调函数的形式进行异步操作的,即在操作完成后调用回调函数来处理结果。…...

xxl-job项目集成实战,全自动项目集成,可以直接使用到项目中
如果你看官方文档,在研究透,至少也得几天时间,如果你直接看我的文档,快速用到项目中,也就10分钟就搞好了。 xxl-job功能确实很强大,而且使用的人比较多,既然在使用xxl-job,那肯定是…...

Qt生成PDF报告
文章目录 一、示意图二、实现部分代码总结 一、示意图 二、实现部分代码 //! 生成测试报告 void MainWindow::createPdf(QString filename, _pdf_msg_& msg, const QMap<QString, int>& ok, const QMap<QString, int>& err) {//QDir dir;if(!dir.exis…...

Jenkins 安装全攻略:从入门到精通
目录 一:安装文件夹准备 1.打开,/home/admin目录 2.新建三个文件夹 二:安装tomcat 1.打开tomcat目录进行tomcat安装 2.解压tomcat文件 3.开放端口号 4.启动tomcat 5.浏览器访问tomcat 三:安装Maven 1.打开maven目录进行…...

迎重阳,话养老:平安养老险如何助力国民“养老梦”?
10月23日,我们将迎来传统节日重阳节,又称敬老节,自古就有祭祖、登高、赏菊三大风俗,流传至今,已成为人们孝老敬老的重要节日。 随着老龄化日益加深,在“敬老”的同时如何“备老”成为人民群众长期热议的话题…...

微信小程序获取用户信息
个人博客 微信小程序获取用户信息 个人微信公众号,求关注,求收藏,求指错。 文章概叙 本文主要讲的是小程序获取用户信息的,更新测试时间是2023-10-25 更改原因 首先,官网上的解释是这样的,为了安全合…...

Mysql 索引原理和优化方式
一、索引原理 什么是索引 索引是存储引擎用于快速找到记录的一种数据结构。可以联想到字典中的目录。 索引的分类 (1) Hash 索引 Hash 索引是比较常见的一种索引,他的单条记录查询的效率很高,时间复杂度为1。但是,…...

Ubuntu安装VM TOOLS解决虚拟机无法和WINDOWS粘贴复制问题
1:首先使用VMware Workstation安装一个Ubuntu的系统。 2:现在已经不建议安装VM TOOLS。建议安装OPEN-VM-TOOLS。 3:进入系统使用下面的命令安装。 sudo apt install open-vm-tools 4:提示下面错误,Package open-vm…...

【Docker】Docker Swarm介绍与环境搭建
为什么不建议在生产环境中使用Docker Compose 多机器如何管理?如何跨机器做scale横向扩展?容器失败退出时如何新建容器确保服务正常运行?如何确保零宕机时间?如何管理密码,Key等敏感数据? Docker Swarm介…...

国产CAN总线收发芯片DP1042 兼容替换TJA1042
说明 1 简述 DP1042是一款应用于 CAN 协议控制器和物理总线之间的接口芯片,可应用于卡车、公交、小汽车、工业控制等领域,支持 5Mbps CAN FD 灵活数据速率,具有在总线与 CAN 协议控制器之间进行差分信号传输的能力,完全兼容“ISO…...

[架构之路-243]:目标系统 - 纵向分层 - 架构是表面轮廓、内部骨架、未来蓝图,企业组织架构、信息系统架构、软件架构、应用程序就架构
目录 一、什么是架构 1.1 架构是表面轮廓 1.2 架构是内部骨架 1.3 架构是蓝图,是愿景 1.4 架构是数据流、控制流、管理流、同步流 1.5 数据、控制、同步、管理的比较 二、架构的层级 2.1 企业组织架构 2.2 企业系统架构 2.2 信息系统架构 2.3 软件架构 …...

【接口技术】定时计数器习题
1:8253芯片有______个端口地址。 【可选】 2 3 4 6 解答:4 2:8253芯片有______种工作方式。 【可选】 3 4 5 6 解答:6 3: 8253芯片内部有完全独立的______。 【可选】 6个16位计数通道 3个16位计数通道 6个8位计…...

DC电源模块的的散热结构合理布局
BOSHIDA DC电源模块的的散热结构合理布局 DC电源模块在工业控制、通讯、汽车电子等领域广泛应用。然而,随着功率密度不断提高,DC电源模块产生的热量也越来越大,散热问题变得越来越突出。为了保障电路的稳定性和可靠性,必须采取合…...

Fedora Linux 38下安装音频与视频的解码器和播放器
Fedora Linux 38 操作系统安装好后,默认是没有音频与视频的解码器的,音频与视频的播放体验非常差劲。但是第三方的软件源中有解码器和播放器的软件,需要我们自己手动安装。、 连接互联网,打开Shell命令行: 1. sudo d…...

边缘计算:云计算的延伸
云计算已经存在多年,并已被证明对大大小小的企业都有好处;然而,直到最近边缘计算才变得如此重要。它是指发生在网络边缘的一种数据处理,更接近数据的来源地。 这将有助于提高效率并减少延迟以及设备和云之间的数据传输成本。边缘…...