What is the correct way to code in Go?

huangapple go评论93阅读模式
英文:

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/

问题如下:

  1. 如果我想要将所有的Go/Revel项目都放在本地机器上的go文件夹中,与phpruby一起,我该怎么做呢?

    home
    └── mark
        └── code
            ├── go
            │   └── my_revel_app
            ├── php
            │   └── my_php_app
            └── ruby
                └── my_ruby_app
    
  2. 我应该如何正确地将它们部署到远程服务器上?

  3. 如果我仍然需要使用GOPATH,我应该如何命名包?是GOPATH/src/mygoappGOPATH/src/mark/mygoapp还是GOPATH/src/bitbucket.org/mark/mygoapp(而这个仓库是私有的)?

我知道这可能是一个初学者的问题,但我在这里看不到逻辑。即使是简单的Go程序,我也不需要将它们放在GOPATH中才能运行。

英文:

Recently I found that Revel is a really good MVC web framework, and I want to try it out.
The problem is that I'm new to Go and everything seems a little different.

When using PHP, I just put files into /var/www/ folder, or use some IDE, then I can open browser and test them live. It's even easier with RoR or Node.js, I just go to local project folder (doesn't matter where), run one command in terminal and already can see the result on localhost:3000.

This way, I have the following structure on my local machine:

home
└── mark
    └── code
        ├── php
        │   └── my_php_app
        └── ruby
            └── my_ruby_app

They all are synced via git. Then, when I want to deploy on my remote machine, I just pull them into /var/www/ and set up Apache2/Nginx

But how do I do this with Go apps? I installed Go both on my Linux machine at home, and on my VPS. When I open ~/code/go/mygoapp and try to run it with revel run, it says that it's not found in GOPATH. So, I assume, I need to keep all my Go projects separately from my other projects, in GOPATH, which could be /usr/local/go/src/ or ~/gocode/src/.

Questions are:

  1. What should I do, if I want to keep all my Go/Revel projects in go folder along with php and ruby on local machine like that:

    home
    └── mark
        └── code
            ├── go
            │   └── my_revel_app
            ├── php
            │   └── my_php_app
            └── ruby
                └── my_ruby_app
    
  2. And how do I actually deploy them on my remote server the correct way?

  3. If I still need to use GOPATH for that, how do I name the packages? Is it GOPATH/src/mygoapp, GOPATH/src/mark/mygoapp or GOPATH/src/bitbucket.org/mark/mygoapp (while this repo is private)?

I know, this could be a noob question, but I don't see a logic here. Even with simple Go programs, I don't need to put them to GOPATH in order to run.

答案1

得分: 1

你可以将包含Go源代码并符合src, pkg, bin结构的目录(工作空间)添加到GOPATH中。你可以在GOPATH中拥有尽可能多的Go工作空间。根据你的系统设置相应的变量。你的服务器上的GOPATH很可能与你本地机器上的不同。

你可以使用git来分享你的项目。在GitHub或Bitbucket上创建一个账号,并进行同步。

你的GOPATH指向Go工作空间。第一个工作空间用于存储go get的包。

英文:

You add directories (workspaces) to GOPATH which contain go source, and conform to the structure src, pkg, bin. You can have as many go workspaces as you want in your GOPATH. Define the variable accordingly on your systems. The GOPATH on your server will most likely be different than the one your local machine.

You can use git to share your projects. Create an account on github or bitbucket, and synchronize.

Your GOPATH points to go workspaces. The first one is used to store the go get packages.

答案2

得分: 1

介绍

我觉得有一些误解。让我们来解决这个问题。

PHP和Go之间有一些根本的区别,其中之一是PHP是一种解释型语言,而Go是一种编译型语言。

PHP被设计为并且对于大多数应用程序来说是一种所谓的解释型语言,这意味着每次调用PHP文件时,源代码都会被翻译成机器可读的指令。

另一方面,Go是一种编译型语言,这意味着源代码只被编译成一个可执行的二进制文件一次,并且默认静态链接,使得生成的可执行文件没有任何依赖项(除了它编译的操作系统),甚至没有Go运行时。因此,您可以构建一个自包含的Web应用程序,包括Web服务器和(使用专用的Go包)资源文件,如图像和样式表。

