小さなPython Webバック゚ンドの移怍によるGoの孊習

内容Goを孊習するために、PythonからGoに小さなサむトにバック゚ンドを移怍し、その過皋で楜で痛みのない経隓を埗たした。

しばらくの間Goの孊習を開始したかったのですが、その哲孊が気に入っおいたした。小さな蚀語、すおきな孊習曲線、非垞に高速なコンパむル静的に型付けされた蚀語などです。 最終的にさらに䞀歩進んで圌に教え始めたのは、特に最近䜿甚したものから、Go- Dockerずngrokで曞かれたより速くお深刻なプログラムが増えおいるこずです。







Goの哲孊は、すべおの人の奜みではありたせん䟋倖はありたせん。独自のゞェネリックなどを䜜成するこずはできたせんが、私の粟神モデルにうたく適合しおいたす。 シンプルで高速、明癜な方法で物事を行う。 ポヌティング䞭、暙準ラむブラリずツヌルがどれだけ充実しおいるかに特に感銘を受けたした。







移怍



Goで20行のスクリプトをいく぀か䜜成したしたが、蚀語ず゚コシステムを理解するには十分ではありたせんでした。 そのため、より倧きなプロゞェクトを採甚するこずにし、サむトGiftyWeddings.comのバック゚ンドを移怍するこずにしたした 。







Pythonでは、Flask、WTForms、Peewee、SQLite、およびS3、画像のサむズ倉曎などのための他のいく぀かのラむブラリを䜿甚しお、玄1300行のコヌドでした。







Goバヌゞョンでは、蚀語をよりよくマスタヌし、暙準ラむブラリで可胜な限り機胜するために、できるだけ少ない倖郚䟝存関係を䜿甚したかったのです。 特に、GoにはHTTPを操䜜するための優れたラむブラリがあり、今のずころWebフレヌムワヌクをたったく怜蚎しないこずにしたした。 しかし、私はただS3、Stripe、SQLiteのいく぀かのサヌドパヌティラむブラリを䜿甚し、パスワヌドを操䜜し、写真のサむズを倉曎したした。







Goの静的型付けず、䜿甚するラむブラリの数が少ないため、コヌドは元の2倍以䞊になるず予想したした。 しかし、結果は1900行でしたPythonの1300行の玄50。







移怍プロセス自䜓は非垞にスムヌズに進み、ほずんどのビゞネスロゞックは、PythonからGoにコヌドを行から行に倉換するこずでほが機械的に移怍されたした。 Pythonの抂念がthings[:20]



スラむス構文に至るたで、Goにどれだけうたく翻蚳されおいるかに驚きたした。







たた、Flaskが䜿甚する危険なラむブラリの䞀郚を移怍したため、Goバヌゞョンぞの移行䞭にPythonサヌビスからセッションCookieを透過的にデコヌドできたした。 暗号化、圧瞮、シリアル化のすべおのコヌドは暙準ラむブラリにあり、簡単なプロセスでした。







䞀般に、 Go Tour 、 Effective Go、およびむンタヌネット䞊のさたざたなコヌド䟋を芋る間、蚀語を孊習するプロセスは苊劎したせんでした。 ドキュメントはかなり短いですが、非垞によく曞かれおいたす。







蚀語ツヌルも非垞に満足しおいたす。コヌドの構築、実行、フォヌマット、およびテストに必芁なものはすべお、 go



サブコマンドを介しお利甚できたす。 開発䞭に、 go run *.go



を䜿甚しおオンザフラむでコンパむルし、サヌバヌを起動したした。 線集ず起動には玄1秒かかりたした。これは、Scalaでの20秒の増分ず20分の完党な線集からの剣の戊いの埌の新鮮な空気でした。







テスト䞭



Go暙準ラむブラリには、基本的なテストパッケヌゞず、 go test



を怜玢、コンパむル、実行するすぐに䜿甚できるテストランナヌ go test



が含たれおいたす。 このパッケヌゞは非垞に簡単でシンプルですが倚すぎる堎合もありたす、必芁に応じおヘルパヌを远加するだけです。







単䜓テストに加えお、実際のGifty WeddingsサヌバヌでセットアップされたHTTPテストを実行するテストスクリプトを䜜成したした testing



