プログラマでありたい

おっさんになっても、プログラマでありつづけたい

中島聡さんの珠玉のアーキテクチャ論まとめ

 サービスのアーキテクチャを考える上で、スケーラビリティを考えることは必須となります。いつも参考にさせて貰っているのが中島聡さんのアーキテクチャ論。まだ自分が作る上で実践出来ていない部分も多いですが、今後も取り入れていこうと思います。自分用のメモも兼ねて、読み返しやすいようにピックアップしました。


Life is beautiful: マルチ・スレッド(multi-thread)プログラミングの落とし穴、その1(かもしれない)
Life is beautiful: マルチスレッド・プログラミングの落とし穴、その2

 そう考えると、私にはCreate/Update/Deleteのリクエストに対して、クライアントを待たせながら(つまり、HTTP Requestの処理に必要なスレッド・プロセスを保持したまま)データベースに変更をかけることが根本的に間違っているように思える。

 そうではなくて、Create/Update/Deleteのリクエストに関しては、そのリクエストをキューにしまい、クライアントにはすぐにレスポンスを返した上で(つまり、HTTP Requestの処理に必要なスレッド・プロセスはすぐに解放して)、別プロセス(それもシングルプロセス)でキューにたまったリクエストを順繰りに非同期で処理すべきだ。


Life is beautiful: スケーラビリティとユーザービリティの話

 また、通常ピーク時に3秒で処理出来ているところに、通常の倍のトラフィックが来てしまった時に、それを倍の6秒で処理出来るように作ってあるか、倍以上の9秒かかってしまうように設計してあるか、というのが「リニアにスケールするかどうか」という部分である。HTTP Requestに対するレスポンスが遅くなると、それに応じて同時アクセス数が増えてしまい、ペンディング中のスレッドが増えてしまう、そのためにさらにレスポンスタイムが遅くなる、という悪循環は、マルチスレッド型のHTTPを使ったアーキテクチャの根本的な弱点。安易にマルチスレッドに頼ったプログラミングは良くない理由はこの辺りにもある。


Life is beautiful: 「RESTful MVC」なアーキテクチャの話

 もちろん、核となるのはビジネスロジックを含んだModelの部分。そこをしっかりと実装し、内部構造を隠す粒度の荒いインターフェイスを定義し、外から何をされてもデータの整合性が壊れない様にすることは何よりも大切。


Life is beautiful: HTML5時代の「運営しやすいアーキテクチャ」の話

 まず一番の特徴は、テンプレート等を利用したHTMLのダイナミックな生成をすべてやめて、データ(JSONもしくはXML)だけをダイナミックに生成するようにし、HTMLはスタティック・ファイルをサーバー側に置いておく(上の図で、CSS, HTML, JS, Media Filesはすべてスタティック・ファイル)。

Life is beautiful: テクノロジー万歳

「戦略的OS」の開発がことごとく失敗している点に関する一考察
 「日の丸OS」だったはずのB-Tronもどこかに行ってしまったし、そもそも「戦略的OS」を意図的に作るってことにかなり無理があるんじゃないかと思える。結局のところ、ソフトウェア作りはアートに近くて、大企業が資金力にまかせて優秀なエンジニアを集めても無理があって、少人数で作ったものが市場原理で自然淘汰されてこそ良いものができると思うんだがどうだろう。

 身も蓋もないけど、ソフトウェア業界って多産多死型がよいのかもしれませんね。試してみて駄目だったらさっさと撤退して、次の成功に備えるとか。


ビューティフルアーキテクチャ

オライリージャパン
売り上げランキング: 5370

無料でVMware Playerのイメージを作る方法

 Windowsの開発環境が必要になったので、VMWare上に作ることにしました。VMWare Playerは無料で配布されているのですが、ゲストOSを作るにはVMWare Workstationが必要になります。しかし、実はツールを使ってVMWareのディスクイメージを作ってしまえば、VMWare PlayerオンリーでゲストOSを作れます。以下、手順のメモです。


 定番のディスクイメージ作成ツールはQEMUだと思うのですが、何とオンラインで簡単にディスクイメージを作成してダウンロード出来るサイトがありました。
 EasyVMX! さっそく試してみたのですが、ものの数分でイメージを作成できました。easyvmxを選択して、VM名、GuestOSの種類、メモリ・ディスクサイズ、CPUの数等を決定するだけです。簡単で、かなり気に入りました。



 ディスクイメージ作成後は、VMWare Playerを立ち上げてOSのインストールを行います。その後は、通常のVMWareと同じです。言わずもがなですが、GuestOSのOSのライセンスはもちろん必要ですよ。



