[my-klas]負荷テストシミュレーション
シミュレーションの定義
アプリケーションの実装は終わりに近づいており、負荷テストを試みます.
目標性能は、実際の学校で発生する可能性のある通信量を基準とします.
私たちの学校では1学期の授業申請を4回に分けて行い、学科や学年によって日程が異なります.
これは川の余石を公平に調整するためであるが,流量の分散効果も得られる.
学校が提供した入試結果資料を基準にして計算すると、学年ごとに2000人近くの学生がいて、在校生だけなら8000人から1万人程度です.
これを4組に分けると、1回の選択授業に2000人以上の生徒が参加する.
このとき申請単位は15~21点であり,学生1人当たり6科目を申請したと考えると,一瞬にして12000件のリクエストが発生する.
問題は連続して講義を申し込むことだ.したがって,応答時間が最も速くてこそユーザ体験が増加し,いくら遅くても1秒を超えてはならない.
シミュレーションの定義は次のとおりです.
400 Bad Request
の回答を受けます.シミュレーション実装
初期化スクリプト:def main():
# 모든 데이터 삭제
request('POST', '/admin/clear')
# 임의의 학생 생성
studentIds = []
for i in range(NUM_STUDENTS):
student = request('POST', '/students', body={ 'studentNumber': random_student_number()})
studentIds.append(student['id'])
# 임의의 강의 생성
lectureIds = []
for i in range(NUM_LECTURES):
level = i
lecture = request('POST', '/lectures', body={
'lectureNumber': random_lecture_number(level=level),
'term': '2021-1',
'name': f"test-{str(i)}",
'subject': f"test-{str(i)}",
'level': level,
'credit': 3,
'capacity': 10,
'schedules': []})
lectureIds.append(lecture['id'])
# 인기 강의 선택
popLectureIds = lectureIds[:NUM_POPULAR_LECTURES]
# CSV 파일 생성 (학생이 어떤 강의를 수강할 것인지 결정)
with open('registrations.csv', 'w', newline='') as csvfile:
spamwriter = csv.writer(csvfile, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL)
spamwriter.writerow(["studentId", "lectureIds"]) # Header
for studentId in studentIds:
registLectureIds = []
# 50% 확률로 인기 강의 중 하나를 골라 수강
if random.random() < 0.5:
registLectureIds.append(random.choice(popLectureIds))
# 정해진 수만큼 강의 수강
while len(registLectureIds) < NUM_LECTURES_PER_STUDENT:
registLectureIds.append(random.choice(lectureIds))
spamwriter.writerow([str(studentId), ' '.join(str(id) for id in registLectureIds)])
gatlingシミュレーションスクリプト:class RegisterSimulation extends Simulation {
// 설정 파일 로드
val simConfig: Config = ConfigFactory.load("simulation.conf")
val httpProtocol = http
.baseUrl(simConfig.getString("baseUrl"))
// 수강신청 시나리오 정의
val scn = scenario("RegisterSimulation")
.feed(csv("registrations.csv").queue)
.foreach(session => session("lectureIds").as[String].split(" ").toSeq, "lectureId") {
exec(http("register lectures")
.post("/students/${studentId}/register")
.body(StringBody("""{"lectureId":${lectureId}}""")).asJson
.check(status.is(200)))
}
// 시나리오 실행
setUp(
scn.inject(atOnceUsers(simConfig.getInt("register.numUsers")))
).protocols(httpProtocol)
}
初回実行結果
平均応答時間はなんと4秒!ユーザーが待つ時間が長すぎます.
しかし、負荷テストでは、JVMのクラス遅延ロードと割り込みプロセスを考慮する必要があります.
JVMのInterpreterは、JIT方式でバイトコードをタイムリーにコンパイルします.また,頻繁に実行されるコードがあれば,それをコンパイルして再利用し,速度を向上させることができる.
JVMのClass Loaderは、必要なクラスをタイムリーにロードし、メモリにロードします.
したがって、この2つのタスクを事前に実行すると、アプリケーションの実行速度が速くなります.これをJVM Warm-upと呼びます.
JVM Warm-up
最も簡単な暖房方法は、予めシミュレーションすることです.これは、必要なクラスをロードし、使用するコードをコンパイルしたためです.
ただし、デフォルト設定を使用しているJVMでは、1回のシミュレーションのみではコードを正常に動作させることはできません.これは、コンパイルするには、コードが特定の制限点以上で実行されなければならないためです.
-XX:CompileThreshold=invocations
Sets the number of interpreted method invocations before compilation. By default, in the server JVM, the JIT compiler performs 10,000 interpreted method invocations to gather information for efficient compilation.
...
This option is ignored when tiered compilation is enabled.
しきい値は、-XX:CompileThreshold=invocations
因子で設定できます.
デフォルト値は10000に達しますが、12000リクエストのうち約2000リクエストが失敗しているため、失敗に関連するコードを熱くするには、5回前にシミュレーションする必要があります.そのため、1000程度に設定すれば適切なはずです.
しかしながら、この因子を用いるためには、-XX:-TieredCompilation
因子を適用する必要がある.
階層型コンパイルは、Java 8からデフォルトで有効になっている機能で、頻繁に実行することでコンパイルレベルを向上させることで、コンパイル時間と最適化の程度の間でトレードオフを提供します.
JVMにはクライアント(-client
)とサーバ(-server
)方式のJITコンパイラがある.クライアントコンパイラのコンパイル速度は速いが、実行性能は悪い.サーバコンパイラのコンパイル速度は遅いが、実行性能は良い.階層コンパイルは,コンパイラの階層化によって実現されるといえる.
この機能はJVMヒーターを妨げ、コンパイル制限を設定できないため、オフにする必要があります.
つまり、次のパラメータを使用してサーバ・アプリケーションを実行します.
-XX:-TieredCompilation -XX:CompileThreshold=1000
次に,実際の測定前にシミュレーションを1回行い,必要なすべてのクラスがロードされ,使用するコードが十分に加熱された場合に実験を行うことができる.
JVM Warup後の実行結果
平均応答時間4239 ms→3253 ms
1000 ms程度改善されたが,満足できる結果は得られなかった.
生徒の立場に立って6時間の授業を受けなければならない.ボタンを押すたびに3秒も待たなければならないなら、本当に憂鬱だ.
問題はもう定義されているので、後でこれを解決します.
Reference
この問題について([my-klas]負荷テストシミュレーション), 我々は、より多くの情報をここで見つけました
https://velog.io/@woodyn1002/my-klas-부하-테스트-시뮬레이션
テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol
def main():
# 모든 데이터 삭제
request('POST', '/admin/clear')
# 임의의 학생 생성
studentIds = []
for i in range(NUM_STUDENTS):
student = request('POST', '/students', body={ 'studentNumber': random_student_number()})
studentIds.append(student['id'])
# 임의의 강의 생성
lectureIds = []
for i in range(NUM_LECTURES):
level = i
lecture = request('POST', '/lectures', body={
'lectureNumber': random_lecture_number(level=level),
'term': '2021-1',
'name': f"test-{str(i)}",
'subject': f"test-{str(i)}",
'level': level,
'credit': 3,
'capacity': 10,
'schedules': []})
lectureIds.append(lecture['id'])
# 인기 강의 선택
popLectureIds = lectureIds[:NUM_POPULAR_LECTURES]
# CSV 파일 생성 (학생이 어떤 강의를 수강할 것인지 결정)
with open('registrations.csv', 'w', newline='') as csvfile:
spamwriter = csv.writer(csvfile, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL)
spamwriter.writerow(["studentId", "lectureIds"]) # Header
for studentId in studentIds:
registLectureIds = []
# 50% 확률로 인기 강의 중 하나를 골라 수강
if random.random() < 0.5:
registLectureIds.append(random.choice(popLectureIds))
# 정해진 수만큼 강의 수강
while len(registLectureIds) < NUM_LECTURES_PER_STUDENT:
registLectureIds.append(random.choice(lectureIds))
spamwriter.writerow([str(studentId), ' '.join(str(id) for id in registLectureIds)])
class RegisterSimulation extends Simulation {
// 설정 파일 로드
val simConfig: Config = ConfigFactory.load("simulation.conf")
val httpProtocol = http
.baseUrl(simConfig.getString("baseUrl"))
// 수강신청 시나리오 정의
val scn = scenario("RegisterSimulation")
.feed(csv("registrations.csv").queue)
.foreach(session => session("lectureIds").as[String].split(" ").toSeq, "lectureId") {
exec(http("register lectures")
.post("/students/${studentId}/register")
.body(StringBody("""{"lectureId":${lectureId}}""")).asJson
.check(status.is(200)))
}
// 시나리오 실행
setUp(
scn.inject(atOnceUsers(simConfig.getInt("register.numUsers")))
).protocols(httpProtocol)
}
平均応答時間はなんと4秒!ユーザーが待つ時間が長すぎます.
しかし、負荷テストでは、JVMのクラス遅延ロードと割り込みプロセスを考慮する必要があります.
JVMのInterpreterは、JIT方式でバイトコードをタイムリーにコンパイルします.また,頻繁に実行されるコードがあれば,それをコンパイルして再利用し,速度を向上させることができる.
JVMのClass Loaderは、必要なクラスをタイムリーにロードし、メモリにロードします.
したがって、この2つのタスクを事前に実行すると、アプリケーションの実行速度が速くなります.これをJVM Warm-upと呼びます.
JVM Warm-up
最も簡単な暖房方法は、予めシミュレーションすることです.これは、必要なクラスをロードし、使用するコードをコンパイルしたためです.
ただし、デフォルト設定を使用しているJVMでは、1回のシミュレーションのみではコードを正常に動作させることはできません.これは、コンパイルするには、コードが特定の制限点以上で実行されなければならないためです.
-XX:CompileThreshold=invocations
Sets the number of interpreted method invocations before compilation. By default, in the server JVM, the JIT compiler performs 10,000 interpreted method invocations to gather information for efficient compilation.
...
This option is ignored when tiered compilation is enabled.
しきい値は、-XX:CompileThreshold=invocations
因子で設定できます.
デフォルト値は10000に達しますが、12000リクエストのうち約2000リクエストが失敗しているため、失敗に関連するコードを熱くするには、5回前にシミュレーションする必要があります.そのため、1000程度に設定すれば適切なはずです.
しかしながら、この因子を用いるためには、-XX:-TieredCompilation
因子を適用する必要がある.
階層型コンパイルは、Java 8からデフォルトで有効になっている機能で、頻繁に実行することでコンパイルレベルを向上させることで、コンパイル時間と最適化の程度の間でトレードオフを提供します.
JVMにはクライアント(-client
)とサーバ(-server
)方式のJITコンパイラがある.クライアントコンパイラのコンパイル速度は速いが、実行性能は悪い.サーバコンパイラのコンパイル速度は遅いが、実行性能は良い.階層コンパイルは,コンパイラの階層化によって実現されるといえる.
この機能はJVMヒーターを妨げ、コンパイル制限を設定できないため、オフにする必要があります.
つまり、次のパラメータを使用してサーバ・アプリケーションを実行します.
-XX:-TieredCompilation -XX:CompileThreshold=1000
次に,実際の測定前にシミュレーションを1回行い,必要なすべてのクラスがロードされ,使用するコードが十分に加熱された場合に実験を行うことができる.
JVM Warup後の実行結果
平均応答時間4239 ms→3253 ms
1000 ms程度改善されたが,満足できる結果は得られなかった.
生徒の立場に立って6時間の授業を受けなければならない.ボタンを押すたびに3秒も待たなければならないなら、本当に憂鬱だ.
問題はもう定義されているので、後でこれを解決します.
Reference
この問題について([my-klas]負荷テストシミュレーション), 我々は、より多くの情報をここで見つけました
https://velog.io/@woodyn1002/my-klas-부하-테스트-시뮬레이션
テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol
Reference
この問題について([my-klas]負荷テストシミュレーション), 我々は、より多くの情報をここで見つけました https://velog.io/@woodyn1002/my-klas-부하-테스트-시뮬레이션テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol