OpenIDでテキストを共有できます
- 新規作成
- OpenIDで認証してエントリーを新規作成します
- 共有
- エントリーにはOpenIDで閲覧と編集に制限かけることができます
- 変更履歴
- 編集履歴もあるので、コラボレーションにも活用できます
新着エントリー
マルチスレッドなmemcachedのincrが想定通り動かない時がある件
memcached-1.4.4、libevent-1.3bな環境で、memcachedをマルチスレッドで動作させた場合、incrの結果が想定通りじゃないことがある。
とりあえず、現象だけ。
memcachedは、-tでスレッド数を変更できる。
$ memcached -U 0 -p 11211 -t 4
テストスクリプト
use Cache::Memcached::Fast; use Parallel::ForkManager; while(1) { my $memcached = Cache::Memcached::Fast->new({ servers => [ { address => "localhost:11211", noreply => 0 } ] , compress_threshold => 4_000, ketama_points => 250, max_failures => 3, failure_timeout => 2, }); $memcached->set("hoge",1); print $memcached->get("hoge"); my $pm =Parallel::ForkManager->new(100); foreach my $id ( 1..1000 ) { $pm->start and next; my $childmemcached = Cache::Memcached::Fast->new({ servers => [ { address => "localhost:11211", noreply => 0 } ] , compress_threshold => 4_000, ketama_points => 250, max_failures => 3, failure_timeout => 2, }); for my $loop ( 1..100 ) { my $ret = $childmemcached->incr("hoge"); warn "[$id] failed?" unless $ret; } $pm->finish; } $pm->wait_all_children; sleep 1; my $ret = $memcached->get("hoge"); print $ret; die if $ret != 100001; sleep 30; }
期待通りに動けば、最後のgetで100001が返ってループが続くのだが、thread数を2以上にしたときに、たまに100001ではない100000とか99999とか返って来てdieする。また、failed?のwarnはでてこない。
TEST
TEST
nginxでクライアントが接続を切った場合の400 bad requestの件
★以下のpatchでは解決しないみたい。=> patch変更して対応。
ECONNRESETでクライアントとの接続が切れることはまぁまぁ、あることだということがわかって来た。
nginxでクライアントがリクエストを送る前に接続を切ると、ログに
127.0.0.1 - - [16/Feb/2010:11:30:44 +0900] "-" 400 0 "-" "-"
な感じのものが残り、エラーログにも残るんだけど。
2010/02/16 11:21:52 [info] 29643#0: *1 client closed prematurely connection while reading client request line, client: 127.0.0.1, server: _
たぶん、アクセスログに残るのは違う気がする。
おそらく以下のpatchだけでアクセスログからは消える。
diff -ur nginx-0.7.65.orig/src/http/ngx_http_request.c nginx-0.7.65/src/http/ngx_http_request.c --- nginx-0.7.65.orig/src/http/ngx_http_request.c 2010-02-02 00:06:25.000000000 +0900 +++ nginx-0.7.65/src/http/ngx_http_request.c 2010-02-16 17:12:09.000000000 +0900 @@ -1132,7 +1132,7 @@ c->error = 1; c->log->action = "reading client request headers"; - ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + ngx_http_close_request(r, NGX_HTTP_CLIENT_CLOSED_REQUEST_LINE); return NGX_ERROR; } @@ -2846,7 +2846,9 @@ log->action = "logging request"; - ngx_http_log_request(r); + if ( error != NGX_HTTP_CLIENT_CLOSED_REQUEST_LINE ) { + ngx_http_log_request(r); + } log->action = "closing request"; diff -ur nginx-0.7.65.orig/src/http/ngx_http_request.h nginx-0.7.65/src/http/ngx_http_request.h --- nginx-0.7.65.orig/src/http/ngx_http_request.h 2010-02-02 00:54:02.000000000 +0900 +++ nginx-0.7.65/src/http/ngx_http_request.h 2010-02-16 13:25:33.000000000 +0900 @@ -112,6 +112,7 @@ * own code to log such situation when a client has closed the connection * before we even try to send the HTTP header to it */ +#define NGX_HTTP_CLIENT_CLOSED_REQUEST_LINE 498 #define NGX_HTTP_CLIENT_CLOSED_REQUEST 499
Coro::LWP::UserAgent::timeout
use Coro::LWP; すると $ua->timeout が効かなくなるのでpatch
package Coro::LWP::UserAgent::timeout; use strict; use warnings; use Coro; use Coro::LWP; use Coro::Timer; use LWP::UserAgent; use ;; use HTTP::Response (); use HTTP::Status (); { no strict 'refs'; no warnings 'redefine'; my $original_send_request = *{'LWP::UserAgent::send_request'}{CODE}; *{'LWP::UserAgent::send_request'} = sub { my $self = shift; my $ua_thread; my $response = HTTP::Response->new(HTTP::Status::HTTP_REQUEST_TIMEOUT, "timeout"); $ua_thread = async { my $to_h = AE::timer( $self->{timeout}, 0, sub { $ua_thread->cancel(""); }) if($self->{timeout}); $response = $self->$original_send_request(@_); } @_; $ua_thread->join; $response; }; }; 1;
モブストライクについて全裸で考えてみた
課金による無限レベルアップ効率についてちょっと計算
ヤマモトさんがついったーでつぶやいてらっしゃったことを僕なりにまとめてみました。
レベルアップごとに上昇する必要経験値
1レベルごとに上昇する必要経験値 = 12.5exp
母国に旅行の経験値効率 = 95exp / 46体力 = 2.06
無限レベルアップ状態を維持するために上げなければならない体力 = 12.5exp / 2.06 = 6.07sp
レベルアップボーナスのみでの不足分 = 6.07 - 5 = 1.07
リワードポイントをスキルポイントと引き換えて無限レベルアップ
不足分1.07spを、課金でのスキルポイントブーストによって補う
RPのスキルポイント効率 = 14rp / 4sp = 3.5
不足分1.07spを補うのに必要なRP = 1.07sp * 3.5 = 3.75rp
レベルアップすると1rpボーナスがあるので、課金によって必要なRP = 3.75rp - 1rp = 2.75rp
リワードポイントを体力回復に使用して無限レベルアップ
同様に必要経験値量/2の体力で、RPによる体力回復を利用して無限LvUpを行うと
レベルアップごとに 10rp - 1rp = 9rp 必要になる。
また、連続レベルアップを止めた時点で保有する体力に大きな差もでる。
レベルアップの効率に焦点を当てると、ステータスアップのほうが効率が良い
RP購入による無限レベルアップの、日本円効率は、ステータスアップのほうが3倍以上良い。
15,000円課金すると 700 / 2.75 = 254.5レベルのスパイラルが可能になる
これにより上昇する体力は約1500となる
ヤマモトさんのご発言
http://twitter.com/ymtkyk/status/6643610318
http://twitter.com/ymtkyk/status/6643627785
LWP::UserAgent::parse_headerのベンチマーク
titleタグとか勝手にparseするやつね
結果
% perl parse_header.pl
Rate lwp lwp-header
lwp 543/s -- -19%
lwp-header 673/s 24% --
script
use Benchmark; use LWP::UserAgent; my $count = 3000; Benchmark::cmpthese( $count , { 'lwp' => sub { my $ua = LWP::UserAgent->new; my $res = $ua->get('http://localhost/') }, 'lwp-header' => sub { my $ua = LWP::UserAgent->new; $ua->parse_head(0); my $res = $ua->get('http://localhost/'); }, });
LWP vs AnyEvent::HTTP (+ Coro)
AnyEvent::HTTPで同時にアクセスしないようにしてテスト
% perl -I. anyevnet_http.pl
Benchmark: timing 3000 iterations of anyevent, lwp...
anyevent: 3 wallclock secs ( 1.57 usr + 1.27 sys = 2.84 CPU) @ 1056.34/s (n=3000)
lwp: 6 wallclock secs ( 4.25 usr + 1.22 sys = 5.47 CPU) @ 548.45/s (n=3000)
script
use Benchmark; use LWP::UserAgent; use AnyEvent; use AnyEvent::HTTP qw//; $AnyEvent::HTTP::MAX_PER_HOST = 1; my $count = 3000; timethese( $count , { 'lwp' => sub { my $ua = LWP::UserAgent->new; my $res = $ua->get('http://localhost/') }, 'anyevent' => sub { my $cv = AE::cv; AnyEvent::HTTP::http_get 'http://localhost/', sub { $cv->send('1'); }; $cv->recv; }, });
LWPは速くない
同じようにCoro::LWP
若干遅くなる
Benchmark: timing 3000 iterations of coro...
coro: 5 wallclock secs ( 1.75 usr + 3.83 sys = 5.58 CPU) @ 537.63/s (n=3000)
use Benchmark; use Coro; use Coro::LWP; use LWP::UserAgent; my $count = 3000; async { timethese( $count , { 'coro' => sub { my $ua = LWP::UserAgent->new; my $res = $ua->get('http://localhost/') }, }); exit; }; schedule;
Coro::Channelとtimeoutを組み合わせる
use Plack::Request; use HTTP::Request; use Time::HiRes; use Coro; use Coro::Channel; use Coro::AnyEvent; use Coro::LWP; use LWP::UserAgent; my $UA_WORKER_CORO_DESC_IN_WORK = "ua_worker_coro_desc_in_work"; my $UA_WORKER_CORO_DESC_NOT_WORK = "ua_worker_coro_desc_not_work"; my $UA_WORKER_CORO_TIMEOUT_MESSAGE = "ua_worker_coro_timeout"; my $MAX_UA_WORKER = 100; sub coro_timeout { my $timeout = shift; my $cb = shift; my $coro = $Coro::current; $coro->{timeout_at} = Time::HiRes::time() + $timeout; my $installed_destroy = $coro->{on_destroy_once} ? 1 : 0; $coro->{on_destroy_once} = $cb; if ( !$installed_destroy ) { $coro->on_destroy( sub { my $message = shift; return if $message ne $UA_WORKER_CORO_TIMEOUT_MESSAGE; my $cb = delete $coro->{on_destroy_once}; return unless $cb; $cb->($message); }); } } sub ua_worker { my $channel = shift; async { $Coro::current->desc($UA_WORKER_CORO_DESC_NOT_WORK); while(1) { my $req = $channel->get(); $Coro::current->desc($UA_WORKER_CORO_DESC_IN_WORK); my $ua = LWP::UserAgent->new( timeout => $req->[1] ); #timeout is ignored coro_timeout( $req->[1], sub { $req->[2]->send( LWP::UserAgent::_new_response( $req->[0], &HTTP::Status::RC_INTERNAL_SERVER_ERROR, "request timeout" ) ); }); my $res = $ua->request($req->[0]); $Coro::current->desc($UA_WORKER_CORO_DESC_NOT_WORK); $req->[2]->send($res); } } } # build worker threads my $worker_timer; sub build_ua_channel { warn "[$$] build channel"; my $channel = Coro::Channel->new(); for ( my $i=0; $i < $MAX_UA_WORKER; $i++ ) { ua_worker($channel); } $worker_timer = AnyEvent->timer( after => 0.5, interval => 0.5, cb => sub { #timeout my $now = Time::HiRes::time; my @lwp_coro = grep { $_->desc eq $UA_WORKER_CORO_DESC_IN_WORK } Coro::State::list; for my $coro (@lwp_coro) { if ($now > $coro->{timeout_at}) { $coro->cancel($UA_WORKER_CORO_TIMEOUT_MESSAGE); } } #spawn worker my $dead_worker = $MAX_UA_WORKER - scalar( grep { $_->desc eq $UA_WORKER_CORO_DESC_IN_WORK || $_->desc eq $UA_WORKER_CORO_DESC_NOT_WORK } Coro::State::list ); for ( my $i=0; $i < $dead_worker; $i++ ) { warn "[$$] respwan"; ua_worker($channel); } } ); return $channel; } my $ua_channel; sub async_ua_request { my $request = shift; my %args = @_; my $timeout = $args{timeout} || 180; my $cb = $args{cb} || sub {}; $ua_channel ||= build_ua_channel(); my $cv = AE::cv; $ua_channel->put( [$request, $timeout, $cv ] ); $cv->cb( sub { $cb->(shift->recv) } ); } my $hanlder = sub { my $env = shift; my $req = Plack::Request->new($env); my $cv = AE::cv; my $request = HTTP::Request->new( $env->{REQUEST_METHOD}, URI->new_abs($env->{REQUEST_URI},'http://localhost/'), [ map { (my $field = $_) =~ s/^HTTPS?_//; ( $field => $env->{$_} ); } grep { /^(?:HTTP|CONTENT|COOKIE)/i } keys %$env ], $req->raw_body ); async_ua_request( $request, timeout => 1, cb => sub { my $res = shift; my @res_header; $res->headers->scan(sub{ push @res_header, @_; }); $cv->send([ $res->code, \@res_header, [$res->content] ]); }); return sub { my $start_response = shift; $cv->cb( sub { $start_response->( shift->recv ); } ); }; }
TODO: global変数の部分をpackageにしてオブジェクトの中に納める
Coro::Channelをつかってみた
use Plack::Request; use HTTP::Request; use Data::Dumper; use Coro; use Coro::Channel; use Coro::AnyEvent; use Coro::LWP; use LWP::UserAgent; # worker threads sub build_channel { warn "[$$] build channel"; my $channel = Coro::Channel->new(); for my $i (0..100) { async { while(1) { my $req = $channel->get(); my $ua = LWP::UserAgent->new(); my $res = $ua->request($req->[0]); my @res_header; $res->headers->scan(sub{ push @res_header, @_; }); $req->[1]->send([ $res->code, \@res_header, [$res->content] ]); } } } return $channel; } my $channel; my $hanlder = sub { my $env = shift; my $req = Plack::Request->new($env); $channel ||= build_channel(); my $request = HTTP::Request->new( $env->{REQUEST_METHOD}, URI->new_abs($env->{REQUEST_URI},'http://localhost/'), [ map { (my $field = $_) =~ s/^HTTPS?_//; ( $field => $env->{$_} ); } grep { /^(?:HTTP|CONTENT|COOKIE)/i } keys %$env ], $req->raw_body ); my $cv = AE::cv; $channel->put( [$request,$cv ] ); return sub { my $start_response = shift; $cv->cb( sub { $start_response->( shift->recv ); } ); }; }
mobstrikeに於ける他攻略要素についての考察等
mobstrikeの攻略とか
自由に編集して下さって結構です。
各階層に於けるクリアボーナス
- ごろつき(lvls 1 - 4)
- 体力 -50秒短縮
- ちんぴら(lvls 5 - 8)
- スタミナ -50秒短縮
- ソルジャー(lvls 9 - 12)
- 体力 -40秒短縮
- 用心棒(lvls 13 - 17)
- スタミナ -40秒短縮
- ヒットマン(lvls 18 - 24)
- 体力 -30秒短縮
- 支部長(lvls 25 - 34)
- スタミナ -30秒短縮
- アンダーボス(lvls 60 - 99)
- 体力 -20秒短縮
- ボス(lvls 99 ~)
- スタミナ -20秒短縮
各々をクリア(全ての仕事をレベル3の習熟度100%)することでボーナスが享受出来る。勿論、ごろつき->ソルジャーの様な順にクリアしても問題無く効果を享受出来る様だ。
懸賞金TIPS
- 気になったマフィアに掛ける事で、現状のそいつを倒せるマフィアを探す事が出来る。
上限
- ファミリ
- 2510#(ただし、URLによる追加は可能な為、更なる高見は目指す事が可能。