参考にしたサイト:
Beginner's Guide:VMware Playerを用いたイメージの作成法 - ITmedia エンタープライズ
あるSEのつぶやき: VMWare Player + CentOS5 環境構築メモ

サイボウズの予定を、Googleカレンダーを使ってiPhoneのカレンダーと同期する

 仕事のスケジュール管理は、サイボウズを使用しています。またプライベートのスケジュールはGoogleカレンダーを使っています。そして普段持ち歩いているのはiPhoneです。スケジュールをiPhoneで一本化して見たいなぁと思って試行錯誤してみました。幾つか方法があるのですが、サイボウズのスケジュールをGoogleカレンダーに同期を取って、更にGoogleカレンダーとiPhoneのカレンダーを同期させれば良いようです。少々手間は掛かりますが、一度設定してしまえば後は自動で同期がとれるのでかなり便利です。


 全体の流れとしては、次の通りです。
1. Cybozu2ICalを使って、サイボウズのスケジュールをiCal形式でエキスポートする
2. iCalファイルをWebサーバに公開する
3. Googleカレンダーから、Web上のiCalファイルを読み込むように設定する
4. Micrisoft Exchangeの機能を使ってiPhoneのカレンダーとGoogleカレンダーを同期する


 では、順を追って設定していきましょう。

1. Cybozu2ICalを使って、サイボウズのスケジュールをiCal形式でエキスポートする

 まずは、サイボウズとGoogle Calendarの同期を取ります。Ogawa::BuzzさんのCybozu2ICalというCPANモジュールが便利です。事前に下記のモジュールをCPANでインストールしておきます。

Text::CSV 1.0+
DateTime
LWP
Crypt-SSLeay
Class::Accessor::Fast
Data::ICal
YAML または YAML::Tiny 

その後に、Cybozu2ICalのモジュールをダウンロードして解凍するだけです。


次に環境ファイルの設定です。

cybozu_url: http://www.example.com/cbag/ag.cgi
calname: Your Calendar Name
username: user
#userid: XX
password: pass
time_zone: Asia/Tokyo
tzname: JST
input_encoding: shiftjis
output_encoding: utf8
#output_encoding: ncr
calendar_driver: SyncCalendar
#calendar_driver: ApiCalendar
date_range: 30

 urlやusername,passwordはそれぞれ設定して貰うとして、注意すべきは文字コードとcalendar_driverです。私の場合、最初にUTF-8でインポートしたらGoogleカレンダーの方で文字化けしました。そこで、ncrというコードに変更したらGoogleカレンダーの方では文字化けしなかったのですが、iPhoneのカレンダーで文字化けしました。色々試行錯誤したところ、文字コードをUTF-8で指定して.htaccessで文字コードの指定をしてやればGoogleのbotはちゃんと読み込んでくれるようになりました。(他の環境では試していないので、人によっては変わるかもしれません。)
 calendar_driverについては、お使いのサイボウズによって変える必要があるようです。


設定ファイルをconfig.yamlとして保存して下記のコマンドを実行します。

$ ./cybozu2ical > cybozu.ics

2. iCalファイルをWebサーバに公開する

 無事、iCal形式でエキスポート出来ればひとまず成功です。次は、適当なWebサーバにファイルを公開しましょう。注意点は、Webサーバにアップロードする為に、検索エンジンからインデックスされる可能性があることです。ファイル名を長くしたり、.htaccessで制限したりとある程度の対策はとれますが、根本的には人に見られる可能性があるということは必ず念頭に置いておいて下さい。また、前述の通り、.htaccessで文字コードの指定をしてください。

AddType "text/calendar; charset=UTF-8" .ics


 GoogleカレンダーからのiCalファイルの読み込みはGoogleBotで来るようなので、下記の設定を追加すればよいはずです(試していません) 2009/10/05追記:駄目っぽいです。後でアクセスログがちゃんと出る環境を作って試してみます。

