このエントリーをはてなブックマークに追加
 

Profile

mmiyaji

なんちゃって情報系な大学院生。
大阪北摂から京都南部にかけて出没
... more

Contents

Site History

Tags

... more

About this site

その時々に思いついたことを綴るサイトです。 
GoogleAppEngine/python で作ったサイトで、主にこのサイトの開発についてだらだらメモ出来たらいいなとか思ってます。
本サイトに記載されている情報に関しては私が適当に書いたものなので、保証はできません。しません。

Recent News

Screen Capture支援ソフトの開発(Mac専用)その2
こないだ作ったやつに機能追加とかしました
前回の記事-> http://mmiyajix.appspot.com/entry/52001

主な変更点は以下のとおり
  • 画像の初期位置変更。(画面中央から撮影した場所へ)
  • 右クリックメニューの追加
    • 透過率変更(100, 90, 80, 50, 30%)
    • ウインドウの影表示
    • ウインドウの最前面化 on off
  • Zoomとかのコマンド発行時に内容を表示

どんなアプリかいまいちイメージしづらいかと思うので、紹介ビデオアップしました。

なんだかウインドウ領域変更時の動きが気持ち悪く見えるけど 気のせいだ。フレームレートのせいだと思いたい。


マルチウインドウ環境では撮影できるけど 初期位置の調整ができてない。
ちゃんと名前も付けないとね。

実行環境

Mac OSX 10.7.3でのみテストしてます。ver 0.6は他の環境で試してないので動かない可能性あります。
そのうちやる

ダウンロード

Snapshot.app.zip (ver 0.6) ※追記 バグ有り 下記ver0.7を推奨 

Snapshot.app.zip (ver 0.7)

... Read full text
Screen Capture支援ソフトの開発(Mac専用)

作ったもの

自分が欲しかったからスクリーンキャプチャ(静止画)の支援ソフト作りました。
どんなものかというと、キャプチャ -> デスクトップ最前面表示 の流れを自動化しているイメージです。
現在表示中の画面をちょっと脇に表示させときたいときに使います。

コンセプト

Mac OSX標準のスクリーンキャプチャツールの拡張のイメージです。
  • 表示中の画面のうち、重要な部分のみを切り取って表示させておきたい
    • 見たい情報はほんの一部なんだからウインドウ領域もったいないなあ
  • キャプチャ画面をスライドによく使うので 作成の手間を省きたい
そんな気持ちがあったので作りだしました。

なので、軽量かつ手軽に使えるものを目指しています。

多機能なキャプチャツールはいくらでもあると思うので、撮った画像の編集等を望むなら、別のツール(Skitchとか)の利用をお勧めします。

動作環境

  • Mac OSX 10.6 以降で動作確認

一応Cocoaで作った常駐アプリ(メニューバーでのみ表示)で ホットキーから機能を呼び出せるようにしています。
動かなくても保証できません。

機能

  • キャプチャ:「Command + Shift + 5」
    グローバルホットキーで呼び出して、マウスで範囲指定して使います。
    OSの機能として用意されているキャプチャが Com + Shift + 3 or 4 なので 5 にしました。

    キャプチャするとデスクトップの中心に最前面化されたウィンドウを表示します。
    このウィンドウはドラッグで移動可能です。

    キャプチャウィンドウは微妙に透過しています(Alpha0.9)。
    ドラッグ中はもっと透過します(Alpha0.3)。
    このあたりは、いづれ設定できるようにしたいところ。

  • 保存:「Command + s」
    キャプチャした画像をファイル保存できます。
    サポートしている形式は png、jpeg、gif です。
    名前を付けて保存するときにつけた拡張子から判断して変換するようにしてます。

    デフォルトはpng形式です。

  • コピー:「Command + c」
    キャプチャした画像のデータをクリップボードへコピーします。
    スライド作成するときにダイレクトに貼り付けられるので重宝します。

  • ウィンドウを閉じる:「Command + w」
    キャプチャウィンドウを閉じるときに使います。

    現時点のバージョン(0.1)では閉じるボタンの表示がありません。
    コマンドから閉じてください。

  • カット:「Command + x」
    上記の コピー + 閉じる を行います。

  • アプリケーションの終了:「Command + q」
    普通のアプリケーションと同じですね。
    メニューバーからも終了できます。

