├── benchmarks ├── beego.jpg ├── static.jpg ├── vanilla.jpg └── graph.plot ├── examples ├── beego │ ├── Makefile │ ├── main.go │ ├── routers │ │ └── router.go │ ├── .htaccess │ ├── controllers │ │ └── default.go │ ├── tests │ │ └── default_test.go │ └── views │ │ └── index.tpl └── vanilla │ ├── Makefile │ ├── .htaccess │ └── hello_world.go ├── fig.yml ├── .gitignore ├── web ├── Dockerfile ├── public │ └── index.html └── sites-enabled │ ├── default.conf │ ├── beego.conf │ └── vanilla.conf ├── Makefile ├── LICENSE └── README.md /benchmarks/beego.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bsingr/golang-apache-fastcgi/HEAD/benchmarks/beego.jpg -------------------------------------------------------------------------------- /benchmarks/static.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bsingr/golang-apache-fastcgi/HEAD/benchmarks/static.jpg -------------------------------------------------------------------------------- /benchmarks/vanilla.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bsingr/golang-apache-fastcgi/HEAD/benchmarks/vanilla.jpg -------------------------------------------------------------------------------- /examples/beego/Makefile: -------------------------------------------------------------------------------- 1 | SCRIPT=beego 2 | 3 | pack: 4 | go build 5 | unpack: 6 | mv ${SCRIPT} ${SCRIPT}.fcgi 7 | -------------------------------------------------------------------------------- /examples/vanilla/Makefile: -------------------------------------------------------------------------------- 1 | SCRIPT=hello_world 2 | 3 | pack: 4 | go build ${SCRIPT}.go 5 | unpack: 6 | mv ${SCRIPT} ${SCRIPT}.fcgi 7 | -------------------------------------------------------------------------------- /fig.yml: -------------------------------------------------------------------------------- 1 | web: 2 | build: web 3 | ports: 4 | - "80:80" 5 | - "81:81" 6 | - "82:82" 7 | volumes: 8 | - "web/logs:/var/log/apache2" 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | web/logs/* 2 | **/*.fcgi 3 | web/public/vanilla 4 | web/public/beego 5 | examples/vanilla/hello_world 6 | examples/beego/beego 7 | Gemfile.lock 8 | benchmarks/*.tsv 9 | -------------------------------------------------------------------------------- /examples/beego/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/astaxie/beego" 5 | _ "golang-apache-fastcgi/examples/beego/routers" 6 | ) 7 | 8 | func main() { 9 | beego.UseFcgi = true 10 | beego.UseStdIo = true 11 | beego.Run() 12 | } 13 | -------------------------------------------------------------------------------- /examples/beego/routers/router.go: -------------------------------------------------------------------------------- 1 | package routers 2 | 3 | import ( 4 | "golang-apache-fastcgi/examples/beego/controllers" 5 | "github.com/astaxie/beego" 6 | ) 7 | 8 | func init() { 9 | beego.Router("/", &controllers.MainController{}) 10 | } 11 | -------------------------------------------------------------------------------- /examples/beego/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | AddHandler fcgid-script .fcgi 3 | 4 | 5 | Options +ExecCGI 6 | 7 | RewriteEngine On 8 | RewriteCond %{REQUEST_FILENAME} !-f 9 | RewriteCond %{REQUEST_FILENAME} !-d [OR] 10 | RewriteRule ^(.*)$ beego.fcgi [QSA,L] 11 | -------------------------------------------------------------------------------- /examples/vanilla/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | AddHandler fcgid-script .fcgi 3 | 4 | 5 | Options +ExecCGI 6 | 7 | RewriteEngine On 8 | RewriteCond %{REQUEST_FILENAME} !-f 9 | RewriteCond %{REQUEST_FILENAME} !-d [OR] 10 | RewriteRule ^(.*)$ hello_world.fcgi/$1 [QSA,L] 11 | -------------------------------------------------------------------------------- /examples/beego/controllers/default.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "github.com/astaxie/beego" 5 | ) 6 | 7 | type MainController struct { 8 | beego.Controller 9 | } 10 | 11 | func (this *MainController) Get() { 12 | this.Data["Website"] = "beego.me" 13 | this.Data["Email"] = "astaxie@gmail.com" 14 | this.TplNames = "index.tpl" 15 | } 16 | -------------------------------------------------------------------------------- /web/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:14.04 2 | MAINTAINER Jens Bissinger 3 | 4 | RUN apt-get update && apt-get -y install apache2 libapache2-mod-fcgid && apt-get clean 5 | RUN a2enmod rewrite fcgid 6 | 7 | ENV APACHE_RUN_USER www-data 8 | ENV APACHE_RUN_GROUP www-data 9 | ENV APACHE_LOG_DIR /var/log/apache2 10 | 11 | RUN rm -rf /etc/apache2/sites-enabled 12 | ADD ./sites-enabled /etc/apache2/sites-enabled 13 | 14 | RUN rm -rf /var/www 15 | ADD ./public /var/www 16 | 17 | EXPOSE 80 18 | EXPOSE 81 19 | EXPOSE 82 20 | 21 | CMD ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"] 22 | -------------------------------------------------------------------------------- /web/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Go lang + Apache + FastCGI via standard I/O 5 | 15 | 16 | 17 |

Go lang + Apache + FastCGI via standard I/O

18 |

Apps

19 | 23 |

See github.com/bsingr/golang-apache-fastcgi.

24 | 25 | 26 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | EXAMPLES_VANILLA=examples/vanilla 2 | EXAMPLES_BEEGO=examples/beego 3 | 4 | default: 5 | make deploy 6 | make run 7 | run: 8 | fig rm --force && fig build && fig up 9 | deploy: 10 | GOOS=linux GOARCH=amd64 make build 11 | cp -R ${EXAMPLES_VANILLA} web/public/ 12 | cp -R ${EXAMPLES_BEEGO} web/public/ 13 | make unpack 14 | unpack: 15 | cd web/public/vanilla && make unpack 16 | cd web/public/beego && make unpack 17 | build: 18 | cd ${EXAMPLES_VANILLA} && make pack 19 | cd ${EXAMPLES_BEEGO} && make pack 20 | bench: 21 | ab -n 5000 -c 100 -g benchmarks/static.tsv http://localdocker:80/ 22 | ab -n 5000 -c 100 -g benchmarks/vanilla.tsv http://localdocker:81/ 23 | ab -n 5000 -c 100 -g benchmarks/beego.tsv http://localdocker:82/ 24 | make graph 25 | graph: 26 | gnuplot benchmarks/graph.plot 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Jens Bissinger 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/beego/tests/default_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httptest" 6 | "testing" 7 | "runtime" 8 | "path/filepath" 9 | _ "golang-apache-fastcgi/examples/beego/routers" 10 | 11 | "github.com/astaxie/beego" 12 | . "github.com/smartystreets/goconvey/convey" 13 | ) 14 | 15 | func init() { 16 | _, file, _, _ := runtime.Caller(1) 17 | apppath, _ := filepath.Abs(filepath.Dir(filepath.Join(file, ".." + string(filepath.Separator)))) 18 | beego.TestBeegoInit(apppath) 19 | } 20 | 21 | 22 | // TestMain is a sample to run an endpoint test 23 | func TestMain(t *testing.T) { 24 | r, _ := http.NewRequest("GET", "/", nil) 25 | w := httptest.NewRecorder() 26 | beego.BeeApp.Handlers.ServeHTTP(w, r) 27 | 28 | beego.Trace("testing", "TestMain", "Code[%d]\n%s", w.Code, w.Body.String()) 29 | 30 | Convey("Subject: Test Station Endpoint\n", t, func() { 31 | Convey("Status Code Should Be 200", func() { 32 | So(w.Code, ShouldEqual, 200) 33 | }) 34 | Convey("The Result Should Not Be Empty", func() { 35 | So(w.Body.Len(), ShouldBeGreaterThan, 0) 36 | }) 37 | }) 38 | } 39 | 40 | -------------------------------------------------------------------------------- /web/sites-enabled/default.conf: -------------------------------------------------------------------------------- 1 | 2 | # The ServerName directive sets the request scheme, hostname and port that 3 | # the server uses to identify itself. This is used when creating 4 | # redirection URLs. In the context of virtual hosts, the ServerName 5 | # specifies what hostname must appear in the request's Host: header to 6 | # match this virtual host. For the default virtual host (this file) this 7 | # value is not decisive as it is used as a last resort host regardless. 8 | # However, you must set it for any further virtual host explicitly. 9 | # ServerName www.example.com 10 | 11 | ServerAdmin webmaster@localhost 12 | 13 | DocumentRoot /var/www 14 | 15 | # Available loglevels: trace8, ..., trace1, debug, info, notice, warn, 16 | # error, crit, alert, emerg. 17 | # It is also possible to configure the loglevel for particular 18 | # modules, e.g. 19 | #LogLevel info ssl:warn 20 | 21 | ErrorLog ${APACHE_LOG_DIR}/error.log 22 | CustomLog ${APACHE_LOG_DIR}/access.log combined 23 | 24 | 25 | AllowOverride All 26 | 27 | 28 | 29 | # vim: syntax=apache ts=4 sw=4 sts=4 sr noet 30 | -------------------------------------------------------------------------------- /web/sites-enabled/beego.conf: -------------------------------------------------------------------------------- 1 | Listen 82 2 | 3 | 4 | # The ServerName directive sets the request scheme, hostname and port that 5 | # the server uses to identify itself. This is used when creating 6 | # redirection URLs. In the context of virtual hosts, the ServerName 7 | # specifies what hostname must appear in the request's Host: header to 8 | # match this virtual host. For the default virtual host (this file) this 9 | # value is not decisive as it is used as a last resort host regardless. 10 | # However, you must set it for any further virtual host explicitly. 11 | # ServerName www.example.com 12 | 13 | ServerAdmin webmaster@localhost 14 | 15 | DocumentRoot /var/www/beego 16 | 17 | # Available loglevels: trace8, ..., trace1, debug, info, notice, warn, 18 | # error, crit, alert, emerg. 19 | # It is also possible to configure the loglevel for particular 20 | # modules, e.g. 21 | #LogLevel info ssl:warn 22 | 23 | ErrorLog ${APACHE_LOG_DIR}/error.log 24 | CustomLog ${APACHE_LOG_DIR}/access.log combined 25 | 26 | 27 | AllowOverride All 28 | 29 | 30 | 31 | # vim: syntax=apache ts=4 sw=4 sts=4 sr noet 32 | -------------------------------------------------------------------------------- /web/sites-enabled/vanilla.conf: -------------------------------------------------------------------------------- 1 | Listen 81 2 | 3 | 4 | # The ServerName directive sets the request scheme, hostname and port that 5 | # the server uses to identify itself. This is used when creating 6 | # redirection URLs. In the context of virtual hosts, the ServerName 7 | # specifies what hostname must appear in the request's Host: header to 8 | # match this virtual host. For the default virtual host (this file) this 9 | # value is not decisive as it is used as a last resort host regardless. 10 | # However, you must set it for any further virtual host explicitly. 11 | # ServerName www.example.com 12 | 13 | ServerAdmin webmaster@localhost 14 | 15 | DocumentRoot /var/www/vanilla 16 | 17 | # Available loglevels: trace8, ..., trace1, debug, info, notice, warn, 18 | # error, crit, alert, emerg. 19 | # It is also possible to configure the loglevel for particular 20 | # modules, e.g. 21 | #LogLevel info ssl:warn 22 | 23 | ErrorLog ${APACHE_LOG_DIR}/error.log 24 | CustomLog ${APACHE_LOG_DIR}/access.log combined 25 | 26 | 27 | AllowOverride All 28 | 29 | 30 | 31 | # vim: syntax=apache ts=4 sw=4 sts=4 sr noet 32 | -------------------------------------------------------------------------------- /benchmarks/graph.plot: -------------------------------------------------------------------------------- 1 | # Let's output to a jpeg file 2 | set terminal jpeg size 500,500 3 | # This sets the aspect ratio of the graph 4 | set size 1, 1 5 | # The graph title 6 | # Where to place the legend/key 7 | set key left top 8 | # Draw gridlines oriented on the y axis 9 | set grid y 10 | # Specify that the x-series data is time data 11 | set xdata time 12 | # Specify the *input* format of the time data 13 | set timefmt "%s" 14 | # Specify the *output* format for the x-axis tick labels 15 | set format x "%S" 16 | # Label the x-axis 17 | set xlabel 'seconds' 18 | # Label the y-axis 19 | set ylabel "response time (ms)" 20 | # Tell gnuplot to use tabs as the delimiter instead of spaces (default) 21 | set datafile separator '\t' 22 | 23 | # Plot the data 24 | set title "Apache Static" 25 | set output "benchmarks/static.jpg" 26 | plot "benchmarks/static.tsv" every ::2 using 2:5 title 'response time' with points 27 | 28 | set title "Vanilla Go" 29 | set output "benchmarks/vanilla.jpg" 30 | plot "benchmarks/vanilla.tsv" every ::2 using 2:5 title 'response time' with points 31 | 32 | set title "Beego" 33 | set output "benchmarks/beego.jpg" 34 | plot "benchmarks/beego.tsv" every ::2 using 2:5 title 'response time' with points 35 | 36 | exit 37 | -------------------------------------------------------------------------------- /examples/vanilla/hello_world.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "log" 7 | "net/http" 8 | "net/http/fcgi" 9 | "os" 10 | "runtime" 11 | ) 12 | 13 | var app_addr string 14 | 15 | func init() { 16 | runtime.GOMAXPROCS(runtime.NumCPU()) 17 | app_addr = os.Getenv("APP_ADDR") // e.g. "0.0.0.0:8080" or "" 18 | } 19 | 20 | func ServeHTTP(w http.ResponseWriter, r *http.Request) { 21 | headers := w.Header() 22 | headers.Add("Content-Type", "text/html") 23 | io.WriteString(w, "

Hello world from Go!

") 24 | io.WriteString(w, fmt.Sprintf("", r.Method)) 25 | io.WriteString(w, fmt.Sprintf("", r.URL)) 26 | io.WriteString(w, fmt.Sprintf("", r.URL.Path)) 27 | io.WriteString(w, fmt.Sprintf("", r.Proto)) 28 | io.WriteString(w, fmt.Sprintf("", r.Host)) 29 | io.WriteString(w, fmt.Sprintf("", r.RemoteAddr)) 30 | io.WriteString(w, fmt.Sprintf("", r.RequestURI)) 31 | io.WriteString(w, fmt.Sprintf("", r.Header)) 32 | io.WriteString(w, fmt.Sprintf("", r.Body)) 33 | io.WriteString(w, "
Method%s
URL%s
URL.Path%s
Proto%s
Host%s
RemoteAddr%s
RequestURI%s
Header%s
Body%s
") 34 | } 35 | 36 | func main() { 37 | http.HandleFunc("/", ServeHTTP) 38 | 39 | var err error 40 | if app_addr != "" { // Run as a local web server 41 | err = http.ListenAndServe(app_addr, nil) 42 | } else { // Run as FCGI via standard I/O 43 | err = fcgi.Serve(nil, nil) 44 | } 45 | if err != nil { 46 | log.Fatal(err) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Go + Apache + FastCGI = Go for Shared Hosting 2 | 3 | Learning project to deploy a [Go](http://go-lang.org) binary as [FastCGI](http://www.fastcgi.com) script on an [Apache http server](http://httpd.apache.org). 4 | 5 | ## Examples 6 | 7 | * [Vanilla](./examples/vanilla) 8 | * Starting point to make any go binary FastCGI compatible. 9 | * [Beego](./examples/beego) 10 | * Using the Beego framework right now requires this pull-request [astaxie/beego#858](https://github.com/astaxie/beego/pull/858). 11 | 12 | ## Getting Started 13 | 14 | Please make sure you have [Go](http://go-lang.org) and [Beego](http://beego.me) installed. Then just do: 15 | 16 | $ make 17 | 18 | Then open your browser: 19 | 20 | $ open http://localhost 21 | 22 | When you are using [docker-osx](https://github.com/noplay/docker-osx): 23 | 24 | $ open http://localdocker 25 | 26 | ## Why Apache HTTP? 27 | 28 | The primary focus of this project is to demo the use of Go binaries in a shared hosting environment. 29 | 30 | Also on web servers like Nginx there are seperate start/stop scripts required to [spawn FastCGI processes](http://wiki.nginx.org/FcgiExample#Spawning_a_FastCGI_Process). 31 | 32 | ## Performance 33 | 34 | ![benchmarks/static.jpg](./benchmarks/static.jpg) 35 | ![benchmarks/vanilla.jpg](benchmarks/vanilla.jpg) 36 | ![benchmarks/beego.jpg](benchmarks/beego.jpg) 37 | 38 | ## Thanks / References 39 | 40 | * [http://www.dav-muz.net/blog/2013/09/how-to-use-go-and-fastcgi/](http://www.dav-muz.net/blog/2013/09/how-to-use-go-and-fastcgi/) 41 | * [http://httpd.apache.org/mod_fcgid/mod/mod_fcgid.html](http://httpd.apache.org/mod_fcgid/mod/mod_fcgid.html) 42 | * [http://golang.org/pkg/net/http/](http://golang.org/pkg/net/http/) 43 | 44 | ## License 45 | 46 | See [LICENSE](LICENSE). 47 | -------------------------------------------------------------------------------- /examples/beego/views/index.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Beego 6 | 7 | 8 | 62 | 63 | 64 | 65 |
66 |
67 |
68 |
69 |

Welcome to Beego!

70 |

71 | Beego is a simple & powerful Go web framework which is inspired by tornado and sinatra. 72 |
73 | Official website: {{.Website}} 74 |
75 | Contact me: {{.Email}} 76 |

77 |
78 |
79 |
80 |
81 | 82 | 83 | --------------------------------------------------------------------------------