Home > Web+DB
Web+DB Archive
Amazonからメール送信制限に関するEC2 Account Notificationが来た
とあるイベントで使うWebシステムでEC2を使ってたところ、Amazonからメール送信制限に関する通知が来たという話です。
このイベントでは、短時間で多くのリクエストを捌かないといけないので、あらかじめWebサーバを何台か立てて準備していました。さらに想定を超える負荷に備えて、増設用AMIも作成して、すぐに増設できる体制にしていました。もちろん増設のリハーサルもやりました。
そして、イベントが始まります。
アクセスがボチボチ来始めました。リソースにはまだまだ余裕があるので順調に捌いてくれています。とりあえず無難な立ち上がりだなと、ひと安心したところにメールが一通。
EC2 Account Notification
Dear EC2 Customer,
You recently reached a limit on the volume of email you were able to send out of SMTP port 25 on your instance:Instance ID: i-xxxxxxxx
* IP Address: XXX.XXX.XXX.XXX
* Start date: 2010-NN-NN NN:NN +0000
……
うん?メールの送信制限?何これ?
たしかにメール送信は行っていますが、1hで数十通程度です。しかも一気に送るというよりバラバラと送信しています。この程度で制限がくるとは思わなかったのですが、イベント中に送信を止められるとシャレにならないので、すぐにメールに記載されたフォームからメール送信制限解除申請を送りました。
Request to Remove Email Sending Limitations – Amazon Web Services
本文の内容でググってみると同じようなメールが来た人は何人か見つかったのですが、解決したという内容は見あたりませんでした。中には実際に Port25 をblockされて、仕方なく外部のメールサーバに別ポートで接続してメールを送信している人もいました。
その時点では送信はできていたので、イベントが終わるまではとにかく持ってくれという心境でした。
何とか回避策を
制限解除申請をしたものの、ぼーと待っていても仕方が無いので取れる策をということであれこれやってみました。
まず、送られてきたメールにはインスタンスIDとIPが記載されていたので、別インスタンスとIPなら回避できるかもと思い、作成しておいたAMIから新しいインスタンスを起動しました。さらにElastic IPで別IPを発行しました。これはもともと高負荷時にスケールアウトすることを想定していたのですんなりと準備できました。
ここでふと、もしかするとアカウントを停止させられるかも、という考えが頭をよぎりました。もしそうなると何台増設していても意味がありません。
実際にそうなるかは分かりませんが、とにかく止められないので、新たなアカウントを発行することにしました。てきぱきと登録をこなして、スケールアウト用AMIの共有設定を行い、なんとかLB+Web+DBの構成を構築しました。
あらかじめDNSのTTLは短めにしておいたので、もしアカウントを停止された時はDNSを書き換えて別アカウントの構成にアクセスを向けます。
この間もサービスとしては問題無く稼働していました。
Amazonからメールが
あれこれやっているとAmazonからメールが来ました。
内容は「申請を受け付けた」「2営業日以内に回答するよ」といったもの。2営業日以内って!とか思いつつ、とにかく回避策をひたすら準備。
サーバログ(主にmaillog)と睨めっこしながらイベントが無事終わるのを待ちます。inboundもoutboundも変なエラーは無く順調に稼働しています。自分でもサイトにアクセスして試してみますが問題ありません。とにかく無事に終わってくれ。。。
乗り切った!
結局、blockされることなく無事にイベントは乗り切ることができました。やれやれ。
再びAmazonからメールが
無難にイベントが終わって、ひと息ついていた頃に再びAmazonからメールが来ました。
内容は「制限を解除した」とのこと。
本文には申請したインスタンス、IPに対する制限を解除しただけなく、アカウントに紐付く全てのインスタンス、IPに対する制限を解除した、と書かれていました。
対応は2営業日以内と書かれていたのですが、申請から数時間で対応してもらえたのは驚きでした。
イベントはこの日限りで翌日にはサーバを停止する予定だったのですが、今後の利用のためにElastic IPで割り振ったIPだけは残しておくことにします。
EC2からメールを送る際は念のため申請を
イベント中に来たAmazonからのNotificationには本当に焦りました。
それほど多くのメールを送ったわけではなく、どういった基準でこの通知が来るかは分かりません。イベント前のテストでは問題無かったので、閾値は低いものの一定時間中に一定数を外部に送信すると通知される仕組みなのかもしれません。
とにかく、ここぞの時にblockされるとシャレにならないので、EC2から数多くのメール配信を行うなら、あらかじめ制限解除申請をするなり、別環境を用意するなり準備をしておいた方が良いでしょうね。
- コメント (Close): 0
- Trackbacks: 1
CakePHPとnginx+memcachedで手軽にキャッシュを活用する
nginx+memcachedがめちゃ気になったので試してみました。
元ネタは下記です。
nginxをリバースプロキシに利用した構成で、バックエンドの出力をmemcachedにキャッシュしておけば、次回リクエストではnginxがそのキャッシュを読み取ってそのまま出力してくれます。
つまりバックエンドにリクエストを経由させずにnginxから即出力するのでかなりの高速化が見込めるという優れものです。
リンク先ではバックエンドにDrupalを利用していたのですが、ここではCakePHPを利用してみます。
1. 全体構成
リバースプロキシにnginx(Port: 80)を使い、バックエンドにはapache+mod_php(Port: 8080)を使います。
nginxはリクエストが来るとURLをキーとしたキャッシュがmemcachedにあるかを調べます。キャッシュがあればそれをレスポンスとしてそのまま返します。
キャッシュが無い場合はバックエンドにリクエストが飛びます。バックエンドではページ生成処理を行い、出力内容をmemachedURLにキャッシュします。
これにより次回リクエストからはこのキャッシュがnginxから出力されます。
試したバージョンは以下。
- nginx 0.7.65
- memcahed 1.2.8
- apache 2.2.14
- PHP 5.2.10
- CakePHP 1.2.6
2. nginx+memcached設定
nginxのmemcached連携を行うためにNginxHttpMemcachedModuleを利用します。NginxHttpMemcachedModuleはnginxをデフォルトでインストールすれば含まれています。
nginx.confを以下のように設定します。
# バックエンド upstream backends { server 127.0.0.1:8080; } http { #(snip) server { #(snip) location / { # POSTはそのままバックエンドへ if ($request_method = POST) { proxy_pass http://backend; break; } # memcachedにキャッシュがあればキャッシュを # 無ければバックエンドへ set $memcached_key $uri; memcached_pass 127.0.0.1:11211; default_type text/html; error_page 404 502 = @fallback; } location @fallback { proxy_pass http://backend; } } }
ここで注意が必要なのが「error_page 404 502 = @fallback;」の箇所。
memcachedにキャッシュが無い場合はバックエンドへリクエストを投げるのですが、この箇所を[=]抜きにすると404(キャッシュが無い)や502(memcachedが繋がらない)がステータスコードとして出力されてしまいます。
つまりキャッシュが無い場合だとページ自体はバックエンドが生成した内容が出力されるのですが、ステータスコードが404となってしまいます。
[=] を付けるとバックエンドで出力されたステータスコードがそのままレスポンスとして出力されるので、[=]を忘れないようにしましょう。(はまりました。。。)
3. バックエンドでキャッシュ処理
次にバックエンドで生成した内容をmemcachedにキャッシュします。
まずPHPからMemcacheにアクセスするために、pecl::memcacheをインストールします。
$ sudo pecl install memcache
ここではCakePHPでの処理を想定しており、MemcacheViewHelperというヘルパを作成してキャッシュを行います。
MemcacheViewHelperはafterLayoutメソッドでビューからの出力全体をmemcachedにキャッシュします。
ちなみにHelper#afterLayout()はフレームワークから呼ばれるフックメソッドで、下記のように出力全体に対して処理を行うことが出来ます。例えば文字エンコーディングを変換する時などなかなか便利なメソッドです。
[app/views/helpers/memcache_view.php]
<?php class MemcacheViewHelper extends AppHelper { public function afterLayout() { if (!empty($this->params['memcache_view']['is_cache'])) { $view = ClassRegistry::getObject('view'); $memcache = new Memcache(); $memcache->addServer('localhost', 11211); $memcache->set(Router::url(), $view->output, false, 3600); } } }
コントローラでMemcacheViewHelperを読み込みます。キャッシュを有効にするアクションでは$this->params[‘memcache_view’][‘is_cache’]にtrueをセットします。
下のUsersControllerの場合、indexアクションはキャッシュされますが、viewアクションはキャッシュされません。
[app/controllers/users_controller.php]
<?php class UsersController extends AppController { public $name = 'Users'; public $helpers = array('MemcacheView'); public function index() { $this->User->recursive = 0; $this->set('users', $this->paginate()); // キャッシュする $this->params['memcache_view']['is_cache'] = true; } public function view($id = null) { if (!$id) { $this->Session->setFlash(__('Invalid User', true)); $this->redirect(array('action' => 'index')); } $this->set('user', $this->User->read(null, $id)); } }
これでブラウザからnginx(ex. http://localhost/users/index)にアクセスすると 1 回目はバックエンドにリクエストが飛びますが、2回目以降はmemcachedのキャッシュが出力されます。
4. パフォーマンス
キャッシュの効果を見るために、nginx+memachedを使ったパターンとバックエンドに直接接続した場合とでパフォーマンスを計測してみました。
計測はabで行っています。(-c 100 -n 1000)数値はRequests per secondで、5回行った平均値です。
参考にキャッシュをファイルに出力してnginxから出力するパターンも計測しました。
構成 | Requests per second | |
---|---|---|
apache+mod_php+CakePHP (ダイレクト) |
13.814 | 100% |
nginx+memcached | 2034.262 | 14726% |
nginx+ファイル(参考) | 5806.798 | 42037% |
やはりapache+mod_php+CakePHPよりnginx+memcachedが圧倒的に早いですね:D ここまで大きな差があると使ってみたくなります。
5. memcached or ファイル
上記パフォーマンスを見るとmemcachedよりfileの方がより多くのリクエストを捌いています。
パフォーマンスだけを見るとファイルが有利ですが、memcachedの方はnginxサーバやWebサーバが複数台になった場合にキャッシュの取り扱いが簡単(複数台で共有しやすい)という利点があります。また、memcachedなら有効期限が簡単に付けられるというのもあります。
どちらを使うのが良いかはケースバイケースですが、キャッシュの取り扱いを考えると個人的にはmemcachedが良いバランスかなと思っています。更新頻度が少ないがアクセス数が多いページだけはファイルに出力してしまうという方法もあります。
いかに遅い箇所を経由させずにレスポンスを返すかという視点で見るとnginx+memcachedはまさに願ったり叶ったりの構成です。ここではバックエンドにCakePHPを使いましたが、他のフレームワークでも他のシステムでも他の言語でも何でも理屈は同じです。
多くの環境から操作できるmemcachedをキャッシュエンジンに使うということは、バックエンドを選ばずにキャッシュ機構が使えるということで、これはよく考えられていますね。
- コメント (Close): 0
- Trackbacks: 6
PHP×携帯サイト デベロッパーズバイブル
PHP×携帯サイト デベロッパーズバイブルを著者の荒木さんから献本して頂きました。
# 荒木さんおめでとうございます&ありがとうございます。
現場から生まれた本
ざっと読ませて頂いたのですが、さすが携帯サイトの開発に携われてきた荒木さんが書かれた本だけに、様々なノウハウがぎゅっと詰まっています。
3キャリアの公式情報から目的別(文字エンコーディング変換やらメールやら絵文字やらセッション管理等々)に欲しい情報が抽出されているのはもちろんのこと、公式の情報には明示的に記載されいていないノウハウが解説されています。
# 個人的にはメール絵文字(特にVodafone/Softbank)が泣けてきました:-D
こうした情報は実際に携帯サイトの開発に携わらないと分からない点で、まさに現場から出てきた本だと言えます。
対象機種選定の材料に
本書では携帯サイトの仕様について多くの解説があるのですが、構築する携帯サイトの対象機種を現行機種(3G)に限定すると、全てを実装する必要はありません。(もちろん本書内でも触れられれています)
これから携帯サイトを作ろうとしている人が陥りがちなのが、こうした仕様を見ると、全てを実装しようとしてしまう、すべきだ、と思い込んでしまう事です。
全端末対応が理想なのは確かなのですが、下位機種については対応する実装が複雑になりがちなわりにユーザ数が少なかったりするので、こうした機種は対応しないという選択肢が現実的だったりします。
そうしたケースで判断に必要なのが「下位機種対応のコスト」で、コストを計る上でこの本にある詳細な解説が参考になります。(単純にクライアントや上司を説得する材料になりますね)
そういった意味では、実際に開発をしなくても、携帯サイト制作に関わる人(デザイナさんや営業さん等々)なら、携帯サイトの仕様を知る面で価値のある一冊ではないでしょうか。
将来は
個人的には近い将来3キャリアの仕様が統一されて、本書のような解説書が無くとも携帯サイトが作れるようになってくれると嬉しいですね。:-D
現在の流れとしては、以前よりも3キャリア共に同じような仕様になりつつありますので、いずれは実現すると思います。
3キャリアの仕様が統一されて手軽に作れるようになった頃に、本書を見て、携帯サイト開発の歴史を振り返る、なんてのもオツですね:-D
- コメント (Close): 0
- Trackbacks: 0
vodafone.ne.jpアドレスのメールサーバが変わってた
この話は一般的なMTA経由でメールを送る場合は問題ありません。専用メール配信システム等でキャリアのメールサーバに直接配信する時のみ関係ある話です:-D
vodafoneメールアドレスのメールサーバが気がついたら変わってました。
まず各地域アドレスのMXレコードですが、以前は各地域毎にMXが異なっていたのですが、全てmx.k.vodafone.ne.jpに統一されています。
これ自体は以前から変わっていたような気もするのですが、地域毎のメールサーバが生きていたため、実は各々のメールサーバにメールを送ることが可能でした。
しかしここ数ヶ月の間で、[rnsq].vodafone.ne.jpの地域メールサーバ自体が存在しなくなったようで、それらのアドレスに送るには、MXレコードで指定されているmx.k.vodafone.ne.jpを利用するしかなくなりました。
各地域のアドレスを調べてみると以下のようになっていました。
地域 | ドメイン | MX | 地域MX |
---|---|---|---|
北海道地区 | d.vodafone.ne.jp | mx.k.vodafone.ne.jp | ok |
東北・新潟地区 | h.vodafone.ne.jp | mx.k.vodafone.ne.jp | ok |
関東・甲信地区 | t.vodafone.ne.jp | mx.k.vodafone.ne.jp | ok |
東海地区 | c.vodafone.ne.jp | mx.k.vodafone.ne.jp | ok |
関西地区 | k.vodafone.ne.jp | mx.k.vodafone.ne.jp | ok |
北陸地区 | r.vodafone.ne.jp | mx.k.vodafone.ne.jp | – |
中国地区 | n.vodafone.ne.jp | mx.k.vodafone.ne.jp | – |
四国地区 | s.vodafone.ne.jp | mx.k.vodafone.ne.jp | – |
九州・沖縄地区 | q.vodafone.ne.jp | mx.k.vodafone.ne.jp | – |
今のところ[dhtc]については地域メールサーバが生きていますが、MX自体は[k]になっているので将来無くなってしまうかもしれません。
通常のMTAのようにMXレコードを引いて配信先メールサーバを決定する場合は問題無いのですが、メールサーバを決め打ちしている場合は対応が必要となるので要注意です。
いっそvodafone.ne.jpアドレスがsoftbank.ne.jpに移行してくれると助かるのですが:-P
[2009/02/19追記]
どうやら全ての地域メールサーバ(kを除く)が使用できなくなったようです。vodafone.ne.jpアドレスは素直にMXであるmx.k.vodafone.ne.jpに送信するようにしましょう:-D
- コメント (Close): 1
- Trackbacks: 0
Twitterのキーワードをグラフで見るTwitterTrendをリリースしました。
Twitterで、あるキーワードがどれくらい投稿されているかをグラフ化するTwitterTrendをリリースしました。
なにをするの?
キーワードを入力して「集計」ボタンをクリックすると直近1ヶ月でキーワードが含まれた投稿数をグラフで表示します。
流行のキーワードがどの程度反応されているかを見ることができます。
例えば最近物議を醸しているあの話題で集計すると↓のようになります。
キーワード
キーワードはTwitter検索と同じ検索式が使えます。
また3つまでなら複数のキーワードを指定することもできます。この場合それぞれのキーワードについてグラフが表示されるので、投稿数を比較することができます。それぞれのキーワードは[,](カンマ)で区切ります。
集計対象
集計されるのは前日から直近1ヶ月間の日本語ユーザの投稿です。
Twitterでの伝播
今回はblogでリリースを出す前にTwitterでURLを投稿してみました。ものの数分でサーバにアクセスが来だして、はてブにブックマークが付きました。1h経過した頃には10users程度になってました。その後予定があったので外出したのですが、帰ってきた頃には30usersが付いていました。
Twitterで呟いたサービスがどんどん広がっていく様は、改めて驚きました。情報の伝達スピードがハンバじゃないですね。
TwitterやSBMで速攻で反応が返ってくるのはわくわくしますね。この感覚はやみつきになりそうですw。
# ちなみにこのサービスは今話題のPHPで作ってます。
# id:moto0915さん、はてぶで不具合報告ありがとうございました!「OR」の件は修正しましたー。
- コメント (Close): 0
- Trackbacks: 0
PostgreSQL VACUUM FULLせずに不要領域を削除する
- 2007-11-01 (木)
- Web+DB
PostgreSQLではDELETEしたレコードは物理的には削除されずそのまま残り続けます。テーブル自体のサイズ(容量)を削減するにはDELETEした後にVACUUM FULLを行う必要があります。
このVACUUM FULLはテーブルへの書き込みロックがかかります。また数GクラスのテーブルへのVACUUM FULLは数時間かかることがあるので、常時書き込みがあるテーブルへは処理を行うタイミングが難しいです。
そこでVACUUM FULLを行わずに不要領域を削除(テーブルサイズを減らす)する方法を考えました。
方法は単純で「テーブルを新たに作って、そちらにデータを移行する」だけです。流れとしては以下のようになります。
- 不要領域を削除するテーブル(移行元テーブル)と同じレイアウトのテーブルを作成する(移行先テーブル)
- 移行先テーブルにレコードを移行する
- 移行先テーブルにインデックスを設定する
- 移行元テーブル名を変更する
- 移行先テーブル名を移行元テーブル名に変更する
ここでは不要領域を削除するテーブル(移行元)をitems、移行先をitems_newとします。
1. items_newテーブルを作る
create table as文でitems_newテーブルを作成します。「limit 0」を指定することによりレコードを移行することなくitemsと同じレイアウトのテーブルを生成します。但しこの方法では制約等は受け継がれないので別途作成する必要があります。
create table items_new as select * from items limit 0;
2. レコードを移行する
insert into select文でitemsテーブルからitems_newテーブルにレコードを格納します。ポイントは全レコードを一度に移す必要が無く、select文のwhere句で条件を指定して序々に移行することができる点です。ログのような時系列で並ぶレコードでは一度登録されたレコードを変更されることが無いので、この方法で少しづつ移行することができます。
逆に各レコードがランダムに変更されるようなテーブルだとこのアプローチは使えません。
order by句を指定することにより任意の順序でレコードを格納することができます。
insert into items_new select * from items where created between '2007/1/1 00:00:00' and '2007/1/31 23:59:59' order by created;
3. items_newテーブルにインデックスを設定する
items_newテーブルにインデックスを設定します。インデックス設定はロックがかかるのでこの段階で行います。
create index items_new on ...;
● itemsテーブルへの書き込みを停止する
常時itemsテーブルへの書き込みがある場合は、このタイミングで書き込みを一旦停止します。停止の方法には、サービスを一時停止する、テンポラリテーブルに書き出す、ファイルに書き出す等が考えられます。
4. itemsテーブルのテーブル名を変更する
itemsテーブルのテーブル名を適当な名前に変更します。
alter table items rename to items_old;
5. itemsテーブルのテーブル名を変更する
items_newテーブルのテーブル名をitemsに変更します。
alter table items_new rename to items;
● itemsテーブルへの書き込みを再開する
itemsテーブルへの書き込みを再開して完了です。もし停止期間に書き込むデータがあればitemsに書き戻します。またこの方法では外部制約は設定していないので、もしitemsやそれに関連するテーブルに外部参照制約があるならそちらも設定します。(そもそも外部参照がからむようなテーブルにはこの方法自体が不向きですね。)
VACUUM FULLは意外とやっかい
テーブルサイズが巨大化するとVACUUM FULLは意外とヘビーです。通常はコンカレントVACUUMで用が済むかもしれませんが、テーブルサイズを小さくしたい場合などは、今回のようにちょっとした工夫が必要となります。
ちなみにこの方法を取ると当然ながらレコードのoidが変わります。oidに依存しているシステムでは適用できませんのでご注意を。
- コメント (Close): 0
- Trackbacks: 0
PostgreSQL8.1以降はoidに注意
PostgreSQL8.1以降はデフォルトではテーブルにoid列が存在しません。
このままだとPHP側からINSERTした後、pg_last_oid()で追加したレコードのoidを取り、それを使って追加したレコードをSELECTで読み込むという方法が使えません。
過去のアプリケーションを移行するなどしてoid列が必要な場合はテーブルを作成する際に以下のような方法を取る必要があります。
1. default_with_oidsをOnにする
postgresql.confにあるdefault_with_oidsをonに設定する(デフォルトはoff)とそれ以降に作成したテーブルにはoid列が生成されます。
default_with_oids = On
default_with_oidsはset文でも設定可能です。この場合設定したセッション内でのみ有効となります。
set default_with_oids = On;
2. CREATE TABLE文にWITH OIDSを付ける
CREATE TABLE文でテーブルを作成する際にWITH OIDSを付けておくとdefault_with_oidsの設定に関わらず、oid列が生成されます。
create table test(id int) with oids;
–
先日とあるアプリケーションをPostgreSQL7.2から8.1に移行した際にこの問題が発生しました。シーケンスから直前に追加された値を取得するなど他の方法もあったのですが、PHP側を変更したくなかったので、default_with_oids=onにして対応しました。
- コメント (Close): 0
- Trackbacks: 0
IPアドレスなしでSSLサーバ証明書?
確かにグローバルIPが1つしか割り当てられていないサーバに複数サイトを詰め込むというのは良くある話なんでこれは中々興味深いです。
従来、独自ドメインでSSLサーバ証明書を利用するには、ウェブサイトごとにIPアドレスが必要だったが、同サービスにより、IPアドレスなしでも独自ドメインSSLが利用可能になる。
確かにグローバルIPが1つしか割り当てられていないサーバに複数サイトを詰め込むというのは良くある話しなんでこれは中々興味深いです。
でもこれどうやってるんだろ。
SSL 電子証明書 NonIP SSL サービス グローバルサイン株式会社の構成図を見てなんとなく想像してみた。
まず[ssl.example.com]をNonIP SSLから提供されるIPアドレスに設定する。で、証明書もNonIP SSLサーバにインストールする。あとは[ssl.example.com]から来たリクエストをSSLからのアクセスとして扱えば良いという感じなのかな。
ようはSSLのリバースプロキシですな。
もしこの通りなら、[ssl.example.com]<->[www.example.com]をどうやって繋ぐかが問題になりそう。
本来のSSLの機能に近づけるのなら、SSLサーバはホスティング事業者の
管理下におくべきなんじゃないかな?
ですね。ローカルで繋げられればOKです。
でもサービスの内容から想像すると別のホスティング業者でも利用できるのがウリっぽいので、複合化してインターネット経由になるんでしょうね。このサービス利用者がVPN引くとも思えないですし:-p
うーん、微妙かな。。。でも実際問題とりあえずユーザからHTTPSに見えていればそれで良いという人もいたりするんで、ニーズはあるかも。(打診されそうな気もするぞ・・・)
- コメント (Close): 0
- Trackbacks: 1
ITmediaでTwitter検索/Twitterで暇つぶしが紹介されました。
ITmediaでTwitter検索とTwitter で暇つぶしが紹介されました。
昨日ちょうどアクセスログを眺めている時にリファラで知りました。;-) やはりこうやって取り上げて貰えると嬉しいですね。
特に暇つぶしが取り上げられたのが嬉しかったり。
どちらのツールも登録等せずに誰でも使えるので、お暇な時にお試し下さい。
- コメント (Close): 3
- Trackbacks: 0
Twitterで暇つぶし
またまたTwitterネタ。
Twitter 検索が某ネタのおかげで好評でした(@ieiriさん、ありがとうございました)ので、調子に乗ってこんなのも作ってみました。
携帯でTwitterのステータス(投稿)を見るだけのサイトです。
外出している時に携帯でTwitterを見ていると意外と更新されずに何度もリロードする事ありません?
Friendsをバンバン増やせば良いのでしょうけどあまり増やしすぎると今度PCで見るときがつらかったり。
知らない人のステータスをただ見るだけなのですが意外と面白かったりします。
登録等は一切不要なので「Twitterって聞くけど、何?」な方も一度どうぞ。
- コメント (Close): 0
- Trackbacks: 0
ホーム > Web+DB
- 検索
- フィード
- メタ情報