課題

色々あるけどざっくり列挙
  • 拡大 / 縮小
    今はウィンドウだけ拡大縮小できます(画像自体の大きさは変化しません)。
    これはすぐ対応しようと思う。
  • 色々と設定画面から変えられるようにしたい
    • ウィンドウの透過率
    • ウィンドウの影
    • デフォルトの保存パス
    • 常駐アプリ(メニューバーのみに表示)かDock表示するアプリか
  • Growl通知
    各種コマンド発行時に通知
    Growlである必要はないけど、コマンド発行時に何らかのフィードバックをしたい
  • 閉じるボタン表示
    常にあるのは美しくないので、マウスホバーで表示とか
  • アプリケーションのネーミング
    なんにしましょうか
  • アイコン
    今無いのでかっこいいのんを
  • 公開をAppStoreへ
  • ソースコード公開
    もうちょい調整したらレポジトリ公開します

ダウンロード


Snapshot.dmg (ver0.1 build ver 1)


... Read full text
クリップボードに画像を送る(Cocoa)

環境

  • OS:MacOSX 10.7.2
  • Xcode:4.1
  • 言語:Objective-C

NSImage に格納している画像データをクリップボードに送る必要があったので調べたメモ。

どうやら NSPasteboard を使えばできそうだ。
plain textを送る例はけっこう落ちてるけど、画像を送るのはあんまなかった。
Mac OSX Developer Library:
Repeating Motif Wonderland<実践的Macintoshプログラミング解説>:

以下ソース
NSData* imageData = [contentImage TIFFRepresentation];
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
[pasteboard declareTypes:[NSArray arrayWithObjects:NSPasteboardTypeTIFF, nil] owner:nil];
[pasteboard setData:imageData forType:NSTIFFPboardType];
contentImage変数は NSImage型でデータが格納されているとする。
んで、ビットマップにする必要があるのでTIFF形式へ変換しました(line.1 NSData型変数の imageDataに格納)。
ドキュメント眺めてるといろんな形式使えそうですね。
から引用
NSString *const NSPasteboardTypeString;
NSString *const NSPasteboardTypePDF;
NSString *const NSPasteboardTypeTIFF;
NSString *const NSPasteboardTypePNG;
NSString *const NSPasteboardTypeRTF;
NSString *const NSPasteboardTypeRTFD;
NSString *const NSPasteboardTypeHTML;
NSString *const NSPasteboardTypeTabularText;
NSString *const NSPasteboardTypeFont;
NSString *const NSPasteboardTypeRuler;
NSString *const NSPasteboardTypeColor;
NSString *const NSPasteboardTypeSound;
NSString *const NSPasteboardTypeMultipleTextSelection;
NSString *const NSPasteboardTypeFindPanelSearchOptions;
declareTypes で書きこむデータ形式の設定。
arrayWithObjects には上の形式たちが使えそう。

generalPasteboard で汎用ペーストボードが取得できたので、setDataで書き込む。
forTypeで形式を指定する。

そうするとクリップボードに画像が送られる。
結果は上の画像です(愛用のクリップボードアプリ、ClipMenuで表示)


owner てなんやねんとか色々あると思いますが、普通に使う分には気にしないでいいはず。
詳しくはドキュメント読んでください。


... Read full text
Google Analyticsで集計した人気ページランキングをPythonから取得してみよう
サイトのアクセス解析に重宝しているGoogle Analytics。
こいつでサイトの人気ページのランキングをスクリプト(python)から取得してみたのでメモ。

まず認証。パスワードかAuthSubかOAuthで認証可能。
平文でパスワードをスクリプト内に書きたくないとかセキュリティ云々とかで、ちゃんとOAuthなどのToken認証にすべきなんだろうけど
面倒なので今回はパスワード認証の例で書きます。(OAuthでの認証版もつくっているのでそのうち載せよう)
こちらにOAuthでの認証例があります。-> Setting up an OAuth provider on Google App Engine 


