2014年12月31日水曜日

2014年を振り返る

今年もあと8時間程度となりました。

家の大掃除に集中していたのでこのタイミングになりましたが、
今年も1年を振り返っておきたいと思います。

2014年のGDG中国のイベントに関して。

  1. 2/11(火祝)第23回勉強会@岡山(Dart Flight School)
  2. 5/10(土)第24回勉強会@岡山(Android Studio/Android Wear/Go/Managed VMs)
  3. 6/21(土)第25回勉強会@岡山(Android Wearハッカソン)
  4. 7/19(土)GDG Devfest Japan 2014 Summer(Google I/O報告会)
  5. 12/6(土)第26回勉強会@岡山(Android Wear/AngularJS/UI/UX関係+LT2本)
  6. Golang Cafeを毎週(京都に行ったり、Google東京オフィスに行ったり…)
  7. GDG Cafe #1を試行開催(2回)
個人的な講師など
  1. 4/6(日)GDG Devfest Japan 2014 Spring(Go/Google Apps Script)
  2. 8/23(土)AngularJS勉強会 #2(LTをしてきました)
  3. 9/20(土)OSC広島(Google App Engine)
  4. 11/22(土)GDG Devfest Kyoto 2014(Go)
  5. 11/23(日祝)第6回中国地方DB勉強会(Bigquery)

仕事関係でコミュニティの活動に影響が出るかもしれないのが、採用試験に合格したので4月には職場が変わってしまうかもしれないということと、場所の問題で、イベント開催が厳しくなる可能性も出てきました。これはGDG中国の活動を継続するということに関しても考えないといけなくなるかもしれません。
それは現時点では未定なので何も言えることはないのですが…。

それから、昨年も感じていた事でしたが、発表の質が非常に悪くなってしまっている感じがしているのと、技術を追いかけるという作業をする時間が取れなくなってきているのも強く感じる1年でした。(これはどうしても仕事の関係上仕方がないので…)

ですが、GDG中国の活動というのは今後も継続したいと思っているので、何らかの方法でイベントの開催と活動の継続をしたいと思います。
そのための施策がGDG Cafeだったり、Golang Cafeだったりします。今のところ特に問題があるということもなかったので大丈夫でしょう。
最悪、日曜日のイベント開催も考えればいいだけの事ですし…。

さて、2013年を振り返った結果や、2014年の目標を見た感じからの振り返りもしておきたいと思います。

GDG中国のスタッフの立て直し→できてません。私自身が迷っていることではあるけど、大きなイベントの時に自分が動けない。という状況が今年は発生しました。そういう時にスタッフにお願いするのは良いと思うのだけど、自分が何もしないというのが正しいかどうかは少し考えているところです。
なので、来年のDevfestなど、他のGDGと連携して開催するようなものへの参加はできなくなるかもしれません。(むしろ、今のところ不参加の方向で考えています)

Golangが盛り上がる→その通りだったと思います。こちらもGolang Cafeでずっと最新の情報を追いかけてきたのですが、実際にアプリケーションを作ったりする事ができなかったのはちょっと残念でした。
来年もGDG CafeやGolang Cafeを継続する中でアプリケーションを実装するということをやってみたいと思います。

GDGでやってみたいテーマ→大体やったかもしれない。
Dart、Google App Engine、AngularJSはしっかりやれたと思います。よく考えるとMapsとかほとんどやれていないので、どこかでやってみたいですね。

残念な点は、他のコミュニティのイベントにほとんど参加できなかった。
いろいろなコミュニティの方と話をしたりする機会が取りにくくなっているということだと思うのですが、土曜日に仕事の用事が入ることが増えてきて、その時にイベントと被るという事がありました。それは非常に残念だったので、来年は、他のイベントにも参加できるように調整したいと思います。

来年の目標はまた来年になってから設定したいと思います。

2014年もあと少しですが、GDG中国のスタッフ、イベント参加者の皆さん、その他今年お会いした方々には大変お世話になりました。来年もよろしくお願いいたします。

2014年12月13日土曜日

第26回GDG中国勉強会を開催しました。

第26回勉強会@岡山を開催しました。
今回は、Android Wear/AngularJS/UI/UXとLT3本(GDG中国のこと/MIT AppInventor2/Android Studio)
という、豊富なコンテンツを提供できました。参加者の皆さんのおかげです。ありがとうございました。

特に、AngularJSのノウハウを発表して頂いた、@armorik83さんの資料は、いろんな方が注目されていて、勉強になるというコメントも見ました。
今後の、Angular2を見据えたコードを書くというのが、仕様がガラッと変わるということで、重要なポイントになりそうです。

久しぶりにAppInventorの話であったり、AndroidでJUnit4を使うという話も新鮮で良かったです。

私はLTでGDG中国の来年のイベントの計画について話をしました。今年は色々なことがありましたので、来年のコミュニティ運営に影響が出そうだったので発表をしました。
4年半の運営をしてきて、これまでは、割りとフットワークが軽かったのですが、少しずつそうもいかなくなってきて、特に、この1年半はコードを書く量と、時間の確保が減ってきて、これまでのような活動ができなさそうな予感がしています。
今後は今の職業柄、異動もありますので、異動先によってはイベントの開催自体が難しいという状況に陥ってしまう可能性もあって、事前に話をしておきました。

と言っても、毎年適当に「やりたい時にやる」というスタイルを貫いていますので、GDG Chugokuがリストから消えない程度には活動を続けようと思っています。
(次は2月に開催。その後はひとまず未定)

今回は、忘年会的なノリのイベントを目指したものでしたので、来年やってみたいものを紹介しようと思います。
あくまでも個人的なもので。



Compute Engineのお陰で私もクラウド上にサーバを作る事ができるようになったので、AWSと比較されていますが、GCEもお試し下さい。

ということで、今年も残り少ないですが、良いお年をお迎えください。
来年のGDG Chugokuのイベントでお待ちしております。

第6回中国地方DB勉強会で発表してきました。

第6回中国地方DB勉強会に参加してきました。

この勉強会は主にRDBMS(PostgreSQL/MySQL/SQLServer/Oracle…)とか、RDBに関する技術を扱っているようです。が、今回は「クラウド」がテーマになるということで、呼んで頂く事ができました。

私の発表資料はBigQueryとCloudSQLの紹介です。技術的な話題というよりは、「そもそも使っている人がいないだろう」というのを想定して内容を決めました。

話の中心としては、「大量のデータを大量のPCで力づくで短時間にデータを解析してしまうサービス」ということで、BigQueryを紹介し、「クラウド上にあるMySQLでメンテナンスコストと移行コストを下げよう」という所を話したつもりです。

BigQueryは、少ない件数でも3秒〜5秒かかってしまいますが、10万件でも、100万件でも3億件でも3秒〜5秒で結果を返すという特性を持っています。キャッシュの仕組みもありますが、やっぱり3秒程度はかかります。(というデモはやりました)

ただ、BigQuery自体は単体でも使えるので、Googleのサービスと組み合わせなければいけないわけではなく、AWSだろうが、Herokuだろうが主となるサービスは、自由に使って頂いて、BigQueryが使えるのであれば、BigQueryにデータを送るようにしていただければそれで良いと思っています。大量のデータを後で解析する必要がある場合は、ぜひ、BigQueryを導入していただけるとうれしいです。

想定外だったのが、AWSの発表をされた玉川さんがBigQueryのことにも詳しく、私のセッション中にフォローのコメントを頂いたりと、「俺じゃなくて良かったんじゃね?」という感じにもなったのですが、これは私の勉強不足と言わざるを得なかったです。

Cloud SQLの方ですが、中身は、MySQL5.5ですので、ローカルでMySQLを使っている方は特に苦労することはないと思いますが、スケールしませんので1台のMySQLがクラウド上で動いているだけ。というもので、説明することが少なかったので、データベースの作り方と、Read Replica(beta版)の紹介をしました。

本当は、BigQueryもCloud SQLも詳細まで説明すればよかったのかもしれませんが、私自身があまり使っていない(デモ、サンプル程度)ので事例だったり、ヘビーな使い方を紹介する事ができなかったのは残念です。

2014/11/23時点で、中国地方にBigQueryとか、Cloud SQLを本格導入している人はいないだろうと思っているので、まずは利用者を増やす所から始めないと事例は増えないだろうなーと思っています。
(もし使っている人がいれば、+Takashi Yokoyamaまでw)

また何かのタイミングでBigQueryの話をしてみようと思います。

2014年11月30日日曜日

GDG Devfest Kyoto 2014で発表してきました。

もうすでに、公式のイベントレポートが上がっているのですが、私も参加してきたので書き残しておきたいと思います。

皆さんSlideshareで公開されているようですが、私はGDGのオーガナイザーですから、Google Presentationを使って資料を作っています。その資料は以下のURLから参照して下さい。

https://docs.google.com/presentation/d/1nDTrzQ5etYb_7F2GhumY2pNO3KsRWp9TpTjd00g1Dtc/edit?usp=sharing

今回は依頼を受けた時に、私の前に何人か発表するということがわかっていましたので、初心者向けというわけにもいかないだろうなと思ったので、Golang Cafeで1年間いろんなことをやって来ましたので、自分でも整理するためにGolang Cafeで特に議論することがあった部分について紹介しようと思っていました。

が、12月にリリース予定のGo1.4についての話題が出てきて、すでにrc版になっていましたので、ここは早く1.4の内容について紹介するのがいいだろうと思って、半分はこれまでの内容で、半分を1.4の内容にすることにしました。(実際には思いつかなかった(≒思い出せなかった)ということもあるのですが…)

Go.1.4の内容に関してはBlogに書いていませんが、Golang Cafe #55でやった内容をそのまま資料にしています。やっぱり一番衝撃的だったのは、Androidの正式サポートでしょうか。言語仕様からして合わないだろうと思っていたのですが、しっかり入れてきました。動きも申し分なく動いていますし、良さそうに見えます。
後は、ドキュメントがどれくらいでてくるか。ということと、NDKの仕様が変わらないことを祈っています。
(当日の発表前にオリジナルのデモを作ろうと頑張っていたのですが、ちょっと間に合いませんでした。私のNDKのノウハウの不足と時間不足でした。)

今日のGoConの情報で思い出したけど、何らかの書き込み時にWrite()、Flush()、Close()の順番で呼び出すけど、Close()でエラーのチェックをしないと困る時があるというのは入れておくべきだったと思いました。
実は、実装によっては、パッケージ内部で以前発生したエラーをずっと持っていて、Close()を呼び出したタイミングでエラーを返すという場合があるので、必ずClose()はチェックするようにしましょう。というものです。
そうだったなと思い出すけど、なかなか実践できていないというのが現状かなと思いました。

貴重な経験を頂きましてありがとうございました。また、何かのタイミングでGDG京都のイベントに参加できれば、その時はよろしくお願いします。

2014年11月16日日曜日

Golang Cafe #54を開催しました。

Golang Cafe #54を開催しました。今回は、Go1.4 Release Notesを読み進めました。

今回、読み進めた所は、以下の点です。

  • Changes to the language
    • For-range loops
    • Method calls on **T
  • Changes to the supported operating systems and architectures
    • Android
  • Changes to the compatibility guidelines
  • Changes to the implementations and tools
    • Internal packages
    • Canonical import paths
    • Import paths for the subrepositories
    • The go generate subcommand
    • Change to file name handling
    • Changes to package source layout
  • Performance
  • Changes to the standard library
    • Major changes to the library
      • syscall
    • Minor changes to the library
      • Testing周りを中心に見ました。

個人的に読み進めた部分をおさらいしたような流れになってしまいましたが、再度読み返してみて、わりといろんな所が変更になっていると思います。

For-range loopsの変更はブランク演算子("_")を書かなくても良くなった点が変更になっています。
次に、ポインタのポインタのメソッドを呼ぶ事が1.3以前では呼び出せていたものが、コンパイルエラーになります。

参考コード(Go Playgroundなので、1.4に置き換わるまでは動作します。)
http://play.golang.org/p/JTHWyVJ3V-(こちらは1.4でもビルドが通る)
http://play.golang.org/p/k-GxLYJOPO(こちらは1.4からコンパイルエラー)

以前のGolang Cafeでも話題になったように、ポインタをあまり使わないのであれば特に影響はありません。

Go1.4からのAndroidのサポートが増える件については、Qiitaの記事を見て動作確認をしてみてください。

いろいろ変更になっていますが、1.3以前までの互換性のガイドラインに変更はありません。(実際、1.0系/1.1系を使っているという方は、あまりいないかもしれませんが…アップデートを忘れている事例を除いて…)

他に増えたのが、
内部パッケージ(Internal Package)でパッケージのディレクトリ内に"internal"というディレクトリを作ると、同一パッケージ外から参照する事ができないパッケージを作ることができるようになります。

それから、Canonical Import Paths(標準的なインポートパス)ということで、package文の末尾にコメントを記入することで、ローカルのディレクトリ構成が正しい(≒go getしたものが正式である)時でないとコンパイルエラーにすることができるようになりました。
リポジトリもいろんなものがあるので、リポジトリを移設することも考えられるかもしれません。そういう時に古いリポジトリを参照しているといつまでも最新バージョンに置き換えられないことになるので、強制的にアップデートさせるのにいいかもしれません。
ただし、go get -uとかして、最新のコードが取得されていないとダメですが…。

Goの公式パッケージのリポジトリがGoogle Codeからgolang.orgに変更されます。
来年の6月1日から正式移行のようです。

go generateコマンド(実際には、コメントに書かれたコマンドを起動するだけのもの)が追加されました。最初は「ライブラリ作者には便利」とだけ認識していましたが、ビルド時に実行したいコマンドを書いておくことで、任意のコマンドを実行する事ができます。(大体はソースコードの自動生成で利用されることと思いますが…)

以前は、windows.goとか、amd64.goというファイル名でwindows専用だったり、64bit専用のソースコードとしてコンパイルされていましたが、これからはそれができなくなります。

あとは、テストコードを書く時に、これまでだと、全てのテストにsetupの処理と、teardownの処理を書かなくてはいけませんでしたが、Go1.4でTestMain()が追加になったので、全てのテストに共通した準備と後処理を書く事が可能になりました。Go本体だと、テスト後にメモリリークが発生しているかどうかのチェックを行うという使い方がされていました。

次回(これからですが)は、Goで大量データを処理するコードを書いて実行する。
ということをテーマに進めて行く予定です。

2014年11月9日日曜日

Golang Cafe #53を開催しました。

Golang Cafe #53を開催しました。

今回は前回に引き続き、isucon4の予選問題に挑戦しました。Golang Cafeということで、GoのWebアプリケーションを置き換えたりしながら速度アップを図っていきました。

最初の状態のスコアは以下のようになりました。

$ ./benchmarker b
18:12:29 type:info message:!!! DEBUG MODE !!! DEBUGE MODE !!!
18:12:29 type:info message:launch benchmarker
18:12:29 type:warning message:Result not sent to server because API key is not set
18:12:29 type:info message:init environment
18:12:34 type:info message:run benchmark workload: 1
18:13:34 type:info message:finish benchmark workload: 1
18:13:39 type:info message:check banned ips and locked users report
18:14:04 type:report count:banned ips value:4
18:14:04 type:report count:locked users value:2568
18:14:04 type:info message:Result not sent to server because API key is not set
18:14:04 type:score success:6620 fail:0 score:1430

successがリクエストをして正しい結果が返ってきた回数で、failが間違った結果が返ってきた回数です。実際には最後のScoreの値で競っていくようです。

このisuconのアプリケーションはmartiniが使われていたので、gorillaに書き換えてみました。

$ ./benchmarker b
22:40:23 type:info message:!!! DEBUG MODE !!! DEBUGE MODE !!!
22:40:23 type:info message:launch benchmarker
22:40:23 type:warning message:Result not sent to server because API key is not set
22:40:23 type:info message:init environment
22:40:29 type:info message:run benchmark workload: 1
22:41:29 type:info message:finish benchmark workload: 1
22:41:34 type:info message:check banned ips and locked users report
22:41:59 type:report count:banned ips value:4
22:41:59 type:report count:locked users value:2569
22:41:59 type:info message:Result not sent to server because API key is not set
22:41:59 type:score success:6760 fail:0 score:1461

Golang Cafeの最中では、エラーが取れなかったのですが、その後すぐに原因が分かったのでエラーを修正した結果です。Scoreが31上がりました。やっぱりreflectionを使うとスピードがどうしても遅くなるようです。

次に、Golang Cafe #50の時に教えてもらった、gojiに書き換えてみました。

