在 Go 中编写代码的正确方法是什么?
What is the correct way to code in Go?
最近发现Revel是一个非常不错的MVC web框架,想尝试一下。
问题是我是 Go 的新手,一切似乎都有点不同。
当使用PHP时,我只是将文件放入/var/www/
文件夹,或者使用一些IDE,然后我可以打开浏览器并实时测试它们。使用 RoR 或 Node.js 更容易,我只需转到本地项目文件夹(无论在哪里),运行 终端中的一个命令,已经可以在 localhost:3000
上看到结果。
这样,我在本地机器上有以下结构:
home
└── mark
└── code
├── php
│ └── my_php_app
└── ruby
└── my_ruby_app
它们都通过 git 同步。然后,当我想在我的远程机器上部署时,我只需将它们拉入 /var/www/
并设置 Apache2/Nginx
但是我如何使用 Go 应用程序执行此操作? 我在家里的 Linux 机器和 VPS 上都安装了 Go。当我打开 ~/code/go/mygoapp
并尝试用 revel run
对它进行 运行 时,它说在 GOPATH
中找不到它。所以,我假设,我需要将我所有的 Go 项目与我的其他项目分开,在 GOPATH
中,可能是 /usr/local/go/src/
或 ~/gocode/src/
.
问题是:
如果我想将所有 Go/Revel 项目与 php
和 ruby
一起保存在 go
文件夹中,我应该怎么做像这样的机器:
home
└── mark
└── code
├── go
│ └── my_revel_app
├── php
│ └── my_php_app
└── ruby
└── my_ruby_app
以及如何以正确的方式将它们实际部署到我的远程服务器上?
如果我仍然需要为此使用 GOPATH,我该如何命名包?是 GOPATH/src/mygoapp
、GOPATH/src/mark/mygoapp
还是 GOPATH/src/bitbucket.org/mark/mygoapp
(虽然此回购是私有的)?
我知道,这可能是一个菜鸟问题,但我看不出这里有逻辑。即使是简单的 Go 程序,我也不需要为了 运行.
把它们放到 GOPATH 中
您将目录(工作区)添加到GOPATH
,其中包含go source,并符合结构src, pkg, bin
。您可以在 GOPATH
中拥有任意数量的 Go 工作空间。在您的系统上相应地定义变量。您服务器上的 GOPATH
很可能与您本地机器上的不同。
您可以使用 git
来分享您的项目。在 github 或 bitbucket 上创建一个帐户,然后同步。
您的 GOPATH
指向工作区。第一个用于存储 go get
个包。
简介
我觉得有一些误解。让我们努力吧。
PHP 和 Go 之间存在一些根本差异,其中之一是 PHP 是一种解释型语言,而 Go 是一种编译语言。
PHP 的设计对大多数应用程序来说是一种所谓的解释语言,这意味着每次调用 PHP 文件时,源代码都会被翻译成机器可读的指令。
另一方面,Go 是一种编译语言,这意味着源代码被编译为可执行二进制文件 一次 并默认静态链接,导致生成的可执行文件没有依赖项(除了编译它的 OS 之外),甚至是 Go 运行 时间。因此,您可以构建一个自给自足的 Web 应用程序,包括 Web 服务器和(使用特殊用途的 Go 包)甚至资源文件,如图像和样式表。
虽然您可以使用 go run filename.go
,但它只是 go build
和执行生成的编译二进制文件的快捷方式,正如 go run --help
的输出证明:
go run [build flags] [-exec xprog] gofiles... [arguments...]
Run compiles and runs the main package comprising the named Go source files.
A Go source file is defined to be a file ending in a literal ".go" suffix.
By default, 'go run' runs the compiled binary directly: 'a.out arguments...'.
例子
我将向您展示 $GOPATH 及其子目录是如何相互关联的。
让我们以最简单的网络服务器为例:
package main
import (
"net/http"
"fmt"
)
// Default Request Handler
func defaultHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "<h1>Hello %s!</h1>", r.URL.Path[1:])
}
func main() {
http.HandleFunc("/", defaultHandler)
http.ListenAndServe(":8080", nil)
}
我把它放到了这样的目录结构中,包括权限和文件大小。这是在 OS X 系统
$GOPATH/src/github.com/mwmahlberg/DemoServer/
└── [-rw-r--r-- 178] server_main.go
现在,在目录 DemoServer 中调用 GOOS=linux go build ./...
时,在我的例子中,将二进制文件交叉编译为 linux 上的 运行。二进制文件得到构建,目录如下所示:
$GOPATH/src/github.com/mwmahlberg/DemoServer/
├── [-rwxr-xr-x 6.2M] DemoServer
└── [-rw-r--r-- 178] server_main.go
请注意可执行文件 server
,它有 6.3M 的相当大的大小。但是,让我们检查一下:
$ file DemoServer
server: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped
您实际上可以通过调用
将此可执行文件复制到任何 64 位 Linux 和 运行
$ ./DemoServer
现在,用 http://hostname:8080/Mark 调用相应的服务器,您将看到一个欢迎您的网站。
并不是说这会使部署变得非常容易。无需您处理依赖项,无需配置额外的 Web 服务器。您可以从字面上复制二进制文件并 运行 它。但是,这并不妨碍您使用更复杂的方法,例如创建软件包 .deb
或 .rpm
或(我更喜欢)Docker 图像。
根据下面的子目录 $GOPATH/src
:实际上,您完全可以自由选择如何在那里组织您的包。然而,三胞胎
codehoster/username/packagename
是有原因的。 go get
命令实际上可以使用git、Mercurial和bzr站点自动下载包。有关详细信息,请参阅 Package Publishing。是的,直接联系那些代码托管站点。 go get
实际上至少依赖于 git。三元组简单地反映了代码的全局可达位置。当然,您可以将代码存储在 $GOPATH/src/foobar
中并将其推送到 github.com/mark/foobar
,尽管这变得相当不透明,尤其是当我在 github.com 上托管开放项目以及在 [= 上托管所有其他项目时98=].
现在,让我们做一些有用的事情,并在调用日期时显示日期url:
package main
import (
"net/http"
"fmt"
"time"
)
// Default Request Handler
func defaultHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "<h1>Hello %s!</h1>", r.URL.Path[1:])
}
func dateHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w,"<h1>%s</h1>",time.Now())
}
func main() {
http.HandleFunc("/date",dateHandler)
http.HandleFunc("/", defaultHandler)
http.ListenAndServe(":8080", nil)
}
但是,我们的主函数中仍然有所有处理程序。我们将提取 defaultHandler 和 dateHandler:
$GOPATH/src/github.com/mwmahlberg/DemoServer/
├── [-rw-r--r-- 214] dateHandler.go
├── [-rw-r--r-- 165] defaultHandler.go
└── [-rw-r--r-- 178] server_main.go
我们的 server_main.go
现在看起来像这样:
package main
import (
"net/http"
)
func main() {
http.HandleFunc("/date",dateHandler)
http.HandleFunc("/", defaultHandler)
http.ListenAndServe(":8080", nil)
}
defaultHandler.go
:
package main
import (
"net/http"
"fmt"
)
func defaultHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "<h1>Hello %s!</h1>", r.URL.Path[1:])
}
还有我们的dateHandler.go
:
package main
import (
"net/http"
"fmt"
"time"
)
func dateHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w,"<h1>%s</h1>",time.Now())
}
让我们假设我们有一个特定于应用程序的可重用函数,我们希望将其放入一个包中。为了这个例子,我们将为我们的 dateHandler 设置一个 Formatter。
$GOPATH/src/github.com/mwmahlberg/DemoServer/
├── [-rw-r--r-- 214] dateHandler.go
├── [-rw-r--r-- 165] defaultHandler.go
├── [drwxr-xr-x 102] formatter
│ └── [-rw-r--r-- 110] dateformatter.go
└── [-rw-r--r-- 178] server_main.go
dateformatter.go
的内容很简单:
package formatter
import (
"time"
)
func FormatDate(d time.Time) string {
return d.Format(time.RFC850)
}
我们在日期处理程序中使用它:
package main
import (
"fmt"
"net/http"
"time"
"github.com/mwmahlberg/DemoServer/formatter"
)
func dateHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, formatter.FormatDate(time.Now()))
}
所以,我希望这对您有所帮助。
我会一一回答你的问题:
1) 如果我想将所有 Go/Revel 项目与 php 和 ruby 一起保存在本地计算机上的 go 文件夹中,我应该怎么做那:
实际上你不能。 Go 提出了一种构建 Go 代码结构的方法,理想情况下应该遵循这种方法。 Go 项目共享一个工作空间,这与其他项目不同,每个项目都有单独的工作空间。
2) 我如何以正确的方式实际将它们部署到我的远程服务器上?
我能想到的一种有效方法是拥有一个单独的构建服务器,在其中构建所有包并获取远程包(github.com)。 Tar 构建的项目。转到您的远程服务器,只需 运行 来自 bin 的可执行文件。这节省了生产服务器获取和构建远程包的时间。
3) 如果我仍然需要为此使用 GOPATH,我该如何命名包?是 GOPATH/src/mygoapp、GOPATH/src/mark/mygoapp 还是 GOPATH/src/bitbucket.org/mark/mygoapp(虽然此回购是私有的)?
我想第三个是最合适的包命名方式,因为导入应该以主机名开头,然后是其余的东西。
最近发现Revel是一个非常不错的MVC web框架,想尝试一下。 问题是我是 Go 的新手,一切似乎都有点不同。
当使用PHP时,我只是将文件放入/var/www/
文件夹,或者使用一些IDE,然后我可以打开浏览器并实时测试它们。使用 RoR 或 Node.js 更容易,我只需转到本地项目文件夹(无论在哪里),运行 终端中的一个命令,已经可以在 localhost:3000
上看到结果。
这样,我在本地机器上有以下结构:
home
└── mark
└── code
├── php
│ └── my_php_app
└── ruby
└── my_ruby_app
它们都通过 git 同步。然后,当我想在我的远程机器上部署时,我只需将它们拉入 /var/www/
并设置 Apache2/Nginx
但是我如何使用 Go 应用程序执行此操作? 我在家里的 Linux 机器和 VPS 上都安装了 Go。当我打开 ~/code/go/mygoapp
并尝试用 revel run
对它进行 运行 时,它说在 GOPATH
中找不到它。所以,我假设,我需要将我所有的 Go 项目与我的其他项目分开,在 GOPATH
中,可能是 /usr/local/go/src/
或 ~/gocode/src/
.
问题是:
如果我想将所有 Go/Revel 项目与
php
和ruby
一起保存在go
文件夹中,我应该怎么做像这样的机器:home └── mark └── code ├── go │ └── my_revel_app ├── php │ └── my_php_app └── ruby └── my_ruby_app
以及如何以正确的方式将它们实际部署到我的远程服务器上?
如果我仍然需要为此使用 GOPATH,我该如何命名包?是
GOPATH/src/mygoapp
、GOPATH/src/mark/mygoapp
还是GOPATH/src/bitbucket.org/mark/mygoapp
(虽然此回购是私有的)?
我知道,这可能是一个菜鸟问题,但我看不出这里有逻辑。即使是简单的 Go 程序,我也不需要为了 运行.
把它们放到 GOPATH 中您将目录(工作区)添加到GOPATH
,其中包含go source,并符合结构src, pkg, bin
。您可以在 GOPATH
中拥有任意数量的 Go 工作空间。在您的系统上相应地定义变量。您服务器上的 GOPATH
很可能与您本地机器上的不同。
您可以使用 git
来分享您的项目。在 github 或 bitbucket 上创建一个帐户,然后同步。
您的 GOPATH
指向工作区。第一个用于存储 go get
个包。
简介
我觉得有一些误解。让我们努力吧。
PHP 和 Go 之间存在一些根本差异,其中之一是 PHP 是一种解释型语言,而 Go 是一种编译语言。
PHP 的设计对大多数应用程序来说是一种所谓的解释语言,这意味着每次调用 PHP 文件时,源代码都会被翻译成机器可读的指令。
另一方面,Go 是一种编译语言,这意味着源代码被编译为可执行二进制文件 一次 并默认静态链接,导致生成的可执行文件没有依赖项(除了编译它的 OS 之外),甚至是 Go 运行 时间。因此,您可以构建一个自给自足的 Web 应用程序,包括 Web 服务器和(使用特殊用途的 Go 包)甚至资源文件,如图像和样式表。
虽然您可以使用 go run filename.go
,但它只是 go build
和执行生成的编译二进制文件的快捷方式,正如 go run --help
的输出证明:
go run [build flags] [-exec xprog] gofiles... [arguments...]
Run compiles and runs the main package comprising the named Go source files. A Go source file is defined to be a file ending in a literal ".go" suffix.
By default, 'go run' runs the compiled binary directly: 'a.out arguments...'.
例子
我将向您展示 $GOPATH 及其子目录是如何相互关联的。
让我们以最简单的网络服务器为例:
package main
import (
"net/http"
"fmt"
)
// Default Request Handler
func defaultHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "<h1>Hello %s!</h1>", r.URL.Path[1:])
}
func main() {
http.HandleFunc("/", defaultHandler)
http.ListenAndServe(":8080", nil)
}
我把它放到了这样的目录结构中,包括权限和文件大小。这是在 OS X 系统
$GOPATH/src/github.com/mwmahlberg/DemoServer/
└── [-rw-r--r-- 178] server_main.go
现在,在目录 DemoServer 中调用 GOOS=linux go build ./...
时,在我的例子中,将二进制文件交叉编译为 linux 上的 运行。二进制文件得到构建,目录如下所示:
$GOPATH/src/github.com/mwmahlberg/DemoServer/
├── [-rwxr-xr-x 6.2M] DemoServer
└── [-rw-r--r-- 178] server_main.go
请注意可执行文件 server
,它有 6.3M 的相当大的大小。但是,让我们检查一下:
$ file DemoServer
server: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped
您实际上可以通过调用
将此可执行文件复制到任何 64 位 Linux 和 运行$ ./DemoServer
现在,用 http://hostname:8080/Mark 调用相应的服务器,您将看到一个欢迎您的网站。
并不是说这会使部署变得非常容易。无需您处理依赖项,无需配置额外的 Web 服务器。您可以从字面上复制二进制文件并 运行 它。但是,这并不妨碍您使用更复杂的方法,例如创建软件包 .deb
或 .rpm
或(我更喜欢)Docker 图像。
根据下面的子目录 $GOPATH/src
:实际上,您完全可以自由选择如何在那里组织您的包。然而,三胞胎
codehoster/username/packagename
是有原因的。 go get
命令实际上可以使用git、Mercurial和bzr站点自动下载包。有关详细信息,请参阅 Package Publishing。是的,直接联系那些代码托管站点。 go get
实际上至少依赖于 git。三元组简单地反映了代码的全局可达位置。当然,您可以将代码存储在 $GOPATH/src/foobar
中并将其推送到 github.com/mark/foobar
,尽管这变得相当不透明,尤其是当我在 github.com 上托管开放项目以及在 [= 上托管所有其他项目时98=].
现在,让我们做一些有用的事情,并在调用日期时显示日期url:
package main
import (
"net/http"
"fmt"
"time"
)
// Default Request Handler
func defaultHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "<h1>Hello %s!</h1>", r.URL.Path[1:])
}
func dateHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w,"<h1>%s</h1>",time.Now())
}
func main() {
http.HandleFunc("/date",dateHandler)
http.HandleFunc("/", defaultHandler)
http.ListenAndServe(":8080", nil)
}
但是,我们的主函数中仍然有所有处理程序。我们将提取 defaultHandler 和 dateHandler:
$GOPATH/src/github.com/mwmahlberg/DemoServer/
├── [-rw-r--r-- 214] dateHandler.go
├── [-rw-r--r-- 165] defaultHandler.go
└── [-rw-r--r-- 178] server_main.go
我们的 server_main.go
现在看起来像这样:
package main
import (
"net/http"
)
func main() {
http.HandleFunc("/date",dateHandler)
http.HandleFunc("/", defaultHandler)
http.ListenAndServe(":8080", nil)
}
defaultHandler.go
:
package main
import (
"net/http"
"fmt"
)
func defaultHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "<h1>Hello %s!</h1>", r.URL.Path[1:])
}
还有我们的dateHandler.go
:
package main
import (
"net/http"
"fmt"
"time"
)
func dateHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w,"<h1>%s</h1>",time.Now())
}
让我们假设我们有一个特定于应用程序的可重用函数,我们希望将其放入一个包中。为了这个例子,我们将为我们的 dateHandler 设置一个 Formatter。
$GOPATH/src/github.com/mwmahlberg/DemoServer/
├── [-rw-r--r-- 214] dateHandler.go
├── [-rw-r--r-- 165] defaultHandler.go
├── [drwxr-xr-x 102] formatter
│ └── [-rw-r--r-- 110] dateformatter.go
└── [-rw-r--r-- 178] server_main.go
dateformatter.go
的内容很简单:
package formatter
import (
"time"
)
func FormatDate(d time.Time) string {
return d.Format(time.RFC850)
}
我们在日期处理程序中使用它:
package main
import (
"fmt"
"net/http"
"time"
"github.com/mwmahlberg/DemoServer/formatter"
)
func dateHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, formatter.FormatDate(time.Now()))
}
所以,我希望这对您有所帮助。
我会一一回答你的问题:
1) 如果我想将所有 Go/Revel 项目与 php 和 ruby 一起保存在本地计算机上的 go 文件夹中,我应该怎么做那:
实际上你不能。 Go 提出了一种构建 Go 代码结构的方法,理想情况下应该遵循这种方法。 Go 项目共享一个工作空间,这与其他项目不同,每个项目都有单独的工作空间。
2) 我如何以正确的方式实际将它们部署到我的远程服务器上?
我能想到的一种有效方法是拥有一个单独的构建服务器,在其中构建所有包并获取远程包(github.com)。 Tar 构建的项目。转到您的远程服务器,只需 运行 来自 bin 的可执行文件。这节省了生产服务器获取和构建远程包的时间。
3) 如果我仍然需要为此使用 GOPATH,我该如何命名包?是 GOPATH/src/mygoapp、GOPATH/src/mark/mygoapp 还是 GOPATH/src/bitbucket.org/mark/mygoapp(虽然此回购是私有的)?
我想第三个是最合适的包命名方式,因为导入应该以主机名开头,然后是其余的东西。