やりたいこと

  • 月間人気ページ(アクセス数)のランキング取得
  • 特定アドレスでフィルタ(複数)
  • その他情報もとってこれればなおよし
  • JSONっぽく整形して書き出し
それではコード全文。
#!/usr/bin/env python
# encoding: utf-8
"""
GAnalytics.py

Created by mmiyajix on 2011-10-02.
Copyright (c)  mmiyajix. All rights reserved.
"""
import datetime, sys
import gdata.analytics.client

email="YOUR_EMAIL_ADDR"
password="YOUR_EMAIL_PASSWORD"
table_id = 'ga:xxxxxx'

days = 30
span = 10
offset = 1
args = sys.argv
if len(args) > 1:
	days = int(args[1])
	span = int(args[2])
	offset = int(args[3])

SOURCE = 'GData sample client-v2'
client = gdata.analytics.client.AnalyticsClient(source=SOURCE)
client.client_login(email, password, source=SOURCE, service=client.auth_service)

today = datetime.date.today() - datetime.timedelta(days=offset)
lastmonth = today - datetime.timedelta(days=days)

data_query = gdata.analytics.client.DataFeedQuery({
			'ids': table_id,
			'start-date': lastmonth.isoformat(),
			'end-date': today.isoformat(),
			'dimensions': 'ga:pagePath',
			'metrics': 'ga:pageviews,ga:uniquePageviews,ga:visits,ga:bounces',
			'sort': '-ga:pageviews',
			'filters': 'ga:pagePath=~(^/isreport/entry/.+|^/report/.+)',
			'max-results': str(span),
			})
feed = client.GetDataFeed(data_query)
print '{ "entries":['
for count,entry in enumerate(feed.entry):
	print '{"url":',
	for dim in entry.dimension:
		url = str(dim.value)
		print '"'+url+'"',
		for j in entry.metric:
			print ',"'+j.name+'":',j.value,
	print '}',
	if count < len(feed.entry)-1:
		print ','
print '],'
print '"start-date":"', lastmonth.isoformat(),'",'
print '"end-date":"', today.isoformat(),'"',
print '}'
そんで実行結果。

{ "entries":[
{"url": "/report/2010/7/20/2010015005/index.html" ,"ga:pageviews": 448 ,"ga:uniquePageviews": 406 ,"ga:visits": 385 ,"ga:bounces": 228 } ,
{"url": "/report/2010/11/6/2010033003/index.html" ,"ga:pageviews": 432 ,"ga:uniquePageviews": 379 ,"ga:visits": 376 ,"ga:bounces": 330 } ,
{"url": "/report/2011/5/11/2011266002/index.html" ,"ga:pageviews": 236 ,"ga:uniquePageviews": 215 ,"ga:visits": 215 ,"ga:bounces": 194 } ,
{"url": "/report/2010/11/8/2010015006/index.html" ,"ga:pageviews": 177 ,"ga:uniquePageviews": 176 ,"ga:visits": 57 ,"ga:bounces": 40 } ,
{"url": "/report/2011/2/10/2011016006/index.html" ,"ga:pageviews": 167 ,"ga:uniquePageviews": 147 ,"ga:visits": 129 ,"ga:bounces": 101 } ,
{"url": "/report/2011/5/12/2011271002/index.html" ,"ga:pageviews": 149 ,"ga:uniquePageviews": 142 ,"ga:visits": 139 ,"ga:bounces": 132 } ,
{"url": "/report/2011/5/13/2011267001/index.html" ,"ga:pageviews": 135 ,"ga:uniquePageviews": 117 ,"ga:visits": 115 ,"ga:bounces": 100 } ,
{"url": "/report/2011/5/31/2011272002/index.html" ,"ga:pageviews": 133 ,"ga:uniquePageviews": 114 ,"ga:visits": 113 ,"ga:bounces": 101 } ,
{"url": "/report/2011/5/6/2011021007/index.html" ,"ga:pageviews": 123 ,"ga:uniquePageviews": 105 ,"ga:visits": 105 ,"ga:bounces": 92 } ,
{"url": "/report/2011/5/3/2011270001/index.html" ,"ga:pageviews": 108 ,"ga:uniquePageviews": 99 ,"ga:visits": 99 ,"ga:bounces": 91 } ],
"start-date":" 2011-09-10 ",
"end-date":" 2011-10-10 " }

