- 投稿日:2020-05-25T19:00:22+09:00
Go製のToolをHaskellで実装する
初めに
最近Haskellの学習としてGo製のToolをHaskellで実装するようにしています。
Haskellでなにかしら作りたいと考えていて、そういえばGo製のToolはよく作られているから面白そうなものありそうだなと思ったのがきっかけです。
mockサーバであるhttplabをHaskellで作ることにしました。
まだ100%の実装ができていないのですが、ある程度形ができたので記事を書くことにしました。
ソースコードはこちらにあります。
Haskellでbrickというライブラリを使っていますが、使い方等は以前書いた記事を参考してください
GoもHaskellも至らぬところがあるので間違った箇所はアドバイスや助言をいただけると嬉しいです。
HaskellとGoもさほど違いがない箇所があったので、比較していきます。
引数のパース
Goではpflagを使ってパースしています。
pflag.StringVarP(&args.status, "status", "s", "200", "Specifies the initial response status.")Haskellではoptparse-applivativeを使ってパースしてます。
parseStatus :: Parser Status parseStatus = option (maybeReader $ fmap toEnum . readMaybe) $ long "status" <> short 's' <> value status200 <> help "Specifies the initial response status." <> metavar "StatusCode"こうやってみると使い方はさほど違いがないように感じます。
ただHaskellの方は関数の引数ではなくmonoidを使ってプログラム引数の構成が自由にできることと、
IntやBool, String以外のデータ型に対してもパースできる強みがあります。ただその分記述量が多くなっています。
mock server
main.gosrv := &http.Server{ Addr: fmt.Sprintf(":%d", args.port), Handler: http.Handler(middleware(NewHandler(ui, g))), } go func() { // Make sure gocui has started g.Execute(func(g *gocui.Gui) error { return nil }) if err := srv.ListenAndServe(); err != nil { errCh <- err } else { ui.Info(g, "Listening on :%d", args.port) } }()serverの情報を定義してgo rountineで起動させています。
Haskellはwaiを使って実装しています。
App.hsrace_ (WAI.run (argPort cmd) $ middleware $ respHandler state chan) $ customMain vty buildVty (Just chan) app statemiddlewareはまだちゃんと実装していないので
middleware=id
でやっています。こちらもサーバを非同期で起動しています。
handlerについては
main.go// NewHandler returns a new http.Handler func NewHandler(ui *ui.UI, g *gocui.Gui) http.Handler { fn := func(w http.ResponseWriter, req *http.Request) { if err := ui.AddRequest(g, req); err != nil { ui.Info(g, "%v", err) } resp := ui.Response() time.Sleep(resp.Delay) resp.Write(w) } return http.HandlerFunc(fn) }uiをpointerで渡すことでスレッド間の値を共有しています。
App.hsrespHandler :: TVar ResponseData -> BChan Requested -> Application respHandler tvar bchan req send = do dt <- readTVarIO $ tvar threadDelay $ dt ^. resDelay send $ responseBuilder (dt ^. resStatusCode) (M.foldrWithKey' (\k a -> (toResHeader k a :)) [] $ dt ^. resHeaders) $ B.byteString $ T.encodeUtf8 $ dt ^. resBody <* writeBChan bchan (toRequested req) where toResHeader a b = (CI.mk $ T.encodeUtf8 $ CI.foldedCase a, T.encodeUtf8 b)スレッド間の値を共有する為にTVar を使用しています
大きな違いとして、Requestについては、brickはuiの情報は純粋なデータしか受け付けていない為、アプリケーション定義イベントで渡すようにしていますがgocuiも同じことができると思います。
おまけ:Zipper
httplabはRequest情報を履歴で持つ機能が備わっています。それを実現するために内部データは次のように保持しています。
ui.gotype UI struct { ... reqLock sync.Mutex requests [][]byte currentRequest int }
requests
がrequestのあった履歴、currentRequest
がrequestを表示している場所になっています。
ただこれだと毎回リストを走査しないといけなく効率が悪いです。
やりたいことはrequest履歴の追加、履歴の表示、1つ前後の履歴の移動になります。Haskellでよくある手法として Zipper を使うことにしました。Zipperについてはこちらがよくまとめられています。
今回はこのようにしました。
Types.hsdata RequestedZipper a = RZ { _reqzAbove :: [a] , _reqzBelow :: [a] , _reqzCurrent :: Maybe a , _reqzCurPos :: Int , _reqzLength :: Int } reqzLength :: RequestedZipper a -> Int reqzCurPos :: RequestedZipper a -> Int reqzCurrent :: RequestedZipper a -> Maybe a initReqZipper :: RequestedZipper Requested moveAboveReqz :: RequestedZipper a -> RequestedZipper a moveBelowReqz :: RequestedZipper a -> RequestedZipper a最後に
UIについては割愛します。見比べたところIOに関してはHaskellもGoも対して変わらず、実装しやすかったです。
純粋なところは他の言語とかけ離れてしまいますが、実装できたときは嬉しく見比べたときはHaskellのが分かりやすく感じます。Goは面白いToolがたくさんあって色々参考になりますので
Haskellを学びたいけど題材がないという方はぜひチャレンジしてください。
- 投稿日:2020-05-25T12:16:21+09:00
GoでGoogle Maps APIを使って場所の詳細情報を取得する(例:東京タワー)
はじめに
GoでGoogleMapsAPIを使った記事があまりなかったので書いてみました。
GoogleMapsAPIには様々なAPIがあるのですが、今回はPlaceというAPIを使ってみたいと思います!Placeの詳細情報はこちら。
Overview | Places APIGCPで事前準備
GoogleMapsAPIを使うにはGCPのアカウント作成、プロジェクト作成、APIKEYの取得が必要です。
以下からこれらを作成します。
Google Cloud Platform詳しい手順はこちらが参考になります。
Google Maps Platform APIキーの取得・発行について
⇒API有効化では「Place」のAPIを有効化します。とりあえず直接たたいてみる
Placeの中にはさらにいくつかの機能があるのですが、まずは基本のsearch機能を使ってみたいと思います。
各リクエストパラメータの詳細はこちらが参考になります。
Place Search必須パラメータのみ
こんな感じでたたきます!
試しに東京タワーの情報を取得します。requesthttps://maps.googleapis.com/maps/api/place/findplacefromtext/json?key=(取得したAPIKEY)&input=東京タワー&inputtype=textquery返ってきたjsonがこちら。
必須パラメータだけだと味気ないですね。response{ "candidates" : [ { "place_id" : "ChIJCewJkL2LGGAR3Qmk0vCTGkg" } ], "status" : "OK" }リクエストパラメータに
field
を追加するUse the fields parameter to specify a comma-separated list of place data types to return. For example: fields=address_component,name,geometry. Use a forward slash when specifying compound values. For example: opening_hours/weekday_text.
詳細な情報を得るには
field
パラメータに色々設定するみたいです。
とりあえず色々追加してみます。requesthttps://maps.googleapis.com/maps/api/place/findplacefromtext/json?key=(取得したAPIKEY)&input=東京タワー&inputtype=textquery&fields=plus_code,photos,formatted_address,name,geometry返ってきたjsonがこちら。
それっぽいものが返ってきました!response{ "candidates" : [ { "formatted_address" : "日本、〒105-0011 東京都港区芝公園4丁目2−8", "geometry" : { "location" : { "lat" : 35.6585805, "lng" : 139.7454329 }, "viewport" : { "northeast" : { "lat" : 35.66027947989272, "lng" : 139.7469715298927 }, "southwest" : { "lat" : 35.65757982010728, "lng" : 139.7442718701072 } } }, "name" : "東京タワー", "photos" : [ { "height" : 3024, "html_attributions" : [ "\u003ca href=\"https://maps.google.com/maps/contrib/115866158754962714954\"\u003eABD RAHMAN DIN\u003c/a\u003e" ], "photo_reference" : "CmRaAAAA2VO4J5WaFY6Z5-tS2SJKkPhoVCqxjukhhc0b5iadF1EgksxqgJ9PrUipk_6YyHXIbrLhQEsf2cZjJb628Gyj7a61fkFRkjx3HHhmiUs44VRa3DfWT5kghBndXQ4WUL_NEhD2oTfglENvq6tpTpGqvxUvGhTgsdhh44EXIC5MnZKizUygr9LuoA", "width" : 4032 } ], "plus_code" : { "compound_code" : "MP5W+C5 港区、東京都", "global_code" : "8Q7XMP5W+C5" } } ], "status" : "OK" }GoからAPIを実行する
SDKをインストール
$ go get googlemaps.github.io/maps実装
公式githubを参考に実装します。
main.gopackage main import ( "context" "log" "os" "github.com/kr/pretty" "googlemaps.github.io/maps" ) func init() { err := godotenv.Load() if err != nil { log.Fatalf("Error loading .env: %s", err) } } func main() { c, err := maps.NewClient("取得したAPIKEY") if err != nil { log.Fatalf("fatal error: %s", err) } r := &maps.TextSearchRequest{ Query: "東京タワー", } res, err := c.TextSearch(context.Background(), r) if err != nil { log.Fatalf("fatal error: %s", err) } pretty.Println(res) }実行結果$ go run main.go maps.PlacesSearchResponse{ Results: { { FormattedAddress: "4 Chome-2-8 Shibakoen, Minato City, Tokyo 105-0011, Japan", Geometry: maps.AddressGeometry{ Location: maps.LatLng{Lat:35.6585805, Lng:139.7454329}, LocationType: "", Bounds: maps.LatLngBounds{}, Viewport: maps.LatLngBounds{ NorthEast: maps.LatLng{Lat:35.66027947989272, Lng:139.7469715298927}, SouthWest: maps.LatLng{Lat:35.65757982010728, Lng:139.7442718701072}, }, Types: nil, }, Name: "Tokyo Tower", Icon: "https://maps.gstatic.com/mapfiles/place_api/icons/geocode-71.png", PlaceID: "ChIJCewJkL2LGGAR3Qmk0vCTGkg", Scope: "", Rating: 4.400000095367432, UserRatingsTotal: 48610, Types: {"tourist_attraction", "premise", "point_of_interest", "establishment"}, OpeningHours: &maps.OpeningHours{ OpenNow: &bool(true), Periods: nil, WeekdayText: nil, PermanentlyClosed: (*bool)(nil), }, Photos: { { PhotoReference: "CmRaAAAAwPa4Fr0AM5xeZcF5ygLaKHUmOeYMqrhlhm1i0fluDX2WQsv7gGbhdIGG4Tj8W8yX6AC35BXtU4RbF7qc5d72CqIo5vXTdub6tXlhEj9r0GjvnsFPpaN0l8GFOLOkI8X8EhBC_84rpT5MTKJ5_UD_ze0wGhRA8g0XTrLRFPBIv6NRjIIHoPQIhA", Height: 3024, Width: 4032, HTMLAttributions: {"<a href=\"https://maps.google.com/maps/contrib/115866158754962714954\">ABD RAHMAN DIN</a>"}, }, }, AltIDs: nil, PriceLevel: 0, Vicinity: "", PermanentlyClosed: false, BusinessStatus: "OPERATIONAL", ID: "0f193d8a0df922a2bcb369673944240a8cb182c3", }, }, HTMLAttributions: {}, NextPageToken: "", }いい感じに取得できました!
参考
- 投稿日:2020-05-25T11:50:54+09:00
【Golang】構造体
【Golang】構造体
Golangの基礎学習〜Webアプリケーション作成までの学習を終えたので、復習を兼ねてまとめていく。 基礎〜応用まで。
package main //struct 構造体 //クラスのような感じ import ( "fmt" ) //struct //型 //キャピタル=頭文字を大文字にする。小文字だと外部からアクセスできない type Vertex struct { X int Y string Z int } //3 値を書き換える関数は、引数をポインタ型にする //関数 Xを書き換える //引数がポインタ型ではないので変わらない func Changev(v Vertex) { v.X = 100000 } //関数 Yを書き換える //引数はポインタ型で渡す func Changev2(v *Vertex) { //本当は下記で書くといいが、 // (*v).X = 1000 //下記でも、自動で*vにしてくれる v.X = 1000000 } func main() { //1 //宣言 v := Vertex{X: 1, Y: "A"} fmt.Println(v) //>>{1 A 0} //値にアクセス fmt.Println(v.X, v.Y, v.Z) //>>1 A 0 //上書き v.X = 2 fmt.Println(v.X) //>>2 //宣言 //複合リテラルs //順番に値を入れる v2 := Vertex{1, "B", 100} //Xのみ 他はデフォルトが入る v3 := Vertex{X: 1} fmt.Println(v2) //>>{1 B 100} fmt.Println(v3) //>>{1 0} //アドレスはない //宣言 空 デフォルト値が入る v4 := Vertex{} fmt.Println(v4) //>>{0 0} fmt.Printf("%T %v\n", v4, v4) //>>main.Vertex {0 0} //別の書き方 空 var v5 Vertex fmt.Println(v5) //>>{0 0} fmt.Printf("%T %v\n", v5, v5) //>>main.Vertex {0 0} //new //アドレスが帰ってくる。 //v4,v5とは意味合いが違う v6 := new(Vertex) fmt.Println(v6) //>>&{0 0} fmt.Printf("%T %v\n", v6, v6) //>>*main.Vertex &{0 0} //v6と同じ表現(アドレス)をnewを使わないで表現 //明示的にアドレスを取得したい場合はこっちを使う場合が多い v7:= &Vertex{} fmt.Println(v7) //&{0 0} fmt.Printf("%T %v\n", v7, v7) //>>*main.Vertex &{0 0} //2 //空を作る //スライスとかマップの場合はmake([]int, 0)の方が多い /* s := make([]int, 0) s := []int{} fmt.Println(s) */ //goは書き方が複数あって困惑するが、今は書き方が複数あるということが理解できていればOK //3 構造体は値型 //応用 //値を書き換えたい v8 := Vertex{3, "B", 1000} //値渡しなので、コピーが作られて渡される Changev(v8) //書き換わらない fmt.Println(v8) //>>{3 B 1000} //書き換えたい場合 アドレス渡しにする v9 := &Vertex{4, "C", 122} //参照渡し Changev2(v9) fmt.Println(v9) //>>&{1000000 C 122} }
- 投稿日:2020-05-25T09:43:55+09:00
Vuguに触れてみた(構文確認編)
はじめに
こちらは別記事(Vuguに触れてみた(環境構築編) )の続きです。
今回は実際に構文確認しつつ、Vuguに触れていきたいと思います。Vuguに触れてみた(環境構築)のファイル構成前提で始めますので、ご了承ください。
また、今回作ったものに関しては下記リポジトリに置いています。(更新するかもです。)
https://github.com/inagacky/vugu_sample構文確認
まずは
root.vugu
を元に、色々と触れていこうと思います。
初期状態だと色々文言が入っていますので、削除してから始めています。vg-if
Show Button
というボタンをクリックすると、Show
というbool型の値が変わり、
SAMPLE TEXT
という文言を表示するだけのコードです。root.vugu<div> <main role="main" class="container text-center"> <div class="mt-5"> <h1>Vugu Test</h1> <div class="lead"> <input type="button" @click='c.Show=!c.Show' value="Show Button" /> </div> <div vg-if='c.Show' style="margin-top: 10px; font-size: 20px;"> SAMPLE TEXT </div> </div> </main> </div> <script type="application/x-go"> type Root struct { Show bool `vugu:"data"` } </script>実行結果
vg-for
もちろんfor文もかけます。
下記のコードは、Add Button
をクリックする度に、100までの乱数値をリストに追加し、
リスト文を表示しているコードです。root.vugu<div> <main role="main" class="container text-center"> <div class="mt-5"> <h1>Vugu Test</h1> <div class="lead"> <input type="button" @click='c.HandleClick(event)' value="Add Button" /> </div> <div vg-if='len(c.Numbers) > 0'> <span vg-for='_, num := range c.Numbers'> <span vg-content='num' style="margin-left: 10px;"></span> </span> </div> </div> </main> </div> <script type="application/x-go"> import "math/rand" type Root struct { Numbers []int `vugu:"data"` } func (r *Root) HandleClick(event vugu.DOMEvent) { // リストへ追加 r.Numbers = append(r.Numbers, rand.Intn(100)) } </script>実行結果
vg-attr
動的にStyle属性やClass属性の付与を行えます。
下記の例では、乱数を生成した後、乱数に応じたClass名を付与し、数字に色をつけています。root.vugu<div> <main role="main" class="container text-center"> <div class="mt-5"> <h1>Vugu Test</h1> <div class="lead"> <input type="button" @click='c.HandleClick(event)' value="Add Button" /> </div> <div vg-if='len(c.Randoms) > 0'> <span vg-for='_, rNumber := range c.Randoms'> <span :class="rNumber.Style" vg-content='rNumber.Number' style="margin-left: 10px;"></span> </span> </div> </div> </main> </div> <script type="application/x-go"> import "math/rand" type Root struct { Randoms []Random `vugu:"data"` Style string `vugu:"data"` } type Random struct { Number int Style string } func (r *Root) HandleClick(event vugu.DOMEvent) { num := rand.Intn(3) randomNum := Random { Number: num, Style: []string{"red", "green", "blue"}[num], } r.Randoms = append(r.Randoms, randomNum) } </script> <style> .red { color: #f00; } .green { color: #0f0; } .blue { color: #00f; } </style>実行結果
コンポーネント作成
もちろん独自コンポーネントも作成することができます。
今回はサンプルとして、先ほどのコードの乱数の数値表示と色付けを
random-line.vugu
に切り出し、コンポーネントにしました。root.vugu<div> <main role="main" class="container text-center"> <div class="mt-5"> <h1>Vugu Test</h1> <div class="lead"> <input type="button" @click='c.HandleClick(event)' value="Add Button" /> </div> <div vg-if='len(c.Numbers) > 0'> <span vg-for='_, rNumber := range c.Numbers'> <main:RandomLine :Number="rNumber"></main:RandomLine> </span> </div> </div> </main> </div> <script type="application/x-go"> import "math/rand" type Root struct { Numbers []int `vugu:"data"` Style string `vugu:"data"` } type Random struct { Number int Style string } func (r *Root) HandleClick(event vugu.DOMEvent) { num := rand.Intn(3) r.Numbers = append(r.Numbers, num) } </script>random-line.vugu<span :class='c.getStyle(c.Number)' vg-content='c.Number' style="margin-left: 10px;"></span> <script type="application/x-go"> type RandomLine struct { Number int `vugu:"data"` } func (r *RandomLine) getStyle(num int) string { return []string{"red", "green", "blue"}[num]; } </script> <style> .red { color: #f00; } .green { color: #0f0; } .blue { color: #00f; } </style>実行結果
終わりに
Golang
とVue.js
に触れたことがある方なら、ある程度直感的に理解ができるのではないでしょうか。
正直まだまだ発展途上の技術だと思いますし、日本語の情報(というか英語も)ほぼ無く、
GitHubのIssueを確認したり、時にはVugu
自体のコードを読むことが必要になってきそうです。
将来的にGolang
×WebAssembly
といった場合に、一つの選択肢になればいいなと思います。
次回は何かしらアプリでも作ってみようかなと思います。閲覧頂きありがとうございました!
- 投稿日:2020-05-25T09:23:10+09:00
Vuguに触れてみた(環境構築編)
はじめに
一時期小耳に挟んだ
Vugu
という技術について、今更ながら触れてみたので記事にしてみました。Vuguとは
Golang
で、Vue.js
っぽくフロントエンド を記述できるライブラリです。
WebAssebmly
で実行されるのでコードの漏洩がなかったり、実行速度も比較的高速らしいです。
公式URLは下記
https://www.vugu.org/
2020/04/26
にv0.3.0
が出たようなので、まだまだ発展途上の技術ではあります。環境構築
環境構築だけなら、下記公式の手順を行なっていけばできます。
https://www.vugu.org/doc/start前提
Golang
のv1.14
以上らしいので、予めインストールをお願いします。
Macで、homebrewを使っていれば、下記コマンドだけでGolangのインストール可能です。% brew install goその他の方法に関しては、公式のインストールをご確認ください。
https://golang.org/doc/installvgrunのインストール
下記コマンドでvgrunのインストール、その他ツール群をインストールします。
[hoge@mac] ~/vugu_sample % go get -u github.com/vugu/vgrun [hoge@mac] ~/vugu_sample % vgrun -install-tools 2020/05/24 17:20:08 Installing vugugen 2020/05/24 17:20:13 Installing vgrgen [hoge@mac] ~/vugu_sampleサンプルプロジェクトのダウンロード
公式の方がサンプルプロジェクトを用意してくださっているので、
こちらをダウンロードして利用していきます。
https://github.com/vugu-examples下記コマンドで可能です。
(今回はsimpleリポジトリを利用しています。)vgrun -new-from-example=simple .上記コマンドを実行すると、ファイルがダウンロードされているかと思います。
go.mod
も落ちてくるので助かりますね。% ls -la total 56 drwxr-xr-x 9 user staff 288 5 24 17:29 ./ drwxr-xr-x+ 99 user staff 3168 5 24 17:29 ../ -rw-r--r-- 1 user staff 24 5 24 17:29 .gitignore -rw-r--r-- 1 user staff 1070 5 24 17:29 LICENSE -rw-r--r-- 1 user staff 231 5 24 17:29 README.md -rw-r--r-- 1 user staff 662 5 24 17:29 devserver.go -rw-r--r-- 1 user staff 39 5 24 17:29 generate.go -rw-r--r-- 1 user staff 48 5 24 17:29 go.mod -rw-r--r-- 1 user staff 2380 5 24 17:29 root.vuguサーバーの実行
下記コマンドでサーバーを立ち上げます。
% vgrun devserver.go 2020/05/24 17:31:39 Starting HTTP Server at "127.0.0.1:8844"
その後、
http://127.0.0.1:8844/
にアクセスします。
下記画面が表示されれば起動しています!ちなみに、
root.vugu
の内容を修正すると、ホットリロードで変更を反映してくれます。終わりに
ほぼ公式チュートリアルの通りの内容になってしまいましたが、以上で環境構築手順は終了です。
現状のフロントエンドの代替となるかと言われると微妙な気がしますが、
golang
でフロントエンドが書けるのは嬉しいですね。
次回はroot.vuguの内容を修正したり、構文について見ていこうかと思います。
- 投稿日:2020-05-25T08:39:23+09:00
pion/webrtc のexampleのタイムアウトを伸ばす
pion は Go で書かれた WebRTC 実装です。
これを試してみようとして、まずexampleを動かしてみようとしました。https://github.com/pion/webrtc/blob/master/examples/README.md
通信を行うときに両者でお互いの通信条件を取り交わす必要があります。これをシグナリングと呼んでいます。
WebRTCではシグナリングの方法は仕様で定めらていません。
ここのexamplesでは、それを「コピぺ」によって行うようになっています。(1) webページのフォームに表示された文字列をコピぺして実行ファイルの標準入力に食わせる。
(2) 実行ファイルの標準出力に出てきた文字列をコピぺしてwebページのもうひとつのフォームに入力する。
(3) webページ上の'Start Session'のボタンを押す。やってみると、これがなかなかうまくいきません。(3)のボタンを押す直前でタイムアウトしてしまいます。
コピペする文字列は百行近くある巨大なものなので、スクロールしている間に時間がたってしまいます。
何度やっても間に合わなくてイライラが爆発しそうになったので、exampleを修正してタイムアウトを伸ばすことにしました。diff --git a/examples/save-to-disk/main.go b/examples/save-to-disk/main.go index 6318220..9effce0 100644 --- a/examples/save-to-disk/main.go +++ b/examples/save-to-disk/main.go @@ -33,6 +33,8 @@ func saveToDisk(i media.Writer, track *webrtc.Track) { } func main() { + s := webrtc.SettingEngine{} + s.SetCandidateSelectionTimeout(30 * time.Second) // Create a MediaEngine object to configure the supported codec m := webrtc.MediaEngine{} @@ -42,7 +44,7 @@ func main() { m.RegisterCodec(webrtc.NewRTPVP8Codec(webrtc.DefaultPayloadTypeVP8, 90000)) // Create the API object with the MediaEngine - api := webrtc.NewAPI(webrtc.WithMediaEngine(m)) + api := webrtc.NewAPI(webrtc.WithMediaEngine(m), webrtc.WithSettingEngine(s)) // Everything below is the Pion WebRTC API! Thanks for using it ❤️.これで30秒の猶予ができたので、ゆっくりと操作してexampleを動かすことができるようになりました。
- 投稿日:2020-05-25T00:42:21+09:00
Go言語でAWS SNSにメッセージを送信する
概要
Amazon Simple Notification Service(SNS)で、Go言語のSDKを使ってトピックに対してメッセージを送信します。
環境
- Go : 1.14.3
- Mac OS X : 10.15.4
- AWS SDK for Go
シンプルな実装
シンプルなメッセージ送信package main import ( "fmt" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/sns" ) // TopicARNとRegionは環境に合わせます const ( TopicARN = "arn:aws:sns:XXXXXXXXXXXXXXXXX" AwsRegion = "XXXXXX" ) func main() { // SNSクライアントの作成 mySession := session.Must(session.NewSession()) svc := sns.New(mySession, aws.NewConfig().WithRegion(AwsRegion)) message := "This is sample message." // メッセージを送信するための構造体を作成 inputPublish := &sns.PublishInput{ Message: aws.String(message), TopicArn: aws.String(TopicARN), } // メッセージの送信(Publish) MessageId, err := svc.Publish(inputPublish) if err != nil { fmt.Println("Publish Error: ", err) } fmt.Println(MessageId) }シンプルにメッセージを送る場合の実装です。登録されたサブスクリプションに対してメッセージが送信されます。例として、SQSをサブスクリプションにした場合、以下の通り確認できます。
サブスクリプションごとにメッセージを変える場合
サブスクライブするプロトコルごとのメッセージpackage main import ( "encoding/json" "fmt" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/sns" ) const ( TopicARN = "arn:aws:sns:XXXXXXXXXXXXXXXXX" AwsRegion = "XXXXXX" ) func main() { mySession := session.Must(session.NewSession()) svc := sns.New(mySession, aws.NewConfig().WithRegion(AwsRegion)) message := "This is sample message." // サブスクリプションのプロトコルごとにメッセージを指定 messageJson := map[string]string{ "default": message, "sqs": "This is sample message for sqs.", } // メッセージ構造体はJSON文字列にする bytes, err := json.Marshal(messageJson) if err != nil { fmt.Println("JSON marshal Error: ", err) } messageForSQS := string(bytes) inputPublish := &sns.PublishInput{ Message: aws.String(messageForSQS), MessageStructure: aws.String("json"), // MessageStructureにjsonを指定 TopicArn: aws.String(TopicARN), } MessageId, err := svc.Publish(inputPublish) if err != nil { fmt.Println("Publish Error: ", err) } fmt.Println(MessageId) }このソースの実行結果は以下の通りです。SQS用に設定したメッセージが送信されています。
このようにサブスクリプションのプロトコルごとに異なるメッセージを送る場合、
MessageStructure
にjson
を設定します。また、メッセージはJSON文字列として、トップレベルにプロトコル(今回の例ではsqs
)を指定します。トップレベルにdefault
は必須です。
プロトコルとして指定する値はCLIのリファレンスを参照。
subscribe - Optionsなお、メッセージ属性を設定する場合は
MessageAttributes
を使います。詳細は以下の公式ドキュメントを参照。
type PublishInput参考
- 投稿日:2020-05-25T00:22:11+09:00
goose設定ファイルに環境変数を読み込めない(Docker環境)
概要
Docker環境でGo製マイグレーションツールのgooseを使用する際、設定ファイルである
dbconf.yml
が環境変数を読んでくれませんでした。dbconf.yml
development: driver: mymysql open: $MYSQL_URI環境変数は、$変数という形式で読み込んでくれるはずなのですが・・・(公式参照)
Dockerfile
FROM golang:1.14.2-alpine WORKDIR /go/src/server COPY . . ENV GO111MODULE=on RUN apk add --no-cache \ alpine-sdk=1.0-r0 \ && go get github.com/pilu/fresh \ bitbucket.org/liamstask/goose/cmd/goose CMD ["fresh"]コンテナ内でgooseコマンドを使うため、コンテナ内にもgooseを導入してあります。
原因
コンテナ内では外部ファイルやコマンドで指定してあげないと、環境変数を読み込むことができないみたいです。
コンテナ内を確認したところ、.env
がコピーされていなかったので、Dockerの使用上コピーできないようになっているんですかね?
Docker で環境変数をホストからコンテナに渡す方法(ホスト OS 側からゲスト OS に渡す方法各種)解決
docker-compose.yml
にオプション追加version: "3.7" services: server: container_name: server build: context: ./ dockerfile: Dockerfile.dev ports: - 8000:8000 env_file: # 左記オプション追加 - .env depends_on: - db tty: true stdin_open: true volumes: - ./:/go/src/server