1日あたり10万回の訪問(結果報告と新しい実験)

クーナー GAEデータの処理速度に関する前回の記事では、グラフィカルなインプレッションカウンターが統合されました。 誰もがカウンターの値と消費されたCPUリソースを見ることができました。 既に述べたように、カウンターはかなり「重い」ものでした。カウンターが作成する負荷は、キャッシュを使用せずにページ上のデータベースから1000レコードを表示するのと同等です。



カウンター実験は非常に有用であることが判明し、その結果は私にとっては予想外です(同じIPアドレスからの要求とは異なります)。 実験の結果を共有し、間違いを考慮して新しい実験を行いたいと思います。 ちなみに、新しいグラフィックカウンターのソースコードは誰でも利用でき、記事に記載されています。







クエリスケジュール



画像



1秒あたり最大7リクエスト(非常に多くの人が夕方にHabrのメインページを開きます)。 昼間は、この数字は1秒あたり10リクエストに達すると思います。



CPU時間グラフ



画像



グラフは、アクティビティの欠如などの不快な瞬間をキャプチャします。 この時点で、記事はメインページにありましたが、リソースの無料制限が終了したため、写真は表示されませんでした。 午前中、状況は修正されました。



初日の消費



画像



グラフには、午前10時のMCKまでリソースの消費はありません(10時にリソースカウンターがリセットされるため)。 最大で10時間、ほぼ同じ時間を使い果たしました。 合計約15万のリクエストと500 MBのトラフィック(静的な画像のリクエストの一部ですが、はるかに小さいです)。



実行中の権限の数



残念ながら、誤って実行中のインスタンスの画面を削除しました。 1秒あたり6人の訪問者がいると、16個のインスタンスが起動されました(これは記録した最大値です)。



禁止されたリクエスト???



画像



そして、ここが最も興味深いものです。 しばらくして、問題が発生しました:ほぼ5番目のリクエストが、平均(平均して、0.5秒間ロードされたカウンター)の5〜10倍の時間で実行され始めました。 最初は、多くの訪問があっても、これは観察されませんでした。 訪問数が非常に少ない今まで、状況は変わっていません。



これらのエラーの説明方法は? オプション:



1. ウォームアップリクエスト。 いいえ、インスタンスがアンロードするのに十分な時間が経過していないため(たとえば、このグラフでは、3つの誤ったリクエスト間の時間が1分未満です)。

2. 同じキーを使用して、リポジトリ内のレコードを同時に変更します。 いいえ、リクエスト間で10秒以上が経過したためです。

3.アプリケーションは無効であると制限されていました。



最後が残っています。 これは、平均して、要求が1秒未満(約0.5秒)で完了したという事実にもかかわらずです。 アプリケーションの非効率性を判断するためのアルゴリズムは公開されていないため、特定のことを言うのはかなり困難です...



正直なところ、私にとって、これらのエラーは謎のままです。



問題解決



アプリケーションが非効率的であると制限されていることが実際に問題である場合、問題は新しいカウンターコード(org.toyz.litetextが以前に使用されていた)で繰り返されません。 図面は非常にシンプルですが、非常に効果的です(コードは以下にあります-Webサイトで使用できます)。



第2版​​:



public final class DynamicImage

{

//

private static final String FOLDER = "resources" ;



private static Image backgroundImage;



private static final Map< String , Image> imageTable = new Hashtable < String , Image>();



static

{

try

{

backgroundImage = makeImage( "appengine.png" );



imageTable.put( "1" ,

makeImage( "1.png" ));

imageTable.put( "2" ,

makeImage( "2.png" ));

imageTable.put( "3" ,

makeImage( "3.png" ));

imageTable.put( "4" ,

makeImage( "4.png" ));

imageTable.put( "5" ,

makeImage( "5.png" ));

imageTable.put( "6" ,

makeImage( "6.png" ));

imageTable.put( "7" ,

makeImage( "7.png" ));

imageTable.put( "8" ,

makeImage( "8.png" ));

imageTable.put( "9" ,

makeImage( "9.png" ));

imageTable.put( "0" ,

makeImage( "0.png" ));



imageTable.put( "(" ,

makeImage( "leftBracket.png" ));

imageTable.put( ")" ,

makeImage( "rightBracket.png" ));

}

catch (IOException exception)

{

throw new RuntimeException(exception);

}

}



public Image drawText( String text,

Anchor anchor,

long backgroundColor)

{

if ( null == text)

throw new ArgumentNullException( "text" );



if ( null == anchor)

throw new ArgumentNullException( "anchor" );



Collection<Composite> compositeCollection = new ArrayList <Composite>();



// background

compositeCollection.add(ImagesServiceFactory.makeComposite(backgroundImage,

0,

0,

1f,

Anchor.TOP_LEFT));



int xOffset = 0;



for ( int pos = 0; pos < text.length(); pos++)

{

String symbol = Character.toString(text.charAt(pos));



if (!imageTable.containsKey(symbol))

continue ;



Image image = imageTable. get (symbol);



compositeCollection.add(ImagesServiceFactory.makeComposite(image,

xOffset,

0,

1f,

anchor));



xOffset += image.getWidth();

}



return ImagesServiceFactory.getImagesService().composite(compositeCollection,

backgroundImage.getWidth(),

backgroundImage.getHeight(),

backgroundColor);

}



private static Image makeImage( String imageName)

throws IOException

{

InputStream inputStream = null ;

ByteArrayOutputStream outputStream = null ;



try

{

String filePath = FOLDER + File .separatorChar + imageName;

inputStream = DynamicImage. class .getResourceAsStream(filePath);



if ( null == inputStream)

throw new IllegalStateException( "filePath=" + filePath);



outputStream = new ByteArrayOutputStream();



int length;

byte [] buffer = new byte [1024];



while ((length = inputStream.read(buffer,

0,

buffer.length)) > 0)

{

outputStream.write(buffer,

0,

length);

}



outputStream.flush();



return ImagesServiceFactory.makeImage(outputStream.toByteArray());

}

finally

{

if ( null != inputStream)

inputStream.close();



if ( null != outputStream)

outputStream.close();

}

}

}



* This source code was highlighted with Source Code Highlighter .








コミュニティに実験への参加をお願いしています。 この記事で結果を報告します。



実験の結果は私を幸せにしました! 半日の間、カウンターは約10万人によって開かれました(予想どおり)。 同時に、1 GBのトラフィックが消費されました(少なくとも、私は写真を縮小しました。さもないと、3 GBの制限に収まりません)。



画像



この場合、実行中の権限の最大数は9個でした。 (1秒あたり8リクエスト):



画像



そして、この時点でのリクエストは遅滞なく実行されました。 ログからの抜粋は次のとおりです。



画像



さらに、各要求はデータベースに書き込まれ、IPアドレスの一意性を検証しました。 無料で出ました! 十分な無料のリソース!



おわりに



適切に使用すれば、GAEは膨大な負荷に耐えることができます。 Googleは、多くの人々が夢見たものを実際に実行しました。実際に使用したリソースに対してのみ支払います(すべてが公平です。使用しない場合、何も支払う必要はありません)。 多くの訪問者を引き付けるサービスを作成するだけです。



All Articles