ディメンションやmetricsやらの設定は公式(http://code.google.com/intl/ja/apis/analytics/docs/gdata/gdataReferenceDimensionsMetrics.html )で詳しく見ればいいと思う。

filter部分の
ga:pagePath=~(^/isreport/entry/.+|^/report/.+)
というのは
ドメイン+/isreport/entry/ もしくは /report/ から始まるアドレスという正規表現。
なぜ最後が .* ではなくて .+ にしているかというと、 /isreport/entry/ を一覧ページにしているので結果に現れて欲しくないからなんです。

そんで一応引数によってパラメータを変えられるようにしているので

for i in {1..200};
do 
	echo `date --date "${i} day ago" +%F`_30.log ;
	python GAnalytics.py 30 50 ${i} > log/`date --date "${i} day ago" +%F`_30.log ;
done
みたいなshellを書いてやれば、logディレクトリ以下に「2011-10-06_30.json」といった日付入りのログを書き出せそうですね。
(引数:day = 集計期間、span = 上位何件か、offset = 何日遡るか)

以上。

... Read full text
DjangoでのDBマイグレーションのメモ
ちょくちょく使ってるPythonのWebフレームワーク「Django」。
こいつには標準でデータベースのマイグレーション機能がついていない。

開発途中では頻繁にモデル構成変えたりするので、スキーマ変更時にデータベースを更新してくれないと苦しい。
最悪開発中だとデータベース自体を新しく作り直してもいいが、デプロイ版ではそうもいってられない。

なのでちゃんと
データダンプ -> モデル更新 -> モデル初期化 -> ダンプしたデータロード
の手順を踏みましょうね。
*同じDjango風味でもGAEでは勝手にやってくれるよ、すごいね

この手順をSQL直に叩いてもできるんですけど、怖いんだよね。

一応、その辺は用意されているので順に使いましょー
今回は単純にその手順メモです。

オプション詳細は公式で

とりあえずプロジェクトのディレクトリに移動してから
  1. データダンプ
    $ python manage.py datadump APP_NAME > dump.json
    標準出力をdump.jsonに流し混んでるだけー
    フォーマットはデフォルトではJSON。変更可だけど必要ないでしょう

  2. モデル更新
    まあ追加なり なんなりの変更ー

  3. モデル初期化
    $ python manage.py reset APP_NAME
    $ python manage.py syncdb

    たぶんDROP TABLEからのCREATE TABLEなSQLを発行してるだけどと思うけど、自分で叩くのは怖いもんね

    あ、もしかしたらsyncdbいらんかも

  4. ダンプしたデータのロード
    $ python manage.py loaddata dump.json
    1で書きだしたデータを読んでるだけ

手順さえ覚えてれば簡単だね。

... Read full text
Skypeのムードメッセージを現在地に書き換える for mac
昔書いた記事の焼き直し http://ruhenheim-erum.blogspot.com/2010/07/skype.html 
python関係はこっちに移行しようかと。

現在地に書き換えるとは言っても、今接続してる無線LANのSSIDによって書き換えてるだけですけど
自己主張強い人のためのスクリプトです

ってことでpythonスクリプト書いてみた。
ちなみにMac専用のやつね。
他で使いたいときはSSID取得する部分適宜書き換えてみて
※要 Skype4Pyとかいうpython用のskypeAPI -> http://skype4py.sourceforge.net/doc/html/

#! /usr/bin/python
# -*- coding: utf-8 -*-  
"""
skyper.py

Created by mmiyaji on 2010-04-20.
Copyright (c) 2010 __ISDL__. All rights reserved.
"""

import os,re,time
import commands
import Skype4Py

ssids = {u"m-net":u"home",u"air-kc":u"KC104",
			u"AIR-IN":u"IN223N",u"AIR-IN206":u"IN206",u"AIR-IN221N":u"IN221"}

class Skyper:
	def __init__(self, mood=""):
		self.mood = mood
	
	def set_mood(self, mood):
		self.mood = mood

	def get_ssid(self): 
		ssid = commands.getoutput("/System/Library/PrivateFrameworks/Apple80211.framework/Versions/A/Resources/airport"+
									" --getinfo | grep SSID | tail -n 1") 
		ssid = ssid.replace(" ","")
		ids = ssid.partition(":")
		ssid = ids[2]
		return ssid
	
	def check(self, message, mood=""):
		now_mood = ""
		if mood:
			now_mood = mood
		else:
			now_mood = self.mood
		if message != now_mood and message:
			return True
		else:
			return False

	def replace_ssid(self, name):
		ssid = ""
		if name in ssids:
			ssid = ssids[name]
		else:
			ssid = name
		return ssid
	
if __name__ == '__main__':
	isloop = False
	skype   = Skype4Py.Skype()
	sk = Skyper()
	while True:
		profile =  skype._GetCurrentUserProfile()
		sk.set_mood(profile.MoodText)
		message = sk.get_ssid().lstrip()
		message = u"@"+sk.replace_ssid(message)
		if sk.check(message.lstrip()):
			profile._SetMoodText(message)
			print "ChangeMood: "+profile.MoodText
		else:
			print "SameMood: "+profile.MoodText
		if not isloop:
			break
		else:
			time.sleep(60*10)
正規表現は使わない ごり押しな方針で書いてみた。
色々拡張しようと思ってクラス作ったけどそんな気もなくなった。
そんな理由でほんとはもっと短く書けるけど期待通りの動作したから もういいや

これをcronで定期的に叩けばいいと思う。
私は愛用のNerdTool(GeekTool)に登録して回してます


@home のような感じで表示を勝手に書き換えます

あとPythonが64bitで起動するSnow Loepard以降の環境ではSkype4PyがSegmentation Fault起こします
これはSkypeが32bitで動作することに起因するようです。
そんなときはPythonを32bitで起動させてやればOK ここにかいてあった -> http://www.jaharmi.com/2009/08/29/python_32_bit_execution_on_snow_leopard


$ defaults write com.apple.versioner.python Prefer-32-Bit -bool yes
で/usr/bin/python が32bitで起動する
もとに戻したい時は
$ defaults write com.apple.versioner.python Prefer-32-Bit -bool no
です

... Read full text
コメント欄を設置したった(Facebookコメントプラグインの使い方)
Facebookのコメントプラグインを設置したのでメモ。

最近はブログなんかでも、コメント欄を自前で用意せずに外部のTwitterやFacebookなどのサービスを活用しているのをちらほら見かけます。
今回はその流れにのってみようかと思います。

まあ下記アドレスからコードを生成するだけなんですが。

このコード中の fb:comments タグの href 要素に埋め込みたい先(どのページに対してのコメントなのか)のURLを指定する必要があります。
自動でカレントページを指定してくれるわけではないので、こちらで指定しなければいけないんですが、
HTML生成の段階でいちいち指定するなんてそんなの面倒ことするわけないですよね?

javascriptで適当に書き換えましょう。
まず試したのが fb:comments タグの href要素を書き換える方法(結果的にはダメでした)。
<div id="fb-root">
	<script src="http://connect.facebook.net/en_US/all.js#xfbml=1"></script>
	<fb:comments id="fb_comment" href="http://mmiyajix.appspot.com" width="630"></fb:comments>
	<script type="text/javascript">
		document.getElementById("fb_comment").href=location.href
	</script>
</div>
このコードを動かしてみると実際のコメント欄となるiframeの生成タイミング的にうまく反映されないのがわかります。
(iframe生成後にfb〜のhref要素を変更しても反映されないよ)
http://connect.facebook.net/en_US/all.js の読みこむタイミングを変えてみたけど、いまいち上手くいかないのでわからん。

じゃあどうするか。早い話そのコード自体を動的に生成すればいいんじゃないだろうかという結論に至った。
<div id="comments">
	<h2>Facebook Comments:</h2>
	<div id="fb-root"><script src="http://connect.facebook.net/en_US/all.js#xfbml=1"></script></div>
</div>
<script type="text/javascript">
	$("#fb-root").after('<fb:comments id="fb_comment" href="'+location.href+'" width="630"></fb:comments>');
</script>
めんどくさくなったのでjQuery使った。必要です。

単純なことだけどなぜパッと思い浮かばなかったのか。
そのうち研究室で管理しているサイトにも設置しようかなっと。
... Read full text

Social buttons

このサイトにも、Twitter・Google +1・はてブ・Facebookのいいねボタンなどを設置してみました。その設定メモ。


この設置なんですけども、JQueryのプラグインですでにこのようなものがあります。
すごく便利で何度か利用させてもらったのですが、今回は使わないでやって見ようと思います。

まあそれぞれのサービスからボタンのコードを生成するだけなんですけども。
からそれぞれ取得。
一部埋め込みコードにURLが決めうちのものがあるので、Javascriptで動的にカレントページに書き換えるようにしましょう。

それじゃHTMLコード
<div id="social_button">
	<div id="tw-root">
		<a href="http://twitter.com/share" class="twitter-share-button" data-count="horizontal">Tweet</a>
		<script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script>
	</div>
	<div id="gl-root">
		<script type="text/javascript" src="https://apis.google.com/js/plusone.js"></script>
		<g:plusone size="medium"></g:plusone>
	</div>
	<div id="hb-root">
		<a id="hb_like" href="http://b.hatena.ne.jp/entry/" class="hatena-bookmark-button" data-hatena-bookmark-layout="standard" title="このエントリーをはてなブックマークに追加">
			<img src="http://b.st-hatena.com/images/entry-button/button-only.gif" alt="このエントリーをはてなブックマークに追加" width="20" height="20" style="border: none;" />
		</a>
		<script type="text/javascript" src="http://b.st-hatena.com/js/bookmark_button.js" charset="utf-8" async="async"></script>
		<script type="text/javascript">document.getElementById("hb_like").href="http://b.hatena.ne.jp/entry/"+location.href</script>
	</div>
	<div id="fb-root1">
		<iframe id="fb_like" src="" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:100px; height:21px;" allowtransparency="true"></iframe>
		<script type="text/javascript">document.getElementById("fb_like").src="http://www.facebook.com/plugins/like.php?href="+location.href+"&layout=button_count&show_faces=false&width=100&action=like&colorscheme=light&width=120"</script>
	</div>
</div>
途中のscriptタグはheadなりbody下らへんにまとめて仕込んだらいいと思う。

あとはCSSで左寄せするなどなど
#social_button{
	float:right;
} 
#social_button > div{
	float:left;
	min-width:70px;
} 