SetEnvIf User-Agent "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html" calbot
order deny,allow
deny from all
allow from env=calbot

 1,2のエキスポートと公開の部分をcronで登録しておけば、自動的にサイボウズとGoogleカレンダの同期がとれるようになります。

3. Googleカレンダーから、Web上のiCalファイルを読み込むように設定する

 次にGoogleカレンダーの方から、iCalファイルを読み込むように設定します。これは割と簡単で、Googleカレンダにログインして、「他のカレンダーを追加」->「URL」を選択して、先ほど公開したURLを入力するだけです。追加した後で数分で同期されます。その後は、定期的に同期を取ってくれるようです。

4. Micrisoft Exchangeの機能を使ってiPhoneのカレンダーとGoogleカレンダーを同期する

 最後にiPhoneのカレンダーとGoogleカレンダーの同期を取ります。iPhoneでGoogleカレンダーを見るよって方は必要ないかもしれません。やり方はこのサイトに詳しく書いているので、簡単に説明します。
1. iPhoneの設定から「メール/連絡先/カレンダー」を選択する
2. アカウントの追加でMicrisoft Exchangeを選択する
3. アカウントの設定を行う
   ユーザ名、パスワードにGoogleのを設定した後に、サーバにm.google.comといれる
4. Google syncの設定を行う
   iPhoneのsafariで、http://m.google.com/sync/にアクセスして設定する。
   言語モードをEnglishにしてから、アカウントの設定をするだけ


 以上です。文字コードでハマった以外は、すんなりいけました。かなり便利ですよ。一度お試しあれ


参考にしたサイト:
Ogawa::Buzz: Cybozu Office 6のカレンダーを Google Calendarで表示する
Cybozu2ICal - ogawa - サイボウズオフィス6のカレンダーをiCalendar形式に変換するスクリプト。 - Project Hosting on Google Code
【便利!】サイボウズのスケジュールをGoogleカレンダーで確認!!|イムIT! - 仏教系!?IT活用ライフハックメモ
iPhone標準のカレンダーとgoogleカレンダーを同期させる一番便利で簡単な方法(複数のカレンダーのアカウント共有可)
Cybozu2ical (alglab)


Gmail超仕事術―効率と生産性が飛躍的にアップする!
山路 達也 田中 拓也
アスペクト
売り上げランキング: 12927

アジャイルな開発を支えるツールとマシンパワー

 最近、現場レベルだと当たり前のようにアジャイルな開発が導入されてきていると思います。要因としては色々あると思いますが、開発ツールの進化と個々人のマシンパワーが強力になってきたのも見過ごせないと思います。
 JavaでのWeb開発を例にすると、一昔前だとローカルにTomcatとDBを動かしてEclipseで開発してビルドなんかすると重くて使い物になりませんでした。そうなると、ローカルでコーディングしてソースを開発サーバに上げてビルドして動作確認というステップになります。開発サーバは共用なので好き勝手にビルドする訳にもいかず、ライブラリアンのような人間が必要になってきます。もっと昔になると、コンパイルに一晩とかあったとか。
 しかし今は一瞬でビルドが出来るようになっています。そうなると設計に時間をかけるより、さっさと動くものを作った方が早いよという話になると思います。よしんばバグがあったり要件と違っていたものと違っていたとしても、さっさと直してしまえば良いのです。設計コスト<開発コストから設計コスト>開発コストに移行しているのかなと感じます。(もちろんケースによります)


 というところで、アジャイルな開発で必須と思われるツール群です

1.モダンな言語
 贅沢は言いません。オブジェクト指向で作れれば良いのです。
 agileな開発では、ユニットテスト出来る言語というのが必須なのかと思います。
2.ローカルの開発環境
 自分のローカルの環境で完成系の形で動かせることは重要だと思います。
 実装して、ユニットテストして動くのを確認してからコミット
 Eclipseで殆どサポート出来るんですよね。
3.分散型バージョン管理
 Subversionも良いけど、分散コミットが出来ないのでgitとか
4.チケット管理
 TracかRedmine、どちらでも良いけど、誰がどのタスクをしているか
 かつ、ソースコードと連動するBTSも必須だと思います。
