【Go】ServeMux について

<font color="red">※注意: この記事ははあくまで個人学習用に整理しただけの記事で、内容としては不完全なものになります。読んでも参考にならない可能性が高いです。</font>

golang を勉強し始めてたが、ServeMux がいまいちわからないのでまとめる

## ServeMux とは
公式ドキュメントには以下のように書いてある
https://golang.org/pkg/net/http/#ServeMux

> ServeMux is an HTTP request multiplexer. It matches the URL of each incoming request against a list of registered patterns and calls the handler for the pattern that most closely matches the URL.
ServeMuxはHTTPリクエストマルチプレクサーです。各着信リクエストのURLを登録済みパターンのリストと照合し、URLに最も近いパターンのハンドラーを呼び出します。

マルチプレクサー というのは。。。？
> マルチプレクサ、多重器、多重装置、多重化装置、合波器(multiplexer)は、ふたつ以上の入力をひとつの信号として出力する機構である。通信分野では多重通信の入口の装置、電気・電子回路では複数の電気信号をひとつの信号にする回路である。しばしばMUX等と略される。

複数の信号を入力として受け取り、1つの信号にして出力するもののようだ。http リクエストのマルチプレクサーが ServeMux ということらしい。routing を実現するための構造体のようだ。

ServeMux は以下のような構造になっている。

```golang

type ServeMux struct {
	mu    sync.RWMutex
	m     map[string]muxEntry
	es    []muxEntry // slice of entries sorted from longest to shortest.
	hosts bool       // whether any patterns contain hostnames
}
```

sync.RWMutex ? muxEntry ? また未知のものが出てきた。。

### sync.RWMutex とは
https://golang.org/pkg/sync/#RWMutex

> A RWMutex is a reader/writer mutual exclusion lock. The lock can be held by an arbitrary number of readers or a single writer. The zero value for a RWMutex is an unlocked mutex.
RWMutexは、リーダー/ライターの相互排他ロックです。ロックは、任意の数のリーダーまたは単一のライターが保持できます。 RWMutexのゼロ値は、ロックされていないミューテックスです。

正確には理解できていないが、リーダー/ライターの相互排他ロックとのこと。
以下のような構造になっている。
https://golang.org/src/sync/rwmutex.go?s=987:1319#L18

```golang

type RWMutex struct {
	w           Mutex  // held if there are pending writers
	writerSem   uint32 // semaphore for writers to wait for completing readers
	readerSem   uint32 // semaphore for readers to wait for completing writers
	readerCount int32  // number of pending readers
	readerWait  int32  // number of departing readers
}
```

また Mutex というわからないものが出てきた。

### sync.Mutex とは

> A Mutex is a mutual exclusion lock.
The zero value for a Mutex is an unlocked mutex.
Mutexは相互排他ロックです。 
ミューテックスのゼロ値はロックされていないミューテックスです。

ロックについては以下の記事がわかりやすかった
[Go でロックする](https://qiita.com/mrasu/items/7531a5a28e9fda77aa5e)
[sync.Mutex【Go】](https://tech-up.hatenablog.com/entry/2018/12/03/203819)
[A Tour of Go](https://go-tour-jp.appspot.com/concurrency/9)
複数の goroutines から並行処理で、同じ変数を変更したりする際にロックが重要になるとのこと。
RWMutex はそのリーダー/ライター版ってことかな？

そして muxEntry についても確認
### http.muxEntry とは
https://golang.org/search?q=muxEntry

http パッケージ内に以下のように定義されている

```golang
type muxEntry struct {
	h       Handler
	pattern string
}
```

Handler と URL パターンを持った構造体だ。

muxEntry は、以下の ServeMux の Handle method で使用されている。
https://golang.org/src/net/http/server.go?h=muxEntry#L2420

```golang
// Handle registers the handler for the given pattern.
// If a handler already exists for pattern, Handle panics.
func (mux *ServeMux) Handle(pattern string, handler Handler) {
	mux.mu.Lock()
	defer mux.mu.Unlock()

	if pattern == "" {
		panic("http: invalid pattern")
	}
	if handler == nil {
		panic("http: nil handler")
	}
	if _, exist := mux.m[pattern]; exist {
		panic("http: multiple registrations for " + pattern)
	}

	if mux.m == nil {
		mux.m = make(map[string]muxEntry) // フィールド m にmake(map[string]muxEntry) が代入され 
	}
	// muxEntry 構造体を h handler, pattern pattern で初期化した e 変数を作成
	e := muxEntry{h: handler, pattern: pattern}
	mux.m[pattern] = e // mux.m[pattern]に代入されている。
	if pattern[len(pattern)-1] == '/' {
		mux.es = appendSorted(mux.es, e)
	}

	if pattern[0] != '/' {
		mux.hosts = true // パターンにホスト名が含まれているかどうか
	}
}

func appendSorted(es []muxEntry, e muxEntry) []muxEntry {
	n := len(es)
	i := sort.Search(n, func(i int) bool {
		return len(es[i].pattern) < len(e.pattern)
	})
	if i == n {
		return append(es, e)
	}
	// we now know that i points at where we want to insert
	es = append(es, muxEntry{}) // try to grow the slice in place, any entry works.
	copy(es[i+1:], es[i:])      // Move shorter entries down
	es[i] = e
	return es
}
```

mux.esは

>slice of entries sorted from longest to shortest.
最長から最短にソートされたエントリのスライス。

とのこと。appendSorted でソートしたエントリのスライスを生成して、mux.esへ格納している

なぜ ServeMux の Handle メソッドを実装した際に sync.RWMutex で lock する必要があるのかは読み解けなかった。。。

まとめると、

ServeMux は

- reader / writer ロックの為の sync.RWMutex
- URL と Hander の map
- sort した URL の配列
- パターンにホスト名が含まれているかどうかのフラグ

以上を保有した構造体だった。

あんまりわかってないけど、一旦そういうものと整理しておく。