パフォーマンスの向上


概要


前の文章 nGrinderとPinpointを使用してボトルネックの原因を解析し、パフォーマンスを最適化する方法を検討した.次に、大型サーバープロジェクトのパフォーマンスを最適化および検証する方法について説明します.

商品リストを見てAPI性能を改善する


商品リストAPIを表示するには、ディスクI/Oを削減するためにRedisキャッシュを使用することを考慮します.

キャッシュを適用すると、ディスクI/Oのみに依存していたデータアクセスも、以下のようにRedisに分散していることがわかります.

スクリプトのテスト

@RunWith(GrinderRunner)
class TestRunner {

	public static GTest test1
	public static GTest test2
	public static GTest test3
	
	public static HTTPRequest request
	public static NVPair[] headers = []
	public static NVPair[] params = []
	public static Cookie[] cookies = []
	
	public static MAX_RECORDS = 99800

	@BeforeProcess
	public static void beforeProcess() {
		HTTPPluginControl.getConnectionDefaults().timeout = 6000
		test1 = new GTest(1, "27.96.135.51")
		test2 = new GTest(2, "27.96.135.51")
		test3 = new GTest(3, "27.96.135.51")
		
		request = new HTTPRequest()
		grinder.logger.info("before process.");
	}

	@BeforeThread 
	public void beforeThread() {
		test1.record(this, "test1")
		test2.record(this, "test2")
		test3.record(this, "test3")
		
		grinder.statistics.delayReports=true;
		grinder.logger.info("before thread.");
	}
	
	@Before
	public void before() {
		request.setHeaders(headers)
		cookies.each { CookieModule.addCookie(it, HTTPPluginControl.getThreadHTTPClientContext()) }
		grinder.logger.info("before. init headers and cookies");
	}

	@Test
	public void test1(){
		String origin = "http://27.96.135.51:8080/products"
		String deliveryType = "ROCKET"
		int randomNum = Math.abs(new Random().nextInt() % MAX_RECORDS) + 1
		String params = "?deliveryType="+ deliveryType +"&start="+ Integer.toString(randomNum) +"&listSize="+"100"
		HTTPResponse result = request.GET(origin + params)

		if (result.statusCode == 301 || result.statusCode == 302) {
			grinder.logger.warn("Warning. The response may not be correct. The response code was {}.", result.statusCode); 
		} else {
			assertThat(result.statusCode, is(200));
		}
	}
	
	@Test
	public void test2(){
		String origin = "http://27.96.135.51:8080/products"
		String deliveryType = "ROCKET_FRESH"
		int randomNum = Math.abs(new Random().nextInt() % MAX_RECORDS) + 1
		String params = "?deliveryType="+ deliveryType +"&start="+ Integer.toString(randomNum) +"&listSize="+"100"
		HTTPResponse result = request.GET(origin + params)

		if (result.statusCode == 301 || result.statusCode == 302) {
			grinder.logger.warn("Warning. The response may not be correct. The response code was {}.", result.statusCode); 
		} else {
			assertThat(result.statusCode, is(200));
		}
	}
	
	@Test
	public void test3(){
		String origin = "http://27.96.135.51:8080/products"
		String deliveryType = "ROCKET_GLOBAL"
		int randomNum = Math.abs(new Random().nextInt() % MAX_RECORDS) + 1
		String params = "?deliveryType="+ deliveryType +"&start="+ Integer.toString(randomNum) +"&listSize="+"100"
		HTTPResponse result = request.GET(origin + params)

		if (result.statusCode == 301 || result.statusCode == 302) {
			grinder.logger.warn("Warning. The response may not be correct. The response code was {}.", result.statusCode); 
		} else {
			assertThat(result.statusCode, is(200));
		}
	}
}

パフォーマンス向上後の負荷テスト結果

  • nGrinder report
  • 指標
  • TPS : 1,124
  • Peak TPS : 1,547
  • CPU Usage : 100%
  • Errors : 0
  • パフォーマンスの改善前後の比較


  • TPS強化[前ページ]
    平均TPS:662
    ピークTPS:921
  • ✔平均TPSが662で停止した古い設計にRedisキャッシュを適用する.
    平均TPS性能は70%近く向上し、1124に達した.
  • Error強化[前ページ]
    2,674
  • 従来の2674個のエラー発生の設計では,Redisキャッシュを用いて,エラー発生率をゼロに完全に向上させることができた.

    クーポン発行API性能改善

    쿠폰 발급하기 APIのパフォーマンスを向上させるために、nginxを使用してサーバをscale-outに構成します.これにより、nginxは負荷バランサとして機能し、1台のサーバではなく2台のサーバにトラフィックを分散させることができます.

    スクリプトのテスト

    @RunWith(GrinderRunner)
    class TestRunner {
    	public static GTest test
    	public static HTTPRequest request
    	public Object cookies = []
    	
    	def nvs(def map) {
    		def nvs = []
    		map.each {
    			key, value ->  nvs.add(new NVPair(key, value))
    		}
    		return nvs as NVPair[]
    	}
    
    	@BeforeProcess
    	public static void beforeProcess() {
    		HTTPPluginControl.getConnectionDefaults().timeout = 6000
    		test = new GTest(1, "test : [POST] /available-coupons/1") 
    		request = new HTTPRequest()
    		test.record(request); 
    		grinder.logger.info("before process.");
    	}
    
    	@BeforeThread 
    	public void beforeThread() {
    		// reset to the all cookies
            def threadContext = HTTPPluginControl.getThreadHTTPClientContext()
            cookies = CookieModule.listAllCookies(threadContext)
            cookies.each {
                CookieModule.removeCookie(it, threadContext)
            }
            
    		int randomNum = Math.abs(new Random().nextInt() % 50000) + 1 
    		String email = Integer.toString(randomNum) + "@naver.com" 
    		HTTPResponse result = request.POST("http://[nginxIP]:8080/users/login", nvs(["email":email, "password":"1234"])) 
    		
    		cookies = CookieModule.listAllCookies(threadContext)
    		grinder.statistics.delayReports=true;
    		grinder.logger.info("before thread.");
    	}
    	
    	@Before
        public void before() {
            def threadContext = HTTPPluginControl.getThreadHTTPClientContext()
            cookies.each {
                CookieModule.addCookie(it ,threadContext)
                net.grinder.script.Grinder.grinder.logger.info("{}", it)
            }
        }
    	
    	@Test
    	public void couponTest() {
    		int randomNum = Math.abs(new Random().nextInt() % 20000) + 1
    		String couponId = Integer.toString(randomNum)
    		request.POST("http://[nginxIP]:8080/available-coupons/" + couponId)
    	}
    }

    パフォーマンス向上後の負荷テスト結果

  • nGrinder report
  • ААААААААААААААААА
    -存在しないクーポンを発行したい場合
    -同じクーポンを1人で複数回発行する場合
    最終的には、パフォーマンスの低下ではなく、正しいサービスロジック「BAD REQUEST」を通じていると判断しました.

    パフォーマンスの改善前後の比較


  • TPSチャート

    ✔TPSは約400~538の性能改善とは異なる
    性能改善後、TPSは約450から667に向上した.

  • CPU使用量

    АААААААААААААААА
  • ポスト


    キャッシュを適用したり、サーバを横方向に拡張したりしても、実際の環境でのパフォーマンスが改善されたかどうかは判断しにくい.ただし、nGrinderおよびpinpointツールを使用すると、パフォーマンスが向上した点がわかります.