パッケヌゞも䜿甚。 叀いPythonサヌバヌでこのテストを蚭定し、結果が同䞀であるこずを確認できるようにするために、コヌドのGoレベルではなく、HTTPレベルでこれを行いたした。 これにより、サヌバヌを倉曎する前にすべおが正垞に機胜したこずを十分に確信できたした。







たた、ホワむトボックステストも行いたした。スクリプトは応答をチェックしたすが、Cookieをデコヌドし、Cookieに正しいデヌタが含たれおいるこずもチェックしたす。







レゞストリを䜜成しおギフトを削陀するテストの䟋を次に瀺したす。







 func TestDeleteGift(t *testing.T) { client := NewClient(baseURL) response := client.PostJSONOK(t, "/api/create", nil) AssertMatches(t, response["slug"], `temp\d+`) slug := response["slug"].(string) html := client.GetOK(t, "/"+slug, "text/html") _, gifts := ParseRegistryAndGifts(t, html) AssertEqual(t, len(gifts), 3) gift := gifts[0].(map[string]interface{}) giftID := int(gift["id"].(float64)) response = client.PostJSONOK(t, fmt.Sprintf("/api/registries/%s/gifts/%d/delete", slug, giftID), nil) expected := map[string]interface{}{ "id": nil, } AssertDeepEqual(t, response, expected) html = client.GetOK(t, "/"+slug, "text/html") _, gifts = ParseRegistryAndGifts(t, html) AssertEqual(t, len(gifts), 2) }
      
      





クロスコンパむル



これは非珟実的なクヌルだず思いたす-macOSで蚀えたす







 $ GOOS=linux GOARCH=amd64 go build
      
      





これにより、すぐに䜿甚できるLinuxバむナリがMacでコンパむルされたす。 そしおもちろん、これを逆方向に行うこずもでき、Windowsずの間でクロスコンパむルもできたす。 それだけで動䜜したす。







cgoモゞュヌルSQLiteなどのクロスコンパむルは、コンパむルのためにGCCの正しいバヌゞョンをむンストヌルする必芁があるため、少し耇雑でした-Goずは異なり、それほど簡単ではありたせん。 その結果、Linux䞊でビルドするために次のコマンドでDockerを䜿甚したした。







 $ docker run --rm -it -v ~/go:/go -w /go/src/gifty golang:1.9.1 \ go build -o gifty_linux -v *.go
      
      





良い点



Goの最もクヌルな点の1぀は、暙準ラむブラリ、ツヌルgoサブコマンド、さらにはサヌドパヌティラむブラリなど、すべおが具䜓的に匷化されおいるように感じるこずです。 私の内なる本胜は、これは郚分的にはGoに䟋倖がないずいう事実によるものであり、゚ラヌの凊理方法によるある皮の「゚ラヌ凊理文化」が課せられおいるこずを瀺しおいたす。







ネットワヌクおよびHTTPラむブラリは特にクヌルに芋えたす。 わずか数行のコヌドでnet / http Webサヌバヌ実皌働レベルおよびHTTP / 2サポヌト付き、泚を実行できたす。







暙準ラむブラリには、html / template、ioutil.WriteFile、ioutil.TempFile、crypto / sha1、encoding / base64、smtp.SendMail、zlib、image / jpeg、image / pngなど、必芁なもののほずんどが含たれおいたす。 ラむブラリAPIは非垞に理解しやすいものであり、䜎レベルAPIがある堎合、それらは通垞、最も䞀般的なナヌスケヌスのために高レベル関数にラップされおいたす。







その結果、GoフレヌムワヌクなしでWebバック゚ンドを䜜成するこずは倧したこずではありたせんでした。







静的に型付けされた蚀語でJSONを操䜜するのがどれほど簡単か、私はjson.Unmarshal



たした。構造䜓に盎接json.Unmarshal



を呌び出すだけで、リフレクションを䜿甚するず、正しいフィヌルドに自動的に入力されたす。 jsonファむルからサヌバヌ構成をロヌドするのは非垞に簡単でした。







 err = json.Unmarshal(data, &config) if err != nil { log.Fatalf("error parsing config JSON: %v", err) }
      
      





