ただでは済まなかったペルー旅行(7日目)

2015年9月中旬、夫婦(夫30歳、妻28歳)にてペルーを訪問。南米というと地理的にも移動手段的にも日本人にはとっつきにくく、事前情報も限られている地域である。地球の歩き方やインターネット上の情報を頼りに計画を立て旅行を実行したが、困難に直面した場面が多々あり、これからペルー旅行を計画する旅行者のための情報提供として旅行記を記したい。

旅程は1日目参照。

ナスカのツアー

ナスカに来た理由はただ一つ、ナスカの地上絵である。最もベーシックな地上絵の見方といえば、セスナで一気に飛んで空中から見る方法である。ただこのセスナ、小さくてほとんど見えないとかセスナ酔いでそれどころじゃなかったとか、ネガティブな情報があり、旅程もしんどいしなんかとにかく大変そうだったので、地上から見るというイージーかつ確実(チキン)な手段を採用。

ツアーは泊まった宿Anccalla Innで予約した。ナチュラルミラドール(丘)、ナスカの地上絵が見れるミラドール(鉄塔)、マリア・ライヘ博物館、パルパの地上絵が見れるミラドール(鉄塔)を3時間で回るツアーで車1台・ガイドが貸切のため料金は人数によって異なる。1人の場合は135ソル、2人の場合は一人70ソル、3人の場合は一人50ソル、4人の場合は一人40ソルだった。特に地球の歩き方によればパルパの地上絵が見れるツアーはないとのことであったので、ナスカの地上絵と一緒に見れることになってラッキーだった。時間は朝9時からにしてもらった。

Nasca Poster

Nasca Poster


続きを読む

ただでは済まなかったペルー旅行(6日目)

2015年9月中旬、夫婦(夫30歳、妻28歳)にてペルーを訪問。南米というと地理的にも移動手段的にも日本人にはとっつきにくく、事前情報も限られている地域である。地球の歩き方やインターネット上の情報を頼りに計画を立て旅行を実行したが、困難に直面した場面が多々あり、これからペルー旅行を計画する旅行者のための情報提供として旅行記を記したい。

旅程は1日目参照。

バスでナスカへ

リマには昨日着いたばかりだが、もう別の町に移動なのである。改めてこの旅は日程が鬼畜すぎる。考えたの自分だけど。

さて、ナスカにはペルーで最も信頼の置けるバス会社の一つであるクルズ・デル・スール(Cruz Del Sur)を利用する。専用のバスターミナルがミラフローレスの北、サン・イシドロの東にある。ナスカまでは7時間半掛かるため、なるべく朝の早い7:30の便をWebサイトから予め予約した。スタンダードシートで一人73ソルだった。時間によって料金は異なる。ミラフローレスのホテルB&B Miraflores Wasi Independenciaで前日にタクシーを予約した。ホテルから10分ほどでバスターミナルに到着した。タクシー代は14ソル。

Cruz Del Sur Bus Terminal in Lima

Cruz Del Sur Bus Terminal in Lima


続きを読む

ただでは済まなかったペルー旅行(5日目)

2015年9月中旬、夫婦(夫30歳、妻28歳)にてペルーを訪問。南米というと地理的にも移動手段的にも日本人にはとっつきにくく、事前情報も限られている地域である。地球の歩き方やインターネット上の情報を頼りに計画を立て旅行を実行したが、困難に直面した場面が多々あり、これからペルー旅行を計画する旅行者のための情報提供として旅行記を記したい。

旅程は1日目参照。

高地を去る

今日で山岳地帯を去ることになる。高山病にこそならなかったが、とにかく駆け足だったので、日程に余裕があればクスコにもう一日ぐらい滞在してもよいと思った。クスコ空港から首都リマへ向かう。

当たり前のように欠航、国内便

予めホテルにタクシーを呼んでもらって空港に移動。料金は25ソル。

Cusco Airport

Cusco Airport


続きを読む

ただでは済まなかったペルー旅行(3日目)

マチュピチュへ移動

クスコに到着して間もないが、早速マチュピチュに移動する。マチュピチュへの行き方は主に3つある。

  1. 近郊のポロイ駅まで車で行き、そこから列車
  2. 少し離れたオリャンタイタンボ駅まで車で行き、そこから列車
  3. マチュピチュ近郊の水力発電所まで車で行き、そこから歩き

ペルーレイルのタイムテーブル・料金表を見るとわかるが、列車は高価である。完全観光地価格。また、ポロイ発着はそもそも便がすくないので、基本的には2か3を選択することになるだろう。3については2日目で述べたように50ソル程度で済む。