以上。あと追加するとしたらDeliciousとかmixiかな。
まあそのうち

------
6/28追記
------
facebookのコードに関して。iframeではSendボタンの設置に対応していないようなので、XFBML形式での記述が必要だそうで。
こんな感じになります。

<div id="fb-root"><script src="http://connect.facebook.net/en_US/all.js#appId=120147571407250&amp;xfbml=1"></script><fb:like id="fb_script" href="" send="true" layout="button_count" width="100" show_faces="false" font=""></fb:like>
			<script type="text/javascript">document.getElementById("fb_script").href=location.href</script></div>

そんでDeliciousのお気に入りボタンも追加したので以下コード。

<div id="dl-root">
	<a href="http://www.delicious.com/save" onclick="window.open('http://www.delicious.com/save?v=5&noui&jump=close&url='+encodeURIComponent(location.href)+'&title='+encodeURIComponent(document.title), 'delicious','toolbar=no,width=550,height=550'); return false;">
		<img src="http://l.yimg.com/hr/img/delicious.small.gif" height="20" alt="Delicious" />
	</a>
</div>

... Read full text
ふと気になった。
こないだRSS配信はじめましたという記事でmemcacheの使い方を書いてみたが、この使い方で本当に速くなっているのか?ということだ

まぁあんまりmemcacheの仕様とか見て無くて、とりあえず使い方だけ追っかけた感があるのでどんな影響があったのかくらいは調べときたい。