$ ./benchmarker b
23:18:46 type:info message:!!! DEBUG MODE !!! DEBUGE MODE !!!
23:18:46 type:info message:launch benchmarker
23:18:46 type:warning message:Result not sent to server because API key is not set
23:18:46 type:info message:init environment
23:18:53 type:info message:run benchmark workload: 1
23:19:53 type:info message:finish benchmark workload: 1
23:19:58 type:info message:check banned ips and locked users report
23:20:23 type:report count:banned ips value:6
23:20:23 type:report count:locked users value:2567
23:20:23 type:info message:Result not sent to server because API key is not set
23:20:23 type:score success:6920 fail:0 score:1495

更に、スコアが34上がりました。gojiは軽量フレームワークなので処理スピードが早いようです。

ここまでのソースコードはgithubにpushしてありますので興味があればどうぞ。
ソースコードをそれぞれで修正したかったので、branchを分けています。
masterは何もしていない状態なので修正後のものが見たい時は、branchを切り替えて下さい。

Goのフレームワークを変えるだけでもかなり差が出ることがわかりましたが、Sessionの保存にgorilla/sessionを使っているので、データがCookieに全て保存されて通信されてしまっているのでその辺りも修正すればもう少しスピードアップが見込めそうな気がしています。(ローカルでのテストなので通信コストは低いけど…)

次回は、リリース間近のGo1.4のRelease Noteを読み進める予定です。

2014年11月2日日曜日

Golang Cafe #52を開催しました。

Golang Cafe #52を開催しました。

今回は、isuconという「与えられたサーバとそこで動作するWebアプリケーションを高速化してアクセス数を競うイベント」の予選問題でGolangが使われたという情報を聞いたので、実際に挑戦してみようという主旨で開催しました。

本来はAMIというAWSで動作するイメージを使ってAWSにデプロイして競うようなのですが、「課金前提」ということで、GCEに…というのもやめまして、ローカルにデプロイしようと頑張ってみました。

最初、「Dockerを使おう」ということで、

$ docker pull mysql

としたのですが、これが大失敗。関連する全てのコンテナをダウンロードし始めて、1時間待ってもダウンロードが終了しなかったので(Wimax回線の調子が悪かった…?)ローカルのhomebrewを使ったインストールに切り替えました。

$ brew update
$ brew install mysql

これで、mysqlがインストールできたので、早速ログインしてバージョンを確認します。

$ mysql.server start

mysql> select version();
+-----------+
| version() |
+-----------+
| 5.6.20    |
+-----------+
1 row in set (0.03 sec)

インストールが完了したので、次にinit.shを動かしました。
すると、"MySQL server has gone away"というメモリ不足で発生するエラーに悩まされました。
drupalのblogに設定の記載例があったのでそれを元に、my.cnfを修正しました。
ただし、table_cacheという設定はないようで、そのままコピペしても改善しませんでした。実際に編集する時はtable_cacheの行は書かないようにしましょう。

[mysqld]

# Remove leading # and set to the amount of RAM for the most important data
# cache in MySQL. Start at 70% of total RAM for dedicated server, else 10%.
# innodb_buffer_pool_size = 128M

# Remove leading # to turn on a very important data integrity option: logging
# changes to the binary log between backups.
# log_bin

# These are commonly set, remove the # and set as required.
# basedir = .....
# datadir = .....
# port = .....
# server_id = .....
# socket = .....
skip-external-locking
key_buffer = 384M
max_allowed_packet = 64M
sort_buffer_size = 2M
read_buffer_size = 2M
read_rnd_buffer_size = 64M
myisam_sort_buffer_size = 64M
thread_cache_size = 8
query_cache_size = 32M
max_allowed_packet = 32M
# Remove leading # to set options mainly useful for reporting servers.
# The server defaults are faster for transactions and fast SELECTs.
# Adjust sizes as needed, experiment to find the optimal values.
# join_buffer_size = 128M
# sort_buffer_size = 2M
# read_rnd_buffer_size = 2M 

sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES 

これでinit.shを実行した所、今度は、

ということになったので、いろいろ試行錯誤した結果、+0900という記載がダメなようなので
$ sed -e s/\+0900//g dummy_log2.sql

として、+0900の記述を消してから登録しました。homebrewでインストールした時のtimezoneがJSTになっていたので、これをUTCに変えてやればそんなことをしなくても良さそうな気がしてきました。が、本来の主旨と違う気がしたのでそこまで検証していません。

次に、Webサーバの起動ですが、Goの場合は、アプリケーションにhttpサーバの機能があるので、go buildして、起動するだけです。
$ cd webapp/go
$ ./build.sh
$ ./golang-webapp

これで、http://localhost:8080に接続すると「いすこん銀行」が表示されるはずです。

次に、ベンチマーク用コマンドもGoで書かれていますので、go buildすればいいのですが、makefileが用意されているので、それに従ってコンパイルします。
中身を見ると、標準でAWSにリクエストを飛ばすようになっているので、
$ cd benchmarker
$ make debug

として、コンパイルします。すると、localhostに対するリクエストになります。
ですが、そのままだと、http://localhostへのリクエストになり、goのwebappにリクエストが送信されないので、main.goの81行目付近を書き換えます。

    cli.StringFlag{
     Name:   "host",
     Value:  "localhost:8080",
     Usage:  "Bench Endpoint host",
     EnvVar: "ISUCON4_BENCH_HOST",
    },

これでビルドしなおせばローカルホストに対するリクエストが送信されるようになり、スコアも表示されるようになります。

ということで、今回はGoに関する内容ができなかったので、次回(今日、これから)に持ち越しとなります。


2014年10月26日日曜日

Golang Cafe #51を開催しました。

Golang Cafe #51を開催しました。今回は結城浩さんの書籍「増補改訂版 Java言語で学ぶデザインパターン入門 マルチスレッド編」のThread-Per-MessageパターンをGoで書くとどうなるか?というお題に挑戦しました。

Thread-Per-Messageパターンがどういうものかというと、「これやっといてね」とメインスレッドが要求を出します。この要求ごとにスレッドが生成され、ワーカースレッドがそれぞれを処理して終了するというものです。

今回はワーカスレッドの代わりに、Goroutineを生成して、終了をChannelで待つという、以前と余り変わらないコードになりました。(Javaの場合は、ワーカースレッドが動いているとプロセスが終了しないが、Goの場合はmain()が終了するとプロセスが終了してしまうので、終了待ちをする必要がある)
作成したのは以下のコードです。結局、Javaを力づくで翻訳した以前のコードとあまりかわらない結果になってしまいました。

問題点は、「Goroutineの終了」をどうやって待つか。と言うところが、今回のポイントになりました。実際のところは、Channelを戻り値で受け取り、for文で全てを受信待ちする(今回のパターン)か、selectを使った待ち方をすることになると思いますが、今回のように全てが終わったらその時点で終了。なら、for文で良いのかな?と考えてたりします。

ちゃんとした(正規のサービスで使う)プログラムの場合は、タイムアウトとか、close()させることもあると思いますからselect文を使うパターンの方が多いのかもしれません。

その他に、私も初めて気がついたのですが、syncパッケージにtype Poolというのが存在していました。そこで少し試しにコードを書いてみました。
sync.Poolは、複数のGoroutineからアクセスされても安全に取得できるように作られていて、GCの影響が少なくなるようになっているようです。
(確かにソースコードを見ると、unsafeが使われているようなので、GCの影響が少なくなるのかも?)

実際にGet()してみたところ、なんとなくですが、順番の保証はされていないような感じがします。(runtime.GOMAXPROCS()を呼びださなくても、順番が正しく動いていない気もする…)

ですが、順番が気にならないのであれば、sync.Poolを使うほうが排他制御を考えなくても良い分使いやすいかもしれません。(グローバル変数を同期して…。的なコードを書かなくても良くなる。ただし、それが悪いとは言っていませんのでご注意を。)

次回、(今日)はisuconの過去問題に挑戦してみます。私はAmazonのクラウド環境を使ったことがないので、そこからかな…。

2014年10月22日水曜日

「Javaプログラマーなら習得しておきたい Java SE 8 実践プログラミング」を読みました。