虽然您可以使用go run filename.go,但它只是go build和执行生成的编译二进制文件的快捷方式,如go run --help的输出所证明的:

> go run [build flags] [-exec xprog] gofiles... [arguments...]

> 运行编译并运行由命名的Go源文件组成的主包。
> Go源文件的定义是以字面上的".go"后缀结尾的文件。

> 默认情况下,'go run'直接运行编译的二进制文件:'a.out arguments...'。

示例

我将向您展示$GOPATH及其子目录之间的关联。

让我们以最简单的Web服务器示例为例:

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:])
}

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 可执行文件,x86-64,版本 1 (SYSV),静态链接,未剥离

您实际上可以将此可执行文件复制到任何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上托管开放项目,而其他所有项目都在bitbucket.org上托管时。

现在,让我们做一些有用的事情,并在调用日期URL时显示日期:

package main 

import (
    "net/http"
    "fmt"
    "time"
)

// 默认请求处理程序
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)
}

然而,我们仍然将所有处理程序放在了main函数内部。我们将提取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()))

}

所以,我希望这对您有所帮助。

英文:

Introduction

I feel that there are a few misconceptions. Let's work on that.

There are a few fundamental differences between PHP and Go, one of the being that PHP is an interpreted language as opposed to Go, which is a compiled language.

PHP was designed and for most applications is a so called interpreted language, meaning that the source code is translated to machine readable instructions each time the PHP file is called.

Go on the other hand is a compiled language, meaning that the source code is compiled to an executable binary once and linked statically be default causing the resulting executable to have no dependencies (other than the OS it was compiled for), not even the Go runtime. So you can build a self-sufficient web application including a web server and (using special purpose Go packages) even resource files like images and stylesheets.

While you can use go run filename.goit is merely a shortcut for go build and executing the resulting compiled binary, as the output of go run --help proves:

> 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...'.

Example

I'll show you how $GOPATH and it's subdirectories are interconnected.

Let's take the most simple web server example:

package main 

import (
    &quot;net/http&quot;
    &quot;fmt&quot;
)

// Default Request Handler
func defaultHandler(w http.ResponseWriter, r *http.Request) {
      fmt.Fprintf(w, &quot;&lt;h1&gt;Hello %s!&lt;/h1&gt;&quot;, r.URL.Path[1:])
}

func main() {
    http.HandleFunc(&quot;/&quot;, defaultHandler)
    http.ListenAndServe(&quot;:8080&quot;, nil)
}

I have put it into a directory structure like this, including permissions and file size. This is on an OS X system

$GOPATH/src/github.com/mwmahlberg/DemoServer/
└── [-rw-r--r--  178]  server_main.go

Now, when calling GOOS=linux go build ./... in the directory DemoServer, in my case cross compiling the binary to run on linux. The binary gets build and the directory looks like this:

$GOPATH/src/github.com/mwmahlberg/DemoServer/
├── [-rwxr-xr-x 6.2M]  DemoServer
└── [-rw-r--r--  178]  server_main.go

Note the executable serverwhich has a rather large size of 6.3M. However, let's inspect it:

$ file DemoServer
server: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped

You can actually copy this executable to any 64-bit Linux and run it by calling

$ ./DemoServer

Now, call the according server with http://hostname:8080/Mark and you will see a website greeting you.

Not that this makes deployment very easy. No dependencies you need to take care of, no additional web server to configure. You can literally just copy the binary over and run it. However, this does not prevent you from using a bit more sophisticated approach, like creating software packages like a .deb or .rpm or (as I prefer) a Docker image.

As per the subdirectories below $GOPATH/src: Actually you are totally free on how to organize your packages there. However, the triplet

> codehoster/username/packagename

is there for a reason. The go get command actually can use git, Mercurial and bzr sites to automatically download the packages. See Package Publishing for details. Yes, those code hosting sites are contacted directly. The go get actually is dependent on git at least. The triplet simply reflects the globally reachable location of the code. Of course you could store the code in $GOPATH/src/foobar and push it to github.com/mark/foobar, though this becomes rather intransparent, especially when doing it like me hosting open projects on github.com and all others at bitbucket.org.

