
準備する
そのため、開始する前に、次のものが必要です。
- go1.8beta2以降
- Linux(go1.9ではmacOSでも動作するはずです)
- Github.com/bouk/monkeyパッケージ
主なアイデア
goには、モンキーと呼ばれる関数とメソッドの本体をその場で置き換えることができる、モンキーパッチコードのライブラリがあります。 関数の本体を書き換え、そこにコードを挿入することで機能します。 英語での実装の詳細については、 bouk.co / blog / monkey-patching-in-goをご覧ください 。 このライブラリを正しく機能させるには、インラインコードを無効にしてプロジェクトをビルドする必要があります:“ go build -gcflags = -l”。 このライブラリは私たちに適していますが、問題があります。古いコードの代わりに新しいコードでメソッドへのポインタを取得する場所はどこですか?
go1.8のプラグイン
バージョンgo1.8では、プラグインメカニズムが表示されます。これは、goコードを.soファイルとしてコンパイルする機能で、既に実行中のアプリケーションに動的にロードして使用を開始できます。 詳しくはtip.golang.org/pkg/pluginをご覧ください 。 プラグインは動的リンクで構築されており、通常のgoコードとは動作が少し異なる場合があります。 ただし、原則として、作業に違いはありません。
すべてをまとめる
メインとハンドラーの2つのパッケージでWebサーバーを作成しましょう(簡潔にするためにインポートは省略されています)。
// main.go package main func main() { http.HandleFunc("/example", handlers.Example) http.HandleFunc("/reload", handlers.Reload) http.ListenAndServe(":9999", nil) } // handlers/example.go package handlers func Example(rw http.ResponseWriter, req *http.Request) { req.ParseForm() fmt.Fprintf(rw, "Hello, %s!", req.Form.Get("name")) } func Reload(rw http.ResponseWriter, req *http.Request) { p, _ := plugin.Open("handlers.so") sym, _ := p.Lookup("Example") monkey.Patch(Example, sym) }
このWebサーバーを実行し、動作することを確認します。
$ curl 'http://localhost:9999/example?name=Yuriy' Hello, Yuriy!
プラグインを配置する別のパッケージを作成しましょう(ディレクトリの場所は関係ありません。この例では、このファイルをhandlers / plug / main.goに配置します)。
package main import ( "fmt" "net/http" ) import "C" func Example(rw http.ResponseWriter, req *http.Request) { req.ParseForm() fmt.Fprintf(rw, "Hello modified, %s!", req.Form.Get("name")) }
元のパッケージにはなかった
import "C"
注意してください。 このインポートは、プラグインが正しく機能するために必要です。
次のコマンドでこのプラグインをビルドします。
$ go build -buildmode plugin -o handlers.so
handlers.soへの相対パスはhandlers.Reload関数で指定されているため、handlers.soファイルは、Webサーバーを起動したディレクトリに配置する必要があります。
次に、コードをホットスワップしてみます。
$ curl 'http://localhost:9999/reload'
Webサーバーのログにエラーがなければ、すべてがうまくいき、コードが更新されたことを確認できます。
$ curl 'http://localhost:9999/example?name=Yuriy' Hello modified, Yuriy!
おわりに
完全なサンプルコードをgithubに投稿しました: github.com/YuriyNasretdinov/hotreload-example これは単なる概念実証であり、実際に使用する前に最終決定する価値のあるものがたくさんあることに注意してください。
- この方法で関数やメソッドにパッチを当てる簡単な方法はありません。事前にmonkey.Patchに渡すことができる関数のリストが必要です。
- アセンブルされたプラグインは、対応するgoプログラムと同じ量を使用し、場合によっては、プラグインのコンパイルにプログラム全体のコンパイルに匹敵する時間がかかることがあります。 アプリケーションの接続を減らすことを除いて、これについて何もするのは難しいです。
- monkey.Patchはスレッドセーフではなく、理論的にはアプリケーションでSEGFAULTを呼び出すことができます。
それでも、この記事を楽しんで、自分にとって何か新しいことを学んだことを願っています。 お正月おめでとうございます!