golangのnet/httpを使った簡易WEBサーバー「Shiba.」をgithubにて公開していたのですが。HandleFuncが勝手にスレッドを作って動いているマルチスレッドなサーバーだと知って作り直し初めまして
どうせなら、構造体を使ったURLの指定を思い浮かんだので使いたい。そして、実行スレッド数を指定できる方法を海外のウェブで見つけたので、それも取り込んで作り直したら。ワンソースなプログラムでまとまりました
shiba.go
package main
import (
"fmt"
"regexp"
"strings"
"time"
"net"
"net/http"
"golang.org/x/time/rate"
"log"
"context"
"os"
"os/signal"
"runtime"
"runtime/debug"
)
// Server System Settings.
const (
DEBUG bool = true
)
var (
filePath string = "public"
port string = "8000"
urlLength int = 1000
threadCount int = 10
) // サーバーのパラメーターの設定
var (
urlSetting []*urlRotator = []*urlRotator{
{"/", world},
}
) // URLルーティングの設定
var (
limiter = rate.NewLimiter(1, 4) // 秒単位で4アクセス
) // 実行スレッド数の設定
// Server.
type urlRotator struct {
path string
function func(w http.ResponseWriter, r *http.Request)
}
var (
rexUrl = regexp.MustCompile(`\A[A-Za-z0-9\%\#\$\-\_\.\+\!\*\'\(\)\,\;\/\?\:\@\=\&\~\\\|]+\z`)
hostAddress string
)
func main() {
//
mux := http.NewServeMux()
for _, f := range urlSetting {
mux.HandleFunc("/exec"+strings.TrimSpace(f.path), f.function)
}
mux.HandleFunc("/", publicFile)
urlSetting = nil // メモリから削除
//
filePath = strings.TrimSpace(filePath)
port = strings.TrimSpace(port)
if port == "" {
port = "80"
}
h := ""
if DEBUG == true {
h = "127.0.0.1"
}
a := net.JoinHostPort(h, port)
server := &http.Server{
Addr: a,
Handler: start(mux),
ReadHeaderTimeout: 10 * time.Second,
ReadTimeout: 10 * time.Second,
}
runtime.GC()
debug.SetGCPercent(-1)
fmt.Println("start Shiba server.")
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
defer stop()
go func() {
log.Fatal(server.ListenAndServe())
}()
<-ctx.Done()
ctx, cancel := context.WithTimeout(context.Background(), 14*time.Second)
defer cancel()
fmt.Println("shutdown.")
server.Shutdown(ctx)
debug.SetGCPercent(100)
}
var threadCounter int = 0
func start(next http.Handler) http.Handler {
//
threadCounter++
if threadCounter == threadCount {
threadCounter = 0
rtsGarbageCollection()
}
//
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if limiter.Allow() == false {
http.Error(w, http.StatusText(429), http.StatusTooManyRequests)
return
}
waf(w, r)
next.ServeHTTP(w, r)
})
}
func waf(w http.ResponseWriter, r *http.Request) {
if len(r.URL.Path) > urlLength {
http.Error(w, http.StatusText(400), http.StatusBadRequest)
return
}
if rexUrl.FindString(r.URL.Path) == "" {
http.Error(w, http.StatusText(400), http.StatusBadRequest)
return
}
}
func rtsGarbageCollection() {
var mem runtime.MemStats
runtime.ReadMemStats(&mem)
if mem.HeapAlloc > 500<<20 { // 500MB
runtime.GC()
}
}
func publicFile(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, filePath+r.URL.Path)
}
// Action(s).
func world(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "New World.")
}
とりあえず、これをベースとして新型のShibaを作り直すつもりですが。取り急ぎ、動いちゃった試作品をMITライセンスで、qiitaへ置いときます。Your own risks...
今回、スマートなサーバーを作れたと思うけど。検索のみでプログラムを作ると、脳みそをまとめる手間が凄いのだなと思いました。書籍は大切
以上です