各行き方での所要時間等は以下のとおり。地球の歩き方にも似たような図があるが、標高等をもう少し正しく示すならこの図のようになるだろう。

machupicchu_transport

3のケースでの最大標高に注目されたい。道中の高低差が大きく運転手の運転も荒いので酔い止めを用意したほうがよい。
続きを読む

ただでは済まなかったペルー旅行(2日目)

行きの成田で急遽予約したB&B Wasi Aeropuerto Limaはいわばリマでの乗り継ぎの間に一泊することになった旅行者向けのホテルである。設備は最小限だが、スタッフには英語が通じる。スペイン語圏の国には初めて行ったが、面白いほど英語が通じない。ここでは出発のタクシーの時間を英語で相談できて助かった。空港往復のタクシーは20米ドルで手配できた。これは割高だが、空港到着が夜遅く、また出発も朝早かったため、安心には変えられまい。

標高3,400mのクスコ

cusco

リマ-クスコ便はスターペルーを利用した。信頼性で言えばラン航空を選びたいところだが、スターペルーならば半額ほど(往復3万円ほど)で済む。クスコ行きの便は遅れなしで出航。クスコに到着してまず気をつけなければならないのは高山病だが、念のため予防薬を用意した。高山病については以下の項を参照。クスコ空港からは公式(と思われる)タクシーに乗ったが、地球の歩き方には15ソルと書いてあったが、30ソルだった。カウンターのプライスリストにそう書いてあったので公式な価格と思われる。

ガイドブックには首絞め強盗の注意喚起がされているが、クスコの町中は日中・夜間ともに観光客だらけであまり危険な感じはしなかった。
続きを読む

ただでは済まなかったペルー旅行(1日目)

2015年9月中旬、夫婦(夫30歳、妻28歳)にてペルーを訪問。南米というと地理的にも移動手段的にも日本人にはとっつきにくく、事前情報も限られている地域である。地球の歩き方やインターネット上の情報を頼りに計画を立て旅行を実行したが、困難に直面した場面が多々あり、これからペルー旅行を計画する旅行者のための情報提供として旅行記を記したい。

旅程

休暇が限られたサラリーマン故、旅程は以下のとおり。いつ倒れてもおかしくないスケジュール。

日程 行動 宿泊
1日目 18:30成田発-16:10ダラス着(AA60)
22:15ダラス発-翌5:19リマ着(AA980)
機内泊
2日目 8:05リマ発-9:15クスコ着(2I1181)
クスコ観光
クスコ泊
3日目 マチュピチュへ移動(バス+徒歩) マチュピチュ村泊
4日目 マチュピチュ観光
クスコへ移動(列車+バス)
クスコ泊
5日目 10:25クスコ発-11:45リマ着(2I1114)
リマ観光
リマ泊
6日目 7:30リマ発-15:00ナスカ着(観光バスCruz del Sur) ナスカ泊
7日目 ナスカ地上絵鑑賞
イカに移動(バス)
イカ泊
8日目 14:00イカ発-18:20リマ着(観光バスCruz del Sur) リマ泊
9日目 リマ観光
23:45リマ発-翌7:10ダラス着(AA988)
機内泊
10日目 10:40ダラス発-翌14:20成田着(AA175) 機内泊

突然の欠航

成田に着くと、アメリカン航空のチェックインカウンターでダラス-リマ便が欠航になった旨が告げられる。代替便として、ダラスからさらにマイアミで乗り継いでリマに半日程度遅れて着く便を提示されるが、それではリマの乗り継ぎに間に合わない。

アメリカン航空のスタッフが色々なところに電話したりして最終的に提案してくれたアグレッシヴな代替便が、以下のとおり。

  • 17:25成田発-同日11:25ロス・アンゼルス着(AA170)
  • 13:20ロス・アンゼルス発-23:45リマ着(LA601)

続きを読む

HerokuにGo言語+MongoDBのWebアプリケーションをデプロイする

Good! Counter Application

Herokuでは公式ではGo言語はサポートされていないにも関わらず、意外なほど簡単にデプロイすることができる。

http://good-counter-go.herokuapp.com/
https://github.com/inatus/good-counter-go

前準備

  • Herokuへアカウント登録
  • Heroku toolbeltのインストール
  • $GOPATHの設定

ググれば情報がいくらでも出てくる。

Go言語のソースフォルダの作成

$GOPATH/src内にソースフォルダを作成する必要がある。

$ mkdir $GOPATH/src/good-counter-go
$ cd $GOPATH/src/good-counter-go
$ git init

herokuプロジェクトの作成

Heroku Toolbeltにログインし、新規にプロジェクトを作成する。

