20201117のGoに関する記事は8件です。

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便利ですね。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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での開発に集中できる^^

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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での開発に集中できる^^

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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:
    }
}

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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:
    }
}

うーん、一応問題は解決したものの、あんまり綺麗じゃないので何とかしたい。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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分に発火するようになります。

定期実行していてちょっとはまったのでメモしました。

参考

http://okzk.hatenablog.com/entry/2015/12/01/001924

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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を使っていきたいと思ってます!
ご指摘あればぜひお願いします!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Goクイズ Advent Calendar Day4

はじめに

本記事は, Goクイズ Advent Calender 2020の12/4用に書かれました。昨日は, @syumai さんのGo Quiz Advent Calendar【3日目】でした。

これから紹介するクイズは, 実際にアルバイト先で見つけたコードを一部改変したものです。

問題

以下のコードを実行すると, どのように出力されるでしょうか?
Playground

package 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 さん, ご指摘ありがとうございます。

  1. (何も出力されない)
  2. Hello, Alice! Hello, Bob! Hello, Carol! Hello, Dave! Hello, Eve!
  3. Hello, Alice! Hello, Bob! Hello, Carol! Hello, Dave! Hello, Eve!(順不同)
  4. 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も終了します。その関数がいかなる返り値を持とうと関数の終了時に破棄されます。

図で書くと, 以下の通りです。
flow.png

これを起こさないためには, 下記の通りtime.Sleep()などで非同期処理が終わるまである程度待つなどの対策が出来ます。

playground

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が出力されます。

余談

仮に全ての名前を表示したい場合は, 下記の通りアドレスではなく値を引数として渡す必要があります。ここの記法は本質とズレているので, あまり良くなかったかもしれませんね。申し訳ないです。

playground

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です。

ありがとうございました!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む