PlayFrameworkããã ã®éçåä»ãMVCã ã¨æã£ã¦æ¬çªç¨¼åãããã¨æ»ã¬
(3/15 : ã¿ã¤ãã«ä¿®æ£ãã¾ãããwã¯å°æåã§ããããã¿ã¾ããã»ã»ã»)
PlayFrameworkãæµè¡ãå§ãã¦ããå²ã¨çµã¡ã¾ãã®ã§ãããããæ£å¼æ¡ç¨ãããã¨èããæ¹ãå¤ãã®ã§ã¯ãªããã¨æãã¾ãã
å¼·åãªéçåä»ãã§å®ãããPlayã¯ãããã·ã§ã³ã¯ãªãã£ã«ã«ãªã·ã¹ãã ãæ°ä¸è¡ãè¶ ãã大è¦æ¨¡ã·ã¹ãã ã®æ§ç¯ã«ç¹ã«åãã¦ãããããªæ°ããã¾ãã
ã¾ããServletã使ã£ã¦ããªãã®ã«å ãã¦MVCæ§é ããã¼ã¹ãªã®ã§ãä»ã¾ã§Railsãªã©ã§éçºããã¦ãã人ã§ãã·ã¼ã ã¬ã¹ã«ç§»è¡ã§ããã¨æãã¾ãã
ããããå¿ãã¦ã¯ãªããªãã®ãPlayã®ã¢ã¼ããã¯ãã£ãå ¨ã¦ã®å¦çãéåæã§è¡ããããã¨ãåæã¨ãã¦ããã¨ããäºã§ãã
ãããå¿ãã¦Playããã ã®å¼·åãªéçåä»ãã§å®ãããMVCãã¬ã¼ã ã¯ã¼ã¯ã¨ã ãèãã¦éçºãé²ãã¦ãã¾ãã¨ãæ¬çªç°å¢ã§ç¨¼åãããæã«ããã©ã¼ãã³ã¹ãä¸ãããã«å°ããã¨ã«ãªãããããã¾ãããä»åã¯ãã®ãããã«ã¤ãã¦æ¸ããã¨æãã¾ãã
Playã®ã¢ã¼ããã¯ãã£
ä»åè¨ããããã¨ã¯Playå ¬å¼ã®ã¹ã¬ãããã¼ã«å¨ãã®Documentationæåã®æ°è¡ã«ã»ã¼å ¨ã¦æ¸ãã¦ããã¾ãã https://www.playframework.com/documentation/ja/2.1.x/ThreadPools
éè¦ã ã¨æãã¨ãããããã¤ãå¼ç¨ãã¦ããã¾ãã
Play framework ã¯ãä¸ããä¸ã¾ã§ãéåæ㪠web ãã¬ã¼ã ã¯ã¼ã¯ã§ãã
play-core å ã® IO ã¯ãããã¯ãããªãã®ã§ãä¼çµ±ç㪠web ãã¬ã¼ã ã¯ã¼ã¯ã¨æ¯è¼ãã¦ãã¹ã¬ãããã¼ã«ã¯ä½ç®ã«èª¿æ´ããã¦ãã¾ãã
ããããã³ã° IO ããæ½å¨çã«å¤ãã® CPU ãéç´ãã¦å®è¡å¯è½ãªã³ã¼ããæ¸ãããã¨èããå ´åã«ãã©ã®ã¹ã¬ãããã¼ã«ããã®å¦çãå®è¡ãã¦ããããç¥ããããã«å¿ãã¦èª¿æ´ããå¿ è¦ãããã¾ãããããèæ ®ã«å ¥ããã«ããããã³ã° IO ãè¡ãã¨ãPlay framework ã®ããã©ã¼ãã³ã¹ã¯è²§å¼±ã«ãªãå¾ã¾ãã
ä¼çµ±çãªwebãã¬ã¼ã ã¯ã¼ã¯ã¨æ¯è¼ãã¦ãã¹ã¬ãããã¼ã«ã¯ä½ç®ã«èª¿æ´ããã¦ãã¾ããã¨ããç®æãç¹ã«æ³¨æãã¹ãã¨ããã§ããã
ããã ãã§èª¬æã¯ååã¨è¨ãã°ååãªã®ã§ããããã説æä¸è¶³ãªæ°ãããã®ã§ããå°ã詳ããæ¸ãã¾ãã
Webãµã¼ãã®ã¢ã¼ããã¯ãã£ã¨ãã¦ã¯ã主ã«ä»¥ä¸ã®ãããªãã®ãããã¾ãã
- 1ãªã¯ã¨ã¹ãããã1ããã»ã¹ã§æããã«ãããã»ã¹å(LLè¨èªã«å¤ã)
- 1ãªã¯ã¨ã¹ãããã1ã¹ã¬ããã§æããã«ãã¹ã¬ããå(Javaã®Servletãªã©)
- ã¤ãã³ãã«ã¼ããå©ç¨ãã¦è¤æ°ãªã¯ã¨ã¹ãã1ã¹ã¬ããã§æãã¤ãã³ãé§åå(Node.jsãªã©)
ãã®ãããã«é¢ãã¦ã¯è²ã ãªæ¹ã説æè¨äºãæ¸ãã¦ãã ãã£ã¦ããã®ã§ãå²æãã¾ãã
ãªã©ã§èª¬æããã¦ããã®ã§ãã¡ããã覧ãã ããã
ãã®ä¸ã§ã¯Playã¯ãã«ãã¹ã¬ãã + ã¤ãã³ãé§ååã§ãã¦ã CPUã³ã¢æ°åã®ã¹ã¬ãããä½ã£ã¦ãã®ã¹ã¬ããã®ä¸ã§ã¤ãã³ãé§ååã§ãªã¯ã¨ã¹ããå¦çããã¨ããã®ãããã©ã«ãæåã§ãã
ããã§éè¦ãªã®ã¯ãã¤ãã³ãé§ååã§ã¯åºæ¬çã«å ¨ã¦ã®å¦çãéåæã§è¡ãå¿ è¦ããããä»ã¾ã§ã®éçºã¨åãæè¦ã§æ¸ãã¦ãã¨æ»ã¬ã¨ããäºã§ããä»ã¾ã§æ®éã®Javaã§éçºãã¦ããæ¹ãPlayã«ç§»è¡ããå ´åãç¥ããã«ããããã³ã°å¦çã使ã£ã¦ãã¾ãäºãããããããªãããªã¨æãã¾ããå°ãªãã¨ãåã¯æåç¥ããã«ä½¿ã£ã¦ã¾ããã
Playã§ããããã³ã°I/Oã使ã£ãæã®æå
ã§ã¯Playã§ããããã³ã°I/Oã使ã£ãæã®å®éã®æåãè¦ã¦ã¿ã¾ãããã以ä¸ã®ãããªããããã³ã°I/Oã模æ¬ããã³ã³ããã¼ã©ãä½ããApache Benchã§åæã«10ãªã¯ã¨ã¹ããè¡ã£ã¦ã¿ã¾ãããªããçµæãåãããããããããã«Playã®ã¹ã¬ããæ°ã¯1ã«è¨å®ãã¦ããã¾ãã(ããã©ã«ãã§ã¯ã¹ã¬ããæ° = CPUæ°)
ããããã³ã°I/Oã模æ¬ããã³ã³ããã¼ã©
public class TestController extends Controller { public static Result blockingHeavyProcess() throws InterruptedException{ Thread.sleep(3000); return ok(); } public static Result lightProcess() throws InterruptedException{ Thread.sleep(10); return ok(); } }
GET /block controllers.TestController.blockingHeavyProcess() GET /light controllers.TestController.lightProcess()
ã¹ã¬ããæ°ã1ã«è¨å®(application.confã«è¨è¿°)
default-dispatcher = { fork-join-executor { parallelism-factor = 1.0 parallelism-max = 1 } }
軽ããªã¯ã¨ã¹ãã ãå®è¡
ab -n 10 -c 10 http://localhost:9000/_api/light Concurrency Level: 10 Time taken for tests: 0.134 seconds Requests per second: 74.53 [#/sec] (mean) Time per request: 134.171 [ms] (mean)
éããªã¯ã¨ã¹ãã®å®è¡ä¸ã«è»½ããªã¯ã¨ã¹ããå®è¡
ab -n 10 -c 10 http://localhost:9000/_api/block Concurrency Level: 10 Time taken for tests: 30.242 seconds Requests per second: 0.33 [#/sec] (mean) Time per request: 30242.090 [ms] (mean) ab -n 10 -c 10 http://localhost:9000/_api/light Concurrency Level: 10 Time taken for tests: 29.416 seconds Requests per second: 0.34 [#/sec] (mean) Time per request: 29415.664 [ms] (mean)
ãã®éããéããªã¯ã¨ã¹ãããã£ãå ´åã軽ããªã¯ã¨ã¹ãã®æ¹ã¾ã§è©°ã¾ã£ã¦ãã¾ãã¨ããäºãåããã¾ããç¾å®ã§ä¾ããã¨ãSQLã¸æ¥ç¶ããå¦çãéããªãã¨ãMemCachedã«æ¥ç¶ããå¦çã¾ã§éããªã£ã¦ãã¾ãã¨ããæãã§ããããã
ããã¯Playã1ã¤ã®ã¹ã¬ããã§è¤æ°ã®ãªã¯ã¨ã¹ããæãã¤ãã³ãé§åã¢ãã«ã ããã§ããéãå¦çã®æ¹ãã¹ã¬ãããå æãã¦ãã¾ã£ã¦ããã®ã§å¾ããå®è¡ããã軽ãå¦çã®æ¹ãè©°ã¾ã£ã¦ãã¾ãã¾ãã
ããããããããã³ã°I/Oãè¡ããªããã°è¯ãã®ã§ãããå®éã®ããã°ã©ã ã§ããããã³ã°I/Oãå ¨ãè¡ããªãã¨ããã®ã¯ãã¾ãç¾å®çã§ã¯ããã¾ãããã¨ããããã§ä½ããã®æ¹æ³ã§ããããã³ã°I/Oãè¡ã£ã¦ãå¦çãè©°ã¾ããªãããã«ããå¿ è¦ãããã¾ãã
解決ç1 : ã¹ã¬ããæ°èªä½ãå¢ãã
Playã¯ãã«ãã¹ã¬ãã + ã¤ãã³ãé§åã¢ãã«ã§ãããªã®ã§ãããããã³ã°I/Oãé¿ããããªãå ´åã¯ã¹ã¬ããæ°èªä½ãå¢ããã¦ãã¾ãã¨ããã®ãæãåç´ãªè§£æ±ºçã§ãã
ã¨ããããã§ãããã©ã«ãã§ã¯CPUæ°ã¶ãããçæãããªãã¹ã¬ããæ°ã15ã«ãã¦ãå ã»ã©ã¨åããã¹ããè¡ã£ã¦ã¿ã¾ãã
ã¹ã¬ããæ°ã15ã«è¨å®(application.confã«è¨è¿°)
default-dispatcher = { fork-join-executor { parallelism-min = 15 parallelism-max = 15 } }
éããªã¯ã¨ã¹ãã®å®è¡ä¸ã«è»½ããªã¯ã¨ã¹ããå®è¡
ab -n 10 -c 10 http://localhost:9000/_api/block Concurrency Level: 10 Time taken for tests: 3.088 seconds Requests per second: 3.24 [#/sec] (mean) Time per request: 3088.092 [ms] (mean) ab -n 10 -c 10 http://localhost:9000/_api/light Concurrency Level: 10 Time taken for tests: 0.036 seconds Requests per second: 276.31 [#/sec] (mean) Time per request: 36.191 [ms] (mean)
ãã®ããã«å ¨ä½ã®ã¹ã¬ããæ°ãå¢ããäºã§ãéããªã¯ã¨ã¹ããããã¤ãçããéã§ãããç¨åº¦èª¤éåãã¾ããå®éã®ã¹ã¬ããæ°ãããã¤ã«ããããã¨ããã®ã¯å®éã®ãã·ã³ã¹ããã¯ãè² è·ç¶æ³ãè¦ã¦æ±ºãã¦ããã ããã°è¯ãããªã¨æãã¾ãã
ãã®æ¹æ³ã¯é常ã«åç´ã§ããããç¨åº¦ã®å¹æãåºã¾ããããããéãå¦çã¨è»½ãå¦çãåä¸ã®ã¹ã¬ããã使ã£ã¦ããã¨ããç¹ã§ã¯æ ¹æ¬è§£æ±ºã«ã¯ãªã£ã¦ããããçµå±è¨å®ããã¹ã¬ããæ°ä»¥ä¸ã®éããªã¯ã¨ã¹ããæ¥ãå ´åã¯è»½ããªã¯ã¨ã¹ããè©°ã¾ã£ã¦ãã¾ãã¾ãã
ã¾ããã¤ãã³ãé§åã¢ãã«ã使ã£ã¦C10Kåé¡ã«å¯¾å¿ãããã¨ãã¦ããPlayã§ã¹ã¬ãããå¤æ°çæãã¦ãã¾ãã®ã¯ä½ããç³ã訳ãªãæ°æã¡ã«ãªãã¾ãã
ã¨ããããã§å¥ã®è§£æ±ºçãèãã¦ã¿ã¾ãããã
解決ç2 : ããããã³ã°I/Oã¯å¥ã®ã¹ã¬ããã§ãã
éããªã¯ã¨ã¹ããæ¥ãéã§ã軽ããªã¯ã¨ã¹ãã«å½±é¿ãåºãªãããã«ããã«ã¯ãå¥ã ã®ã¹ã¬ããã使ãã®ãè¯ãããã§ãã
Playã§ã¯Promiseçææã«æ示çã«å¥ã®ExecutionContextã渡ãäºã§ãå¥ã
ã®ã¹ã¬ããã§å¦çãè¡ãäºãã§ãã¾ãã
詳ããã¯å¥è¨äºã§æ¸ãã¦ãã¾ãã®ã§ãã¡ããã覧ãã ããã
ã¾ããããããExecutionContextã¨ã¯ä½ããã¨ãã話ã«é¢ãã¦ã¯ãã¡ãã®è¨äºã§è©³ãã説æããã¦ãã¾ãã®ã§ã覧ãã ããã
ããããã³ã°å¦çãå¥ã®ã¹ã¬ããã§è¡ãã³ã³ããã¼ã©
public static F.Promise<Result> blockingHeavyProcess() { final String uniqueId = UUID.randomUUID().toString(); Logger.debug("process Start : " + uniqueId); ExecutionContext executionContext = Akka.system().dispatchers().lookup("play.akka.actor.heavy-promises-dispatcher"); F.Promise<Result> resultPromise = F.Promise.promise(new F.Function0<Result>() { @Override public Result apply() throws Throwable { Logger.debug("promise Start : " + uniqueId); long start = System.nanoTime(); Thread.sleep(3000); long end = System.nanoTime(); Logger.debug("processTime : " + (end - start) / 1000000 + "msec : " + uniqueId); return ok(); } },executionContext); return resultPromise.map(new F.Function<Result, Result>() { @Override public Result apply(Result result) throws Throwable { Logger.debug("return result : " + uniqueId); return result; } }); } public static F.Promise<Result> lightProcess() throws InterruptedException{ final String uniqueId = UUID.randomUUID().toString(); Logger.debug("process Start : " + uniqueId); ExecutionContext executionContext = Akka.system().dispatchers().lookup("play.akka.actor.light-promises-dispatcher"); F.Promise<Result> resultPromise = F.Promise.promise(new F.Function0<Result>() { @Override public Result apply() throws Throwable { Logger.debug("promise Start : " + uniqueId); long start = System.nanoTime(); Thread.sleep(10); long end = System.nanoTime(); Logger.debug("processTime : " + (end - start) / 1000000 + "msec : " + uniqueId); return ok(); } },executionContext); return resultPromise.map(new F.Function<Result, Result>() { @Override public Result apply(Result result) throws Throwable { Logger.debug("return result : " + uniqueId); return result; } }); }
å¥ã®ExecutionContextãè¨å®
heavy-promises-dispatcher = { fork-join-executor { parallelism-min = 10 parallelism-max = 10 } } light-promises-dispatcher = { fork-join-executor { parallelism-min = 10 parallelism-max = 10 } }
éããªã¯ã¨ã¹ãã®å®è¡ä¸ã«è»½ããªã¯ã¨ã¹ããå®è¡
ab -n 10 -c 10 http://localhost:9000/_api/block Concurrency Level: 10 Time taken for tests: 3.168 seconds Requests per second: 3.16 [#/sec] (mean) Time per request: 3167.555 [ms] (mean) ab -n 10 -c 10 http://localhost:9000/_api/light Concurrency Level: 10 Time taken for tests: 0.042 seconds Requests per second: 236.01 [#/sec] (mean) Time per request: 42.371 [ms] (mean)
ããããã³ã°I/Oãæ示çã«å¥ã®ã¹ã¬ããã§è¡ãäºã§ãè©°ã¾ã£ãéã®å½±é¿ããã®ã¹ã¬ããå
ã ãã«ã¨ã©ããäºãã§ãã¾ããç¨éå¥ã§ã¹ã¬ãããåããã¨è¯ããã§ãã(SQLã¢ã¯ã»ã¹ç¨ã¹ã¬ãããmemcachedã¢ã¯ã»ã¹ç¨ã¹ã¬ããããã³ããããã³ã°å¦çç¨ã¹ã¬ããetc...)
åºæ¬çã«ã¯ãã¡ãã®è§£æ±ºçã®æ¹ã綺éºãªæ°ããã¾ãããã ç¨éå¥ã«ã¹ã¬ããåããã®ãé¢åãããæã¯ã¨ããããå
¨ä½ã®ã¹ã¬ããæ°ãå¢ããã¦ãã¾ãã®ãããããªã¨ã¯æãã¾ãã
çµè«
Playã®å¼·åãªéçåä»ããæ¸ããããMVCã¯é常ã«é åçã ããã¤ãã³ãé§ååã§ããäºãå¿ãã¦ããããã³ã°å¦çãæ¸ãã¾ããã¨æ»ã¬
ããããã³ã°å¦çãæ¸ãæã¯ä»¥ä¸ã®ã©ã¡ãããããã¨è¯ã
ç¨éå¥ã«ã¹ã¬ãããåãã¦å¦çãæ¸ã(è©°ã¾ãã«ããã·ã¹ãã ã«ãªã£ã¦ç´ æµã ãããé¢å)
å ¨ä½ã®ã¹ã¬ããæ°ãå¢ããã¦ãã¾ã(æç´ã ã楽)
ã¾ã ã¾ã ç¥ããªãäºãå¤ãã®ã§ãçªã£è¾¼ã¿ãªã©ããã ããã¨å©ããã¾ãï¼
ãªããå¼ç¤¾ã§ã¯ç¾å¨ã¨ã³ã¸ãã¢ãåéä¸ã§ãï¼èå³ããã¾ãããæ¯éãªãã£ã¹ã«éã³ã«æ¥ã¦ãã ããã