Now, let's do something halfway useful and show a date when calling the date url:

package main 

import (
    &quot;net/http&quot;
    &quot;fmt&quot;
    &quot;time&quot;
)

// Default Request Handler
func defaultHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, &quot;&lt;h1&gt;Hello %s!&lt;/h1&gt;&quot;, r.URL.Path[1:])
}

func dateHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w,&quot;&lt;h1&gt;%s&lt;/h1&gt;&quot;,time.Now())
}

func main() {
    http.HandleFunc(&quot;/date&quot;,dateHandler)
    http.HandleFunc(&quot;/&quot;, defaultHandler)
    http.ListenAndServe(&quot;:8080&quot;, nil)
}

However, we still have all handlers inside our main function. We will extract the defaultHandler and 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

Our server_main.go now looks like this:

package main 

import (
    &quot;net/http&quot;    
)

func main() {
    http.HandleFunc(&quot;/date&quot;,dateHandler)
    http.HandleFunc(&quot;/&quot;, defaultHandler)
    http.ListenAndServe(&quot;:8080&quot;, nil)
}

The defaultHandler.go:

package main

import (
    &quot;net/http&quot;
    &quot;fmt&quot;
)

func defaultHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, &quot;&lt;h1&gt;Hello %s!&lt;/h1&gt;&quot;, r.URL.Path[1:])
}

And our dateHandler.go:

package main

import (
    &quot;net/http&quot;
    &quot;fmt&quot;
    &quot;time&quot;
)

func dateHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w,&quot;&lt;h1&gt;%s&lt;/h1&gt;&quot;,time.Now())
}

Let us assume we have an application specific reusable function we want to put into a package. For the sake of this example, we will have a Formatter for our dateHandler.

$GOPATH/src/github.com/mwmahlberg/DemoServer/
├── [-rw-r--r--  214]  dateHandler.go
├── [-rw-r--r--  165]  defaultHandler.go
├── [drwxr-xr-x  102]  formatter
│&#160;&#160; └── [-rw-r--r--  110]  dateformatter.go
└── [-rw-r--r--  178]  server_main.go

The content of dateformatter.go is pretty simple:

package formatter

import (
    &quot;time&quot;
)

func FormatDate(d time.Time) string {
    return d.Format(time.RFC850)
}

and we use it in our date handler:

package main

import (
	&quot;fmt&quot;
	&quot;net/http&quot;
	&quot;time&quot;

	&quot;github.com/mwmahlberg/DemoServer/formatter&quot;
)

func dateHandler(w http.ResponseWriter, r *http.Request) {

	fmt.Fprintf(w, formatter.FormatDate(time.Now()))

}

So, I hope this helps.

答案3

得分: 0

我将逐个回答你的问题:

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(其中这个仓库是私有的)?

我认为第三种方式是最合适的命名包的方式,因为导入的包应该以主机名开头,然后是其他内容。

英文:

I will answer your questions one by one:

1) What should I do, if I want to keep all my Go/Revel projects in go folder along with php and ruby on local machine like that:

Actually you can't. Go proposes a way of structuring your Go code structure and ideally that is how it should be followed. One workspace is shared by Go projects unlike others where you have separate workspace for every project.

2) And how do I actually deploy them on my remote server the correct way?

One efficient way I could think of is to have a separate build server where all the packages are built along with fetching of remote packages(github.com). Tar the built project. Go to your remote server and simply run the executable from bin. This saves time at the production server of fetching and building remote packages.

3) If I still need to use GOPATH for that, how do I name the packages? Is it GOPATH/src/mygoapp, GOPATH/src/mark/mygoapp or GOPATH/src/bitbucket.org/mark/mygoapp (while this repo is private)?

I guess the third one would be the most appropriate way of naming your packages as imports should begin with a hostname and then the remaining things.

huangapple
  • 本文由 发表于 2015年12月17日 00:03:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/34316666.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定