あっ「ab」ってのはapacheインストールしたときに一緒にインストールされる、Webサイトの負荷測定ツールです。
今回はこれを使ってデプロイ版の/rss.xml のレスポンスを測定してみます。

これで同時接続数64、合計1000回のアクセスでやってみましょう。
まず、memcacheなしのバージョン
$ ab -n 1000 -c 64 http://mmiyajix.appspot.com/rss.xml
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking mmiyajix.appspot.com (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests


Server Software:        Google
Server Hostname:        mmiyajix.appspot.com
Server Port:            80

Document Path:          /rss.xml
Document Length:        69981 bytes

Concurrency Level:      64
Time taken for tests:   56.470 seconds
Complete requests:      1000
Failed requests:        0
Write errors:           0
Total transferred:      70234830 bytes
HTML transferred:       70049645 bytes
Requests per second:    17.71 [#/sec] (mean)
Time per request:       3614.095 [ms] (mean)
Time per request:       56.470 [ms] (mean, across all concurrent requests)
Transfer rate:          1214.60 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:       55  112  41.5    106     367
Processing:  1082 3387 2000.6   3133   14319
Waiting:      775 2904 1985.0   2668   13994
Total:       1183 3499 2000.1   3246   14436

Percentage of the requests served within a certain time (ms)
  50%   3246
  66%   4028
  75%   4504
  80%   4847
  90%   5792
  95%   6689
  98%   9778
  99%  11692
 100%  14436 (longest request)

そんでmemcache版
$ ab -n 1000 -c 64 http://mmiyajix.appspot.com/rss.xml
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking mmiyajix.appspot.com (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests


Server Software:        Google
Server Hostname:        mmiyajix.appspot.com
Server Port:            80

Document Path:          /rss.xml
Document Length:        69981 bytes

Concurrency Level:      64
Time taken for tests:   50.247 seconds
Complete requests:      1000
Failed requests:        0
Write errors:           0
Total transferred:      70397413 bytes
HTML transferred:       70211673 bytes
Requests per second:    19.90 [#/sec] (mean)
Time per request:       3215.785 [ms] (mean)
Time per request:       50.247 [ms] (mean, across all concurrent requests)
Transfer rate:          1368.20 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:       56  178  99.1    169    1082
Processing:  1185 2910 1689.0   2365   11797
Waiting:      728 2100 1535.0   1537   10923
Total:       1259 3088 1681.6   2555   11991

Percentage of the requests served within a certain time (ms)
  50%   2555
  66%   3145
  75%   3566
  80%   3925
  90%   5015
  95%   7419
  98%   7957
  99%   9108
 100%  11991 (longest request)

この結果で見ていきたいポイントは2つ。
Time taken for testsという全部のレスポンスを捌くのにかかった時間と、
Percentage of the requests served within a certain time (ms)のレスポンス時間の割合。

まあぼちぼち速くなってはいるか。
もうすこし詳しく検証して、何に有効か知っておきたいね。
まあ時間があったらってことで
... Read full text
このブログでは今まで、画像やファイルを貼り付けるときは別で用意していたアップローダもしくはFTPサーバに置いたものを貼り付けてました。
そいつを自前で用意しました。

GAEでのファイルアップロードに関しては既に先人がすばらしいコードを公開しているので、がっつり参考にしました。

なので特にいうことはありませんが、報告。

アップロードテスト
アップロードテスト
Basic認証でのパスワードロックを実装しているので、これについてはまたいづれ。

*追記5/30
何気なくドキュメント眺めてたらBlobstore http://code.google.com/intl/ja/appengine/docs/python/blobstore/ ってのがあったんですね。
ちょいと調べてこれ使ってみようと思う


... Read full text

Facebook Comments: