メルカリさんのスカラシップを利用してGo Conference 2019 Springに参加してきました
今回、運良くメルカリさんのスカラシップを利用してGo Conference 2019 Springに参加することができました!!
Go Conference 2019 Springでおもしろかったセッションを中心にブログに書き残せたらなと思います。スカラシップ利用して来ました!#gocon pic.twitter.com/rkonBE17ji
— jon (@jon_ground) 2019年5月18日
また、Go Conference 2019 Springの前日にも社内見学やランチなどがあり、その辺りも簡単に触れられればと思います。
参加の経緯
自分は普段、Ruby(Rails)を使うことが多いのですが、個人開発ではGoをちょくちょく使っていて、その開発の際にDBのテストはどう書くの?だったりエラー処理ってどうするのが良いの?かなどの疑問がありました。
たまたまGo Conference 2019 Springのタイムテーブルを見てたら先程の疑問が解決しそうなセッションがあり、行きたいな〜と思ってましたが交通費ツライと思ってて参加を見送ろうと思ってました。しかし、運良くTwitterでメルカリさんのスカラシップの情報が流れてきたのでダメ元で応募してみようと思い応募して参加できました。
前日
ランチ
メルカリさんとのランチ会がありました。が、当日にいきなりGoogleのGoチームも参加することに決まりマジか!ってなりました。GoチームにはKeynoteの方や明日発表する方がいて何を話そうか必死に考えてましたが、ランチ会で飛び交う会話がほとんど英語だったので正直理解しようとするのに必死でした(自分が質問して他の人に訳してもらったところくらいしかちゃんと理解できてない)
GoogleのOSSの開発の話だったりGoのGenericsの話だったりエラー処理の話だったりを話してた気がします。
日本での発表が初めてだからとても緊張しているよって言っていたのはちゃんと聞き取れました!
社内勉強会
ありがたいことにメルカリさんの社内勉強会に参加させていただきました。ガチガチの勉強会ではなくGoについての意見交換の場みたいな雰囲気で、気軽に質問ができました。こういった勉強会は気軽に開催できそうですし、他の社員さんと打ち解ける場にもできそうなのでいいなと思いました。
当日のセッション
Case studies of designing developer friendly libraries
より良いライブラリの開発の仕方についてです。
Goの慣習に従うことや、拡張性を保つこと、コード生成を利用するなどが紹介されています。
特に拡張性を保つために、差し替え可能にする手段としてのFunctional Option Patternは使ったことがないので自分の開発に導入したいです。
エラー設計について/Designing Errors
エラーの基本的なところから、そのエラーをGoでどう処理するのかを聞くことができました。
エラーは関係者によって求められるエラーが違い
- (クライアント)アプリケーション -> エラーを識別できる
- エンドユーザー -> 問題を解決するヒントになるようなメッセージが含まれている
- 運用者 -> 問題の根本的な原因を調査、解決する情報が含まれている
と異なっている部分が紹介されてました。適切な情報を含んだエラーを返すことは運用する上で大事なことだし、Goに限らずアプリケーションの設計として大事だなと思いました。
また、Goでのエラーの処理としてアプリケーション固有のエラーコードを定義する方法があり、コードの見通しがよくなるしエラーの管理が楽になりそうだなと思いました。
Design considerations for container-based Go applications
Goの実装事例を踏まえたコンテナアプリケーションの設計についてです。
- Configration
- Logging
- Monitoring
の3つの設計方針を聞くことができました。
特にloggingの話で、STDOUT/STDERRに書き出し、JSON形式で構造化することでログの抽出が楽になりそうだなと思いました。
Expand observability in Go
net/http/pprofとruntime/traceの使い方について紹介されていました。
すごい丁寧に解説してくれたのでProfilingやTracingをあまり行ったことがない自分でも理解することができました。
実演でどう使うかを見ることができたのでスライド見ながら後で使ってみようと思います。
おまけ
自作キーボードのお店に寄って来ました。
分離式キーボードいいなってなりました。
帰りに寄ってきました pic.twitter.com/54PS7S1ica
— jon (@jon_ground) May 19, 2019
まとめ
今回Googleの社員さんと話したり、今後の開発に活かせそうなセッションをたくさん聞くことができたので参加できてよかったです。セッションの内容は全部は理解できてないので後でスライドを見直そうと思います。
次にGo Conferenceのスカラシップがある時に応募に悩んでいる方の参考になればと思います。
最後にGo Conference 2019 Springに参加する機会を設けてくれたメルカリさんありがとうございました!
elmで作った静的サイトをgithub pagesで公開してみた
最近ブログ書くのをサボっていたのでなにか書こうと思い、そうだ最近触っているElmについて書こう!と思い書きました。時間がかなり遡るのですが年末年始は実家に帰省していて、その際にElmを試しに触っていて簡単なサイトを作ってGithub Pagesで公開したので、その手順を書き残しておきます。
Elmとは
ちょっと触っただけなので詳しくは説明できないのですが公式から抜粋すると以下の特徴があります。
- 実行時の例外が一切起きない
- virtual DOMを使用するがすべての値はイミュータブルなのでベンチマークがすごくいい
- セマンティックバージョンを強制できる
などの特徴があります。
完成品
Elmで書いて動いたものは下のサイトになります
https://jon20.github.io/mypage/
Githubは下になります
最終的にディレクトリは以下になりました。
. ├── README.md ├── elm-stuff ├── elm.json ├── package.json ├── src │ ├── Main.elm │ ├── assets │ │ └── flog.jpg │ ├── index.html │ ├── index.js │ └── main.scss ├── webpack.config.js └── yarn.lock
Elmの準備
Elmのインストールに関しては割愛させていただきます。
Elmのインストールができたら対象のディレクトリでelm init
を行ってください。
src配下にElmのファイルを書いていく形になります。
特にElmで書いたものが手元になければElm-lang.orgのQuick Sampleにあるカウンターアプリがあります。コードは下のIntroductionにかかれています。
Htmlファイル
Javascript Interopに書かれているようなやり方になります。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <link rel="icon" href="./assets/flog.jpg"> <title>jon</title> </head> <body> <div id="elm"></div> </body> </html>
Javascriptファイル
汚いですが、idがelmの要素にelmを紐付ける形になります。
'use strict'; require('./main.scss'); import Image from './assets/flog.jpg' const { Elm } = require("./Main.elm") const app = Elm.Main.init({ node: document.getElementById('elm') });
Webpackの使用
あんまりwebpack詳しくないのですが、色々参考にしながら作成しました。 必須になるプラグインは
html関連
- Elm関連
- CSS関連
- style-loader
- css-loader
- sass-loader ※Sass使う場合
だと思います...
画像とか扱う場合はfile-loaderが必要になると思います。
必要ない場合はfile-loaderらへんはコメントアウトしてください
const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { entry: './src/index.js', output: { path: __dirname + '/dist', filename: 'index.js' }, resolve: { extensions: ['.js', '.elm', '.scss'] }, module: { rules: [{ test: /\.html$/, exclude: /node_modules/, use: [{ loader: 'html-loader', }, ] }, { test: /\.(jpg|png)$/, use: { loader: 'file-loader', options: { name: '[name].[ext]', outputPath: 'assets/', } } }, { test: /\.elm$/, exclude: [/elm-stuff/, /node_modules/], use: { loader: 'elm-webpack-loader', options: { verbose: true, } } }, { test: /\.scss$/, use: [{ loader: 'style-loader', }, { loader: 'css-loader', options: { url: true } }, { loader: 'sass-loader', } ], } ], noParse: /\.elm$/ }, plugins: [ new HtmlWebpackPlugin({ template: "./src/index.html" }) ], devServer: { inline: true, stats: 'errors-only' } };
CIでビルド・公開周りを自動化
今回はTravis-CIを使いました。
masterにpushされたらyarn install
走らせて、gh-pages branchにpushする形となっています。
yarn buildはpackage.jsonで定義されたスクリプトが走っています。
language: node_js elm: - elm0.19.0 node_js: - "10" cache: yarn: true directories: - "node_modules" branches: only: - master install: - yarn install - yarn build script: - echo "Skipping tests" deploy: provider: pages skip-cleanup: true github-token: $GITHUB_ACCESS_TOKEN target-branch: gh-pages local-dir: dist on: branch: master
まとめ
超ざっくりですがElmをgithub pagesで公開する手順を書きました。簡単にElmで作ったプロダクトがノーコストで公開できるので便利ですね。
WebpackらへんやCIらへんも一緒に試せるのでぜひ挑戦してみてください。
Quick Sampleをgithub pagesで公開するだけなら余分な所が多いので余力があったらサンプルを追加します...
あと、今回作ったものを読み込むときに一瞬ちらつくのですが読み込み順の問題なのかな...
gRPCで動画ファイルの送受信を行ってみる
Go4 Advent Calendar 2018 11日目です。
今回はgRPCを使ってmp4で作成した動画ファイルをstreamを使って送受信してみようと思います。
コードがあまりきれいでないかもしれませんがよろしくお願いします。
はじめに
今回作成したサンプルコードはgithubに置いておきます。
gRPCとは
Googleが開発しているOpen SourceのRPC frameworkになり、Protocol Buffersを使ってサービス間のインターフェースをprotoファイルに定義します。また、試してませんが他言語間の通信も可能みたいです。
Protocol Buffersでどう定義するのかは下記のドキュメントに詳しく書かれています。
protoファイルの定義
syntax = "proto3"; package upload; service UploadHandler { rpc Upload(stream UploadRequest) returns (UploadReply) {}; } message UploadRequest { bytes VideoData = 1; } message UploadReply { string UploadStatus = 1; }
syntaxはproto3を利用しています。
serviceブロックの中に利用するメソッドを定義します。今回はmp4ファイルを受け取り、成功したらメッセージを返すようなメソッドを定義しています。
ここでUploadRequestの前にstreamをつけているのですが、これを記述することで一つのリクエストを複数のリクエストに分割することができます。今回はClient Streaming RPC方式になると思います。
また、このprotoファイルで生成したgoのファイルはclientとserverの両方必要になります。
Serverの作成
func NewUploadServer(gserver *grpc.Server) { uploadserver := &server{} upload.RegisterUploadHandlerServer(gserver, uploadserver) reflection.Register(gserver) } type server struct{} func (s *server) Upload(stream upload.UploadHandler_UploadServer) error { err := os.MkdirAll("Sample", 0777) if err != nil { return err } file, err := os.Create(filepath.Join("Sample", "tmp.mp4")) defer file.Close() if err != nil { return err } for { resp, err := stream.Recv() if err == io.EOF { break } if err != nil { return err } file.Write(resp.VideoData) } err = stream.SendAndClose(&upload.UploadReply{UploadStatus: "OK"}) if err != nil { return err } return nil }
handler.goではclientから受け取った動画データをSampleディレクトリにtmp.mp4として保存するような処理を行っています。成功したらOKの文字をclientに送信します。
main.go
func main() { lis, err := net.Listen("tcp", "localhost:8080") if err != nil { panic(err) } server := grpc.NewServer() handler.NewUploadServer(server) if err := server.Serve(lis); err != nil { panic(err) } }
main.goではNewServerで空のgrpcサーバーの作成を行い、NewUploadServerで作成したserviceを登録してlocalhost:8080でサーバーを立ち上げます。NewServerは色々オプションを渡せるみたいです。
Clientの作成
func main() { connect, _ := grpc.Dial("localhost:8080", grpc.WithInsecure()) defer connect.Close() uploadhalder := upload.NewUploadHandlerClient(connect) stream, err := uploadhalder.Upload(context.Background()) err = Upload(stream) if err != nil { fmt.Println(err) } } func Upload(stream upload.UploadHandler_UploadClient) error { file, _ := os.Open("./sample.mp4") defer file.Close() buf := make([]byte, 1024) for { _, err := file.Read(buf) if err == io.EOF { break } if err != nil { return err } stream.Send(&upload.UploadRequest{VideoData: buf}) } resp, err := stream.CloseAndRecv() if err != nil { return err } fmt.Println(resp.UploadStatus) return nil }
予めsample.mp4ファイルを対象のディレクトリに用意してmakeでどれくらい送るか定義しています。そしてfor内でひたすらserverにリクエストを投げまくってます。 送り終わったらCloseAndRecvでstreamをcloseしてサーバーからのメッセージを待ちます。うまく行けばOKと返ってきます。
完成!
実際に試してみるとclientから送られてきたSample.mp4ファイルがserverのSampleディレクトリ内にtmp.mp4として保存されたことが確認できました。
まとめ
- streamを扱うことで動画データを分割して送ることができた。特に大容量の動画ファイルを送信したときに少しずつデータを送ることができるのでネットワークの負荷が減りそう
- ライブストリーミングの変換処理とかstreamで実装すると良さそうな気がする
Go言語でgraphqlを触ってみた
ゆるっとアドベントカレンダー Advent Calendar 2018 2日目です。
最近ハッカソンでGraphqlを使って開発を行っているので、今回はGraphqlの復習で簡単なGraphqlサーバをGo言語を使って実装してみます。
Graphqlとは?
GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data
GraphqlはAPIへ問い合わせるためのクエリ言語になります。一つのエンドポイントを用意して、予め定義しておいたGraphqlのクエリを投げることで必要な情報を持ってくることができます。
今回使う外部ライブラリ
- labstack/echo
- graphql-go/graphql
- graphql-go/handler
今回実装したもの
localhost:8080/graphqlにブラウザでアクセスするとGraphqlのIDEが起動して、Graphqlのqueryを投げると下の写真の右のようにdataが返ってくるようなものになります。
今回実装したものは↓のGithubに置いておきます。
解説
基本的にgraphql-go/graphqlのexampleを参考に実装しています。
まずREST APIのように何か投げたら何か返ってくるものをGraphqlで実装したいためSchemaを定義します。ここで気をつけることは投げられるqueryにはQueryとMutationがあります。SchemaConfigを見ているとSubscriptionがあるのですが調べたかんじだとリアルタイムのデータを取得するときに使うらしいです。まだ使ったことはないので今回は触れません。
QueryとMutationなのですが、QueryはCRUDでいうとCの部分で、単にデータを取得したいときに使います。MutationはR、U、Dの部分で、データに変更を加えたい際に使います。
今回はSampleとしてQueryとMutationを使ってschemaを定義します。
仕様は以下のとおりです。
- Query
- IDとPasswordを渡したらMessageが返ってくる
- Mutation
- UsernameとAgeを渡したらMessageが返ってくる(Mutationなので何かしらの更新処理を入れる必要があるが今回は省略します)
QueryのSchemaの実装
graphql-sample/query.go at master · jon20/graphql-sample · GitHub
func SetQuery() graphql.Fields { query := graphql.Fields{ "SampleQuery": &graphql.Field{ Type: SampleQueryType(), Args: SampleQueryArgs(), Resolve: SampleQueryResolve, }, } return query } func SampleQueryType() *graphql.Object { sampleType := graphql.NewObject(graphql.ObjectConfig{ Name: "SampleQuery", Fields: graphql.Fields{ "Message": &graphql.Field{ Type: graphql.String, }, }, }) return sampleType } func SampleQueryArgs() map[string]*graphql.ArgumentConfig { sampleArgs := graphql.FieldConfigArgument{ "ID": &graphql.ArgumentConfig{ Type: graphql.NewNonNull(graphql.String), }, "Password": &graphql.ArgumentConfig{ Type: graphql.NewNonNull(graphql.String), }, } return sampleArgs } func SampleQueryResolve(params graphql.ResolveParams) (interface{}, error) { resp := &models.SampleMutateResp{ Message: "Hello, This is SampleQuery", } return resp, nil }
汚い実装ですが、肝となるのはSetQuery()です。Fieldsに定義したいFieldを定義します。FieldはArgsがqueryを使うときに渡す値の定義、Resolveにはqueryを叩いたときの処理を渡してあげます。
MutationのSchemaの実装
graphql-sample/mutation.go at master · jon20/graphql-sample · GitHub
func SetMutation() graphql.Fields { mutateQuery := graphql.Fields{ "SampleMutate": &graphql.Field{ Type: SampleMutateType(), Args: SampleMutateArgs(), Resolve: SampleMutateResolve, }, } return mutateQuery } func SampleMutateType() *graphql.Object { sampleType := graphql.NewObject(graphql.ObjectConfig{ Name: "SampleMutate", Fields: graphql.Fields{ "Message": &graphql.Field{ Type: graphql.String, }, }, }) return sampleType } func SampleMutateArgs() map[string]*graphql.ArgumentConfig { sampleArgs := graphql.FieldConfigArgument{ "Username": &graphql.ArgumentConfig{ Type: graphql.NewNonNull(graphql.String), }, "Age": &graphql.ArgumentConfig{ Type: graphql.NewNonNull(graphql.Int), }, } return sampleArgs } func SampleMutateResolve(params graphql.ResolveParams) (interface{}, error) { resp := &models.SampleMutateResp{ Message: "Hello, This is SampleMutate", } return resp, nil }
実装手順としてはqueryと同じです。どちらも共通して気をつけるポイントとしては指定できる型です。Scalar型があるのですが公式のDocumentを見たかんじだとID、String、Int、Float、Booleanだけになります。もしそれ以外の型を使いたい場合は自分で定義しなければなりません。
SchemaのSetupとIDEの用意
graphql-sample/graphql.go at master · jon20/graphql-sample · GitHub
func GraphqlSetting() http.Handler { query := fields.SetQuery() mutateQuery := fields.SetMutation() rootMutation := graphql.NewObject(graphql.ObjectConfig{Name: "RootMutation", Fields: mutateQuery}) rootQuery := graphql.NewObject(graphql.ObjectConfig{Name: "RootQuery", Fields: query}) schema, err := graphql.NewSchema(graphql.SchemaConfig{ Query: rootQuery, Mutation: rootMutation, }) if err != nil { panic(err) } h := handler.New(&handler.Config{ Schema: &schema, Pretty: true, GraphiQL: false, Playground: true, }) return h }
GraphqlのIDEはhandler.Config{}のところで設定できます。指定できるIDEの種類としてはgraphiqlとgraphql-playgroundがあります。今回はgraphql-playgroundを使います。
Graphqlサーバの立ち上げ
graphql-sample/main.go at master · jon20/graphql-sample · GitHub
func main() { route := echo.New() route.POST("/graphql", echo.WrapHandler(graphql.GraphqlSetting())) route.GET("/graphql", echo.WrapHandler(graphql.GraphqlSetting())) route.Logger.Fatal(route.Start("localhost:8080")) }
あとは localhost:8080/graphqlのroutingを追加するためechoを使います。
完成!!
定義したSchemaは↓のように見れます。
Queryに定義したqueryの実行結果 Mutationに定義したqueryの実行結果
まとめ
- 単一エンドポイントにまとめられるのでREST APIで実装する場合と比べてエンドポイントの管理が楽になりそう
- Documentを自動で生成してくれるのでSwaggerなどを用いてDocumentを実装する手間が省ける。
- ファイルアップロードとかを実装する場合はどうじっそうすればいいのだろうか?
今日作る料理をサジェストしてくれるlinebotを作ったよ!
少し前からLINE community labというワーキンググループに友達と参加し面白いLine Botが作れたので紹介しようと思います
作成したもの
機能としては例として「なんか、豚肉とか使ったものない?」とメッセージを送信してますが、言葉の中にある材料が含まれる料理を提案してくれます。 またレシピ確認ボタンで詳細レシピURLの表示、材料確認ボタンで材料の表示をしてくれます。 これで冷蔵庫に残った余り物で何を作ろうか考えなくて済みますね!
構成
私は今回バックエンドの方を主にやっていたのでlineのapiはよくわかってません(友達がやってくれました)ので、バックエンドを中心に説明します。
使用言語
今回はRubyを使いました。 web frameworkは軽量のsinatraを使用
実装
まず、レシピのデータについてですが、RakutenレシピがAPIを公開していたので負荷をかけないように注意しながらレシピ情報をいただきました(利用規約に引っかかってそう...)。
材料名のところに全角スペースや★がついててそこら辺整形するのが面倒くさかったです。
次に柔軟に文章に対応したいため送られてきた文章に対して形態素解析を行い材料と思われる単語を抜き出します。
そしてElasticsearchで全文検索を行いレシピをピックアップしています。
最後に開発にあたってはコンテナ環境を使いたかったのでDockerを使用してます。で、docker-composeを使って複数のコンテナを管理してます。
本番サーバはGCPを借りて動かしました。
まとめ
小規模ですが初めてチーム開発をしました。
gitやrubyをもっと学ばないといけないなと思いました。
途中までテストを書いていたのですが、途中でめんどくさくなってやめてしまったのでちゃんと書かないとな...
今回は公にサービスを公開する気はないのですが、いざサービスを公開するときに外部のサービスを利用している場合、利用規約ちゃんと見ないといけないな。
Amazon Echoを使って自宅の照明を操作してみた
目的
最近家のリモコンがよくなくなるのでAmazon Echoで操作できたら最強なのでは?と思い作成に至りました。
完成品
作成手順(雑にしか書きません)
- 赤外線LED,赤外線センサー、Raspberry piをなんとかして手に入れる。
- lircを使い赤外線を記憶させて送るところまでを行うのですが、以下の記事が参考になりました。(ありがたや〜)
- コマンドで照明がON, OFFできたらいよいよAmazon echoと連携させます
- raspberry piでsmart home(正確にはBelkin WeMoデバイス)をエミュレートしてくれるものを見つけたのでそれを使います
- 使い方としてはREADMEに書いているとおりにセットアップすれば問題なく動かせました。(Python3.6以上を要求されることに注意)
- セットアップが終わったらfauxmo/config-sample.jsonをconfig.jsonにリネームしてPLUGINSのとこを書き換えます。
私はRaspberry pi上にFlaskでサーバーを立てて、httpリクエストを受け取ったらON, OFFのコマンドが走るようにしました。
まとめ
この内容はPycon Kyushuの懇親会のLTで発表した内容となっています。
発表したスライドも一応載せておきます
AnsibleでArchLinuxの自動インストール
AnsibleでArchLinuxの自動インストールをやってみた
今回、AnsibleでArchlinuxの自動インストールに挑戦しました。
結果として成功したのですが、tasksが結構汚いのでまだまだ改善の余地はあります…
インストールの説明はGithubに載せているのでそっち見たほうがいいです。