5.テストツール
 開発工程の中には、必ずユニットテストの作成をいれること
 後は、Seleniumのようなものを使ってUIのテストも自動化したい
 またそれをTestLinkで管理出来れば幸せ
6.ビルド・テストの自動化
 せっかくバージョン管理やユニットテストを頑張ってやっても、ビルド・テストの実施が手動だと行われません。
 ということで開発者に意識させずにビルド・テストを実行してくれる構成管理ツールが必須です。
 今だとHudsonの一択かな


 後、何かよいのないですかねぇ?

Google App Engineの始め方

 八角研究所 : 誰でも簡単にできる Twitter ボット作成入門を読んでいて、Twitter botを作ってみたいなぁと思いました。Twitter API仕様書の日本語訳を読んでいたのですが、やり取りは全てHTTPなので簡単そうです。空いているサーバにボットをしこめば簡単に運用できそうです。が、それだとあまり面白くないので、Google App Engineを使って実装してみようと思います。Pythonを使いこなせれば良いのですが、残念ながらそれに挑戦する時間的余裕が無いのでJava版の方を使うことにします。以下、準備編です。

アカウントの作成

今使っているGoogleのアカウントがあるのであれば、それにGoogle App Engineもサインアップするだけです。すぐ出来ました。
Google App Engine - Google Code

スタートガイドを読む

スタート ガイド: Java - Google App Engine - Google Code
・Java 5 or 6で動く
・開発環境はEclipseとGoogle Plugin for Eclipseがお勧め
SDKの中にデモアプリが入っている
・プロジェクトの作り方
・アプリケーションの作り方
  JDOの部分がポイントかな
・アプリケーションのアップロードの仕方

開発環境の準備

Eclipseにプラグインの追加をするだけで準備完了です。簡単
Installing the Plugin - Google Plugin for Eclipse - Google Code
詳しい事は、次のページから
Google Plugin for Eclipse の使用 - Google App Engine - Google Code

デモアプリの起動

 Google Plugin for Eclipseの使用のページの通りに設定して、Webサーバを起動してhttp://localhost:8080/にアクセスしたら、あっさりとGAEのアプリが見れました。拍子抜けするほど簡単です。


 次回は自作のアプリを作ってみようかと思います。

郵便番号データに緯度経度を付加する手順

 一昔前に、GoogleMapsから郵便番号に経度緯度情報に変換するプログラムを書きました。そのデータを使おうと思ったのですが、見当たりません。確かブログにコード書いていたなぁと思って、過去の記事「郵便番号から緯度経度情報を取得する」を見たのですが、我ながらこれはひどいw

 緯度経度を取るには、住所から検索する必要があります。郵便番号直では出来ないので注意してください。一連のデータを作る方法は次回書きます。

 続きを書いていませんね。と言う訳で、その当時どんなソースを書いたか謎ですが、さくっともう一度作りました。以下手順とプログラムです。


 まずは郵便番号データをダウンロードしてきて、必要な形に整形します。
utf8に変換して、必要な項目のみピックアップします。ついでに、少しだけ項目の補正をします。補正処理はもっともっと色々しないといけないことがあるのですが、ここでは割愛します。

nkf -w KEN_ALL.CSV > ken_all.csv
cat ken_all.csv | awk -F,  '{print $3","$7","$8","$9}' | sed -e s/以下に掲載がない場合// > zip_cds.csv


 次はデータベースにインポートします。ここではDBはMySQLを使用しています。
テーブルを作って、データの流し込みを行います。

mysql yourdatabase
CREATE TABLE zip_cds (
        id INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
        zip_cd CHAR(7),
        pref_name_kanji VARCHAR(21),
        city_name_kanji VARCHAR(234),
        town_name_kanji VARCHAR(234),
        longitude FLOAT,
        latitude FLOAT
)
DEFAULT CHARSET=utf8
;
CREATE INDEX idx_zip_cd ON zip_cds (
        zip_cd
);

load data local infile '/foo/var/zip_cds.csv'
into table zip_cds
fields terminated by ',' enclosed by '"'
(zip_cd,pref_name_kanji,city_name_kanji,town_name_kanji);


結果は次のようになります。
番地の辺りに改善の余地がありそうですが、とりあえず放置してあります。

