golang serve SPA
最近有前端開發的需求,選用了 react-redux-starter-kit 來進行二次開發,省去一些想要使用 React, redux, redux-router 的基本配置,這樣速度會快一點
因為 react-redux-starter-kit
也使用 webpack 進行程式碼的打包, 所以最後的產出預設在 dist
資料夾中,所以部署時只需要一個簡單的 host server 即可
1
2
3
4
5
6
7
8
9
10
11
12
| # dist example
$ l
total 752
-rw-r--r-- 1 cage staff 3317 Aug 4 00:22 1.counter.fa53ea42bc9ff9de19bd.js
-rw-r--r-- 1 cage staff 144953 Aug 4 00:22 app.5d0f2ab61ef7dd5daac5.js
-rw-r--r-- 1 cage staff 2619 Aug 4 00:22 app.97a1751c9624097874a4b54cb93fa067.css
-rw-r--r-- 1 cage staff 173 Aug 4 00:33 app.go
-rw-r--r-- 1 cage staff 24838 Aug 4 00:22 favicon.ico
-rw-r--r-- 1 cage staff 103 Aug 4 00:22 humans.txt
-rw-r--r-- 1 cage staff 604 Aug 4 00:22 index.html
-rw-r--r-- 1 cage staff 24 Aug 4 00:22 robots.txt
-rw-r--r-- 1 cage staff 183224 Aug 4 00:22 vendor.9012d9d99074521f418e.js
|
考慮效能的問題, 最後打算使用 golang 來當作 host server, golang 內建的 net/http
可以輕鬆的使用 http.FileServer(http.Dir("./"))
來 host 整個靜態目錄
1
2
3
4
5
6
7
8
9
10
11
| package main
import (
"log"
"net/http"
)
func main() {
log.Println("Listening port 3000...")
log.Fatal(http.ListenAndServe(":3000", http.FileServer(http.Dir("./"))))
}
|
但是上述的作法基本上是可以動的, 不過如果前端自己有使用到 redux-router
時, golang 並不會將請求導至前端的 router 而是直接得到 golang 404 而不會進到前端 redux-router 訂定的 router (如果有使用 redux-router 對 Notfound 進行處理)
1
| http://localhost:3000/dfa
|
![/posts/golang-serve-static-site/img/golang-serve-static-site-404-golang.png](/svg/loading.min.svg)
所以我們使用了 golang echo
的 web framework, 監聽所有的請求並直接導至 index.html
的前端靜態檔案
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
| package main
import (
"flag"
"fmt"
"net/http"
"os"
"strings"
"github.com/labstack/echo"
"github.com/labstack/echo/engine/standard"
mw "github.com/labstack/echo/middleware"
)
const (
wwwRoot = "./"
)
var (
httpPort = flag.Int("http", 3000, "http port number")
)
func Init() *echo.Echo {
e := echo.New()
e.Debug()
e.Use(mw.Logger())
e.Use(mw.Recover())
e.Any("/*", echo.HandlerFunc(func(c echo.Context) (err error) {
r := c.Request().(*standard.Request).Request
w := c.Response().(*standard.Response).ResponseWriter
requestPath := r.URL.Path
fileSystemPath := wwwRoot + r.URL.Path
endURIPath := strings.Split(requestPath, "/")[len(strings.Split(requestPath, "/"))-1]
splitPath := strings.Split(endURIPath, ".")
splitLength := len(splitPath)
if splitLength > 1 && splitPath[splitLength-1] != "go" {
f, error := os.Stat(fileSystemPath)
if error == nil && !f.IsDir() {
http.ServeFile(w, r, fileSystemPath)
return
}
}
http.ServeFile(w, r, wwwRoot+"index.html")
return
}))
return e
}
func main() {
flag.Parse()
server := Init()
server.Run(standard.New(fmt.Sprintf(`:%d`, *httpPort)))
}
|
1
| http://localhost:3000/dfa
|
![/posts/golang-serve-static-site/img/golang-serve-static-site-404.jpg](/svg/loading.min.svg)
run SPA as with docker image
處理完 golang 對 single page application 的支援, 進一步簡化部署的流程, 可以將整個 single page application 連同 app.go
利用 golang build 的方式編譯成執行檔, 再將 golang 執行檔透過 Dockerfile
編譯成 docker image, 這樣一來就可以很容易的部署在任何可以執行 container 的環境
![img/golang-serve-static-site-flow.jpg](/svg/loading.min.svg)
上述一系列的流程我們可以使用 npm run scripts
的方式把它完全串起來來達到一鍵建立 docker image
1
2
3
4
5
6
7
8
9
10
| FROM alpine:3.3
MAINTAINER cage.chung <[email protected]>
WORKDIR /go
ADD . /go/
EXPOSE 3000
CMD ["./counter"]
|
Dockerfile 檔案中我們需要指定 docker image 啟動時直接執行我們透過 GOOS=linux GOARCH=amd64 go build -o counter
編譯出來的執行檔 counter*
執行自訂義 npm scripts $ npm run docker
成功執行後會自動建立 docker image
1
2
3
4
| # list docker image
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
username/counter v0.1.0 1771ddbe0a98 4 seconds ago 14.67 MB
|
執行 docker image
1
2
3
4
5
6
7
8
| # run docker image
$ docker run -d --name counter -p 3000:3000 username/counter:v0.1.0
f8394fec624a4e3b989f7ce48857f64178e39aa8a5195f39e2d0d5a6572ee55c
# docker ps
$ dps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f8394fec624a username/counter:v0.1.0 "./counter" 2 seconds ago Up 1 seconds 0.0.0.0:3000->3000/tcp counter
|
repo
Demo example cage1016/golang-serve-spa
1
2
3
4
5
6
7
8
9
10
11
| # clone repo
$ git clone [email protected]:cage1016/golang-serve-spa.git
# npm install package
$ npm install
# 一鍵建立 docker image
$ npm docker
# docker run
$ docker run -d --name counter -p 3000:3000 username/counter:v0.1.0
|