Python simple_server tuning
GAE/PythonでごそごそやってますがたまーにGAEじゃ出来ない事をしたくなるときが有ります。ローカルのデバイス操作したりファイルごにょごにょしたり。そんな時は…Apache入れてmod_wsgi入れて…って言うのが王道にも思いますが結構大変。いっそのことdev_appserverで逃げようかとか思ったりもするもののライセンスも良く解からん上、シングルスレッド動作のwebサーバなので気が引けます。
で、Pythonに漏れなく付いてくるwsgiref.simple_serverとbottle+sqlalchemy/sqlite+flaskで落ち着いてたんですがやっぱりパフォーマンスが…。って事でちょっとぐぐる先生に聞いてみるとこんなページを紹介されるのでした。
Nicholas Piël » Benchmark of Python Web Servers
すげー、その調査力に感服。って違うry
gevent/libeventが良いですね~。wsgiref.simple_serverだと 2.6TPS エラー有りまくりだったアプリを試しにgevent.wsgi.WSGIServerにするとそれだけで13.7TPS!エラーも無しといい感じ。なんですが個人的なホーム鯖でgevent/libeventは流石にやり杉な雰囲気です。お試しはLinuxでやってますが諸事情からホーム鯖はWindowsです(ドライバがWindowsしかない…)。Internet公開もしないしってことでもうちょっとwsgiref.simple_serverで頑張ってみることにします。
そもそもシングルスレッドを何とかします。
from SocketServer import ThreadingMixIn
class ThreadingWsgiServer(ThreadingMixIn, WSGIServer):
passclass ThreadingWSGIRefServer(ServerAdapter):
def run(self, handler): # pragma: no cover
from wsgiref.simple_server import make_server, WSGIRequestHandler
if self.quiet:
class QuietHandler(WSGIRequestHandler):
def log_request(*args, **kw): pass
self.options['handler_class'] = QuietHandler
self.options['server_class'] = ThreadingWsgiServer
srv = make_server(self.host, self.port, handler, **self.options)
srv.serve_forever()bottle.run( server=ThreadingWSGIRefServer, host='0.0.0.0', port=8080,
interval=1, reloader=False, quiet=True )
ついでにログ抑制もやってみたり。後、Windowsだと特に遅くなるクライアントIPアドレスのDNSリバースlookupをhttp://bugs.python.org/issue6085を参考に外します。まー、ログ出さないなら外す必要も無いんですが、出す出さないでパフォーマンスの変化を見るために一応。
def _bare_address_string(self):
host, port = self.client_address[:2]
return '%s' % hostimport BaseHTTPServer
BaseHTTPServer.BaseHTTPRequestHandler.address_string = \
_bare_address_string
ぐぐる先生はこんなの↓も教えてくれましたがこれはちょっとやり杉。
def getfqdn(name=''):
return name
import socket
socket.getfqdn=getfqdn
quiet=Falseで8.4TPS、quiet=Trueで 10.9TPSって所まで改善。5倍差があると流石にgeventしかないなと思うものの2倍差も無いところまでくるとまーgevent使うことも無いなに変わるから不思議。ログ出すとパフォーマンスは悪くなりますがまぁ良いかと思えなくも無いのでとりあえず出す感じで。
Androidは複数形
「Androidのアップグレード問題に関してひとこと」を読んで。
直感的に感じているのは各端末毎のスペックがばらばらで、端末毎のおもてなしを最適化するのが困難な上に、1端末種類のパイが少ないから最適化にかけた情熱が見合わないと言うこと。端末の画面サイズの違いやタッチパネルの感度・感覚の違いが低いレイヤで吸収できてないから、使い心地を作り込む作業が難しい。直感的操作がメインのスマホなら当然のこと。直感的操作・使い心地という抽象的な指標だから抽象的にしかかけないので伝わるかどうか微妙だけど。
まー、iOSも複数端末有るけどfewで収まる範囲。Androidはmany。あとHWの変更サイクルが今のところ推測可能かつ緩やかかつUIというかUXに関してはある程度統一されてるのでiOSの方が安心と言うことか。
おいらはこの記事「The Problem with Inconsistent Button Layouts on Android」読んでAndroidは難しいなーと思いました。
英語で読もう
GAE(Google App Engine)上のTwitterクライアント、一つ放置してた問題に対応。それはsearch apiの回数制限。他のAPIはユーザベースでrate limitされているのでログインしていればAPI制限に引っかかる事はまずないんだけど、search apiだけはTwitter側でリクエスト元のIPアドレスベースでrate limitしている・・・のでGAEのインスタンス共有している他ユーザーがsearch api叩きまくってるとすぐにlimitにひかっかる。ちょっとネットを彷徨った感じsearch apiだけは別鯖へproxyしろっていうのがgoogle様のお告げの様でその様に。
とはいえ別鯖ってOpenProxy使うって訳にも行かないのでVPSに飛ばしてと思ってたら…という所で本題。
VPS上(つまりこのサーバ)ではapacheが上がってます。apacheのmod_proxy辺りでGAEから受けてsearch.twitter.comへ転送しても良いんだけどmod_proxyは一般的かつ便利なだけにまじめに設定しないと穴になることも多く…と避けて単純にstoneで転送させる方針で=つまりapacheの80番ポートとは別ポートで。と思ったらGAEのurlfetchのドキュメントに以下書かれてます。
URL は、HTTP(80)および HTTPS(443)の標準ポートを使用しなければなりません。ポートはスキーマによって決まりますが、ポートがスキーマの標準である限り、URL で指定することもできます(https://...:443/)。アプリケーションはリモート ホストの任意のポートに接続することはできません。また、スキーマの非標準ポートを使用することもできません。
うーん、と思って早半年(笑)ふと思い立って英語のドキュメントを見ると。
An app can fetch a URL using HTTP (normal) or HTTPS (secure). The URL specifies the scheme to use: http://... or https://...
The URL to be fetched can use any port number in the following ranges: 80-90, 440-450, 1024-65535. If the port is not mentioned in the URL, the port is implied by the scheme: http://... is port 80, https://... is port 443.
なんじゃそりゃー。という事でtweepyのsearch apiの投げ先をここにしてstoneをほにゃっ※1て無事search復活。長い半年でしたwwwww