1 Introduction
Golang
It is a static high-level language that has been around for ten years now. It was officially launched in 2009, and then in the most recent one or two years, from 2017 to 2018, it suddenly went up and burst into flames, thanks to the container. O & M / Live / Short Video / Blockchain ...
Golang
The syntax is simple, and simplicity is complexity. The core of software construction is to simplify complex things and handle the complexity.
As one gopher
, we must know his package management, so that we can rationalize the code structure and do a good job in project management. ( gopher
: Gopher)
2. Before GOPATH / Golang 1.5
Golang
The package management so that the population has the disease, start it with GOPATH
to rely on library management, especially simple and crude.
If the environment variable:
export GOROOT=/home/love/go
export GOPATH=/home/love/code
export GOBIN=$GOROOT/bin
The above GOROOT
refers to Golang编译器以及其工具链,基础源码库
the directory is located, GOPATH
it is the code of the user-defined location.
The GOPATH
following structure:
├── src
└── github.com
└── hunterhug
└── rabbit
└── a
└── a.go
└── main.go
├── bin
├── pkg
The development package we wrote has simple and easy-to-understand paths. For example, my package is called github/hunterhug/rabbit
.
We enter the rabbit
directory, main.go
the code:
package main
import "github/hunterhug/rabbit/a"
func main(){
...
}
Then go build
, then, when looking for packages, from'll GOPATH src
start looking for below, such as rabbit
the package under main.go
relied github/hunterhug/rabbit/a
, then it first from src
below by stitching down the path to find, and then find, the last generation, and the package name github/hunterhug/rabbit
as the one called rabit
binary.
If we are go install
, then this will be saved in the binary GOBIN
case (if not present GOBIN
, will be saved in GOPATH bin
case). If we want to compile the package is missing, it go get -v
will download the source code dependencies to GOPATH src
the next, and then GOPATH pkg
generates a static library of the package directory (with the next will not need to build from source, and count cache).
But when our package cannot be found:
love@love:~/code/src/github.com/hunterhug/fafacms$ go build
core/server/server.go:4:2: cannot find package "github.com/gin-contrib/cors" in any of:
/home/love/go/src/github.com/gin-contrib/cors (from $GOROOT)
/home/love/code/src/github.com/gin-contrib/cors (from $GOPATH)
We discovered that, in fact, is to go GOROOT
down to find the package, the package can not be found, go GOPATH
find, I moved to shed tears! For example, our GOPATH
next build a fmt
package:
package fmt
func PPrintln() {
print("i am diy fmt")
}
But we want to reference this library and main.go
use:
package main
import fmt
func main(){
fmt.PPrintln()
}
I can't find it, 2333! Therefore, GOPATH
under the package it would be best not to GOROOT
the standard library under the same name!
You look at GOROOT
the structure:
├── src
└── time
└── fmt
├── bin
├── pkg
This is not our GOPATH
like it, yes, now Golang编译器
is a self-compiled, is used Golang
to write Golang编译器
, its compilers and intermediate products, basic libraries, and to maintain GOPATH
a hair as seamless.
However, there are versions for different dependent packages. What if the version changes? This requires manual management.
3. Vendor / Vendor1.5 以后
Manage their own library version, I think is unlikely, after all, Java
there maven
, Python
there pip
, PHP
there compose
, NodeJs
there npm
.
So from Golang1.5
the launch began vendor
folder mechanism ( vendor
: vendors / hawkers).
From Golang1.6
officially open this function.
For example, our package called awesomeProject
in GOPATH
under construction:
├── src
└── awesomeProject
└── vendor
└── fmt
└── fmt.go
└── main.go
├── pkg
Among them main.go
:
package main
import "fmt"
func main() {
fmt.PPrintln()
}
We enter the awesomeProject
directory, and go build
, even successfully.
It will not be like not erupted in the above vendor
referenced directly when GOROOT
the standard package, we can finally use a standard package and a package of the same name, and that is on the main.go
same directory vendor
below!
Now, we import
are at the same level package will first vendor
look at, and then can not find under the previous method.
If we main
change the reference to a non-existent package b
:
package main
import (
"b"
)
func main() {
b.P()
}
Then go build
Tip:
main.go:4:2: cannot find package "b" in any of:
/home/love/code/src/awesomeProject/vendor/b (vendor tree)
/home/love/go/src/b (from $GOROOT)
/home/love/code/src/b (from $GOPATH)
If at this time we re a little wayward in GOPATH src
the establishment of an empty under vendor
a folder, you are prompted:
main.go:4:2: cannot find package "b" in any of:
/home/love/code/src/awesomeProject/vendor/b (vendor tree)
/home/love/code/src/vendor/b
/home/love/go/src/b (from $GOROOT)
/home/love/code/src/b (from $GOPATH)
Well, we found that the loading method is:
包同目录下的vendor
GOPATH src 下的vendor
GOROOT src
GOPATH src
If GOROOT
and GOPATH
built at vendor
what will happen? We don't stop hurting, 233. .
Well, the question now is vendor
how the bubbling, if I main.go
quoted vendor/b
, while b
inside the package refers to a c
package. At this point vendor/b
will be how to find the library?
├── src
└── awesomeProject
└── vendor
└── b
└── b.go
└── main.go
├── pkg
Now vendor/b/b.go
the contents:
package b
import "c"
func P() {
print(" i am vendor b\n")
c.P()
}
We enter the awesomeProject
project go build
, appears:
vendor/b/b.go:3:8: cannot find package "c" in any of:
/home/love/code/src/awesomeProject/vendor/c (vendor tree)
/home/love/code/src/vendor/c
/home/love/go/src/c (from $GOROOT)
/home/love/code/src/c (from $GOPATH)
Now the loading process is:
包同目录的包(即b包同目录看看有没有c包)
GOPATH src 下的vendor
GOROOT src
GOPATH src
At this point we are vendor/b
at an empty construction vendor
:
├── src
└── awesomeProject
└── vendor
└── b
└── vendor
└── b.go
└── main.go
├── pkg
Into the awesomeProject
project and then go build
there will be:
vendor/b/b.go:3:8: cannot find package "c" in any of:
/home/love/code/src/awesomeProject/vendor/b/vendor/c (vendor tree)
/home/love/code/src/awesomeProject/vendor/c
/home/love/code/src/vendor/c
/home/love/go/src/c (from $GOROOT)
/home/love/code/src/c (from $GOPATH)
If we meet the above c
package, empathy in c
turnkey empty vendor
:
├── src
└── awesomeProject
└── vendor
└── b
└── vendor
└── c
└── vendor
└── c.go
└── b.go
└── main.go
├── pkg
But c
the package c.go
references nonexistent d
package:
package c
import "d"
func P() {
d.P()
}
Into the awesomeProject
project and then go build
there will be:
vendor/b/vendor/c/c.go:3:8: cannot find package "d" in any of:
/home/love/code/src/awesomeProject/vendor/b/vendor/c/vendor/d (vendor tree)
/home/love/code/src/awesomeProject/vendor/b/vendor/d
/home/love/code/src/awesomeProject/vendor/d
/home/love/code/src/vendor/d
/home/love/go/src/d (from $GOROOT)
/home/love/code/src/d (from $GOPATH)
Discovery, find the package vendor
is bubbling up, a package references another package, take a look the same catalog vendor
there under this package, if not all the way back on the floor vendor
to see if there is not, then to the next level vendor
, until GOPATH src/vendor
.
So now the loading process is:
包同目录下的vendor
包目录向上的最近的一个vendor
...
GOPATH src 下的vendor
GOROOT src
GOPATH src
Summary: vendor
Bubble up! ! ! !
In this case, we can rely on the package are placed vendor
under, and then submitted to the warehouse, so you can save time pull the package, and relatively free, you can think how to change, you can put a person has been deleted github
package in the vendor
case. In this way, it is still manual and cannot manage dependent versions.
So many third-party, such as glide
, godep
, govendor
tools emerged, the use of these tools rely package must have a complete git
version, then all will depend on the version written in a configuration file.
For example godep
:
go get -v github.com/tools/godep
Execute under package
godep save
Generated Godeps/Godep.json
record version-dependent, and collected in the current packet vendor
under.
3. After go mod / Go1.11
Golang 1.11
Began, there have been experimentally defined can not GOPATH
function, and there is official go mod
support. Golang 1.12
This feature is also formalized.
Now with Golang1.12
were:
go mod init
go: modules disabled inside GOPATH/src by GO111MODULE=auto; see 'go help modules'
Wherein GO111MODULE=auto
a switch on or off the module support, which has three possible values: off
/ on
/ auto
default values auto
.
-
GO111MODULE=off
, No module support, same as before. -
GO111MODULE=on
The module supports, ignoredGOPATH
andvendor
folders, based onlygo.mod
download dependence. -
GO111MODULE=auto
The project atGOPATH src
the outside and the root directory hasgo.mod
a file open module supports.
When using the module, GOPATH
is meaningless, but it will still depend download store in GOPATH/src/mod
the, it will go install
result in GOPATH/bin
(if GOBIN
it does not exist)
We move the project out GOPATH
, then:
go mod init
appear:
go: cannot determine module path for source directory /home/love/awesomeProject (outside GOPATH, no import comments)
Now main.go
read:
package main // import "github.com/hunterhug/hello"
import (
"b"
)
func main() {
b.P()
}
Will generate go.mod
:
module github.com/hunterhug/hello
go 1.12
At this time we:
go build
build github.com/hunterhug/hello: cannot load b: cannot find module providing package b
This time can not find vendor
, and we come together with parameters:
go build -mod=vendor
build github.com/hunterhug/hello: cannot load c: open /home/love/awesomeProject/vendor/c: no such file or directory
Tears of tears shed, vendor
what about bubbling? The original enabled go.mod
, vendor
under the package b
can not be found b/vendor
under the package c
, only to find one, 2,333,333, this is good or bad?
In general, vendor
here are vendor
unscientific, godep
and other tools will depend straighten out, to ensure that there is only one vendor
.
Then go.mod
lead to vendor
impacts that can not bubble generated, is not large, shed moved to tears.
Now let's use it correctly go mod
, under normal circumstances:
省略N步
Here, and we are very sorry to say goodbye, and now go mod
has just come out, probably will be another update, you can search Google or other articles in this area, or:
go help modules
This part may be written in detail after a while.
The current production environment with go mod
not very realistic, I recommend first the definition GOPATH
and vendor
usage.
4. Use Docker to compile Golang in multiple stages
Installation environment too hard, my God, every time I loaded environment, we can use the following method So easy
to switch at any time Golang
version.
If your Golang
project depends stored in vendor
the lower, then we can use the multi-stage constructed and packaged into containers image, Dockefile
as follows:
FROM golang:1.12-alpine AS go-build
WORKDIR /go/src/github.com/hunterhug/fafacms
COPY core /go/src/github.com/hunterhug/fafacms/core
COPY vendor /go/src/github.com/hunterhug/fafacms/vendor
COPY main.go /go/src/github.com/hunterhug/fafacms/main.go
RUN go build -ldflags "-s -w" -o fafacms main.go
FROM alpine:3.9 AS prod
WORKDIR /root/
COPY --from=go-build /go/src/github.com/hunterhug/fafacms/fafacms /bin/fafacms
RUN chmod 777 /bin/fafacms
CMD /bin/fafacms $RUN_OPTS
Where github.com/hunterhug/fafacms
is your project. Use golang:1.12-alpine
to compile binary, and then into a binary base image: alpine:3.9
This image is particularly small.
Compile:
sudo docker build -t hunterhug/fafacms:latest .
We have an extra mirror hunterhug/fafacms:latest
, and it is very small, only a few M.
run:
sudo docker run -d --net=host --env RUN_OPTS="-config=/root/fafacms/config.json" hunterhug/fafacms
However, if we use cgo
, then please Dockerfile
read:
FROM golang:1.12 AS go-build
WORKDIR /go/src/github.com/hunterhug/fafacms
COPY core /go/src/github.com/hunterhug/fafacms/core
COPY vendor /go/src/github.com/hunterhug/fafacms/vendor
COPY main.go /go/src/github.com/hunterhug/fafacms/main.go
RUN go build -ldflags "-s -w" -o fafacms main.go
FROM bitnami/minideb-extras-base:stretch-r165 AS prod
WORKDIR /root/
COPY --from=go-build /go/src/github.com/hunterhug/fafacms/fafacms /bin/fafacms
RUN chmod 777 /bin/fafacms
CMD /bin/fafacms $RUN_OPTS
5. Summary
Managing dependencies, how to compile code into binary, is a process, and there are many details. The above is my experience, thanks for reading.