$ heroku login
Enter your Heroku credentials.
Email: you@example.com
Password:
Uploading ssh public key /Users/you/.ssh/id_rsa.pub
$ heroku create -b https://github.com/kr/heroku-buildpack-go.git
Creating vast-brook-7638... done, stack is cedar
BUILDPACK_URL=https://github.com/kr/heroku-buildpack-go.git
http://vast-brook-7638.herokuapp.com/ | git@heroku.com:vast-brook-7638.git

プロジェクト名の変更

ランダムにプロジェクト名が付けられるので、任意の名前に変更する。

$ heroku apps:rename good-counter-go
Renaming vast-brook-7638 to good-counter-go... done
http://good-counter-go.herokuapp.com/ | git@heroku.com:good-counter-go.git
Git remote heroku updated

HerokuプロジェクトにMongoDBアドオンの追加

プロジェクトにMongoDBアドオン(無料版)を追加する。無料版を利用する場合であってもHerokuの設定画面からクレジットカード情報を入力する必要があるようだ。

$ heroku addons:add mongohq
Adding mongohq on good-counter-go... done, v4 (free)
Use `heroku addons:docs mongohq` to view documentation.

Go Webアプリケーションを作成

MongoDBと連携したWebアプリケーションを作成する。ポイントは以下の通り。

httpサーバのポートに環境変数PORTを指定

http.ListenAndServe(":"+os.Getenv("PORT"), nil)

MongoDBのURLに環境変数MONGOHQ_URLを指定

環境変数MONGOHQ_URLにユーザ名、パスワードも含まれている。

sess, err := mgo.Dial(os.Getenv("MONGOHQ_URL"))

データベース名をMongoHQの管理画面を確認して指定

HerokuのApps管理画面からMongoHQの管理画面に入り、DB名を確認する。Goが接続するDB名にこれを設定する。

Heroku Apps管理画面

MongoHQ管理画面

const MONGO_DB_NAME = "app21817638"
c := mgoSession.DB(MONGO_DB_NAME).C("count")

DBにコレクション・ドキュメントを挿入

コマンドラインから追加する場合

Admin画面のUsersタブより新規ユーザを追加し、コマンドラインから以下のようにコンソールにログインする。

MongoHQ User追加画面

$ mongo troup.mongohq.com:10084/app21817638 -u <user> -p <password>

管理画面から追加する場合

Colletions画面のCreate a collectionからコレクションを作成する。

MongoHQ Collection追加画面

Herokuレポジトリへのアプリケーションの登録

ソースをレポジトリにコミットする。

$ git add -A .
$ git commit -m 'Add source'

Heroku用の設定ファイルを作成、ライブラリをダウンロードし、レポジトリにコミットする。

$ echo 'web: good-counter-go' > Procfile
$ go get github.com/kr/godep
$ godep save 
$ git add -A .
$ git commit -m 'Add dependencies'

最後に、Herokuにpushする。

$ git push heroku master
-----> Fetching custom git buildpack... done
-----> Go app detected
-----> Using go1.1.2
-----> Running: godep go install -tags heroku ./...
-----> Discovering process types
       Procfile declares types -> web
-----> Compressing... done, 3.0MB
-----> Launching... done, v5
       http://good-counter-go.herokuapp.com deployed to Heroku

動作確認

$ heroku open

リソースをGithubにもpushする場合

Githubで新規レポジトリを作成し(自動initにしない)、以下のようにする。

$ git remote add origin https://github.com/inatus/good-counter-go.git
$ git push -u origin master

Go言語:go-gtkでネットワーク対戦型○×ゲームのネイティブアプリケーションを実装する

screen_shot

Go言語のGUIライブラリgo-gtkを利用してネットワークを介して対戦できる○×ゲームのネイティブアプリケーションを実装した。

コード:
github.com/inatus/noughts-and-crosses-go

使い方

事前準備

  • PC2台と対戦してくれる友達を用意する
  • GTK+2のインストール
    Macならbrew install gtk+でインストールするのが楽。
  • 6392ポートを開けておく。
    通信に勝手にこのポートを使う。

ビルド

$ go get github.com/inatus/noughts-and-crosses-go
$ cd $GOPATH/src/github.com/inatus/noughts-and-crosses-go
$ go build
$ ./noughts-and-crosses-go

解説

実装上の特徴は以下のとおり。

  • GUIにGTKを利用。つまりマルチプラットフォームに動作する(はず)。
    ライブラリはgo-gtkを利用。
  • 対戦のための通信にはUDPを利用。ポート番号は決め打ち。
    標準ライブラリnetパッケージを利用。

