- 投稿日:2019-03-07T23:35:44+09:00
Go mapの中身が空かどうかを判定する
mapの中身が空かどうかを判定する方法。つまり
map[]この状態であることを判定したかった。mapの長さ(length)がゼロであることで判定できる。
package main import "fmt" func main() { m := make(map[string]Vertex) fmt.Println(m) //map[] if len(m) == 0 { fmt.Println("nil!") //空だった場合の処理 } }
- 投稿日:2019-03-07T22:54:27+09:00
【Go】初めてのFizzBuzz
初めてのFizzBuzz
Goを今日から始める。
まずは簡単なFizzBuzzから。フォーマットギュっとなってる。
FizzBuzzimport "fmt" func main() { fizz := 3 buzz := 5 fizzbuzz := 15 for i := 1; i <= 100; i++ { if i%fizzbuzz == 0 { fmt.Println("FizzBuzz") } else if i%buzz == 0 { fmt.Println("Buzz") } else if i%fizz == 0 { fmt.Println("Fizz") } else { fmt.Println(i) } } }
- 投稿日:2019-03-07T17:47:59+09:00
今さら聞けないPlaceholder
問題
golangの標準パッケージ "database/sql"を使って、以下のようにPlaceholder付のクエリ書いたら、Syntax errorが発生してしまう、なぜか。
参考
- https://golang.org/pkg/database/sql/#example_DB_Query_multipleResultSets
- https://play.golang.org/p/D-5ZVSzhcXxfunc main() { age := 27 q := ` create temp table uid (id bigint); -- Create temp table for queries. insert into uid select id from users where age < ?; -- Populate temp table. -- First result set. select users.id, name from users join uid on users.id = uid.id ; -- Second result set. select ur.user, ur.role from user_roles as ur join uid on uid.id = ur.user ; ` rows, err := db.Query(q, age) if err != nil { log.Fatal(err) // Syntax error }原因
PostgresDB使ってたため、?を$1に修正すればエラーは解消できる。
プレースホルダ付きのSQLをDBにあらかじめ送信しているため、DBごとに記法が異なる。
- 投稿日:2019-03-07T14:20:10+09:00
Go言語のMap
Go言語の中でMAPもよく使います。
ただし性能的にはArray、Sliceより遅いので、Array、Sliceを利用できる場合は、Mapを利用しないほうが良いと思います。mapの定義
初期化なしの場合
var 変数名 map[キーの型]値の型
例:var users map[string]string fmt.Println(users)出力:
map[]
初期化ありの場合
var 変数名 map[キーの型]値の型 = map[キーの型]値の型 {key1: value1, key2:value2}
例:var users map[string]string = map[string]string{"takana": "atakana amika", "join": "join simith"} fmt.Println(users)出力:
map[takana:atakana amika join:join simith]
makeで生成
例:var users map[string]string = make(map[string]string)
値取得
変数名[キー]
例:var users map[string]string = map[string]string{"takana": "atakana amika", "join": "join simith"} fmt.Println(users["takana"])出力:
atakana amika
値変更
変数名[キー] = 新しい値
例:var users map[string]string = map[string]string{"takana": "atakana amika", "join": "join simith"} users["takana"] = "tanaka amiko" fmt.Println(users["takana"])出力:
tanaka amiko
LOOPする
rangeを利用してLOOPできますが、順番は不定です。
例:var users map[string]string = map[string]string{"takana": "atakana amika", "join": "join simith"} for key, value := range users { fmt.Println(key, ":", value) }出力:
takana : atakana amika
join : join simithkeyをsliceに保存して、sliceをソートした状態でLOOPすると、順番になります。
存在チェック
var users map[string]map[string]string = make(map[string]map[string]string) users["tanaka"] = make(map[string]string) _, exist := users["tanaka"] fmt.Println(exist) _, exist2 := users["yamaha"] fmt.Println(exist2)出力:
true
falseexistがfalseの場合は初期化すれば、値の付与はできます。
以上
- 投稿日:2019-03-07T13:55:32+09:00
Goで,レイヤードアーキテクチャ
レイヤードアーキテクチャとは
APIサーバを実装するときはだいたい以下の流れだと思います.
データを受け取って -> 処理して -> データベースに保存
レイヤードアーキテクチャは簡単に言うと,この流れを責務に乗っ取り分割することで,依存関係を少なくし,メンテナンスのしやすい構成にしようと言う考え方になります.
構成
- UI(Presentation)層: クライアントのことを指します. APIサーバでは考えません.
- Application層: Domain層を用いて,クライアントが欲しいデータを返すのが責務です.
- Domain層: クライアントからのデータを処理するのが責務です.
- Infrastructure層: DBとの通信が責務です.
題材
https://github.com/tozastation/gRPC-Training-Golang
を使って説明していきます構成
├── domain: ドメイン層 │ ├── repository: 依存性逆転の原則 │ └── service: ロジック ├── idl: Protocol Bufferの定義ファイル ├── implements: アプリケーション層 ├── infrastructure: インフラ層 │ └── persistence │ ├── model: DBモデル │ └── mssql: mssqlサーバ用Repository ├── interfaces: その他 │ ├── auth: 認証 │ ├── di: 依存性の注入 │ ├── handler: ハンドラー │ └── rpc: gRPC生成ファイル ├── main.go ├── protoc.sh └── vendor: Goのパッケージドメイン層
repository
repositoryでは,インフラ層におけるrepositoryのインターフェースを作成します.
依存性逆転の原則(抽象に依存せよ)
先ほど説明したレイヤードアーキテクチャだけでは困ることがあります.それは,上位レイヤが下位レイヤの実体を持っている必要があることです.
下位レイヤの実装が変更されると,上位レイヤは影響を受けることや,上位レイヤは下位レイヤの実装後でないと参照できないなどの不便さが残ります.
そのため,interfaceを用いて,抽象に依存します.これにより,上位レイヤは下位レイヤの詳細は知らずとも良くなります.実装
package repository // IUserRepository is ... type IUserRepository interface { FindUserByUserToken(ctx context.Context, token string) (*db.User, error) CreateUser(user *db.User) (string, error) Login(uID string, password []byte) (string, error) }service
実装
- アプリケーション層がサービス層を参照するためにインターフェースを定義しています.
- インフラ層のインターフェースに依存した構造体を定義しています.
- 構造体を生成するメソッドを定義しています(のちに解説しますがこれがDIと呼ばれるものです.)
- 定義したインターフェースに合わせてメソッドを実装します.
package service // IUserService ... type IUserService interface { GetMe(ctx context.Context, token string) (*rpc_user.GetUser, error) SignIn(ctx context.Context, uID, password string) (string, error) SignUp(ctx context.Context, user *rpc_user.PostUser) (string, error) } type userService struct { irepo.IUserRepository } // NewUserService is ... func NewUserService(repo irepo.IUserRepository) IUserService { return &userService{repo} } func (srv *userService) GetMe(ctx context.Context, token string) (*rpc_user.GetUser, error) { user, err := srv.IUserRepository.FindUserByUserToken(ctx, token) if err != nil { return nil, err } return dbToPostUser(user), nil } func dbToPostUser(user *db.User) *rpc_user.GetUser { return &rpc_user.GetUser{ Name: user.Name, CityName: user.CityName, } }アプリケーション層
implement
実装
Application層が依存するもの無くない?と思われると思います.
これはGoの特徴なのですが,DIを行うメソッドを定義した際に,インターフェースを返り値として実装しています.
このようにすることで,定義したインターフェースの内容を満たさないと関数が実装されたとコンパイラが認めないため,この構造体は定義したメソッドを全て含んでいるということを担保することができます.package implements // IUserImplement is ... type IUserImplement interface { Get(ctx context.Context, p *rpc_user.GetRequest) (*rpc_user.GetResponse, error) Login(ctx context.Context, p *rpc_user.LoginRequest) (*rpc_user.LoginResponse, error) Post(ctx context.Context, p *rpc_user.PostRequest) (*rpc_user.PostResponse, error) } type userImplement struct { isrv.IUserService *logrus.Logger } // NewUserImplement is ... func NewUserImplement(s isrv.IUserService, l *logrus.Logger) IUserImplement { return &userImplement{s, l} } func (imp *userImplement) Get(ctx context.Context, p *rpc_user.GetRequest) (*rpc_user.GetResponse, error) { imp.Logger.Infoln("[START] GetMyBoughtVegetablesRPC is Called from Client") token := p.GetToken() user, err := imp.IUserService.GetMe(ctx, token) if err != nil { return nil, err } res := rpc_user.GetResponse{ User: user, } imp.Logger.Infoln("[END] GetMyBoughtVegetablesRPC is Called from Client") return &res, nil }インフラ層
persistence
責務はデータ永続化です
model
実装
DBに受け渡すデータを構造体として定義します
package db // User is ... type User struct { BaseModel Name string CityName string Password []byte AccessToken string }mssql
実装
特徴としては,database/sqlを用いて,SQL文を直書きしています.
理由としては,ormは独自の構文を使用するため他ライブラリの以降にコストがかかりますが,SQL文だと汎用性があるためです.package mssql // UserRepository is type UserRepository struct { *sql.DB } // NewUserRepository is ... func NewUserRepository(Conn *sql.DB) irepo.IUserRepository { return &UserRepository{Conn} } // FindUserByUserToken is ... func (repo *UserRepository) FindUserByUserToken(ctx context.Context, token string) (*db.User, error) { dbUser := db.User{} if err := repo.DB.QueryRow("SELECT CityName FROM [Weather].[dbo].[Users] WHERE AccessToken = " + token).Scan(&dbUser.CityName); err != nil { return nil, err } return &dbUser, nil }その他
auth
JWT(Json Web Token)という技術を用いています.興味ある方はこちらを参照
di
依存性注入(Dependency Injection)
先ほどからDIDIと言ってきましたが,DIとは外部からクラスを射し込むことを言います.
実装
テストを書くときを考えてみてください.
サービス層のテストをするとなると,DBの情報が欲しいとなります.
となるとリポジトリが必要そうです.しかしこれでは,DBに依存してしまい切り分けた意味がなくなってしまいます.
DIをすることにより,決まった値を返すモックのリポジトリを注入することができ,晴れて依存性を解消できるという訳です.package di var logger = logrus.New() // InitializeUser is ... func InitializeUser() implements.IUserImplement { repo := mssql.NewUserRepository(handler.OpenDBConnection()) srv := service.NewUserService(repo) imp := implements.NewUserImplement(srv, logger) return imp }handler
DBなどのコネクションを生成するメソッドを集めています.
rpc
gRPCの説明は後に回します
- 投稿日:2019-03-07T12:05:34+09:00
[入門] GoでAPIを書いて,k8sにデプロイするまで [随時更新]
概要
新しい技術を使ってみたいけど, 何がいいの?どう使うの?時間ないよ と躊躇してしまうこともあると思います.
本記事では,自分が躓いたポイントも踏まえつつ題材を元にガシガシ書いていこうかなと思います.
自分自身も最近学び始めた技術ですので,入門として書かせていただきます!構成
Go編
- Goとは
- Goで,レイヤードアーキテクチャを書いてみる
- GoでgRPCを使ってみる(お待ち下さい?)
- Goのテストを書こう(お待ち下さい?)
Docker編
- Dockerとは(お待ち下さい?)
- Dockerfileの書き方
- Dockerの使い方(お待ち下さい?)
Kubernetes(k8s)編
- Kubernetes(k8s)とは (お待ち下さい?)
- ログの収集 (お待ち下さい?)
- kubectlコマンド
- 投稿日:2019-03-07T12:02:58+09:00
[入門] Goとは [随時更新予定]
Goについて
プログラミング言語Goの情報サイトであるgolang.jpさんに書いています.Googleさんが発表したオープンソースのプログラミング言語になります.
自分が特に抜粋するならば,以下の二点になります.シンプルさ
大規模なシステムにおいて,属人性(個の技術スキル)による複雑な実装は,運用やメンテナンスを難しくさせます.
Goには,継承・Generics・例外が実装されていません(参考記事).また,ループ処理を例にあげると,他の言語だとfor・ while・do/whileなど選択肢がありますが,Goでは,forしか実装されていません.このように,プログラマーによる表現の違いを抑制することができます.
このことから,初学者でも取っつきやすいと思っています.自分を例に出すと,APIサーバを作ろうとなった時に,Java, Scala->Pythonなどを学んできましたが, Goの学習コストの方が低いですし,モチベーションが上がりました.システムを開発している方達でも,多人数で精度やばらつきを維持するプログラムを生産していくのは難しいと思います(ペアプロやモブプロもありますが).高速さ
開発において,速度は重要です.
ここでいう開発速度は,プログラミング言語のパフォーマンスではなく,チームとしての開発速度になります.
特にプログラムのビルド時間は特に重要であると考えています.実装したシステムを開発環境やステージング環境で動かしたいことを考えてみて下さい.経験した方もいると思いますが,規模の大きなシステムになる程ビルド時間はものすごい量になります.
下記は,C#とGoで同様の処理のプログラムを書き,ビルドを行なった結果です.C#ももちろん好きです
結果
C#の2分の1ほどの速度で実現できています.
- C#: 約447秒
- Go: 約200秒まとめ
プログラミングを初めて3年ほど経ち,チーム開発を何度か経験してみてGoはいいなと思ったので本記事を書かせていただきました.興味のある方はぜひ試してみて下さい!
- 投稿日:2019-03-07T00:27:54+09:00
競プロのテストを「go test」でやりたかったので作った
競プロのテストを
go testでやりたかったので作った。競技プログラミング(以下競プロ)でよく使われる、 標準入力からデータを取得して標準出力へ結果を吐き出すプログラムを簡単にテストしたかった。というお話。
いきさつ
競プロは、いくつかテストケースとその回答が与えられており事前にテストできるようになってます。
しかし動作確認のたびに「テストケースをコピーして…」とかやってたので非常に効率が悪い……
時間との戦いでもあるし、こんな事で時間使いたくねぇな…
「これ自動化できないかな……テストなんだしgo testとかでテストできるならむっちゃ楽。」リポジトリ / 実行イメージ
という事で自動化してしまったのがこちらになります。
github.com/0Delta/GolangSkeletonForSpeedrun
使い方はREADME.mdか本体コードをどうぞ。
実装
標準出力の取得
テストなので、結果を変数に格納して期待値と同じか評価する必要があります。
…が、結果は標準出力に吐き出してしまいます。これは、テストコードに定義してある
captureStdoutを噛ませ、一時的に標準出力を表すos.Stdoutを自作パイプに置き換えることで実現させてます。
こちらは先人の知恵を丸パク借りていますので、詳細はこちら -> Goで標準出力をテストする方法標準入力の代替
テストなので、標準入力へのデータ入力は自動化したいです。可能なら通常のテストと同じように、配列とかに格納して
forで一気に行きたい。ここはメイン処理を少し変更することで対応しました。
getLineで、プログラムの引数が存在する場合はその最後、ない場合は標準入力を取得するようにしています。
何故最後なのかというと、go testの後に続くフラグの数がわからないから。入力が一行だけなのであれば、コレで十分です。(つまり複数行には非対応)
後は、テストコード側でプログラムの引数を表すos.Argsに入力されるべき文字列を追加してあげればテスト時は変数を、通常実行時は標準入力を見るようになります。で、どうなった?
めっちゃ良い
思いっきり自画自賛だが気にしない。
結果がわかりやすいし自分でテストケース増やすことも簡単、go testはGoの標準機能なのでほぼ全ての開発環境で使えて親和性もバッチリです。これでスケルトンを作ってしまったので、競プロが開催されたときに「標準入力からの取得どうすんだっけ…」とか「動作確認の度にテストケースをコピーして目で確認して…」とかいう悩みから完全開放され、問題を解く為に全時間を捧げることができるのが一番良いですね。
以上!
皆さんもテストを自動化して、良き競プロライフを。