mysql> select * from zip_cds limit 10;

                                                                                                                                                                                                                                              • +
id zip_cd pref_name_kanji city_name_kanji town_name_kanji longitude latitude
                                                                                                                                                                                                                                              • +
1 0600000 北海道 札幌市中央区 NULL NULL
2 0640941 北海道 札幌市中央区 旭ケ丘 NULL NULL
3 0600041 北海道 札幌市中央区 大通東 NULL NULL
4 0600042 北海道 札幌市中央区 大通西(1〜19丁目) NULL NULL
5 0640820 北海道 札幌市中央区 大通西(20〜28丁目) NULL NULL
6 0600031 北海道 札幌市中央区 北一条東 NULL NULL
7 0600001 北海道 札幌市中央区 北一条西(1〜19丁目) NULL NULL
8 0640821 北海道 札幌市中央区 北一条西(20〜28丁目) NULL NULL
9 0600032 北海道 札幌市中央区 北二条東 NULL NULL
10 0600002 北海道 札幌市中央区 北二条西(1〜19丁目) NULL NULL
                                                                                                                                                                                                                                              • +

10 rows in set (0.00 sec)


 最後にperlのコードです。下準備としてCPANのGeo::Coder::GoogleMapsをインストールしておく必要があります。また、GoogleMapsAPIのキーも必要です。

#!/usr/bin/perl

use strict;
use warnings;

use DBI;
use Data::Dumper;
use Geo::Coder::GoogleMaps;

my $apikey = 'yourapikey';
my $gmap = Geo::Coder::GoogleMaps->new( apikey => $apikey, output => 'xml', host => 'maps.google.co.jp');

my $user = 'db_user';
my $passwd = 'db_password';

my $dbh = DBI->connect('DBI:mysql:test:localhost', $user, $passwd);
$dbh->do("set character set utf8");



my $sql;
$sql = "SELECT count(*) cnt FROM zip_cds WHERE latitude is null";
my $count = $dbh->selectrow_array($sql);
print $count,"\n";


my $i = 0;
my $increment = 1000;
my $update = "UPDATE zip_cds SET latitude=?,longitude=? WHERE id = ?";
my $sth = $dbh->prepare($update);
while ($i <= $count) {
  $sql = "SELECT * FROM zip_cds WHERE latitude is null limit $i,$increment";
  my $rows = $dbh->selectall_arrayref( $sql,{ Columns => {} });
  foreach my $row (@{$rows}) {
    my $address = $row->{'pref_name_kanji'}.$row->{'city_name_kanji'}.$row->{'town_name_kanji'};
    print $address,"\n";
    my $location = $gmap->geocode(location => $address);
    if (defined($location)) {
      print $location->latitude,',',$location->longitude,"\n";
      $sth->execute($location->latitude,$location->longitude,$row->{'id'});
    }
    sleep 1;
  }

  $i = $i + $increment;
}

 1件ごとにスリープを入れているのは、あまり連続でやるとGoogleさんに怒られるからです。郵便番号データが12万件くらいで、1秒のスリープを入れているので最低33時間以上かかります。GoogleMapsAPIの返事も1件あたり0.5秒くらい掛かっているので、全件終了するのに二日以上掛かるでしょう。実行速度を上げたければ、複数のAPIキーを取って多重に実行すれば多分いけると思います。(追記2を参照してください)
 こんなことを皆がしても無駄なので、著作権のあるデータでもないことですし、作成後にどこかで公開しようかと思います。


 ちなみに、上記の処理で経度緯度情報がとれない住所も結構あります。そこは後で対策しようかと考えています。とりあえずの郵便番号から緯度経度への変換ということで。


追記:
※書いた後で気がついたのですが、GoogleMapsAPIの制約で、1日のリクエストの上限がありますね。1万5千。


追記2:
Google Japan Blog: ジオコーディングの制限を IP アドレス単位に変更します
APIの使用制限は、APIキー単位ではなくIPアドレス単位のようです。


追記3:
位置参照情報データと郵便番号データのマッチング
 位置参照情報データといって、住所と位置情報のマッピングをしているデータがありました。これと郵便番号データをマッチングしたら、だいたい郵便番号データの半分に一気に位置情報に変換できます。これをした後に、上の対応をしたら時間短縮につながりますね。