Go中正确的编码方式是什么?

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.

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.

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 (
    "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)
}

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 (
    "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)
}

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 (
    "net/http"    
)

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

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

And our 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())
}

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
│   └── [-rw-r--r--  110]  dateformatter.go
└── [-rw-r--r--  178]  server_main.go

The content of dateformatter.go is pretty simple:

package formatter

import (
    "time"
)

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

and we use it in our date handler:

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()))

}

So, I hope this helps.

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.