Blogを書くのが遅くなってしまいましたが、「Javaプログラマーなら習得しておきたい Java SE 8 実践プログラミング」(http://www.amazon.co.jp/dp/4844336673)を読みました。

目次は以下の構成です。

  • 第1章 ラムダ式とは
  • 第2章 ストリームAPIの使い方
  • 第3章 ラムダ式を使ったプログラミング
  • 第4章 JavaFXによるGUIプログラミング
  • 第5章 日付と時刻の新たなAPI
  • 第6章 並行処理の機能強化
  • 第7章 Nashorn JavaScriptエンジンの活用
  • 第8章 その他のJava 8機能を理解する
  • 第9章 Java 7の機能を復習する
私はJava 5が出たあたりで、Javaをほとんど書かなくなってから、AndroidのプログラミングでJava 6のコードを書いているという状態でした。従って、Java 7の内容とか、Java 8の事は殆どわかりません。(以前、Java 8のハンズオンイベントに参加したので、Stream APIとラムダ式は教えて頂いたことがある。)

実は、今回縁あってImpress Japanさんから献本を頂きまして、本書を読むことができました。ありがとうございました。書籍を読む時間が取りづらいのと、貸してほしいとお願いされたこともあって、私は5章まで読んで、以降は流し読みになっていますが、私の印象を書いておきたいと思います。

読む前の印象としては、柴田さんの翻訳本の文章は「日本人には読みづらい」という印象がありました。Goフレーズブックとかが、わりと頭が痛くなる文章だった気がします。
ですが、本書に関しては、わかりやすい文章になっていると思います。

しかし、コード例や、説明がJava 6、Java 7の書き方をしっかり理解していないと、内容を理解するのが難しいと感じました。従って、Java 7の内容がわかっていないのであれば、一度9章を読んでからの方が良いのかもしれません。
(初心者の方は、恐らく途中で挫折してしまうと思いますので、最初の書籍に選択するのはおすすめしません)

ラムダ式の所は、型の記述を省略できるようになっているのですが、引数の型が見てわかるようであれば良いのですが、書籍の例のラムダ式のコードを見てもすぐに型が分からないこともあったので、ある程度、Javaのクラスの事が分かっている方がいいかもしれません。(もしくは、ドキュメントを見ながら読み進める必要があるかもしれません)

本書には(私はまだ取り組んでいませんが)練習問題が用意されていて、練習問題に取り組むことで、理解を深めることができるようになっています。ですから、よくわからなかった所は、練習問題でもう一度、内容を整理して理解するということが可能になっています。

書籍は260ページ程度で、比較的薄い本ですから読み終わるまでに時間はかからないと思います。

今のところ、Java 8の内容全体を見渡せる書籍は本書のみだと思いますので、全体を見渡したい方はお手に取って見てください。

2014年10月13日月曜日

Golang Cafe #50を開催しました。

Golang Cafe #50を開催しました。今回は、開催地を東京のGoogleオフィスに設定して開催をしました。

今回はGolang Cafe #50での内容ではなく、準備段階などの話に重点をおこうと思います。
Golang Cafe #50を開催するにあたって、Golang Cafe #30の開催時から、+Yoshifumi YAMAGUCHIさんと連絡を取って、「Golang Cafeの開催が50回になったら、オフィスを貸して欲しい+α」とお願いをしました。
すると、簡単にOKが出たので、+Ryuji Iwataさんと、+Takanobu Haginoさんと私の3人でGoogleのオフィスでGolang Cafeをしようという計画が開始されました。

この段階で、他にもGolang Cafeに参加して頂いた方はいらっしゃいましたが、この時点で30回も続くと思っていなかったので、少なくとも、25回以上は参加しているであろう、2人に絞りました。(この時点で他に10回を超える方はいなかった)

ということで、そこから開催を重ねていったわけですが、このGolang Cafe #50のテーマとして、「私のネタに付き合って頂いた方へのお返し」というのを(勝手に、誰にも言わず)掲げて、可能な限り要望に答えられるように進めていったつもりです。2人には今後のGDG中国のGoのコンテンツを支えていただけるようなので(笑)非常に感謝しております。

次に、東京での開催ということで、正直最初の計画段階ではネタで募集して3人で適当にオフィスでやろうか。と話をしていたと思うのですが、だんだんと希望は膨らんでしまって、「東京のGopherと交流を図ろう」ということになりました。
しかし、「規模が大きいものはGoConがある」ので、規模は大きくしない。というもので。
ただ、普段通り募集をかけると、恐らく一瞬で満員御礼+多数のキャンセルによる悲劇が待っているだろうというのが、簡単に想像できたので、Golang Cafe #50に関しては、「非公開+技術者のつながり」で募集をかけようということになりました。
また、西日本のGopherは、今回は私がお誘いするつもりがありませんでした。(理由は交通費の問題と、西日本在住であればすぐに会えるので東京に来ていただく必要がなかった。敢えてお呼びしませんでしたが、悪意はありません。)が、+Yasutaka Kawamotoさんだけは、特別に、+Ryuji Iwataさんの強い要望があったので、(Iwataさんは、なぜか自分から呼ばなかった)私からお誘いしました。
昔、少しだけやっていた、Golang Office HourというHangoutのイベントのことなど、覚えて頂いたようで、ちょっとうれしかったです。+Takuya UedaさんもGolang Office Hourのことを覚えていて、「戦友」だと思いました。
東京のGo言語を使っている方と直接連絡が取れる方というのが非常に少なかったので、こればかりは、+Yoshifumi YAMAGUCHIさんに頼ろうということで、告知等をお願いしました。
そこで、よく考えると、東京にはGDG中国のスタッフ+Shingo Ishimuraさんがいるということに気がついたので、彼を巻き込み、総勢18名の参加者になりました。

後は、直前に風邪をひいたりして、活動がしっかりできませんでしたが、なんとか本番にこぎつけた。という流れです。
本当に、東京でのサポートをして頂いた +Yoshifumi YAMAGUCHIさん、+Shingo Ishimuraさんには助けられました。ありがとうございました。

そして、無茶ぶりにもかかわらず参加していただいた、参加者のみなさんもありがとうございました。いい勉強になりました。楽しかった!

本番のことにも少し触れると、「Golang Cafeの主催者はTakashi Yokoyamaさんです!」と言いながら、自分が主催者であるかのように、突撃してGolang Cafe #50の時に、「Golang Cafeとは」という説明をし始めたり、Rob Pike氏に人の名前を入れてメールを送り、「何かメッセージをくれ」と言って、メッセージを頂いたりと暴れまくってくれた+Ryuji Iwataさんは非常に楽しそうでした。(普段は、私がGoDEとして持ち上げているのでプラスマイナスゼロですかね)セッションを2つもやった挙句に、本番のルール説明は私に投げるという、新しい勉強会のスタイルを教えてくれました(笑)

最終的に2人には満足して頂いたかどうかはわかりませんが、非常に楽しい1日でした。
100%の要望に答えることは不可能だと「再」認識したので、お2人の反応に期待することにします。

Golang Cafeという「毎週みっちり」というのを1年間続けてみた結果ですが、3人はGolangのことは大体わかるようになったのかな?と感じています。ですが、40回台には、テーマ設定が難しい時もありました。(大体見ても、新発見することが少なくなってしまう)それは、成長したからなのでしょうが、連続の開催回数で理想的なのは30回ぐらいまでなのかな?と個人的に思っています。

それから、最初は「個人的にコードを書く時間」という設定で開催をした(zusaarの募集はネタ)のですが、結局それが許される環境には、現時点ではなかったようで、こんなに大きなものになってしまったというのは驚きがあったのと、誤算だったのとでいろいろ発見がありました。それで、Go言語というものが広まるようになるのなら、「まあいいや」ということかなとも思います。

「Golang Cafeの今後」についても少しずつ考えていますので、また次回の#51で話をしようかと思っています。

次回のGolang Cafe #51ですが、台風19号の影響により危険だと判断しましたので、来週(10/19)に順延することにしました。zusaarは満員御礼なのですが、キャンセルをしっかり行っておいて下さい。

2014年9月27日土曜日

Golang Cafe #48を開催しました。

Golang Cafe #48を開催しました。

今回は、Gunosy.go#10の発表資料が公開されましたので、資料を読み進めました。

読んだ資料は@y_matsuwitterさんの「Go言語におけるテストの基本」と@daimatzさんの「Dependency Injectionからモックライブラリまで」の2つです。

テストの基本についての大体の内容は、Golang Cafeでも触ってきたものだったので、参加者全員が知っているものでした。特徴として、テストコードに与える引数と、期待する戻り値を書いておいて比較するというサンプルコードが見受けられましたが、標準パッケージでも(特にmathあたり)そのような構成になっています。

ここで、話が出たのは、「どこまでテストをするか?」という話。これまでも何度も出てきていることなのですが、その辺を議論し始めるとGoの話ではなく、開発手法の話になってしまうので難しいところです。
それと、「テストを書きながら作る」か「開発した後で作る」かという話もありました。
それ以外にも「TDDする」というようなことも。
後でテストコードを見ることで、仕様や、プログラマの考えていたことがわかるようなテストを書かなくてはいけないということも話しました。

個人的には、「後でデグレを起こさないようにする」ということに主軸を置いているので、境界値のテストを主にやっておけば良いのかな?と考えていたりします。
全てのコードを通すようなテストを書いておくのは良いと思うのですが、
例えば、fmt.Printf()が失敗した後のケース(昔、そういう仕事をしたもので…)のテストが要るか?と言われると、その「PCが壊れてしまっている」状況で何ができるか?を考えたら、そんなに重要とは思えないので、panic()が書かれている事をコードレビューしておけば良いと思われます。

(こういうのを書いてしまうと、炎上すると困るのでこの程度に…)

Dependensy Injectionの方に入ると、やっぱり「Generics欲しい」という話がありました。私はそこまでGenericsを使いこなしていないので、便利さに気がつかないのですが、Javaや、C#をやっている人にはないと辛いようです。

資料の中身としては、通信が必要だったりするアプリケーションなどにはモックを作って擬似的なテストを行わなければいけない時に遭遇する。それをするためにはDIを使って、テストの時と、本番の時で挙動を変えられるようにしておくと便利ということ。
それをGoでやるにはどうするか。ということで、メソッド引数、オブジェクトを渡す、インターフェイスを引数で。という例が紹介されたようです。

私ももう少しこういう話題について勉強しておかないといけないのかな。と思った回でした。

次回は昔やった、結城浩さんの「Java言語によるデザインパターン入門(マルチスレッド編)」のGoに置き換えるというのを再開する予定です。

個人的には開発したいものがあるんだけど今のGolang Cafeでやるのは難しそう…かな。

2014年9月21日日曜日

Golang Cafe #47を開催しました。

Golang Cafe #47を開催しました。前回はかなり脱線してしまったので、blogは省略しました。今回は、GoでWebsocketをさわってみるという内容で、go.netのwebsocketを使ってみました。

go.net/websocketのインストールは、以下のコマンドで行います。

$ go get code.google.com/p/go.net/websocket

サンプルコードはgithubにpushしていますので、そちらをごらん下さい。
使い方は非常に簡単で、単純な折り返しのWebsocketならすぐに作れるようになっています。

package main

import (
 "io"
 "net/http"

 "code.google.com/p/go.net/websocket"
)

func echoHandler(ws *websocket.Conn) {
 io.Copy(ws, ws)
}

func main() {
 http.Handle("/echo", websocket.Handler(echoHandler))
 http.Handle("/", http.FileServer(http.Dir("./gdgchugoku_html5_business/html/011")))
 err := http.ListenAndServe(":8080", nil)
 if err != nil {
  panic("ListenAndServe: " + err.Error())
 }
}

githubにも同じものが上がっていますが、転載しました。
Websocketのハンドラ登録は、標準パッケージのhttp.Handle()を使って登録します。
指定するHandlerのオブジェクトでwebsocketパッケージの型を指定するようになっています。

折り返しするだけであれば、io.Copy()で引数のWebsocketのオブジェクトにコピーするだけでいいようになっています。
送信用と受信用の引数があるかと思ったのですが、Websocketの場合はまとまっているようです。

当日、見つけたポイントとしては、Websocketのオブジェクトをずっと使いたい場合は、Handlerの関数を抜けてはいけないということでした。関数を抜けるとsocketがcloseされるので、for {}で無限ループさせる必要があります。

ローカルでのテストしか行っていませんが、レスポンスも早く、サーバ側からの通知などにも利用できるのでWebsocket自体の仕様が固まって、何かに使えればいいなと思いました。

次回は、gunosy.goの資料が公開されているようなので、その資料を読みます。

2014年9月7日日曜日

Golang Cafe #45を開催しました。

Golang Cafe #45を開催しました。

今回も、3人での開催になりました。

内容は、急にqiitaに投稿された記事が気になったので、3人で読み進めてみました。
タイトルが「Goでxxxのポインタを取っているプログラムはだいたい全部間違っている」という、結構攻めた感じのタイトルだったので、食いついてしまいました。

内容としては、ポインタを使うのは良いが「自分が何をしているのか理解していない」のであれば、ポインタを使う意味がないということでした。
少し補足をすると、文字列、interface、channel、Map、sliceは「元からポインタ」なので、ポインタを使うと、「ポインタのポインタ」になってしまって、「直接参照すればいいのに何してるの?」ということになってしまう。ということです。

例えば、Mapだと、 +Takanobu Haginoさんのサンプルコードで確認すると、Mapは引数にポインタを渡さなくても、呼び出し先でMapに値を追加することができ、当然、呼び出し元でも追加されたものは反映されます。(=元からポインタということ)

fmt.Println()で書きだしてみると、channelもアドレスが出力されるのでやっぱりポインタだということがわかります。

従って、ポインタを使う時というのがどういう意図があるのかを明確にして使いましょう。ということでした。

次に、@lestrratさんの資料を読みました。
ここで議論になったのが、「intのバイト数が変わる」ということについて。

昔は、intという型はなく、int8、int16、int32、int64とビット数をつけた型だけがありました。(当然、byteもない。昔はint8と宣言していたけど、いつの間にかbyteを使うようになってしまった。これは大きさが変わらないので大丈夫。)

実際、intは”same size as uint"ということで、(個人的にはint32のaliasだと思っていたけど)uintと同じサイズの整数型という定義になります。
そこで、uintのサイズは何かというと、32bit or 64bitなので、goのコンパイラのビルド時に決まる。ということでしょうね。
(C言語のintと変わらず、どちらになるかわからないということ)

したがって、intのサイズが変わることでハマるよ。ということですから、明確にサイズが影響するなら、int32やint64の変数の型を使うようにするべきだろう。という結論になりました。(と私は理解している)

最後に、 +Takanobu Haginoさんのtextream解析ツールのソースコードを読みました。
非常にシンプルだったので、特に何か言わなければならないというものはなかったように思います。
構成としては、バックエンドでサイトの情報を取得して、フロントはgorillaを使ったWebサーバという構成で作られています。
いろんな便利なライブラリも使われていて、「実戦向き」のプログラムで非常に楽しく読ませていただきました。

最近、qiitaにGoogle Apps Scriptの記事を2個書いてましたが、Goに興味がないわけではなく、必要になった要求を簡単に満たせるのがGASだったということで、要求を満たしやすい言語を選ぶのがいいんだろうと思ったりしています。

次回(後2時間後)は、また最近話題になった記事とかソースコードレビューとか、何か作る時間にしたいと思います。

2014年8月30日土曜日

Golang Cafe #44を開催しました。

Golang Cafe #44を開催しました。

今回は東京で行われたGolang勉強会の資料のうち、 +Takuya Uedaさんの「インターフェイスの実装パターン」を読み進めました。

主な内容としては

  1. 独自の型定義(typeキーワードの使い方)
  2. インターフェイス型変数への代入とキャストの方法
  3. Goのインターフェイスはメソッドが全て実装されていれば、インターフェイスとして認識される。
  4. 関数型を定義してメソッドを実装すると、関数にインターフェイスを実装することができる。
  5. 構造体に別の型の埋め込みについて
  6. 埋め込みによるインターフェイスの実装
ポイントとして、私も以前から思っていたことなのですが、
何でもstructで実装するのはGo言語っぽくない。
ということで、typeとメソッドの理解をしっかりしましょうということでした。他にも埋め込みのところで、同じメソッド名でも埋め込むと型名を省略する事ができるなど(参考:http://play.golang.org/p/XRFBWEtqpu)Goの仕様を確認しました。

現時点の個人的なプロジェクトのみで話をすると、埋め込みやインターフェイスを活用するケースはほとんどないのですが、技術は知っておいていいと思うのですが、できるかぎり「仕様をシンプルに」できるようにするのがいいんだろうなー。と思っています。(それができれば苦労はしないのでしょうが…。)

埋め込みも便利ですが、DBに格納するとか、jsonに変換する時に複雑にな(≒ハマ)りがちな気がするので埋め込みもほどほどに…(?)


2014年8月23日土曜日

Golang Cafe #43を開催しました。

Golang Cafe #43を開催しました。

今回は久しぶりに、+Takanobu Haginoさんと+Ryuji Iwataさんと私の3人だったので、
今後のGolang Cafeで扱ってみる内容を話してみました。さすがに43回もみっちりやってくると、昔のことは忘れてくるとはいえ、経験値が上がってきて新しい発見も少なくなってきます。

Golang Cafe #24とか、Golang Cafe #25のblogあたりで触れた「熟練度による需要の差」も気になっているままではありますが、だんだんとやることがなくなってきました。(という気がする。が、無いわけではない。)

次回は資料を読むことにしていますから、その次ぐらいから「実際に作る」ことをメインの活動にしようと思います。都度、何か資料が出てきたらそれを読む回にしようかなと。
(もとからそのつもりでやって来たので、43回の遅れがあっただけということで。)

その作るという作業の中で、個人的に作りたいツールとか、APIを実行したりということがあるので、Goで(主にGAE/Goで)やってみたいと思ってます。

Managed VMsのPreviewにも申し込んだし、基本的にはGoというよりは、GCP関連の作業がしたいだけなんですが、言語はGoでやってきたので、それを実践するという感じで。

Goに関するアピールとしては、40回以上やってきたので、だいぶ知れ渡ったかな?と思っていますが、どうなんでしょうね。

それから、新たな人が参加する事が増えてきて、人数も気にしないといけないと考えたりしましたが、そんなに満員御礼が長続きすることもなく、規模は大きくするつもりがないので、もしも満員になったら補欠で登録しておいてください。

あまりまとまっていませんが、計画もまとまっていないのでこんな感じで。

2014年8月9日土曜日

Golang Cafe #41を開催しました。

Golang Cafe #41を開催しました。今回は、Goで作られたORマッパーのgorpを触ってみました。
今回の内容を進めるにあたっては、@mattn_jpさんのblogを参考にしました。
blogの記事自体は古めのものですが、最新バージョンでも安心して利用できますので、
読み進めて見てください。
ただし、blog記事では、SQLiteを使ったサンプルになっているため、PostgreSQLで使うためには、

gorp.SQLiteDialectをgorp.PostgresDialectに読み替えて進める必要があります。
読み替えるだけで、プログラムの動作の結果は変わりませんので、インストールしてあるデータベースを利用して使えばいいでしょう。
ただし、利用するデータベースとDialectが一致しないと全く動作しませんのでご注意下さい。

サンプルコードはgithubにpushしてありますのでそちらをごらんください。

個人的な感想としては、Golang Cafe #4の時に標準ライブラリでデータベースを利用する時に比べると段違いに便利になります。(ORマッパーを普段から使っていらっしゃる方の場合は、大体分かっていると思いますが)
私は、ORマッパーを使ってみたのはほぼ初めてに近かったので、非常に感動しました。

ちなみに、機能としては、テーブルの作成、検索/更新は動作確認をしました。本家のサンプルを見る限りだとテーブルの削除もできるようですし、構造体のフィールドタグも利用できるようになっています。(`db:"project_id`など)

今回は、初めての参加者がいらっしゃったのですが、今回も+Ryuji Iwataさんにお願いして最初のセットアップのお世話をしていただきました。ありがとうございました。
また、Windowsユーザだったので、+Takanobu Haginoさんのノウハウを参考に進めたのですが、最初のセットアップのノウハウを本家サイトに載せておいた方がいいんじゃないの?という話題がでてきました。これまでは、Golangのセットアップ自体は適当に参加者がやってくるというスタイルだったので適当だったのですが、前回は初心者の方を置き去りにする形になってしまったので、ちょっと考えないといけないと思った瞬間でした。
(ちなみに、座席の関係で+Ryuji Iwataさんにお願いしましたが、私の隣だったら私が対応するつもりだったという前提でお願いします。)

次回はこれまで読んでいなかったEffective Goを読み進める予定です。

が、台風が近づいていてかなり危ないと予想されるので初めての中止となるかも…?

2014年8月2日土曜日

Golang Cafe #40を開催しました。

Golang Cafe #40を開催しました。

今回は、Gorilla Toolkitの残りの4つ(reverse、rpc、schema、securecookie)を使う回でしたが、schemaとsecurecookieはドキュメントを読むだけで終わっています。

reverseですが、元になるURLの文字列をコンパイルしておいて、部分文字列を指定するとURLの文字列が生成されるというパッケージです。用途として、URLの一部の文字列を渡して復元してリクエストを送信するというケースで使えそう(クライアント側の処理を作る時など)ですが、いまいちそこまでの需要があるのかどうかは疑問が残る結果となりました。

package main

import (
 "log"
 "net/url"

 "github.com/gorilla/reverse"
)

func main() {
 var err error

 regexp, err := reverse.CompileRegexp(`/foo/1(\d+)3`)
 if err != nil {
  log.Fatalln(err)
 }

 // url.Valuesの値からURLを生成。
 url, err := regexp.Revert(url.Values{"":{"45678"}})
 if err != nil {
  log.Fatalln(err)
 }

 log.Println(url)
}

reverse.CompileRegexp()で正規表現を含むURL文字列のひな形を作成して、
reverse.Revert()で復元した文字列を返しています。最初は他のパッケージで使っているものなのかと思いましたが、どうやら使われている様子はなさそうです。

次に、rpcパッケージですが、これは、rpcサーバを作る時に利用します。
サーバ側の実装はそこまで難しくなく、Codecの登録と、RPCのリクエストに対して呼び出されるServiceのTypeのインスタンスを登録しておくだけです。

package main

import (
 "net/http"

 "github.com/gorilla/rpc"
 "github.com/gorilla/rpc/json"
)

func main() {
 s := rpc.NewServer()
    s.RegisterCodec(json.NewCodec(), "application/json")
    s.RegisterService(new(HelloService), "")
    http.Handle("/rpc", s)

 http.ListenAndServe(":8080", nil)
}

type HelloArgs struct {
    Who string
}

type HelloReply struct {
    Message string
}

type HelloService struct {}

func (h *HelloService) Say(r *http.Request, args *HelloArgs, reply *HelloReply) error {
    reply.Message = "Hello, " + args.Who + "!"
    return nil
}

サーバ側はそんなに難しくないのですが、動作を確認するためにクライアントを作成して通信させる所でハマりました。

Goにも標準のnet/rpcパッケージが存在するのでそれを利用するのが便利だと思うのですが、標準のパッケージだとgorilla/rpcと通信することはできません。
理由は、gorilla/rpcでの待受はPOSTのリクエストを待っているようなのですが、
標準のnet/rpcパッケージはどうやらGETのリクエストを送っているらしく、レスポンスが400とか、415が戻されてしまいます。したがって、自分でリクエストの処理を実装してやらないといけません。

package main

import (
 "io"
 "log"
 "net/http"
 "os"
 // "net/rpc"
 "strings"
)

type HelloArgs struct {
 Who string
}
func main() {
 // HTTP ClientによるRPC呼び出し。
 // jsonのパラメータを自分で作って上げる必要がある。
 client := http.Client{}
 res, err := client.Post("http://localhost:8080/rpc", "application/json", strings.NewReader(`{"method":"HelloService.Say","params":[{"Who":"Test"}], "id":"1"}`))
 if err != nil {
  log.Fatalln(err)
 }

 // 標準のDialHTTPPathはGETのリクエストが投げられるらしい。
 // したがって、標準のrpcパッケージは使えない(?)
 // client, err := rpc.DialHTTPPath("tcp", "localhost:8080", "/rpc")
 // if err != nil {
 //  log.Println("DialHTTP")
 //  log.Fatalln(err)
 // }

 // defer client.Close()

 // var reply int

 // args := HelloArgs{Who: "hoge"}
 // err = client.Call("HelloService.Say", args, &reply)
 // if err != nil {
 //  log.Println("Call")
 //  log.Fatalln(err)
 // }

 // log.Println(reply)
 io.Copy(os.Stdout, res.Body)
}

RPCのリクエストを送ったことがなかったので、どうすればいいのかわかりませんでしたが、curlの例が検索にヒットしたのでパラメータを設定して送り、無事レスポンスが得られました。でも、これはめんどくさい!
設定があるのか、コードが悪いのかは調べきる事はできなかったので、また今後調べていこうと思います。ただ、RPCサーバを実装する事があるかどうかはわかりません。

次回は、GoのO/Rマッパーであるgorpを使ってみる会になります。

2014年7月26日土曜日

Golang Cafe #39を開催しました。

Golang Cafe #39を開催しました。

今回も前回に引き続き、Gorilla Toolkitのパッケージを使ってみる会になりました。

使ったのは、gorilla/contextgorilla/sessionsの2つですが、どちらも単純な機能を提供しています。

gorilla/contextの方は、任意の値をcontextに保存しておくだけです。非常に機能が少ないのでサンプルコードの提示も難しいのですが、以下のように使うことができます。

package main

import (
 "fmt"
 "log"
 "net/http"
 "strings"

 "github.com/gorilla/mux"
 "github.com/gorilla/context"

)

func main() {
 r := mux.NewRouter()


 r.HandleFunc("/", IndexHandler)

 log.Fatal(http.ListenAndServe(":8080", r))

}

func IndexHandler(w http.ResponseWriter, r *http.Request) {
 for _, param := range strings.Split(r.URL.RawQuery, "&") {
  params := strings.Split(param, "=")
  context.Set(r, params[0], params[1])
 }

 fmt.Fprintf(w, "IndexHandler\n")
 fmt.Fprintf(w, "hoge = %s\n", context.Get(r, "hoge"))
 fmt.Fprintf(w, "fuga = %s\n", context.Get(r, "fuga"))
}

context.Set(http.Request, キー、値);でリクエストが生きている間はキーと値のペアによって保存されます。非常に単純ですが、パラメータを解析した後、contextの中に保存するなどすれば、便利に使えるのか?と考えていましたが、どうやら、gorillaの他のパッケージの内部で使われているようです。単体で使ったりすることがあるかどうかは、当日の話では出なかったと思います。

次に、gorilla/sessionsを使ってみました。
sessionsを使うポイントは、パッケージ変数にsessions.NewCookieStore()で新しく確保する変数を定義しておきます。
この定義した変数内にsessionの値が保存されることになります。

package main

import (
 "fmt"
 "log"
 "net/http"

 "github.com/gorilla/mux"
 "github.com/gorilla/sessions"

)

func main() {
 r := mux.NewRouter()


 r.HandleFunc("/", IndexHandler)
 r.HandleFunc("/session", SessionHandler)

 log.Fatal(http.ListenAndServe(":8080", r))

}

var store = sessions.NewCookieStore([]byte("something-very-secret"))

func IndexHandler(w http.ResponseWriter, r *http.Request) {
 session, _ := store.Get(r, "mysession")

 session.Values["hoge"] = "1"
 session.Values[2] = 2

 session.Save(r, w)

 fmt.Fprintf(w, "IndexHandler\n")
}

func SessionHandler(w http.ResponseWriter, r *http.Request) {
 session, _ := store.Get(r, "mysession")

 fmt.Println(session)

 hoge := session.Values["hoge"]
 val := session.Values[2]

 session.Values["hoge"] = "session"
 session.Values[2] = 5

 session.Save(r, w)

 fmt.Fprintf(w, "SessionHandler [%s] [%d]\n", hoge, val)

}

動きとしては、sessionの動きと同じだったのですが、
なぜか、

  • サーバを再起動してもsessionが残っている
  • Chromeを閉じてもSessionの値が残っている

という現象に遭遇して、原因を特定する作業に没頭しました。
シークレット・ウインドウを使った場合は、ブラウザを閉じるとsessionは無くなっていました。(Windowsの場合は、シークレット・ウインドウを閉じても残っているということでしたが、環境なのか、設定なのかは特定できていません)

実際、ChromeのDevToolでCookieの中身を確認すると、session IDだけが保存されているかと思っていたら、データがかなり多く、どうやら、Cookieの中に全てのデータが保存されているようです。確かに、GAEなどのように複数のインスタンスが起動するような時に、サーバ側のメモリにのみ保存しておくと、別のインスタンスにリクエストが送られてしまった時に困るのでこういう実装になっているのだろうと考えていました。

が、Cookieのデータは4KBまでという制約があるので、Sessionに何でもかんでも入れておけば良いという考えはやめておきましょう。

次回も、Gorilla Toolkitの残りのパッケージ(reverse、rpc、schema、securecookie)を読み進めます。

2014年7月18日金曜日

Golang Cafe #38を開催しました。

Golang Cafe #38を開催しました。今回からGoのフレームワークを触ってみるというテーマにするということにしました。最初はgorilla toolkitを使ってみようということで、今回は、gorilla/muxを使いました。

gorilla/muxは主にURLからのroutingをサポートします。標準パッケージ(net/http)と同じように使えるので、薄いラッパーのようにも見えますが、強力な機能を持っているようなので、実際に自分のアプリケーションに利用してみようと思っています。

gorillaの使い方ですが、まず、Handlerを登録しておきます。

 r := mux.NewRouter()

 // RouterにHandlerを登録する。
 // 正規表現に一致しないURLは全て404になる。
 // 末尾に"/"をつけると、付けないと404になる。
 // Host()を使うとアクセス制限ができる。
 r.HandleFunc("/", IndexHandler).Host("www.sample.com")
 r.HandleFunc("/articles/{category}/", ArticleHandler).Methods("POST")
 r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler)

 r.NotFoundHandler = http.HandlerFunc(NotFound)

 log.Fatal(http.ListenAndServe(":8080", r))

ここで、http.HandleFunc()を使うのではなく、mux.NewRouter()を呼び出した戻り値のRouter#HandleFunc()を呼び出すのがポイントになります。
muxパッケージを介することで、gorilla toolkitでHandlerを認識することができるようになります。

引数は、http.HandleFunc()と同じなので省略しますが、指定するパスは拡張されていて、正規表現を入れることもできますし、{}で囲んでキーを指定することで、プログラムから指定されたパスの値を取得する事ができるようになります。

コメントにもありますが、Handlerに登録されていないパスのリクエストは全て404が返されます。
(これについては、私は400を返すものと思っていたので404を返す方が良いというのを聞いて新しい発見でした。400だと、存在している事を教えてしまうから、攻撃対象になるということでした。)

最後に、mux.Routerをhttp.ListenAndServe()の引数に指定することで、Routingの設定は完了です。
(GAE/Goだと、http.Handle()の引数に指定すれば同じように反映させる事ができます)

Routing時に登録したキーを取得するためには、mux.Vars()を呼び出します。
戻り値には、map[string] stringが返ってくるので、文字として扱えばPath内にある、キーの値を取得する事ができます。

次に挑戦したのが、NotFoundを返す時にログを出力するというのができるかどうかを試してみました。NotFoundを返すのは良いのですが、アクセスログぐらいは残せた方が便利だという理由からです。

方法は、ソースコードのr.NotFoundHandlerのHandlerを上書きすることで可能になるようです。ただし、上書きしたHandlerの中で、NotFoundのレスポンスを返すように処理を記述しなければいけません。

次回は、gorilla toolkitのcontextとsessionsを見る予定です。

2014年7月12日土曜日

Golang Cafe #37を開催しました。

Golang Cafe #37を開催しました。

今回は、Gmail APIをGoで実行するというテーマで進めました。
OAuth2の認証が必要だったので、#18のサンプルコードを見ながら実行するというアプローチをとりました。

goauth2パッケージは結構便利に使えるので良いですね。

リクエストを送信する所は、以前と同じなので、省略するとして、受信後にデータを展開する所は、レスポンスがjson文字列なので、encoding/jsonを使って、Unmarshal()をすればオブジェクトに復元できます。

最初にUsers.messages.listにリクエストを投げました。これは、Gmailのメールのリストが返ってきますが、本文は含まれていません。

    // Users.messages.listにアクセス
    r, err := transport.Client().Get(request_url)
    if err != nil {
        fmt.Println("Get: ", err)
        return
    }

    defer r.Body.Close()

    buf := bytes.Buffer{}
    if _, err = buf.ReadFrom(r.Body); err != nil {
     log.Fatalln(err)
    }

    if err = json.Unmarshal(buf.Bytes(), &l); err != nil {
     fmt.Println(buf.String())
     log.Fatalln(err)
    }

goauth2のtransportから戻ってくるのはio.Readerなので、bytes.Bufferにでもコピーして書き出さないとUnmarshal()に渡す事ができないので、一つ処理を入れています。

jsonをオブジェクトに変換した後は、もう一度、Users.messages.getにリクエストを投げて、詳細を取得しました。

    // Users.messages.getにアクセス。(最初の1件目)
 r2, err := transport.Client().Get(message_get_url + l.Messages[0].Id)
 if err != nil {
        fmt.Println("Get: ", err)
        return
 }

    // // Write the response to standard output.
    // io.Copy(os.Stdout, r2.Body)

    buf2 := bytes.Buffer{}
    if _, err = buf2.ReadFrom(r2.Body); err != nil {
     log.Fatalln(err)
    }

    if err = json.Unmarshal(buf2.Bytes(), &t); err != nil {
     fmt.Println(buf2.String())
     log.Fatalln(err)
    }

これで、中身が取得できたのですが、本文がBase64エンコードでエンコードされた状態だったので、復元しないといけなかったのですが、復元には至らず。ここで終了。

それでも、後日時間を取って、粘ってみたら、Base64.Stdencodingを使っていたのを、Base64.URLencodingを使えば本文が読めるようになるというのを+Takanobu Haginoさんが見つけていたので、それを適用して本文が読めるようになりました。
    // 本文をエンコードする場合はURLEncodingを使う。
    data, encerr := base64.URLEncoding.DecodeString(string(t.Payload.Body.Data))
    if encerr != nil {
        // fmt.Println("base64", t.Payload.Body.Data[190])
        log.Fatalln(encerr)
    }

あと、q=is:unreadをつけてリクエストを送信したりしたものがありますが、今は時間がないので、あとで、githubに上げておこうと思います。


2014年7月5日土曜日

Golang Cafe #36を開催しました。

Golang Cafe #36を開催しました。

今回は、Google I/O 2014以降公開された動画を見る予定だったのですが、セッションが無い事に気がついていたのですが、いろんな動画が公開されていたので、1個ぐらいはGoの動画があるかと思ったのですが、(DevByteあたりのやつが…)残念ながら全くなかったので、仕方なく、Codelabに一つだけGo関連のものがあったので、Go:Build a Backend on Google App Engineを進めることにしました。

Codelabの内容はGAE/Goのチュートリアルだったので、#34の時にやった公式のチュートリアルと内容が被ってしまったのですが、このCodelabでは、gorillaフレームワークを使ったルーティングの実装があったので、その点が新しいものでした。

あとは、jsonを返すAPIを実装して、クライアント側はAngularJSを使ったAjaxでのやり取りを作り上げるという内容になっていました。

いろいろ説明しつつゆっくり進めたので、今回はStep03まで進めて終了となりました。

WindowsでのGAE/Gの開発はまだまだ苦しいのかなー。と思わされる事例も出てきて、ためになりました。

さて、今後暑い季節がやってきますが、もうすぐ40回目(と言っても、順調にいってまだ1ヶ月先)ですが、いろんな資料を読み進めるというのはだいぶやってきたので、
次回は、GoでGmail APIを実行する。ということになっていますが、以降の数回は、独断と偏見で、Goのいろんなフレームワークを使ってみるというのにしたいと思います。

個人的には、Effective Goをしっかり読んでみたりしたいと思ったりするのですが、
それよりも先に、ここ最近でいろんなフレームワークが開発されてリリースされているのでGopherの端くれとして、一度は触ってみないといけないだろうと思っています。

Pure Goも良いですが、そろそろ幅を広げてみるというのも必要かな…?と思ってみたり。

ということで、明日は、#37で、GoでGmail APIを実行する回です。
恐らく、参加者の皆さんはすでにダウンロード済みだと思いますが、goauth2をダウンロードしておいて下さい。コマンドは

go get code.google.com/p/goauth2/oauth

です。

google-go-api-clientも存在していますが、これだと多分まだ実行できないのではないかと思われるので、少し中身を見るかどうかはまた考えましょう。

2014年6月28日土曜日

Golang Cafe #35を開催しました。

Golang Cafe #35を開催しました。

今回は、GoCon 2014 Springの時に発表されたDave Cheneyさんの資料
読み進めました。

今回の資料は、「Goのプログラムを早くする5つのこと」ということで話されたようです。
最初、参加者の全員が思っていたと思うのですが、「Goのプログラムを早くするために”プログラマ”がすればいいこと」が書かれていると思っていました。

しかし、中身は「Goのコンパイラがコードを解析してコンパイルしてリンクする時にやっていること」が書かれているようでした。
しかも、結構深い内容で、読む方も疲れてしまいました。(私だけかもしれませんが)

5つのうち、2つ目までを読み進めたので、そのまとめを書いておきます。

1つ目は"value"
ポイントは、Goでは、例えばint32の変数だと、正確に4バイトが確保されます。
ですが、Pythonだと、24バイト。Javaだと、16バイト(32bit版)か32バイト(64bit版)が確保されます。同じ、4バイト変数なのに。

で、これが重要な理由はなにか?というと、CPUとメモリのバスの速度差がでてくるようになった背景があって、メモリの転送時間がCPUの処理時間に比べて長いので、CPUが転送待ちになってしまうので、処理速度が上がらないわけです。

そこで、キャッシュの機構をCPUとメモリの間にいれてやれば早くなるわけですが、キャッシュの領域もそこまで大きくない。

ということで、コンパクトな構造体を作り、余計な間接部分を避けるようにGoのコンパイラが良いようにしてくれている。
コンパクトなデータ構造はキャッシュをしっかり使える。
これにより、より良いパフォーマンスを発揮できるようになっている。

ということでした。

2つ目は"inlining"
ここでのポイントは関数呼び出しをインライン化すること。
デッドコードの除去。

1つ目の関数呼び出しのオーバヘッドは回避できないので、Goのコンパイラはinline化できるものは、勝手にinline化しているようです。これにより、関数呼び出しのオーバーヘッドをなくしています。ですが、inline化は、「コピペ」であるわけですから、バイナリサイズは大きくなってしまいます。速度を求めるために、バイナリサイズが大きくなる事に目をつぶっているわけですね。
(個人的には、バイナリサイズが大きくなるとは言え、1ファイルになるのはいいことだと思うけど…。こだわりがある人はまた違う見解があるんだろうか。)

そして、デッドコードの除去に関しては、if(false) { ... }となるようなケースだと、「絶対にif文の中を通らないわけですから、なくしても構わない。ということで、思い切ってコンパイル時には外しているようです。
したがって、Debug用コードを書く場合は、単純にDebugフラグをtrueにして、本番環境ではDebugフラグをfalseにした状態にしてビルドすればいいわけです。
(ただし、オプション引数によってtrue/falseを切り替えるケースでは、デッドコードにならないので、除去されません。)

今回は、結構深くて重たい資料だったと思うのですが、がんばって読んでみると良いものだと思います。が、これは業務とかに利用するのは難しい気が…。

次回は、Google I/O 2014が終わったので、いろんな動画やCodelabの資料が公開されてきているので、動画を見たり、Codelabの資料を追いかける予定です。
(ただし、Goのセッションは今年はなかった…)







2014年6月19日木曜日

Golang Cafe #34を開催しました。

Golang Cafe #34を開催しました。

今回はhtmlday 2014の一環として開催しましたので、GAE/Goのチュートリアルを進めてみました。

内容は

  1. 開発環境の構築
  2. Hello World
  3. ユーザ認証
  4. FormからのPOST
  5. データストアの使い方
  6. deploy
という流れで、私が以前執筆した書籍の頃からあまり変化はなく、GAE/Goに関してはGoの変化に対して大きく変わっていないというのがわかりました。

本当は、世界の+Ryuji IwataさんがGAE/Pythonを使われているようなので、解説をお願いしたかったのですが、どうしても遠慮したいということだったので、未熟者の私が少し解説をさせていただきました。

Hello Worldは、text/plainのデータを出力しますので、文字コードが化ける事があるようでした。ブラウザがShift-Jisで表示しようとしてしまう(?)ようなので、本番では、<meta charaset="UTF-8" />とか、httpヘッダに文字コードを設定するなどが必要になります。

ユーザ認証のためのappengine/userパッケージがGoogleの認証をそのまま利用できる優れもので、アプリケーションを作るときには非常に便利なのですが、実際使われているケースは少ないのかもしれません…。(独自認証とか、OAuthを使うとか…)

FormからのPOSTは単純なPOSTの処理と、リクエストパラメータを表示させるだけのものですが、html/templateを使って、htmlを出力させています。
html/templateはAngularJSと相性があまり良くない気がする…。({{と}}を使うので。)
ちゃんとこの時に、<!DOCTYPE html>をつけてhtml5感を出しておきました。

データストアの使い方に関してはGetとPutだけで、Goのノウハウとは違ってくるのですが、話題としては色々話したと思います。

最後のdeployまでは私はしませんでしたが、+Hideki Matsudaさんは実行されたのかな?と思いますが、コマンドとしては、appcfg.pyのフロント側なので、特に難しい所は無いと思います。(appcfg.pyもまだ使えます)

また、GAE/Goを扱ったりしてみたいと思っているのである程度資料を読んだら
戻ってくることにしようと思います。

次回は、GoConの資料の後で公開されたものを読み進める予定です。


2014年6月15日日曜日

Golang Cafe #33を開催しました。

Golang Cafe #33を開催しました。

今回はGo Conference 2014のKeynoteの資料のGo: 90% Perfect, 100% of the time.を読み進めました。この資料はKeynoteなので、「Goは良いよ」という事が書かれていました。他の言語と比べてのGoの評価もあっておもしろいのですが、ちょっと評価が高過ぎるかな?と思えましたが、Keynoteなのでその辺は少し大きく表現しているのかもしれませんね。

Camlistoreという会社でのGoの導入までの話がつらつらと書かれていましたが、以前はPerlと、C、たまにJavaという感じでアプリケーションを開発していたけど、Perlとかだと、Callback地獄になってしまうのでちょっと面倒という話がありました。
Goだと、ある程度軽減できるようです。

気になるキーワードとしては、
Framework:gorilla、martini、Revelなど
net/httpの標準ライブラリ
nginxが不要な標準httpサーバ
libpng、imagemagickが不要(Goにはimage/pngパッケージがあるので)
pure goでpng、jpeg、gifのエンコード、デコードができる。
標準ライブラリでの暗号化
シェルスクリプトの置き換え

ということで、Go良いよ。という話題を見て終わりました。

次回は、htmldayの一環として、GAE/Goをやりま「した。」
またblogを書きます…。



2014年6月3日火曜日

Golang Cafe #32を開催しました。

Golang Cafe #32を開催しました。

今回は、先日東京で行われたGo Conference 2014 Springの発表資料をみんなで読むという回になりました。

一番まとまってそうな+Yoshifumi YAMAGUCHIさんのBlogから資料を遡っていきました。

読み進めた順に話を進めていくと、

  1. 300万人をGoで捌いた話 // Speaker Deck
  2. Do not communicate by sharing memory - Google ドキュメント
  3. Go in Production at Mackerel.io // Speaker Deck
  4. Martini - Web framework for Go
  5. pt&Goroutines // Speaker Deck

300万人をGoで捌いた話の資料を見てみると、goroutinesとchannelを使って、通信させて、リクエストを受けたgoroutineはDBアクセスなどの重たい処理を極力させないようにしてレスポンスを返すという主旨の内容だったと感じます。
恐らくchannelについてもバッファ付きチャネルを使って、チャネルにデータが来るのを待ち続けるgoroutineとchannelにデータを送るgoroutineに分かれているのだろうという想像をしました。
スピーカーがどういう話をしたかはわからないのですが、話の方向性としては、重たい処理をgoroutineでバックグラウンドで処理をさせておくようにしようという話になりました。

Do not communicate by sharing memoryは、簡単にいうと、複数のgoroutinesからいわゆる「グローバル変数」へのアクセスはやめよう。という事が書かれていると認識しました。変数でアクセスするぐらいならchannelでデータのやり取りをしよう。という感じです。どうしても変数へのアクセスが必要なら、sync.Mutexとか、sync.WaitGroupを使った同期を考えないといけません。

Mackerel.ioは、Mackerel-agentがGoで書かれていて、agentについての話です。
資料の中にはtimeパッケージを使ったchannelの通信なんかも書かれていますので、参考にしてみて下さい。

Martiniの話は恐らくMartini自体の紹介だと思いますが、このFWは便利そうな気がします。Goの場合、現状ではrouting部分は必ず書かないといけないので、書き方が簡単になるならいいんじゃないのかな?と思ったりします。ここで、ORMについての話も出てきて、どれかの資料に書いてあった、gorpが便利というTweetとか、投稿を見た(気がする)ので、Martini+gorpでDBを使ったアプリケーションを開発すると良いのかもしれません。

最後のpt&Goroutinesは、「今日のまとめ」的な資料で、goroutineとchannelを使った並列化が焦点になっていると思います。Goでの並列処理を考える時には、Goroutineの切り替わりと、channelのバッファを考えないと高速化できないのですが、(channelのバッファが1だと、入ってくるまで取り出す側がブロックされる)バッファについても言及されているので良かったです。

今回は、ちょうど前回やった、「シダ植物」の並列化のお題と被ったお陰で、いろんな並列化についての議論ができて良かったと思います。ネックになる所と、Goroutineの切り替わりが発生する処理が全くない場合など、深いお題だったと皆さん感心されていました。さすが、世界の+Ryuji Iwataさん。

おまけですが、「シダ植物」の計算量もN=25あたりになると応答が無くなるぐらい計算量が上がるそうです。

次回は、GopherCon 2014の資料を読み進める予定です。

2014年6月1日日曜日

Golang Cafe #31を開催しました。

Golang Cafe #31を開催しました。

最近、次の回とBlogを書く回が何回目だったかよくわからなくなる今日このごろです。
それぐらい長い長い運営が続いているようです。(Blogを書くのは31回目で、次回(今日)が32回目です。)
本来は、「私が適当にコードを書くために時間を強制的に確保するためのネタ」だったのですが、今では、常に4人集まる「巨大イベント」になってしまいました。

さて、今回は、+Takanobu Haginoさんがいらっしゃらなかったので、Blog記事を読み進めるというのはやめて、世界の+Ryuji Iwataさんがお書きになった「プログラムでシダを描画する」をGoで描画するをGoroutineを使って高速化しようということにしました。
流れでそうなってしまいました。

今回のソースコードはgithubにあげていますのでそちらもごらんください。
shida.goがとりあえず、関数f()の呼び出しをgoroutineに入れておくパターンで作り、
shida2.goはif文が付くものと、必ず呼び出されるものの大きく2つにわける形でgoroutineに登録したものです。

結果として、shida.goを実行すると、N=11あたりで、panicが発生して描画することができなくなりました。で、改善(?)したものもやっぱりN=11あたりでpanicが発生してしまいました。もう少し、検証を進めてみたいと思ったのですが、時間があまり取れなかったので、進めることができませんでしたが、恐らく、登録数が多すぎてオーバーフローしているのではないか?と当日は考えていました。

が、今、ちゃんとログを見てみると、panic: runtime error: index out of range
ということなので、何か違う問題がありそうです。

で、原因が、math/rand/rng.goで発生しているので、じっくりと見てみると、
rng.feed--とか、計算している場所がありました。ほぼ確実に、マルチスレッドのブロックがないので、GOMAXPROCS(4)とかで動かしていると配列エラーになりそうな予感がします。ということで、goroutineの登録が多すぎたのではなくて、randで乱数を生成するときの配列の添字がマイナスになってしまうのが原因のように感じられました。

ということで、今回のポイントは「randとgoroutineを組み合わせる時は、しっかり排他処理をしましょう」ということでした。

2014/06/02追記:
Golang Cafe #32でも並列化についての話題が議論となり、私のshida3.goでは、goroutineの終了を取りこぼしている可能性があることに気が付きました。
結局、sync.WaitGroupを使って、カウントを取ることと、バッファ付きChannelを使って、終了を待ち受けるような構成にしなければいけないだろうという話になりましたが、
そもそも、goroutineの切り替わりが発生しない処理ばかりなので、実際にどこで切り替わるの?ということも考えないといけないことになりました。その点については、runtime.GOMAXPROCS()で同時実行数を変更することで、並列処理されるから、問題はないのでは?という話になったり…。+Ryuji Iwataさんのお題は実は難しかった。と再認識させられる結果になりました。

次回(今日)は、5/31のGo Conferenceでの資料を読み進める予定です。

2014年5月25日日曜日

Golang Cafe #30を開催しました。

Golang Cafe #30を開催しました。

今回はGoのプレゼンテーションツールのpresentの使い方を紹介して頂いた後、Dockerのソースコードを読み進めるという内容になりました。

Goのプレゼンテーションツールであるpresentの使い方は+Takanobu Haginoさんが事前に調べてくれていたので、その結果から使い方を説明していただきました。
インストール方法は、まず、go getを使ってソースコードをダウンロードします。ダウンロード後、go installでコマンドができあがるので準備完了です。

$ go get code.google.com/p/go.tools/cmd/present
$ cd src/code.google.com/p/go.tools/cmd/present
$ go install

その後、プレゼンテーション用のファイルを作成(中身はテキストファイル、フォーマットはmarkdown風)して

$ $GOPATH/bin/present

とするだけで、ローカルにWebサーバが起動しプレゼンテーションを参照する事ができます。このプレゼンテーションツールはプレゼンを表示した状態で、Goのプログラムを動かす事ができるので、いちいち画面を切り替えなくても良いという点では非常に便利です。

ただし、PowerPointとか、Google Presentationのような柔軟な配置が難しいので、凝ったプレゼン資料が作りにくいのも確かです。

後半は、Dockerのソースコードを読み進めました。
今回は、docker versionと、docker runコマンドの2つを読み進めました。
結局、クライアント側を読んだ結果、プロセス通信とHTTPのプロトコルの組み合わせの通信をしているということがわかったので、サーバ側も、各コマンドがリクエストURLから分析した結果から実行する処理が決まるというものでした。

あまり、新しい成果が生まれなかった(Engine構造体とJob構造体がキモというのが変わらない)のと、Docker自体がかなりのペースでMergeとCommitが加わっているので
現状のソースコードを読むことに意味を成さなくなる可能性がでてきました。
(最新のものと比べてかなりの乖離がある)

ということで、最新ソースを追いかけながら読み進める事をすると、Dockerの勉強会になりそうだという判断もあって、次回はGoの資料を読み進める事になりました。

個人的に、ですが、Dockerのソースコードは読みにくい…。と思います。
でも、いい経験になったと個人的に思っているので、人のソースコードを読むのは
間違いじゃないと思います。

最近、Golang Cafe直前になってBlogを書いているので、少し改善したい今日この頃。


2014年5月17日土曜日

Golang Cafe #29を開催しました。

Golang Cafe #29を開催しました。

今回は、#27に引き続き、dockerのソースコードを読み進めました。

前回は、deamon側に向かって、リクエストを飛ばすクライアント側のソースコードを読みましたので、docker/docker.goの98行目から開始しました。
(-d、-deamonオプションを付けた場合に動作する部分)

dockerにはHandlerという内部コマンド(?)があって、最初にHandlerを実行するための準備をします。(builtins/builtins.go)内部コマンドはコマンドの名前(例:initserverなど)をキーにして実行する関数を保持するmapとして定義されていて、各処理の時に、名前を指定して呼び出します。
最初は、"initserver"、"init_networkdriver"、"serveapi"の3つが登録されています。

initserverの中で、クライアントからのリクエストに対するHandlerが登録されます。

ただ、読み方としてコツがあって、以前にもでてきた、「goroutinesの切り替わり」を考えて処理の順番を考えないといけません。

したがって、登録したHandlerの順番としては、最初に"serveapi"が実行されます。これは、docker.goの133行目付近はgoroutineになっていますので、バックグラウンドで動くことになります。よって、先にdocker.goの179行目にある、job.Run()が実行されることになります。docker.goを見る限りでは、runtime.GOMAXPROCS()を呼び出していませんのでMAXPROCSは1で動作していますから、「明示的なスレッドのブロック」が無い限りは"serveapi"が動作すると思われます。(ということは、runtime.GOMAXPROCS()を呼び出すとまともに動作しないのかも…?)

job.Run()の中では、登録されているHandlerを呼び出すので、初見では、何かの関数か?と思うかもしれませんが、関数型のmapの関数を呼び出すような仕組みなので、呼び出しの部分は勘違いしないようにしなければいけないと思います。

以前のクライアントからUnixソケットを使って接続するようになっていましたが、docker.goの155行目でacceptconnectionsが実行されています。
(中で、net.DialUnix()されています。(Unixソケットってサーバ、クライアントどちらもDialなのか…?))

正直、dockerのソースコードは読みづらい…。俺だけか?

次回は、各コマンドの内部を読み解いていく予定です。

2014年5月11日日曜日

Golang Cafe #28を開催しました。

Golang Cafe #28を開催しました。

今回は前回に引き続き、dockerのソースコードを読もうと思っていたのですが、東京から返ってきた、+Shingo Ishimuraさんの「デプロイツールをGoで」というお題がでて、その検証をしていました。

結局、os/execパッケージのCommandを使って、標準入力と標準出力を取得する。というのがポイントになります。これは、以前のGolang Cafeでもやりました。
ということで、自身満々に

と、言ったのですが、単純なコードだとうまくいくソースコードは書けたのですが、実際にはどうなんだろうか。(業務に係わるものだったので、直接見たわけではないので…)

作成したソースコードはgithubにありますのでごらんください。

今回は、os/execパッケージが主な機能だったので、golangcafeのexecsampleディレクトリに含めてあります。exec.Commandを使うときの参考までに。

それから、その後、今回の内容を活かして、aetesthelperというツールを作ってみました。これは、GAE/Gのgoapp testをサポートするコマンドです。

公式サイトの通りに、goapp testを行えば良いのですが、どうしても、パッケージを分けて、別のパッケージを参照する時に、GOPATH変数を参照して、GOPATH変数で設定したパスの上にファイルがないとテストが動作しない(私だけ?)ので、仕方なく、Tempdir()で作った、ディレクトリにSetenv()して、ファイルを全てコピーして、goapp testを流すという単純な動作にしています。
これで、プロジェクト内の全てのパッケージに対してgoapp testが実行できるので、非常に快適になりました。

次回(と言っても、あと3時間後ですが)は、先々週に引き続いてdockerのdaemon側のソースコードを読みます。

2014年4月29日火曜日

Golang Cafe #27を開催しました。

Golang Cafe #27を開催しました。

今回も前回に引き続き、Dockerのソースコードを読み進めました。

前回は、クライアントでのコマンド実行のところまで読んだので、今回は、docker runコマンドの実行部分を読んでみました。

該当の場所は、api/client/command.goの1776行目にある、

func (cli *DockerCli) CmdRun(args ...string) error {

からとなります。

処理の概要は

  1. コマンドの解析(ParseSubCommand)
  2. ContainerIDFileが存在しなければ、ContainerIDFileを生成する。
    1. ただし、ホストの設定(hostConfig)で指定されている場合のみ。
    2. ContainerIDFileは最後に削除する。
  3. cli.call("POST", "/containers/create?")で、デーモン側にコンテナの作成を指示。
    1. ファイルがない(httpのレスポンスが404の時)時は、コンテナImageがないので、imageを作成を指示する。(これも、cli.call())
  4. コンテナの作成の結果を受け取る。
  5. コンテナにattach(cli.hijack("/containers/"+runResult.Get("Id")+"/attach?"))
  6. コンテナの開始(cli.call("/containers/"+runResult.Get("Id")+"/start"))
  7. 結果を取得
結局クライアントからはデーモンに向かってプロセス通信しているだけで、何も処理がありませんでしたので、デーモン側の処理を読むことで、コンテナの配置とか、処理の実行の部分の理解を深めることができそうです。

デーモンはいつ起動させるのか?
Ubuntuだと、サービスとしてPC起動時に起動してしまうようです。また、Macだと、boot2dockerを使ってサービスを起動させた状態で使っています。
Windowsだとどうやら無理みたいですw(中身をみると、syscallをバリバリ使っているので、Windowsとは相性が悪そうです…。)

さて、細かい処理を(複雑で時間がかかるので)見ていないのですが、処理の概要は読めました。

今回のポイントですが、プロセス通信をする時に中身では、httpを使っています。
標準パッケージのnet/http/httputilを使って、http通信をしていました。

httputil.ClientConnを使ったリクエストを行う場合、net.Connを自分で指定させるため、Socketを自分で作ります。今回は、Unixドメインソケットだったり、tcpソケットだったりするわけです。(Macだと-Hオプションで指定したURL(私の環境だとtcp://localhost:[Port])を使ってソケットを生成されるようです)

よく考えると、これまでnet/http周りはじっくり読むということをしていなかったので、またのタイミングで読みたいと思います。

ということで、次回はデーモン側のソースコードを読む予定です。

2014年4月26日土曜日

Golang Cafe #26を開催しました。

Golang Cafe #26を開催しました。

今回からdockerソースコードを読み進めることになりましたので、”当然”main()から読み進めるのが妥当だろうということでmain()が書かれているdocker/docker.goから読み始めました。

流れとしては、最初に".dockerinit"という文字列が含まれているとinit modeでの起動ということで、sysinit.Sysinit()が呼び出されます。パスに".dockerinit"が含まれる時ということなので、docker自身が隠しディレクトリを作る事があるのだと思いますが、sysinitは少し重たそうだったので、少しだけ見て、先に進むことにしました。sysinitパッケージは後日に回します。

dockerは引数、オプションを扱うflagパッケージを自作しているようで、別名でflagパッケージを定義してありました。

flag "github.com/dotcloud/docker/pkg/mflag"

使っている所をみると、オプションを配列で指定することができるようにしてあるだけのようにも見えますが、とりあえずは最初の起動の処理をみるということにして、flagも存在だけのチェックで終わっています。

それぞれの引数がどのような意味なのかは、mflagパッケージを読む時にじっくり見たいと思います。デフォルトコメントが書かれているので、そこから判断して頂いてもいいかと思います。

処理の流れとしては、


  1. versionフラグがtrueの時、version情報を出力して終了
  2. Hosts(?)の長さが0(指定されていない時)に環境変数DOCKER_HOSTを取得
    1. DOCKER_HOSTが未定義または、Daemonとして動作する時はデフォルトのUnixSocketを保持する。
      1. ちなみに、デフォルトのUnixSocketのパスは/var/run/docker.sockなので、Macだと存在しないと怒られて動作しないので、-HオプションでSocketを指定する。
  3. -bと--bipオプションが両方指定してあるとエラーになる。
  4. -Dか--debugオプションが指定してある場合は、環境変数DEBUGを1にする。
  5. -dか--daemonオプションが指定されているとDaemonモードで動作する。
    1. ※こちらは今回読んでいないので、中身はまだわかりません。
  6. 指定されていない場合は、clientモード(docker run XXXとかで動かすモード)の処理
    1. TLSを使うかどうかのチェック
    2. client.NewDockerCli()を呼び出し、clientオブジェクトを取得
      1. TLSを使うかどうかの違いは最後の引数がnilか、configが指定してあるかの違い。
    3. ParseCommands()でコマンドの解析とサブコマンドの実行が行われる。
      1. 解析だけかと思ったら実行もされていた。
今回のポイントとして、Unused Importsを使ったパッケージの初期化処理が便利だということと、strings.SplitN()を使って、最大N個までの分割で済ませるというAPIがあることが、新たな発見でした。

特に、Unused Importsはsql/driverあたりでも使われるのですが、その時は、interfaceのみだから、interfaceを認識させるために使うと思っていましたが、init()関数が呼び出されることで、ドライバの初期化ができるというのは、初期化処理の呼び出しをいちいち書かなくても良いという点でいい発見でした。(使う時があるかどうかは別にして…)

次回はdockerのサブコマンドである、apiパッケージを読み進めたいと思います。
新たな発見が出るかな?




2014年4月19日土曜日

Golang Cafe #25を開催しました。

Golang Cafe #25を開催しました。

今回は、初めての方の参加があり、Goが初めてということだったので、+Ryuji Iwataさんにお願いして、#1の時にやった、「How to Write Go Code」の内容を説明して頂きました。
私と+Takanobu Haginoさんはずっとやっていますので、改めて話を聞く必要はありませんでしたが、無茶ぶり以外のなにものでもなかったことは確かでした。
(本来は私がやるのですが、復習というのも兼ねて別の方に頼むのもいいのかなとも思いましたので。)

で、今回のGolang Cafe #25では、私は特に成果物がありませんでしたので、少しだけ考えた、「Golang Cafeの今後」についてまとめてみたいと思います。

今回で25回開催してきたのですが、1回目から欠かさず続けているという事は、正直すごいと思うのですが、その状況での問題点がちらほらでてくるわけです。
ずっと参加して頂いている2人に関してはGoのノウハウが蓄積されてきて、それぞれの熟練度もある程度上がってきました。

ここからは恐らく…ですが、例えば、コードを書いて動かしたい人と、いろんな資料を読み、ノウハウを蓄積したい人、公式ドキュメントとか、書籍を読みたい人といういろいろ「やりたいこと」がわかれてくるのだろうと思っています。

Golang Cafe自体、「ネタ」ですから、私一人でもやるのですが、独断で決めてもいいのですが、せっかく集まってくれているので、ある程度、希望に沿ったものにしたいなーと考えたりもしました。

それから、仕事が詰まって忙しくなるとかで、参加者が変化する(入れ替わりが発生する)かと思ったのですが、入れ替わることもなく、増えることもなかったので、今の状態なったのかもしれません。

今のところのテンションだと、「終了」はまだまだ先になりそうですが、
個人的な野望としては、「Golang Cafeの並列化」が実現するとおもしろいなと思っています。ですが、今のところ、私以外にGolang Cafeを開催する人はいませんね。

あとは、いつGoがプロダクションで使われ、金に繋がるか。
それまではがんばってみようかと思ってたりしてます。

2014年4月8日火曜日

Golang Cafe #24を開催しました。

Golang Cafe #24を開催しました。今回は、Devfest Japan 2014 Springのなかの1トラックでの開催となりました。

40分しかなかったのと、参加者のGoを使っている度合いをつかむことが難しかったので、当日も参加して頂いた、+Takanobu Haginoさんに協力していただきまして、ある程度使っている人とGolang Cafeをやってもらいました。

私は、というと、初心者の方が多かったので、Goの導入と簡単なプログラムの書き方(Golang Cafe #1に至る直前までの内容)を自由に話してみました。
導入とか、実行方法などをお話してみましたので、今後ハマったら質問など投げて見て下さい。

Golang Cafe自体が、自由だったので、Devfestでやることか?とも思いましたが、「Golang Cafeへの幻想をぶち壊す!」的なノリで、好き勝手にやってみることにしましたw

中身は、「雑談」と、「質問への回答」が主なので、力をいれずやるのが良いのかなと思っています。

で、参加して頂いた方にもGolang Cafeがどんなものかは、十分わかったと思いますので、ご自由に開催して下さい。

某所で、このGolang Cafeのイベント情報を共有したいという意見(アピールしたいという意味で)も上がってきたのですが、できれば、GDG中国にあるGolang Cafeのサイトに誘導して頂く方が、私はうれしいです。
ですが、性質上たくさんの人が申し込まれると、場所の確保と内容に制限が出てくるので、難しいところです。

次回からは、また倉式珈琲店での開催となります。多分、出張はもうありませんw

Devfest Japan 2014 Spring

Devfest Japan 2014 Springに主催者側で参加してきました。

今回は、各地のGDGオーガナイザー達が集結して、いろんなプロダクトのセッションを担当するという感じでした。

全体では、セッションのトラックとハンズオンのトラックの2つに分かれての構成で、
私は、Google Apps Scriptと、Goを担当しました。

本来は、90分ぐらいかけて、しっかりやりたかったのですが、運営上の都合で、両方共40分でのハンズオンとなってしまいました。

Goの方はこの後別の記事で書くので、ここでは、Google Apps Scriptのセッションについて書いてみたいと思います。

当日の資料は、http://goo.gl/ccI5j1で、「Google Apps ScriptでYouTubeを検索する」というタイトルで、見て頂ければ分かる通り、40分で収まる内容ではありませんでした。
(当日も、言われましたがw)

ですが、チューターとして参加して頂いた、+Keisuke Oohashiさん、+Atusi Nakamuraさん、+Yoshifumi YAMAGUCHIさんの3人のすばらしいサポートと、参加者の方のポテンシャルに助けられまして、大半の方が、YouTubeの検索とメール通知ができるようになっていました。本当に大成功でした。チューターの方、ありがとうございました。
(後は、京都でGoogle Apps Scriptが流行るかどうか)

後で話を聞くと、コピペ駆動で動かしたということでしたが、ハマりポイントもうまくフォローされたようで、良かったです。

最終的にできたソースコードは20行程度ですが、Google Apps Scriptだとそれだけで、かなりのことができるようになっています。
大規模なプロジェクトには合わないと思いますが、小規模なものなら便利に使えると思います。

それから、Google Apps Scriptのハンズオンが申し込み時点では一番多いという結果になり、運が良かったなと思っています。

また、今後、Google Apps Scriptのイベントでお会いしましょう!

2014年4月6日日曜日

Golang Cafe #23を開催しました。

Golang Cafe #23を開催しました。

ソースコードは、githubにありますので、そちらを御覧ください。

今回も、「OAuthライブラリをリファクタリングする。」というテーマで、
進めました。

実際、Goに関する新しい事は特にないのですが、私が進めたのは、以下の点に留意して進めました。


  • 繰り返しをしない
  • テストコードを書く

で、最初のライブラリは、文字列の連結部分がコピペで行われているので、その処理をまとめる。ということにしました。

そこで、ファイルの分割をし、テストコードを追加している最中に時間が来てしまったので、終了となりました。

で、参考になるかどうかはわかりませんが、今回のOAuthだとNonceの生成や、Timestampの設定で、ランダムな値を生成しなければならないので、その辺りのテストを
どうしているかなどは、参考になるかもしれません。

次回は、京都でDevfestの1トラックで行いますので、奮ってご参加下さい。
(ただ、後1時間少々で始まりますが!)

2014年3月29日土曜日

Golang Cafe #22を開催しました。

Golang Cafe #22を開催しました。

ソースコードはgithubにありますので、そちらをごらんください。

今回は、私がインフルエンザにかかってしまって、少し時間が取れたので、じっくり準備をすることができました。(今回のテーマにする予定はしていなかったのですが…)

今回のテーマは、「OAuthのライブラリをリファクタリングする」というのをテーマにしてみました。私は出題者なので、出題の背景を書き残しておきます。

前回までに、Twelve Go Best Practicesを読み、ある程度、コードを書くためのコツを掴んだので、それを実践する事ができないかというところから考えました。
(Golang Cafe自体、私がコードを書く時間をネタで募集をしたところから始まっているので)
資料と標準パッケージの使い方はある程度やってきたので、次は、「人のソースコード」を読んでみるという方向性で考えました。公式サイトには、候補として、vitessと、dockerのソースコードを読むというのを挙げていましたが、以前、OAuth2をやったので、https://github.com/mrjones/oauthを読むのも良いかと思い、先行して読み進めていました。ただ、動きをみないとわからない点も多かったので、力づくで、最低限動くコードを作ってみた所、いい感じにダメコードができたので、ちょうど良いのかな?と思ったので設定しました。

ただ、ソースコードを修正するということをしようと思うと、まず、読んでもらわないといけないのもあって、今回は、最初のコードを読み、出題者(私)に質問するという内容になりました。

個人的には、以下の点について議論ができそうな気がしているので、良いテーマなのかな?と思っています。

  1. ライブラリ開発ということで、APIの設計(?)
  2. Twelve Go Best Practicesの適用
  3. ランダム文字列が生成される部分についてのテスト
  4. 通信が発生するので通信に関するテスト
  5. 必要ならMockを使ったテスト
それから、次回は、その次の京都(祝半年)のための準備もしておきたい気がしていますが、そういう時間が取れれば良いですが…。


2014年3月19日水曜日

Golang Cafe #21を開催しました。

Golang Cafe #21を開催しました。今回は、毎週日曜日に開催しているのですが、香川で「春のGoogle勉強会」があるということで、今回の参加者が全員参加する、しかも、開始時間が16:00からということで、その前にやってしまって、日曜日をゆっくりしようということになりました。

メモと動作確認したソースコードはgithubにありますのでごらんください。

では、続きです。

その14

 APIで非同期にするのは避けよう!


  • 順番にAPIを利用したい時はどうすればいいのか?
    • 同期APIを用意すると、並列実行が簡単に呼び出せる。
    • (※runtime.GOMAXPROCS(2)とかすると、順番がおかしくなるが、呼び出し順をどうするか?という議論ではない!)

ここでの議論は、API側で非同期の処理を実装するのではなく、同期APIにしておいて、非同期で実行させるかどうかは呼び出し側の都合で変えられるようにしておくのがいいということでしょう。

その15

  goroutineと通信するための構造体のメンバのChannelか、単体のChannelを使え!

Channelの動作について(何度も復習するところですが、私の認識は以下です。)
chanのバッファが0の時は、他のGoroutinesがChannelを受け取らないとGoroutineがブロックされる。chanのバッファが1の時は、1個はバッファに置けるので、Goroutineがブロックされず、突き抜ける。

goroutineを使った時のgoroutine間の通信をどうするか?という話題で、channelを使って、データのやりとりを行えばいいということです。

で、ここで議論になったのは、以下のコードです。(抜粋ですので、全体は原著の資料か、githubの資料をごらんください。

func (s *Server) Stop() {
    fmt.Println("server stopping")
    s.quit <- true
    <-s.quit
    fmt.Println("server stopped")
}

s.quit<-trueをして、すぐに、<-s.quitでChannelから値を取り出しているように見えますが、Channelはバッファ0のChannelですので、s.quit<-trueした時点で、goroutineはブロックされます。(他のgoroutineでChannelから抜き取るまで。私はスレッドがブロックされるという表現ではなく、goroutineの切り替わりと言っています。)したがって、別のgoroutineが終了処理を行って、完全に終了した後に<-s.quitが実行されることになります。

その16

Buffered Channelを使って、goroutineのリークを避けよう!

  1. 何度も繰り返しになりますが、channelに書き込むと、goroutineはブロックされます。
  2. goroutineはchannelの参照を握っているので、channelがガベージコレクトされることはないことになります。
  3. goroutinesの切り替わりは「明示的なブロック」で発生するので、今回のようにバッファがないchannelだと、goroutineの切り替わりが発生するので、goroutineの処理が終わる前にchannelの受信待ちが終了してしまうことがある。資料の例だとlocalhost:8080がエラー応答になった時に、エラーを検知して終了する。しかし、google.comの処理がまだ残っているので、結果としてリークしたことになる。

その17

quit chanを使って、goroutinesのリークを避けよう!

close(quit)を行うと、channelに受信しない事が明示されます。
その16と同じように、先にエラー応答が発生した時にそのまま終了するのではなく、close(quit)することで、登録されているgoroutineはすべて、case: <-quit:が実行されます。したがって、全て、終了させる事ができます。
goroutineの切り替わりを意識したコードを書くことで、channelのリークを防ぐことができるのでしっかり意識しましょう。
chan(struct{})はクローズ(終了)用なので、誰もChannelに送信できないようにするために、空の構造体にしているものと思われます。

次回は明確にテーマを決めなかったのですが、何かやります。

2014年3月9日日曜日

Golang Cafe #20を開催しました。

Golang Cafe #20を開催しました。

今回は、前回に引き続き、Twelve Go Best Practicesの13ページから読み進めました。
プラクティスのメモはgithubにありますので、ごらんください。
(今回は、新しいコードを書くことはしていないので、メモが追記されただけです)

以下、今日のまとめです。資料を見ながら読み進めてみてください。

その7

重要なコードは先頭に書くこと!


  1. ライセンス情報、ビルドタグ、パッケージドキュメントを記載する。
  2. import文は、(gofmtコマンドを実行すればいい気がするが…。)標準パッケージを最初に記述し、1行開けて、go getで取得したパッケージを記述する。
    1. gofmtは自動的にアルファベット順に並べてくれるが、1行あけると、並べ替えの適用を受けなくなります。
    2. gofmt sample.goだと、標準出力に出力されてファイルが書き換わらないので、gofmt -w sample.goと-wオプションをつけよう!
  3. 重要な型から始まり、ヘルパー関数などで終わるようにする。
  4. ファイル名は型に合わせたものにした方が良い!
    1. という結論に至りました。goの標準ライブラリのソースコードを見る限りだと、最初にtypeの宣言があり、typeの名前とファイル名が一致していたので。
    2. 機能ごとにファイルを分けて、わかりやすくするべきなんだろう。

その8

パッケージ名の前に連想させるようなドキュメントを書くこと!

godocに出力されるものは、ドキュメントを書きましょう!

その9

短い事はいいこと。


  1. 意味がわかって、できるだけ短い名前をつけるようにしましょう!
    1. "MarshalWithIndentation"よりも"MarshalIndent"という名前が好ましい。
  2. 識別子にパッケージ名が含まれる場合は、つける必要はない!
    1. encoding/jsonにEncoderがあるが、JSONEncoderではない。
    2. 参照される時は、json.Encoderとなるから、必要ないでしょう?
      1. 確かに、Goでパッケージのコードを書く時は、json.JSONEncoderだと命名としてブサイクなのかな…?

その10

1つのパッケージを複数に分けるべきか?


  1. コードの長いファイルは避けよう。
    1. net/httpパッケージは15734行を47ファイルに分けている。
  2. コードとテストは分けよう。
    1. net/http/cookie.goとcookie_test.goみたいに。
    2. そもそも、go testが動かないはずなので、ほぼ強制的にそうなるではないかと思われる。(go testは**_test.goを実行するので)
  3. パッケージドキュメントをdoc.goに分けておくといい。
    1. 複数のファイルがある時に、どこにパッケージドキュメントを書くか迷うので、わかるようにdoc.goを作り、そこに書く。標準のパッケージもdoc.goはpackageの宣言と、ドキュメントの記述しかない。
    2. ファイルが1つの時はそのファイルの先頭に書けばいい。


その11

go getができるようなパッケージを作りましょう。

ネットワークプロトコルを定義するようなパッケージは使われるかもしれない。
mainから始まるコマンドは再利用されないかもしれない。


その12

必要なものだけを求める。

Gopher型を使います。
    
以下のメソッドを定義した。
func (g *Gopher) WriteToFile(f *os.File) (int64, error) {

しかし、テストがしにくい!そこで、インターフェイスに書きなおした。
func (g *Gopher) WriteToReadWriter(rw io.ReadWriter) (int64, error) {

必要最低限のインターフェイスを使ってメソッドを定義すべきだ。
このメソッドは、ファイルへの書き出し機能だけなので、Readerの機能は必要がない。したがって、必要なものを引数に渡すように求めること!
func (g *Gopher) WriteToWriter(f io.Writer) (int64, error) {

ここでは、余計なものを指定できると、よくわかっていない人が想定外の引数を指定してしまうのを防ぐためにもこのプラクティスは必要!という結論になりました。
(が、それぞれ流儀があると思うので…。と議論がおかしい方向になる前にやめました)

その13

独立を保つ!パッケージごとに独立させておく。


  1. parserは解析のみ。drawerは描画のみ。pngへのエンコードはエンコードのみにする。など。
  2. 依存するパッケージをimportするのではなくて、interfaceを使って、Parse機能を持った関数を受け取り、呼び出すようにしよう!
  3. 具体的な型を使うよりも、インターフェイスを使った方がテストが簡単に作れる。
  4. TestFunc型はEval()を実装しているので、interfaceを実装していることになる。

この時点で12個じゃない!と思いましたが、資料の最後に12個のリストがあるようです
…。が、12個じゃないよな…?という気がします。

引数で受け取った関数を呼び出す形にして、他パッケージの依存を防ぐというのは良いのだけど、引数で指定する関数の中身は呼び出し側が実装するわけなので、「誰でも新たな処理を追加できる」というのが良いのか悪いのか…。という意見が出ました。
これも奥が深い話になる(というか、プロジェクトのメンバー間の意識統一かな?)と思ったので、やめておくことにしました。

ということで、次は、並列処理などのパターンが記述された、25ページ以降を読み進める予定です。

2014年3月3日月曜日

Golang Cafe #19を開催しました。

Golang Cafe #19を開催しました。

今回は、Twelve Go Best Practicesの資料を読むということで、(恐らく)その6まで読み進めました。
ソースコードと、プラクティスのメモはgithubにありますので、ごらんください。
commitログと履歴を遡ればどういう変化があるかを見ることができるようになっています。

以下、今日のまとめです。資料と共に読み進めて下さい。

その1
深いネストは避けよう!

Goは例外機構がないので、全ての呼び出しについて、エラーチェックが必要になります。
(エラーチェックじゃなくても、戻り値を受け取った後に何らかのif文が必要になる)
そこで、全ての(仮にここではerrorをチェックするとして)エラーをif文で囲むとネストが深くなりやすくなります。
では、どうするかというと、”エラーがある場合は、即return”ということになります。
こうすることで、ネストが深くなるということが避けられます。

その2
構造体に必要なオブジェクトを格納することで、シンプルなコードに!

その1で”エラーがある時は即return”としたのですが、「毎回チェック」をする必要はないわけです。というと、errorを構造体のメンバにしておき、戻り値をメンバに退避しておきます。
そして、メソッド呼び出しの最初に、メンバのerrorがnilかどうかをチェックし、nilでなければ、即終了させる。という形にします。
これにより、呼び出し側でエラーを確認する必要がなくなるので、シンプルな呼び出しにすることができます。

その3
型で処理が決まる場合は、型で処理を分岐させましょう!

型によって、処理がわかれる場合に、switch v.(type) {}を使うと、型の判定を行うことができます。実は、標準パッケージだと、よく書かれている記述で、ユーティリティを作るのに非常に便利です。(Go以外の言語で型による分岐って無い気がするので、独自のパターンかもしれません)
引数は、interface{}で受け取ることがポイントです。

その4
switch文の:=を使え!

その3で型による分岐が出てきましたが、実際に処理を進めるに当たって、Type Assertionをする必要があるのですが、v.(type)については、Goの構文として短縮構文にすると、case文は型で判定し、値は短縮構文の時にType Assertionされているという状況を作ることができます。したがって、型によって、Type Assertionをする必要がなくなります。

その5
全て書くか、何も書かないかのどちらかにする。

データベースのトランザクションと同じような感じですが、例えば、通信プログラムなど、「途中まで成功」というのは都合がよくありません。そこで、一旦、bytes.Bufferに送信電文のデータを書き出しておき、Flush()メソッドなどで、一括で送信するというようにしておくと、良いですよという話。
しかも、このソースコードは、再帰呼び出しを使っていて、エラーのチェックも1か所に集約されています。が、これは日本(の中小企業(?))だと怒られるのではないか?という話になりました。慣れないとすぐには気がつかない…w

その6
リクエストを処理し、エラーを返す関数と、エラーを受け取り、エラーを処理する関数に分ける!

ここから、Webサーバの例になりますが、複数の処理に対するエラーの処理をどうするか。というのがテーマだと思います。(実は、私もGAE/Gで同じ失敗をしているのですが…)
毎回if文でエラーチェックして、エラーレスポンスの処理を書くとイケてないコードになります。(これは、私も疑問に思っていた)そこで、エラーを返すだけの主処理と、主処理を呼び出し、エラーチェック後、レスポンスを返す、エラー処理関数を用意します。
すると、エラー処理がきれいに集約されてきれいになります。
また、errorを変数で宣言しておくと、エラーの判定により、レスポンスを変える事ができるので非常に便利です。ただ、資料のfmt.Errorf()は、内部でerrors.New()をしているので、一見便利に見えますが、エラー判定ができなくなるので、使うと後で困ることになるので使わないようにしようということでした。

今回は、資料の12ページまで読み進めたので、次回は、13ページから読み進める予定です。


2014年2月23日日曜日

Golang Cafe #18を開催しました。

Golang Cafe #18を開催しました。

今回は、goauth2というライブラリを使って、GoogleのAPIを実行するというのが
テーマでした。

サンプルコードは、githubにありますので、そちらをごらんください。
(ディレクトリを別にしていて、goauth2sampleに入っています)

今回は、サンプルの解説と、installedAppsの認証を1回で完結させるというのをやりました。
installedAppsの認証を1回にするというのは、oauth2の認証(installedApps)では、最初にユーザに承認させることになっていて、承認後のコードをアプリケーションに教えてあげる必要があります。サンプルでは、引数で承認コードを指定するようにしていますが、その引数での指定をやめて、Webアプリのように承認後、継続する処理に変えるというのにチャレンジしました。

サンプルの解説からですが、まず、calendarsampleは、CalendarAPIを実行し、カレンダーリストを取得するサンプルです。

認証の設定をいれる

最初に、認証の設定を保持するConfig構造体にClientID、Secretなど必要な設定をいれます。
config := &oauth.Config{
       ClientId:     auth.ClientID,
       ClientSecret: auth.Secret,
       RedirectURL:  auth.RedirectUrl,
       Scope:        scope,
       AuthURL:      request_token_url,
       TokenURL:     auth_token_url,
       TokenCache:   oauth.CacheFile(cachefile),
}

ClientID、Secret、RedirectUrlはGoogle Developers Consoleでアプリケーションキーを取得したものを設定します。(私のサンプルはgithubにキーが上がってしまうと困るので、最初は入力させて、以降は、ファイルに保存したものを利用するコードになっています。)

ScopeはAPIによって指定するものが違うので、APIのドキュメントを参照して下さい。
TokenCacheは一度認証した時にキャッシュとしてファイルに保存され、ファイルがある間で、有効期限が過ぎていなければファイルの認証トークンを使ってAPIを実行するようになっています。

キャッシュから認証情報を取得する

次に、キャッシュからトークンを取得します。

    _, err = config.TokenCache.Token()
if err != nil {
    // キャッシュなし

    // 認証コードなし=>ブラウザで認証させるためにURLを出力
    if code == "" {
        url := config.AuthCodeURL("")
        fmt.Println("ブラウザで以下のURLにアクセスし、認証して下さい。")
        fmt.Println(url)
        return
    }

    // 認証トークンを取得する。(取得後、キャッシュへ)
    _, err = transport.Exchange(code)
    if err != nil {
        fmt.Println("Exchange: ", err)
        return
    }

}

キャッシュファイルがない場合は、errorが返されるので、認証コードを取得するURLを生成し、コードを取得します。(ここで、一度プログラムを終了させる。)
もし、引数で指定してあれば、transport.Exchange(コード)でExchangeし、認証トークンを取得します。

APIを実行する

APIを実行します。oauth2のパッケージで認証トークンを持っているので、何も考えずにGet()すれば、レスポンスが返ってきます。

// Calendar APIにアクセス
r, err := transport.Client().Get(request_url)

思ったよりも簡単だった。

やることがたくさんあるわけではなく、簡単にアクセスできることはわかりましたが、承認キーを渡してもらう部分を何とかしないと、installedAppsでは使えない気がします。(これをGAEのappcfg.pyは上手にやっているらしく、どうやっているのかソースを読んで解説して貰う必要がありそうです。+Ryuji IwataさんがPythonを知っているので今度読んで、解説してもらおうという話にもなりました。)
+Takanobu Haginoさんは、goroutineでWebサーバを起動(http.ListenAndServe())して、リダイレクト先をlocalhostにするという荒業を実現していました。私も挑戦したのですができなかったので、そのコードの解説はそちらに譲ることにします。)

次回は、2013の12個のベストプラクティスを読む予定です。

追記:
それから、今回無断欠席をされた方がいらっしゃったので、無断欠席に関する注意点を増やしました。申込をして、来られないのは仕方がないと思いますが、何の連絡も無いのはどうかと思いますので、次回から、そういう方は管理者権限で参加取り消しをする可能性があります。

2014年2月19日水曜日

Google Compute Engine(CentOS6)にGoのCI環境を作る

今日、少し時間があったので、Google Compute Engine(以下、GCE)上でJenkinsを動かしてビルドとテストを行う環境を作ってみました。
GCEは、DebianとCentOS6が選べるのですが、私はCentOS6を選びました。

GCEのインスタンスを起動するには、Google Cloud Consoleからインスタンスを起動するのですが、まず、前提として、「課金設定をする」必要があります。(課金設定をしないと、GCEの初期化が成功しない。)インスタンスを生成して、起動するところまでのメモは以前投稿しているのでそちらをごらんください。Google Cloud SDKもダウンロード済みだということにします。(私は忘れていたけど…)

ファイアウォールの設定


まず、GCEのファイアウォールの設定を行います。これは、iptablesではなく、GCEのインスタンスに対するファイアウォールの設定です。Cloud Consoleからでもできますが、以下のコマンドを実行しました。ポートを8888を許可する設定にしていますが、Jenkinsのデフォルトポートをそのまま使えると思うので、デフォルトのポートを使う場合は、tcp:8080にしてください。
$ gcutil addfirewall [インスタンス名] --description="Jenkins用のポート" --allowed="tcp:8888"

そして、sshでログインして、iptablesを切る。(GCEのファイアウォールに委ねるので。これに気がつくのに時間がかかってしまいました…。二重にブロックされるので結構うっとうしい…。)

$ sudo service iptables save
$ sudo service iptables stop
$ sudo chkconfig iptables off

次回の起動でiptablesが動いてくれると困るので、chkconfigでoffにしておきます。

Javaのインストール


Javaもyumで入れましたが、公式のものが入れられなかった(多分、ブラウザでアクセスして、利用規約に同意のチェックがいる?)のでopenjdkを入れました。Jenkinsが動けば問題は無いので、細かいことは無視しました。
$ sudo yum search jdk

java-1.7.0-openjdk.x86_64 : OpenJDK Runtime Environment
java-1.7.0-openjdk-demo.x86_64 : OpenJDK Demos
java-1.7.0-openjdk-devel.x86_64 : OpenJDK Development Environment
java-1.7.0-openjdk-javadoc.noarch : OpenJDK API Documentation
java-1.7.0-openjdk-src.x86_64 : OpenJDK Source Bundle
ldapjdk-javadoc.x86_64 : Javadoc for ldapjdk
icedtea-web.x86_64 : Additional Java components for OpenJDK - Java browser plug-in : and Web Start implementation
ldapjdk.x86_64 : The Mozilla LDAP Java SDK

$ sudo yum install java-1.7.0-openjdk-devel.x86_64

Jenkinsのインストール

Jenkinsをインストールします。Jenkinsのrpm版が提供されていたのでもしかしたら?と思ったら、yumでインストールも可能だったので、yumにKeyを登録してインストールしています。

$ sudo wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.org/redhat/jenkins.repo

$ sudo rpm --import http://pkg.jenkins-ci.org/redhat/jenkins-ci.org.key

$ sudo yum install jenkins

Jenkinsの設定とサービスの登録

PORTを8888に変更する。(デフォルトが8080なので、そのままでもいいと思いますが)
$ sudo vi /etc/sysconfig/jenkins
JENKINS_PORT="8888"

Jenkinsのchkconfigをon
$ sudo chkconfig jenkins on

Jenkinsを起動させる。(初回だけ)
$ sudo service jenkins start

Jenkinsの起動確認


ブラウザで、http://[VM InstanceのEXTERNAL ADDRSSのipアドレス]:8888/
にアクセスしてJenkinsが表示されれば、成功です。

Gitも忘れずにインストールしておきましょう
Gitのインストール

$ sudo yum install git


Goをソースからインストールする。

私はこれしかしたことがないので(?)、今回もソースからGoをインストールしました。
Goをコンパイルするために、gccとmakeを、ソースコードをダウンロードするためにmercurialをインストールしました。

$ sudo yum install mercurial-hgk.x86_64

$ sudo yum install gcc

$ sudo yum install make

$ hg clone -u release https://code.google.com/p/go

$ cd go/src

$ ./all.bash

GCE上でGoのコンパイルをしてみたのですが、意外と早くて、1分30秒程度で終わったような気がします。そこからtestが始まりましたが、testはもう少しかかったかも?
で、喜んでいたら、一般ユーザのディレクトリでビルドしてしまったので、Jenkinsからコマンドを実行しようと思うと、Permissionで弾かれてしまいますから、これも良くないのですが、/home/ユーザのディレクトリにchmod o+rxしてしまいました。
次回以降は気をつけようと思います。基本は/usr/local以下ですかね。

Jenkinsのプロジェクト設定

Jenkinsにgitプラグインを入れたのですが、よく考えると、Goの場合は、go getでダウンロードしてからgo testですので、gitプラグインは無くても大丈夫です。
したがって、ビルドの設定の「シェルの実行」のところに、コマンドをいくつか書きました。

export GOPATH=${WORKSPACE}
/home/yokoyama/go/bin/go get github.com/taknb2nch/go-pop3

cd ${WORKSPACE}/
/home/yokoyama/go/bin/go test 

ポイントは、GOPATH変数を都度exportしているところでしょうか。go get/go testコマンドを実行しようと思うと、GOPATHに入れておかないといけませんからこのような形になります。
リポジトリは、+Takanobu Haginoさんが作られた、POP3のライブラリを対象にしてみました。(私のリポジトリにしようかと思いましたが、私のはどれもレガシーコードですので…w)

結果


ちゃんと、PASSの文字が輝いております。
Started by user anonymous
Building in workspace /var/lib/jenkins/jobs/Sample/workspace
[workspace] $ /bin/sh -xe /tmp/hudson5366985436882835859.sh
+ export GOPATH=/var/lib/jenkins/jobs/Sample/workspace
+ GOPATH=/var/lib/jenkins/jobs/Sample/workspace
+ /home/yokoyama/go/bin/go get github.com/taknb2nch/go-pop3
+ cd /var/lib/jenkins/jobs/Sample/workspace/
+ /home/yokoyama/go/bin/go test
PASS
ok   _/var/lib/jenkins/jobs/Sample/workspace 0.003s
Finished: SUCCESS

今回は、「手動実行」まででとどめました。常時起動するほどテストをすることがないということと、課金の問題があるので、無駄に起動しておくのはまずい。ということが大きな理由です。これで、GCEについてのノウハウが少しずつ貯めていけそうです。

参考サイト

https://developers.google.com/compute/docs/quickstart#centos
http://d.hatena.ne.jp/katsuren/20121030/1351569655
http://d.hatena.ne.jp/okinaka/20120404/1333524730

2014年2月17日月曜日

Golang Cafe #17を開催しました。

Golang Cafe #17を開催しました。
今回のサンプルコードはgithubにおいてありますのでごらんください。

今回は、os/exec、os/signal、os/userパッケージを見ていきました。
その中で、Windows版だと、動かない現象があったので、メモを残しておきます。
(来週の土曜日まで残しておくと、記憶が薄れそうなので)

サンプルコードのコメントにも書きましたが、syscall.Kill()がWindowsでは、定義されていないため、コンパイルエラーになります。こういうOSの仕組みがまったく異なっていることによるソースコードの差分でしょうが、以前の"return -1"ではなく、定義さえもされないということで、signalsample2.goはWindowsではコンパイルもできないということになります。
(今後、こういう問題は改善されるのだろうか…?)

execパッケージのポイント


Command構造体


execパッケージのサンプルのポイントは、Command構造体で、execするプロセスを管理するのですが、標準入力、標準出力、標準エラー出力にPipeを使って、アクセスすることができます。
それぞれ、StdoutPipe()StdinPipe()StderrPipe()を使ってio.ReadCloser()、io.WriteCloser()を受け取り、io/ioutilパッケージを利用するなどして読み取ります。

Start()でプロセスの起動、Wait()でプロセスの終了を待つ


exec.Command("コマンド名", パラメータ...)でCommand構造体を生成し、cmd.Start()でプロセスが起動します。Pipeで標準出力などにアクセスする場合は、必ず、Start()の呼び出しよりも前にStdoutPipe()を呼び出しておく必要があります。Start()の後に、呼び出すと、errorが返されてしまい、アクセスすることはできません。また、Start()を呼び出さずに、Wait()だけを呼び出してしまっても、無限待ちになってしまいますのでStart()→Wait()の順番は守るようにして下さい。
Start()はブロックするわけではなく、そのまま処理を続行します。もし、プロセスの終了をその場で待ちたい場合は、cmd.Run()を呼び出すと、内部でStart()→Run()の順番で呼び出しますので、ブロックされます。

ブロックして、標準出力を得るのであれば、Output()


コマンドの終了を待ち、しかも出力結果も受け取りたい場合は、
exec.Command().Output()とすると、戻り値に[]byteとして、標準出力の結果を受け取ることができます。また、exec.Command().CombinedOutput()にすると、
標準出力と、標準エラー出力がマージされた状態(恐らく、出力された順番)で[]byteを受け取ることができます。
この辺りは、実行して監視するコマンドによって使い分けが必要でしょう。

Signalは受け取るのみ可能

os/signalパッケージは、Notify()とStop()だけが定義されています。Notify()は外部からSignalを受け取った時に、引数で指定した、Channelに対して、受け取ったSignal構造体を送信します。Channelに送信されるSignalは同じく、引数で指定したSignalのみとなります。(サンプルコードでどんなものか、確認してみてください)

Stop()はChannelに格納されているSignal構造体を全てキャンセルする関数で、何もなければ、すぐに終了します。したがって、「Signalが送られていないことを保証する」事ができます。プログラムを終了するときなどに呼び出すもの。と思えばいいと思います。

Windows版はAPIに依存しているos/userパッケージ


user.Current()は実行ユーザを取得、user.Lookup("アカウント")は指定したユーザを取得、user.LookupId(uid)はユーザIDからユーザを取得します。
Mac/Linuxは、この説明で問題はないのですが、Windows版はkernel32.dllをLoadlibrary()した挙句に、Win32API(?)をコールしてAPIの動作に依存したコードになっています。(DLLの読み込みなどは、cgoが使われています。unsafeパッケージの使い方などは、いい勉強になるかも知れません)
(システムに依存しているのでどうにもならないと思いますが…。今後大丈夫なんだろうか…?)

実際User構造体はuid、gid、アカウント名、ホームディレクトリ、ユーザのフルネームなどが格納されますが、uid、gidは、GetTokenInformation()を使って、SIDを取得しています。uidの方は、TokenUserで指定し、gidはTokenPrimaryGroupで指定した結果を受け取ってそれぞれ設定していました。
ホームディレクトリは、GetUserProfileDirectory()が使われていました。
ユーザ名とフルネームについては、(Golang Cafeの最中には途中でやめたのですが、せっかくなので読みました。)ドメイン/ワークグループに参加しているかどうかを、NetGetJoinInformation()を呼び出し、結果が取得できるかどうかを確認しています。
ActiveDirectoryとかそういうのに参加している場合はこのAPIがエラーを返すのでしょう…。恐らく。
ドメイン/ワークグループに参加している場合は、TranslateName()を呼び出し、情報を取得します。ActiveDirectoryの場合は、NetUserGetInfo()を呼び出し情報を取得します。
その結果から、フルネームとユーザ名を取得し、UsernameとNameにセットされるという流れになります。

結果として、Windows版は大丈夫か?と言いたくなる程度にはWin32APIに依存しています。今後、.NETFrameworkの機能しか提供されなくなったらかなりまずそうです…。

ということで、Golangの標準パッケージを使うというのもひと通り見たなということで、
次回は、goauth2パッケージを使ってGoogle Apps APIを叩くという内容になります。

サンプルコードで挙動を確認するという段階は十分やったと思うので、今後は、実際に
コードを書き、"Goらしいコードの書き方"というのを追求できたらと思っています。

2014年2月15日土曜日

Golang Cafe #16を開催しました。

Golang Cafe #16を開催しました。

今回は、net/mailパッケージとnet/smtpパッケージを使って、メールを送信するサンプルを動かしました。

ソースコードはgithubにありますので、そちらをごらんください。

net/mailパッケージは、主にメールの解析に使います。

例えば、Barry Gibbs <bg@example.com>というサンプルで、mail.ParseAddress()を呼び出すと、
Barry Gibbsとbg@example.comをそれぞれ、メンバに格納するという事をします。メールアドレスの書式がおかしい(例えば、@が2個あるとか。)と、エラーが返り、不正なメールアドレスだということになります。

某携帯キャリアメールのアドレスに"."(ピリオド)が3つとか付いて仕様から外れるメールアドレスがあるということで、確認してみたところ、@の後ろにある場合はエラーになりますが、@の前にある場合は、エラーにはならないようです。

mail.ReadMessage()は、メール本文を読み取ると、メールヘッダ、宛先、BODYなどに分離して格納します。これにより、どんなヘッダがあるか、宛先メールアドレスなどが取得できます。multipartのメールを受信したい場合は、mime/multipartパッケージの関数を使うと、ファイルの解析などもできます。
で、詳細は+Takanobu Haginoさんが、解析されていたので、blogを見たほうがいいかもしれません。

net/smtpパッケージはパッケージ名からわかるようにメールの送信用の関数が定義されています。
smtp.SendMail()を使ってメールを送信するのですが、メールサーバの設定によって、コードを書き換える必要があります。今回のサンプルはGmailを利用してメール送信を実現しましたが、接続しているプロバイダのアドレスを使ってメールを送信する場合や、自前サーバからの送信の場合は、サンプルのままで動作するかどうかはわかりません。
(例えば、pop over smtpのように先にpop3のサーバと認証しておくというシーケンスになる場合など)
Gmailの場合は、認証がありますので、認証用のオブジェクトを生成しておいて、SendMail関数の引数に指定しています。認証が無ければ、nilを指定すれば良いと思います。

最近、Golang Cafe関係のblogの内容が薄い(後日の動作検証などができていない)ので改善しないといけないなと思うものの、VBAと格闘しています…。
ちょっと本格的にがんばろう。

2014年2月11日火曜日

第23回GDG中国勉強会@岡山を開催しました。

第23回勉強会@岡山を開催しました。

今回は、Dart Flight Schoolということで、DartのハンズオンとAngularDartのハンズオン、それから、Dartのテストの方法を紹介しました。

資料は、この後、GDG中国のサイトで公開しようと思います。

最初のDartのTutorialを進めて行きました。
こちらは、差分表示があったので、スケルトンから進める事ができて良かったかな?と
思います。いくつか、Dart固有の書き方なども出てきたので、それも説明できたので、個人的には満足していますが、参加者の方はいかがでしょうか。

次に、AngularDartのTutorialを進めていきましたが、Dartのハンズオンで時間を使ってしまったので、説明ベースのものになってしまいましたが、これに関しては、AngularJSをある程度やっている前提でないと辛いのかな?と思いました。
(実際聞いていると、やってない人もいらっしゃったし、Tutorialを少し…。という方もいらした。)
また、講師を務めた私も勉強不足だったように思いました。(最近、こういう「事前の調査」が足りない気がするので、少し考えないとダメかも…)

最後に、Dartのテストについて、少し紹介しました。時間が少ないのと、事前の練習不足もあって、結果は出たものの、しっかり伝えられたかは”?”がつくかもしれません。

フィードバックとしては、JavaScriptに変換した時に6000行ぐらい出力されるという結果を教えてもらったので、mobileでは使えないかも知れない(ロードに時間がかかる)ということと、速度面でどれくらい早いのか?が疑問ということも言われました。
(1.0→1.1で25%の速度UPがされていますが、「当社比」なので、他との比較結果が欲しいというご意見も…)
導入しようにも、「環境が整わないと」という意見もあったので、DartVMがChrome(リリース版)に入るとか、Chrome Packaged Appで使えるという方向に進んでくれたら良いのにな。とも思ったりしています。

全体的に、Dart Editorの挙動が怪しくて、いまいち、動きを確認するのに手間取ったり、JavaScriptの変換とか、pub getコマンドが動かなかったりと、Dart Editorの品質がもう少し上がってくれると非常にうれしいんだけど…。

このイベントはDartのスタートなので、今後もDartを触ってみていただけたらと思います。
重い(ロードに時間がかかる)とはいえ、一応、(JavaScriptに変換すれば)本番導入もできますので…w

後は、少し懇親会で情報交換を行ったりもしたので、今後の課題の洗い出しは、またゆっくりやろうと思います。

2014年2月7日金曜日

Golang Cafe #15を開催しました。

Golang Cafe #15を開催しました。

今回はnetパッケージを使ったsocket通信でした。
サンプルコードはgithubにあります。
netパッケージは、tcp、udp、unixsocketを同じインターフェイスで扱える
きれいな設計になっています。

まず、サーバ側のプログラムのポイントから。
C言語だと、bind()→listen()→accept()の順番で関数を呼び出しますが、Goの場合は、bind()はありません。
したがって、以下のようにListen()とAccept()の順番で呼び出せばいいです。

 // Listenはtcp/tcp4/tcp6/unix/unixpacketでなければエラーになる。
 // エラーを繰り返すと、指定したアドレス名のファイルができる?(for MacOSX)
 listener, err := net.Listen("tcp", "localhost:22000")
 if err != nil {
  log.Fatalln(err)
 }

 for {
  conn, err := listener.Accept()
  if err != nil {
   log.Printf("Accept: %v\n", err)
   continue
  }

  go receiveGoroutine(conn)
 }

Accept()の戻り値でnet.Connインターフェイスのインスタンスが受け取れますので、Read()、Write()を使って、クライアント側に受信、送信します。
(実際の型は、net.TCPConnなので、Type Assertionすれば型変換ができます。
タイムアウトの設定は、net.Connからはできないので、net.TCPConnに変換してから、設定する必要があります)
Read()とWrite()の処理はos.Fileの時と変わりませんので、説明は省略します。

UnixSocketを使う場合も同様に、net.Listen("tcp", アドレス)を、net.Listen("unix", アドレス)に変更すれば、net.UnixConnが返されるのでTCPConnと同じように扱えます。

今回のサンプルはAccept()後、goroutineを使って別スレッドで送受信するようにしていますので、たくさんのクライアントからの接続があっても、同じ挙動をします。もし、1対1の通信であれば、goroutineにしなくても良いです。(そのようなサーバプログラムを書くことは無いかもしれませんが…。)

net.Connはio.Reader、io.Writer、io.Closerなどのインターフェイスは持っていますので、ioパッケージ周りの便利関数群は使えます。
また、encoding/binaryパッケージを利用して、エンディアンを考慮したデータ構造を作る処理も入れてあります。
encoding/binaryは便利ですが、引数で使える型の制約があるので、(int32とかだと、errorが返される)符号がない、uint32などを利用しなければいけません。

クライアント側のポイントは、Connect()の代わりにDial()を使うことです。

 var recvData uint32

 // net.Dial() タイムアウトがないConnect
 // net.DialTimeout() タイムアウト検知するConnect
 // -5とかだとi/o timeoutと出るが…。
 // Dialerからtimeoutを設定する方法もあるが…。

 conn, err := net.DialTimeout("tcp", "192.168.0.6:8888", 5 * time.Second)
 // conn, err := net.Dial("tcp", "localhost:22000")
 // dialer := net.Dialer{Timeout: 10 * time.Second}
 // conn, err := dialer.Dial("tcp", "localhost:22000")
 if err != nil {
  log.Fatalln(err)
 }

 for i := 0; i < 2; i++ {
  err = binary.Read(conn, binary.LittleEndian, &recvData)
  if err != nil {
   log.Printf("Receive: %s\n", err)
   break
  }
  log.Printf("Receive From Server %v\n", recvData)

  log.Printf("Send To Server %d\n", recvData + 1)
  sendData := uint32(recvData + 1)

  err = binary.Write(conn, binary.BigEndian, sendData)
  if err != nil {
   log.Printf("Send: %v\n", err)
   break
  }
 }

 if err = conn.Close(); err != nil {
  log.Printf("Close: %v\n", err)
 }

サンプルのコードは、タイムアウトの検証をしようと思った途中のところだったので、net.DialTimeout()を使っていますが、net.Dial()で接続開始します。

net.Dial()の戻り値もnet.Connなので、サーバ側と同様の通信処理を実装すれば通信可能です。

本番の時に、「UDPだけは、うまく動作しない」状態だったのですが、私がUDPのプログラムを作ったことがなかっただけのようで、本番時に送受信ができるようになりました。
ただ、TCP/Unixsocketとは違って、コツがいるのでメモを残しておきます。

UDPのListenは、udpConn, err := net.ListenUDP("udp", udpAddr)を使います。
関数が違いますので注意して下さい。

func receiveGoroutine(conn *net.UDPConn, ch chan<- int) {
 var count uint32 = 1

 for i := 0; i < 2; i++ {
  var data [1024]byte

  _, addr, err := conn.ReadFrom(data[:])
  if err != nil {
   log.Printf("Recv: %v", err)
  }

  buf := bytes.NewBuffer(data[:])
  err = binary.Read(buf, binary.BigEndian, &count)
  if err != nil {
   log.Printf("Buffer: %v\n", err)
   break
  }
  log.Printf("Receive From Client %d\n", count)

  count++

  log.Printf("Send To Client %d\n", count)
  bufW := new(bytes.Buffer)
  err = binary.Write(bufW, binary.LittleEndian, count)
  if err != nil {
   log.Printf("Buffer: %v\n", err)
  }
  _, err = conn.WriteTo(bufW.Bytes(), addr)
  if err != nil {
   log.Printf("Send: %v\n", err)
   break
  }

 }

 if err := conn.Close(); err != nil {
  log.Printf("Close: %v\n", err)
 }

 ch <- 1
}

受信の処理は、ReadFrom()、送信の処理はWriteTo()を呼び出すようにしなければ、クライアントとキャッチボールができません。(これに気がつくのに時間がかかってしまいました)
また、TCPのサンプルは「わざと」接続後にサーバ側から送信させるようにしましたが、UDPだと、そのシーケンスは不可能(?)ということになります。理由は、ReadFrom()で受け取った、Addrインターフェイスの情報をWriteTo()の引数に設定しなければいけないので、最初は必ず受信ということになってしまいます。
(そういうものなの?って感じはするが…。)

次回は、メール関連(net/mail、net.smtpパッケージ)の予定です。