go-gtkライブラリには説明がなくて分かりにくかったが、他のGTK APIの解説サイトで解決。
これが役に立った。だいたい似ているメソッド名になっている。
GTK+ リファレンスマニュアル

ネットワーク対戦に必要な通信は下記の3つで成り立っている。

  • 他端末に自分の存在を知らせるために一定間隔でブロードキャストを繰り返すためのgoroutineを起動
  • 他端末から送られてくる通信を受信するためのgoroutineを起動
  • Startボタンや手を決めるボタンのクリック時に対戦相手にデータを送信

Go言語でもこういったネイティブで動くGUIアプリが作れるとなるとかなり用途が広がるだろう。

Go言語:goroutineとchannelを使ってWebSocketでブラウザと通信するXMPPチャットWebクライアントを実装する

screen_shot

Go言語でGoogle Hangout(旧Google Talk)などで利用できるXMPPチャットクライアントのWebアプリを実装した。
github.com/inatus/xmpp-web-client-go

使用した技術要素は以下のとおり。

  • Webブラウザとの通信はWebSocketを利用
  • チャットサーバとの通信はXMPPを利用
  • 受信データはGo言語のgoroutine、channelを用いて並行処理を行う

WebSocketには準標準パッケージのcode.google.com/p/go.net/websocketを利用する。

XMPPはcode.google.com/p/rsc/xmppを使おうと思ったが、Roster(コンタクトリスト)がうまく取得できなかったため、github.com/agl/xmppを利用することにした。

方針

本プログラムでは以下のように、異なるプロトコルの通信を扱う必要がある。

xmpp_chat_client_overview

Webブラウザから入力されたメッセージは、WebSocket経由で受信し(①)、XMPPサーバへ送信する(②)。
チャット先の相手がメッセージを送信した際には、XMPPサーバからメッセージを受信し(③)、WebSocket経由でWebブラウザに送信する(④)。
このようなリモートからの通信(①、③)を受ける際には並行処理が必要になる。

今回は、以下のようにWebSocket、XMPPからの通信を受けるgoroutineを別々に立て、メインの処理を走らせるgoroutineに集めて処理を行う方法をとった。

goroutine_structure

Go言語では、goroutine、channelを用いることで、こういった並行処理を驚くほど簡単に実現できる。

実装

まずコネクションを1つの型にまとめる。
goroutineとして実行する関数は、この型のメソッドとして定義する。(後述)

type session struct {
    talk *xmpp.Conn
    ws   *websocket.Conn
}

その上で、それぞれのコネクションからの受信データを格納するchannelを定義し、これを引数にしてgoroutineを起動する。
メインのgoroutineはそのままselect-case構文によりchannelからのデータを待ち受ける。
case文にそれぞれのchannelを指定してやれば複数のchannelでデータを待ち受けることが可能。
なお、okにはchannelが開いていればtrue、閉じていればfalseが返る。

ses := session{
    talk: talk, // XMPPのコネクション
    ws:   ws, // WebSocketのコネクション
}

stanzaChan := make(chan xmpp.Stanza) // xmpp.Stanza型のchannelを定義
go ses.receiveMessage(stanzaChan) // goroutineの起動

webSocketChan := make(chan string) // string型のchannelを定義
go ses.receiveWebSocket(webSocketChan) // goroutineの起動

for {
    select {
    case receivedStanza, ok := &lt;-stanzaChan: // XMPP用channelからのデータを待ち受ける
        if !ok {
            // エラー処理
        }
        // 処理
    case receivedMessage, ok := &lt;-webSocketChan: // WebSocket用channelからのデータを待ち受ける
        if !ok {
            // エラー処理
        }
        // 処理
    }
}

XMPP、WebSocketからの通信を待ち受けるgoroutineで行うことは、受け取ったデータを引数のchannelに渡すことのみである。

func (ses *session) receiveMessage(stanzaChan chan&lt;- xmpp.Stanza) {
    defer close(stanzaChan)
    for {
        receivedStanza, err := ses.talk.Next() // XMPPからのデータを待ち受ける
        if err != nil {
            // エラー処理
        }
        stanzaChan &lt;- receivedStanza // channelにデータを渡す
    }
}

func (ses *session) receiveWebSocket(webSocketChan chan&lt;- string) {
    defer close(webSocketChan)
    for {
        var receivedMessage string
        if err := websocket.Message.Receive(ses.ws, &amp;receivedMessage); err != nil {  // WebSocketからのデータを待ち受ける
            // エラー処理
        }
        webSocketChan &lt;- receivedMessage // channelにデータを渡す
    }
}

たったこれだけで上記の図に示すような並行処理を組むことができる。