ちなみに、 err= Nilに぀いお-䞀郚の人々が思い぀くほどひどいものではありたせんでした私の1900行のコヌドで玄70回チェックが行われたす。 たた、「本圓に信頌性が高く、すべおの゚ラヌを正しく凊理したす」ずいう非垞に良い感じを䞎えたす。







ちなみに、各リク゚ストハンドラヌは独自のゎルヌチンで機胜するため、垞に機胜するはずのデヌタベヌスコヌルのようなもののためにpanic()



コヌルも䜿甚したした。 そしお、最䞊䜍レベルで、私はrecover()



これらのパニックをキャッチし、それらを正しく蚘録し、スタックトレヌスをメヌルに送信するコヌドを远加したした。







PythonずFlaskの埌、私の手は、Not FoundたたはBad Request応答にパニックの特別な意味を䜿甚するのが非垞にかゆくなりたしたが、これらの衝動を抑え、より慣甚的なGoのパス正しい戻り倀に進むこずにしたした。







たた、すべおの単䞀の同期APIに加えお、バックグラりンドゎルヌチンで実行するための玠晎らしいgo



キヌワヌドも気に入っおいたす。 これは、Python / C/ JavaScript非同期APIずは察照的です。これは、各I / O関数の新しいAPIに぀ながり、APIの衚面を2倍にしたす。







time.Parse()



の圢匏は、「基準日」ずいう考え方には少し颚倉わりですが、実際には、埌でコヌドに戻ったずきに非垞に読みやすくなっおいたすこの「もう䞀床、bはどういう意味ですか」







context



ラむブラリを入力するには少し時間がかかりたしたが、リク゚スト凊理に関䞎するすべおの人にさたざたな远加情報ナヌザヌセッションデヌタなどを送信するのに圹立぀こずが刀明したした。







よくある癖



Goは間違いなくPythonよりも少ないしかし、やはりGoは26歳ではないが、それでもいく぀かある。 移怍䞭に気づいたこずがいく぀かありたす。







関数たたは匏の結果のアドレスを取埗するこずはできたせん 。 倉数のアドレス、たたは特殊なケヌスずしお&Point{2, 3}



ような構造リテラルを&time.Now()



できたすが、 &time.Now()



実行するこずはできたせん。 これは䞀時的な倉数を䜜成するため、少し面倒です。







 now := time.Now() thing.TimePtr = &now
      
      





Goコンパむラは私のためにそれを簡単に䜜成し、 thing.TimePtr = &time.Now()



曞かせおくれるようにthing.TimePtr = &time.Now()



たす。







HTTPハンドラヌは、応答を返す代わりにhttp.ResponseWriterを受け入れたす。 http.ResponseWriter API は基本的なケヌスでは少し奇劙であり、 Header().Set



、 WriteHeader



およびWrite



呌び出される正しい順序を芚えおおく必芁がありたす。 ハンドラヌが単玔に応答付きのオブゞェクトを返した方が簡単です。







たた、ハンドラヌを呌び出した埌ロギングなどにHTTP応答コヌドを取埗するのが少し面倒になりたす。 応答コヌドを保存する停のResponseWriterを䜿甚する必芁がありたす。







この蚭蚈にはおそらく十分な理由がありたした効率互換性が、すぐにはわかりたせん。 ハンドラヌがオブゞェクトを返すラッパヌを簡単に䜜成できたしたが、そうしないこずにしたした。







html / templateパッケヌゞは私にはかなり良いように芋えたしたが、「関連するテンプレヌト」が䜕であり、䜕のためにあるのかを理解するのに少し時間がかかりたした。 特にテンプレヌトの継承に぀いおは、もう少し䟋を参考にしおください。 テンプレヌト゚ンゞンが十分に拡匵されおいるこずが気に入っおいたすたずえば、独自の関数を簡単に远加できたす。







テンプレヌトのダりンロヌドは少し奇劙なので、 html/template



をレンダリングパッケヌゞにラップし、ディレクトリ党䜓ずベヌステンプレヌトを䞀床にダりンロヌドしたした。







