2

Currently I have the following HTTP server written in Go:

func main() {
    http.HandleFunc("/", func(response http.ResponseWriter, request *http.Request) {
        http.ServeFile(response, request, "/var/www/default/htdocs/index.html")
    })

    http.Handle("/public/", http.StripPrefix("/public/", http.FileServer(http.Dir("/var/www/default/htdocs/public"))))

    http.HandleFunc("/json", func(response http.ResponseWriter, request *http.Request) {
        // serves a JSON response
    })

    http.HandleFunc("/socket", func(w http.ResponseWriter, r *http.Request) {
        // replies to the WebSocket
    })

    http.ListenAndServe(":3000", nil)
}

I've seen some benchmarks that show that nginx can serve a lot more requests per second when it comes to static files. Another useful feature of nginx is that it can gzip responses transparently and even serve pre-compressed files with the gzip_static module.

Ideally, I want nginx to serve all existing (static) files and proxy everything that doesn't exist to Go:

location / {
    try_files               $uri $uri/ @go;
}

location @go {
    proxy_pass              http://127.0.0.1:3000/;
    proxy_set_header        Host $host;
}

Unfortunatly, nginx doesn't like the above configuration:

nginx: [emerg] "proxy_pass" cannot have URI part in location given by regular expression, or inside named location, or inside "if" statement, or inside "limit_except" block in /etc/nginx/sites-enabled/default:23

At this point, I know I have several options:

1) put proxy_pass in the location / block

But this will proxy everything, even the existing (static) files.

2) write individual /json and /socket location blocks in nginx

But if I want to add more handlers in Go, I also have to update the nginx vhost accordingly.

3) rewrite the Go code and use fastcgi_pass in nginx instead of proxy_pass

location @go {
    fastcgi_pass            127.0.0.1:9000;
}

I would also have to change the Go code to use net/http/fcgi instead of net/http, the problem with this is that I don't know how to specify the paths (/json or /socket) with fcgi.Serve().

Additionally, FastCGI seems to be 4 times slower than HTTP: https://gist.github.com/hgfischer/7965620.

4) discard nginx altogether

Make Go serve everything (including static files) and handle gzip compression on every request.


How can I make nginx and Go to behave like I want (preferably with the HTTP interface)?

Apologies if this question is too basic, this is my very first experience writing a Go web app.

Additional Question

In PHP, I point fastcgi_pass to the php-fpm Unix socket. If any request experiences a PHP fatal error, the upcoming requests will still be served. However, if my Go code calls panic() the program will be terminated and the service will stop responding. What is the best way to handle that in Go?

2
  • I've been looking at go for all of two hours, but would it be helpful to use the concurrency and channels (prefix some of those with the 'go ' keyword) for parallelization?
    – gWaldo
    May 22, 2014 at 15:04
  • @gWaldo If your task is parallel in nature, then yes.
    – Alix Axel
    May 22, 2014 at 15:52

2 Answers 2

4

Problem with "proxy_pass" seems to be because of trailing /, try to remove it, as it does nothing any way. If you need to alter URI before passing it to proxy, try to use rewrite, i.e to remove some part of URI try something like:

location @go {
    rewrite           ^/part_to_remove(/.*)$ $1;
    proxy_pass        http://127.0.0.1:3000;
    proxy_set_header  Host $host;
}
1
  • That was exactly it, thank you for pointing that out.
    – Alix Axel
    Mar 27, 2014 at 2:14
2

I used the following nginx config - resource

server {
  server_name _;
  root /home/vagrant/htdocs/sinatra-test;

  location / {
    try_files $uri $uri/ @backend;
  }

  location @backend {
    proxy_pass http://localhost:4567;
  }
}

Below is my directory structure:

/home/vagrant/htdocs/sinatra-test
|-- Gemfile
|-- Gemfile.lock
|-- app.rb
`-- assets
    `-- css
        `-- screen.css

According to this site I run a simple sinatra app:

require 'sinatra'

get '/hi' do
  "Hello World!"
end

And that are the responses

# logged in the sinatra log
$ curl localhost/hi
Hello World!

# not logged in the sinatra log - comes directly from nginx
$ curl localhost/assets/css/screen.css
body { background: red; }

# logged in the sinatra log
$ curl localhost/test
<!DOCTYPE html>
<html>
<head>
  <style type="text/css">
  body { text-align:center;font-family:helvetica,arial;font-size:22px;
    color:#888;margin:20px}
  #c {margin:0 auto;width:500px;text-align:left}
  </style>
</head>
<body>
  <h2>Sinatra doesn&rsquo;t know this ditty.</h2>
  <img src='http://localhost:4567/__sinatra__/404.png'>
  <div id="c">
    Try this:
    <pre>get '/test' do
  "Hello World"
end
</pre>
  </div>
</body>
</html>

As You can see if the file does not exist the request goes to the the sinatra app.

$ nginx -V
nginx version: nginx/1.2.1

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .