- 投稿日:2020-11-17T23:02:48+09:00
ngrok使ってみた
概要
新卒,3回目の投稿です。
ngrokを使う機会があり、非常に便利だと感じたので使い方を書いていきます。
ngrokはlocalhostで立ち上げたサーバに外部からアクセスできるようにするツールです。
今回はGoをでlocalhostを立ち上げ、私物のスマホからlocalhostにアクセスしてみました。ngrokをインストールする
公式サイトよりユーザー登録をしたら、zipファイルをダウンロードし、解凍します。
ユーザー登録にはgithubまたはgoogleアカウントを使うことができます。
解凍が済んだら、ngrok.exe
を起動し、コマンドを打ち込みます。ngrok http 8080下記のような表示が見えれば完了です。
localhost:8080に対し、https://66336728576f.ngrok.io
で外部環境からアクセスすることができます。ngrok by @inconshreveable (Ctrl+C to quit) Session Status online Version 2.3.35 Account アカウント名 (Plan: Free) Region United States (us) Web Interface http://127.0.0.1:4040 Forwarding http://66336728576f.ngrok.io -> http://localhost:8080 Forwarding https://66336728576f.ngrok.io -> http://localhost:8080 Connections ttl opn rt1 rt5 p50 p90 0 0 0.00 0.00 0.00 0.00実際に外部からアクセスしてみる
package main import ( "fmt" "net/http" ) func main() { http.HandleFunc("/", handler) http.ListenAndServe(":8080", nil) } func handler(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "ngrok超便利") }私物のスマホより、
https://66336728576f.ngrok.io
にアクセスすると、ngrok超便利
が表示されました!
ngrok便利ですね。
- 投稿日:2020-11-17T21:56:47+09:00
goでサーバーを立ちあげたらvscodeがフリーズ&PCが落ちる
起こった事
go run ***.go
でサーバーを立ちあげた後にエディターでgoファイルを編集するとgocode-gomod
がかなりCPUを食いエディターがフリーズ&PCが落ちてしてしまう、、、、環境
- go 1.15.3
- docker
- vscode
解決策
設定ファイル(
setting.json
)を開き以下のコードを追記すると治るそうsetting.json{ "go.useLanguageServer": true, }原因
https://github.com/golang/vscode-go/issues/249#issuecomment-647902730
go
の候補表示してくれるgocode-gomod
は現在メモリをかなり食ってしまうようになっているらしくメンテナンス中かつ開発がストップしているそう、、
なので、変わりにgopls
を使ってくれとの事でした!これでgoでの開発に集中できる^^
- 投稿日:2020-11-17T21:56:47+09:00
goでサーバーを立ちあげたらVSCodeがフリーズ&PCが落ちる
起こった事
go run ***.go
でサーバーを立ちあげた後にエディターでgoファイルを編集するとgocode-gomod
がかなりCPUを食いエディターがフリーズ&PCが落ちてしまう、、、、環境
- go 1.15.3
- docker
- VSCode
解決策??
設定ファイル(
setting.json
)を開き以下のコードを追記すると治るそうsetting.json{ "go.useLanguageServer": true, }と言ってもまだ動きモッサリしていてマシになった程度でした、、、
原因
https://github.com/golang/vscode-go/issues/249#issuecomment-647902730
go
の候補表示してくれるgocode-gomod
は現在メモリをかなり食ってしまうようになっているらしくメンテナンス中かつ開発がストップしているそう、、
なので、変わりにgopls
を使ってくれとの事でした!
これでgoでの開発に集中できる^^
- 投稿日:2020-11-17T18:10:07+09:00
golangでnet.Connをcontextでcancel可能にする方法
こんな感じでSetDeadlineを使うと良い。
なお、デッドラインの設定時間により、ctx.Done()
への応答時間が伸びたりするが、
Readへの応答時間要求に比べてctx.Done()への応答時間要求はかなりゆるい場合が多いと思うので問題になることは少ないはず。err := conn.SetReadDeadline(time.Now().Add(time.Millisecond * 500)) if err != nil { eturn err } continue_read := true for continue_read { size, err := conn.Read(buf) switch { case err == nil: continue_read = false case errors.Is(err, io.EOF): continue_read = false case os.IsTimeout(err): conn.SetReadDeadline(time.Now().Add(time.Millisecond * 500)) default: return err } select { case <-ctx.Done(): return ctx.Err() default: } }
- 投稿日:2020-11-17T18:10:07+09:00
golangでnet.ConnのRead・Writeをcontextでcancel可能にする方法
モチベーション
ソケットから読もうとするとブロックする可能性があるので、
それをcontextでキャンセルできる様にしたい。可能なら以下の様なコードが書きたいが、それはできないので、代替案をメモ
select { case size, err := conn.Read(): // 略 case <-ctx.Done(): //略 }解決策
こんな感じでSetDeadlineを使うと良い。
なお、デッドラインの設定時間により、ctx.Done()
への応答時間が伸びたりするが、
Readへの応答時間要求に比べてctx.Done()への応答時間要求はかなりゆるい場合が多いと思うので問題になることは少ないはず。continue_read := true for continue_read { err := conn.SetReadDeadline(time.Now().Add(time.Millisecond * 500)) if err != nil { return err } size, err := conn.Read(buf) switch { case err == nil || os.IsTimeout(err): continue_read = true // Do nothing case errors.Is(err, io.EOF): continue_read = false default: return err } // bufの操作等を書く select { case <-ctx.Done(): return ctx.Err() default: } }うーん、一応問題は解決したものの、あんまり綺麗じゃないので何とかしたい。
- 投稿日:2020-11-17T15:05:10+09:00
Golang: 指定した間隔でぴったり毎時00分とか30分に処理を実行したい件。
tickerが思い通りにならない!
常駐して定期的に実行したいjobがあるが、普通のtickerだと起動してからtickerを作った時からintervalカウントが始まるので、時刻ぴったりに設定できない。しかも、tickerはちゃんと12:00:00になるようにintervalを設定しても、たまに11:59:59.999に動くことがあって、処理したデータがダブって登録されてしまうことがあった。
tickerの使い方と、時刻制御の練習もかねて書いてみた。
https://github.com/xbridges/Scheduler
単純に、指定した時間間隔でぴったりにtickerが呼び出してくれる。ex) sch, next := NewScheduler(3600, 0)
こんな感じで開始すると、毎時00分に一度tickerが発火します。
さらに、オフセット値を使うと、インターバルは毎時(3600)だけど、offset: 600とか設定すると、毎時05分にtickerが発火します。偉そうに書いてるけど、やったことはただのラップ(笑
tickerを単純にラップしているだけなんですが、キモは、
is := time.Duration(interval) * time.Second // <-- 指定時刻をDurationに n := time.Now() // <--現在の時刻 ns := n.Round(is) // <--現在時刻を、インターバル時刻でマスクして丸めるような感じ。こーすると、たまにtickerが早めに動いて、59分59秒.9999になっちゃっても四捨五入してくれるので、時間がちゃんと次の00分に発火するようになります。
定期実行していてちょっとはまったのでメモしました。
参考
- 投稿日:2020-11-17T06:50:13+09:00
Golang mysql CREATE DATABASE IF NOT EXISTS
こんにちは!Golang 勉強中です!
今回はDB作成〜接続を行なっていきます!
DB接続の方法はすぐに見つかりましたが、DB作成は手動でしている記事が多い印象でした。
直接Mysqlから実行ではなく、GOからオートマチックに操作したかったので方法を調べてみました。package main import ( "database/sql" _ "github.com/go-sql-driver/mysql" ) // DbConnection grobal var DbConnection *sql.DB func checkDb() { DbConnection, err := sql.Open("mysql", "root:@tcp(127.0.0.1:3306)/") if err != nil { panic(err) } defer DbConnection.Close() _, err = DbConnection.Exec("CREATE DATABASE IF NOT EXISTS db_name") if err != nil { panic(err) } DbConnection.Close() return } func main() { checkDb() }packageがimportできてないとエラーが出るので適宜importしてください!
以下解説
こちらのコードでmysqlと接続しています
"root:@tcp(127.0.0.1:3306)/"
root ユーザー名
:以降 パスワード (今回はルートユーザーなのでパスワードなしです)
@以降 プロトコルmysqlと接続後にこちらのコードでDBの存在チェックと作成を行います。
DbConnection.Exec("CREATE DATABASE IF NOT EXISTS db_name")
関数を呼び出して実行します。
func main() { checkDb() }以上です!
DBの操作はORMを使っていきたいと思ってます!
ご指摘あればぜひお願いします!
- 投稿日:2020-11-17T00:22:02+09:00
Goクイズ Advent Calendar Day4
はじめに
本記事は, Goクイズ Advent Calender 2020の12/4用に書かれました。昨日は, @syumai さんのGo Quiz Advent Calendar【3日目】でした。
これから紹介するクイズは, 実際にアルバイト先で見つけたコードを一部改変したものです。
問題
以下のコードを実行すると, どのように出力されるでしょうか?
Playgroundpackage main import ( "fmt" ) type character string func sayHello(name *character) { fmt.Printf("Hello, %s! ", *name) } func main() { names := []character{"Alice", "Bob", "Carol", "Dave", "Eve"} for _, name := range names { go sayHello(&name) } }※2020/12/04追記:
こちらのクイズには不備があり, 全ての選択肢が発生する可能性があります。作問ミスです。申し訳ありませんでした......
@kyoh86 さん, ご指摘ありがとうございます。
- (何も出力されない)
- Hello, Alice! Hello, Bob! Hello, Carol! Hello, Dave! Hello, Eve!
- Hello, Alice! Hello, Bob! Hello, Carol! Hello, Dave! Hello, Eve!(順不同)
- Hello, Eve! Hello, Eve! Hello, Eve! Hello, Eve! Hello, Eve!
解答と解説
解答と解説
1. (何も出力されない)
TL;DR
- 呼び出し元の関数が終了すると, 呼び出し元によって生成されたgoroutineは終了する
- 対処法は, time.Sleep等で多少待ってあげること
解説
go sayHello(name)
とあるので, goroutineに関する並行処理が絡んでいると気づいた方が多かったのではないのでしょうか?解答でも書いた通り, 答えは1です。
なぜなら, main関数の終了によって, 新しく生成されたgoroutineが終了するためです。Specificationには下記のように書かれています。
The function value and parameters are evaluated as usual in the calling goroutine, but unlike with a regular call, program execution does not wait for the invoked function to complete. Instead, the function begins executing independently in a new goroutine. When the function terminates, its goroutine also terminates. If the function has any return values, they are discarded when the function completes.
関数の値とパラメータは通常どおり呼び出し元のgoroutineで評価されますが, 通常の呼び出しとは異なり, プログラムの実行は呼び出された関数が完了することを待ちません。その代わり呼び出された関数は新しいgoroutineで独立して実行を始めます。もし, 呼び出し元の関数が終了すると呼び出されたgoroutineも終了します。その関数がいかなる返り値を持とうと関数の終了時に破棄されます。
これを起こさないためには, 下記の通り
time.Sleep()
などで非同期処理が終わるまである程度待つなどの対策が出来ます。package main import ( "fmt" "time" ) type character string func sayHello(name *character) { fmt.Printf("Hello, %s! ", *name) } func main() { names := []character{"Alice", "Bob", "Carol", "Dave", "Eve"} for _, name := range names { go sayHello(&name) } // 1秒間待ってあげる time.Sleep(time.Second) }この場合の出力は, 4の
Hello, Eve! Hello, Eve! Hello, Eve! Hello, Eve! Hello, Eve!
になります。この場合, character型のオブジェクトのアドレスを渡しているため, 実行時には最後のEve
が全て対象となり, 全てEve
が出力されます。余談
仮に全ての名前を表示したい場合は, 下記の通りアドレスではなく値を引数として渡す必要があります。ここの記法は本質とズレているので, あまり良くなかったかもしれませんね。申し訳ないです。
package main import ( "fmt" "time" ) type character string func sayHello(name character) { fmt.Printf("Hello, %s! ", name) } func main() { names := []character{"Alice", "Bob", "Carol", "Dave", "Eve"} for _, name := range names { // 値を渡してあげる go sayHello(name) } // 1秒間待ってあげる time.Sleep(time.Second) }
おわりに
こちらのコードはスクレイピング用のコードに潜んでいたバグでした。何度実行しても想定通りに動かないので, 何度か実行していた時に問題に気づけました。
以上で, Goクイズは終わりです。
ここまでお付き合いいただきありがとうございました!明日は, @hajimehoshi さんのGo Quizです。
ありがとうございました!