テンプレヌトの構文も倧䞈倫ですが、匏の構文は少し奇劙です。 Go自䜓に䌌た構文を䜿甚する方が良いず思いたす。 実際、次回は本質的にGo構文を䜿甚し、匏の別の構文を匷制的に孊習させないため、 ã‚šã‚Žã‚„quicktemplateのようなものを䜿甚する可胜性が高くなりたす。







デヌタベヌス/ SQLパッケヌゞが軜すぎたす。 私はORMの最倧のファンではありたせんが、 database/sql



がリフレクションを䜿甚しお、 encoding/json



䌌た構造フィヌルドにデヌタを入力できるず䟿利です。 Scan()



よくScan()



たす。完党に䜎レベルです。 ただし、たさにそれを行うように芋えるsqlxパッケヌゞがありたす。







テストは単玔すぎたす Go go test



ファンであり、Go党䜓のテストは単玔ですが、暙準セットに少なくずもAssertEqual



スタむルの関数があればいいずAssertEqual



たす。 最埌に、 AssertMatches



関数ずAssertMatches



関数を䜜成したした。 繰り返しになりたすが、それを行うサヌドパヌティのパッケヌゞが存圚するようです stretchr / testify 。







flag



パッケヌゞは掟手です。
どうやら、デザむンは Googleのコマンドラむンフラグのパッケヌゞに基づいおいた-



ですが、 -



圢匏は、 -s



および--long



を䜿甚したGNU圢匏がほが暙準であるため、䟝然ずしお奇劙に芋えたす。 繰り返しになりたすが、このパッケヌゞには、コヌドを倉曎する必芁のないドロップむン眮換を含む倚数の眮換がありたす。







ビルトむンURLルヌタヌ ServeMux は 、固定プレフィックスのみをチェックできるため単玔すぎたすが、 regexp



ベヌスのルヌタヌの䜜成は簡単な䜜業でした数十行のコヌド。







パむニストからの䞻匵



Pythonの埌、コヌドは実際にはいく぀かの堎所でもう少し冗長に芋えたす。 既に曞いたように、ほずんどのコヌドは文字列をPythonの文字列に倉換したすが、それは非垞に自然なこずです。







しかし、リストや蟞曞を含めるのを本圓に忘れおいたした。 これを有効にできたら玠晎らしいず思いたす。







 gifts := []map[string]interface{}{} for _, g := range registryGifts { gifts = append(gifts, g.Map()) }
      
      





これに







 gifts := [g.Map() for _, g := range registryGifts]
      
      





実際、そのような堎所は私が予想したよりもはるかに少なかった。







同様に、 sort.Interfaceは 冗長すぎたす。 sort.Sliceを远加するのが正しいステップでした。 しかし、スラむスむンデックスにたったく觊れるこずなく、Pythonのキヌで䞊べ替えるのがどれほど簡単かは今でも気に入っおいたす。 たずえば、文字列のリストを倧文字ず小文字を区別せずに゜ヌトするには、Pythonでは次のようになりたす。







 strs.sort(key=str.lower)
      
      





およびGoの堎合







 sort.Slice(strs, func(i, j int) bool { return strings.ToLower(strs[i]) < strings.ToLower(strs[j]) })
      
      





そしお、原則ずしお、私が欠けおいたのはそれだけでした。 私は䟋倖を芋逃すず思っおいたしたが、それは違いたす。 そしお、䞀般的な信念に反しお、ゞェネリック医薬品の欠劂は私をい぀も䞀床だけ悩たせたした。







なぜ行くの



近い将来、Pythonの䜿甚をやめる぀もりはありたせん。 スクリプト、小さなプロゞェクト、Webバック゚ンドに匕き続き䜿甚したす。 しかし、Goを真剣に芋お、より倚くのプロゞェクト静的型付けがリファクタリングをはるかに容易にしたすず、蚀語パフォヌマンスが重芁なナヌティリティたたはシステム正盎に蚀うず、Cで曞かれたPythonのすべおの䜎レベルラむブラリではそうではありたせん重芁。







芁玄するず、私がGoを奜きになった理由のいく぀かず、あなたがそれを奜きになる理由









それでは、Goで䜕か曞いおみおくださいGoで曞きたす 。








All Articles