|
|
|
Contents
[edit]
1 °³¿ä #Çö´ëÀÇ ¿î¿µÃ¼°èµéÀº µ¿½Ã½ÇÇà¿¡ ±âÃÊÇÑ ¾îÇø®ÄÉÀ̼Ç(¼¹ö)¸¦ °³¹ßÇÏ´Â °ÍÀ» Áö¿øÇϱâ À§ÇØ ¿©·¯°¡Áö ¸ÅÄ¿´ÏÁòµéÀ» Á¦°øÇÑ´Ù. µ¿±âÀû ¸ÖƼ¾²·¹µùÀº ¿©·¯ µ¿ÀÛÀÌ µ¿½Ã´Ù¹ßÀûÀ¸·Î ½ÇÇàµÇ´Â ¾îÇø®ÄÉÀ̼ÇÀ» °³¹ßÇϴµ¥ ÀÖ¾î¼ ÀαâÀÖ´Â ¸ÅÄ¿´ÏÁòÀÌ´Ù. ±×·¸Áö¸¸ ¾²·¹µå´Â Á¾Á¾ ³ôÀº ¼º´É °úºÎÇϸ¦ °¡Áú ¶§°¡ ÀÖ°í, µ¿±âÈ ÆÐÅϰú ¹ýÄ¢µé¿¡ ´ëÇÑ ±íÀº Áö½ÄÀ» ¿ä±¸ÇÑ´Ù. ±×·¯¹Ç·Î, ¿©·¯ ¿î¿µÃ¼°èµéÀº µ¿½Ã½ÇÇà 󸮿¡ ÀÖ¾î¼ ¸ÖƼ¾²·¹µùÀÇ °úºÎÇϳª º¹ÀâµµÀÇ ´ëºÎºÐÀ» ¿ÏȽÃŰ´Â ÀåÁ¡ÀÌ ÀÖ´Â ºñµ¿±âÀû ¸ÅÄ¿´ÏÁòÀ» Áö¿øÇÑ´Ù.
ÀÌ ³í¹®¿¡¼ Á¦½ÃÇÏ´Â proactor ÆÐÅÏÀº ¿î¿µÃ¼°è¿¡ ÀÇÇØ Á¦°øµÇ´Â ºñµ¿±â ¸ÅÄ¿´ÏÁòµéÀ» È¿À²ÀûÀ¸·Î ´Ù·ç´Â ¾îÇø®ÄÉÀ̼ǰú ½Ã½ºÅÛÀ» ±¸ÃàÇÏ´Â ¹ý¿¡ ´ëÇØ¼ ¼³¸íÇϰí ÀÖ´Ù. ¾îÇø®ÄÉÀ̼ÇÀÌ ºñµ¿±âÀûÀÎ ÀÛ¾÷À» ¼öÇàÇÒ ¶§, ¿î¿µÃ¼°è´Â ¾îÇø®ÄÉÀ̼ÇÀ» ´ë½ÅÇÏ¿© ÀÛ¾÷À» ½ÇÇàÇÑ´Ù. À̰ÍÀº ¾îÇø®ÄÉÀ̼ÇÀ¸·Î ÇÏ¿©±Ý ÇÊ¿äÇÑ ¸¸ÅÀÇ ¼öÀÇ ¾²·¹µå¸¦ °¡Áú ÇÊ¿ä¾øÀÌ µ¿½Ã¿¡ µ¿ÀÛÇÏ´Â ´Ù¼öÀÇ µ¿ÀÛÀ» ½ÇÇàÇÒ ¼ö ÀÖ°Ô ÇØÁØ´Ù. µû¶ó¼, proactor ÆÐÅÏÀº ¼¹ö ÇÁ·Î±×·¡¹ÖÀ» ´Ü¼øÈ ½ÃÄÑÁÖ¸ç, ºñµ¿±â 󸮸¦ À§ÇÑ ¿î¿µÃ¼°èÀÇ Áö¿ø¿¡ ÀÇÁ¸ÇÏ°í º¸´Ù ÀûÀº ¾²·¹µå¸¦ ¿ä±¸ÇÏ°Ô µÊÀ¸·Î½á ¼º´ÉÀ» ÁõÁø½ÃÄÑÁÖ°Ô µÈ´Ù.
[edit]
2 ÃëÁö #proactor ÆÐÅÏÀº ºñµ¿±â À̺¥Æ®µéÀÇ ¿Ï·á½ÃÁ¡¿¡ ½ÇÇàµÇ´Â ´ÙÁß À̺¥Æ® Çڵ鷯µéÀÇ µð½ºÆÐΰú µð¸ÖƼÇ÷¢½ÌÀ» Áö¿øÇÑ´Ù. ÀÌ ÆÐÅÏÀº ¿Ï·á À̺¥Æ®µéÀÇ µð¸ÖƼÇ÷¢½ÌÀ» ÅëÇÕÇÏ°í ±×¿¡ ¾Ë¸ÂÀº À̺¥Æ® Çڵ鷯µéÀ» µð½ºÆÐĪÇÏ´Â °ÍÀ» Áö¿øÇÔÀ¸·Î½á ºñµ¿±â±â¹ÝÀÇ ¾îÇø®ÄÉÀÌ¼Ç °³¹ßÀ» ´Ü¼øÈÇØÁØ´Ù.
[edit]
3.1 °í¼º´É ¼¹öÀÇ Àǹ̰ú ´É·Â #µ¿±âÀûÀÎ ¹æ½ÄÀÇ ¸ÖƼ¾²·¹µå ȤÀº ¹ÝÀÀÀûÀÎ(reactive) ÇÁ·Î±×·¡¹Ö»ó¿¡¼ Á¦¾à¾øÀÌ µ¿½Ã󸮹æ½ÄÀ¸·Î ÀÛ¾÷µéÀ» ½ÇÇà½ÃŰ·Á Çϴµ¥ ¼º´É»óÀÇ ÀÕÁ¡ÀÌ ¿ä±¸µÉ ¶§¿¡´Â proactor ÆÐÅÏÀ» »ç¿ëÇÑ´Ù. ÀÌ ÀÕÁ¡µéÀ» ¼³¸íÇϱâ À§Çؼ µ¿½Ã󸮹æ½ÄÀ¸·Î ´ÙÁßÀÇ Ã³¸®¸¦ ½ÇÇàÇÒ Çʿ䰡 ÀÖ´Â ³×Æ®¿öÅ© ¾îÇø®ÄÉÀ̼ÇÀ» °í·ÁÇÏÀÚ. ¿¹¸¦ µéÀÚ¸é, °í¼º´ÉÀÇ À¥¼¹ö´Â ¹Ýµå½Ã ¿©·¯°³ÀÇ Å¬¶óÀÌ¾ðÆ® [1, 2]·ÎºÎÅÍ º¸³»¾îÁø HTTP ¿äûµéÀ» µ¿½Ã¿¡ ó¸®Çؾ߸¸ ÇÑ´Ù. "Figure 1"Àº À¥ºê¶ó¿ìÁ®µé°ú À¥¼¹ö»çÀÌÀÇ ÀüÇüÀûÀÎ »óÈ£ÀÛ¿ë°ü°è¸¦ º¸¿©ÁØ´Ù. »ç¿ëÀÚ°¡ ºê¶ó¿ìÁ®¿¡°Ô URLÀ» ¿µµ·Ï Áö½ÃÇϸé, ºê¶ó¿ìÁ®´Â HTTP GET ¿äûÀ» À¥ ¼¹ö·Î º¸³½´Ù. ¿äûÀ» Á¢¼öÇÏ¸é ¼¹ö´Â ¿äûÀ» ÆÄ½ÌÇϰí Àû¹ýÇÑÁö °Ë»çÇÑ ÈÄ, ÁöÁ¤µÈ ÈÀÏ(µé)À» ºê¶ó¿ìÁ®·Î µÇµ¹·Á º¸³½´Ù.
![]() °í¼º´ÉÀÇ À¥¼¹ö°¡ °³¹ßµÇ±â À§Çؼ´Â ´ÙÀ½ ´É·ÂµéÀ» °¡Á®¾ßÇÑ´Ù:
[edit]
3.2 ÀüÅëÀûÀÎ µ¿½Ã󸮹æ½Ä ¼¹ö¸ðµ¨µéÀÇ ÀϹÝÀûÀÎ µ£°ú ÇÔÁ¤µé #µ¿±âÈµÈ ¸ÖƼ¾²·¹µù°ú reactiveÇÑ ÇÁ·Î±×·¡¹ÖÀº µ¿½Ã󸮸¦ ±¸ÇöÇÏ´Â ÀϹÝÀûÀÎ ¹æ¹ýÀÌ´Ù. ÀÌ ÀåÀº ÀÌ ÇÁ·Î±×·¡¹Ö ¸ðµ¨µé¿¡ ´ëÇÑ °áÁ¡µéÀ» ¼³¸íÇÑ´Ù.
[edit]
3.2.1 ´ÙÁß µ¿±âÈ ¾²·¹µå¸¦ ÅëÇÑ µ¿½Ãó¸® #¾Æ¸¶µµ ´ëºÎºÐÀÇ µ¿½Ã󸮹æ½ÄÀÇ À¥¼¹ö¸¦ ±¸ÇöÇÏ´Â Á÷°üÀûÀÎ ¹æ¹ýÀº µ¿±âÀûÀÎ ¸ÖƼ¾²·¹µù ¹æ½ÄÀÌ´Ù. ÀÌ ¸ðµ¨¿¡¼´Â ´ÙÁß ¼¹ö ¾²·¹µåµéÀÌ µ¿½Ã¿¡ ¿©·¯ Ŭ¶óÀÌ¾ðÆ®·Î ºÎÅÍ HTTP GET ¿äûÀ» ó¸®ÇÑ´Ù. °¢ ¾²·¹µå´Â Á¢¼Ó ±¸ÃàÀ» ½ÇÇàÇϰí, HTTP ¿äûÀ» Àаí, ¿äûÀ» ÆÄ½ÌÇϸç, ÈÀÏ Àü¼Û 󸮸¦ µ¿±âÀûÀ¸·Î ¼öÇàÇÑ´Ù. °á°úÀûÀ¸·Î °¢ ó¸®´Â ÇØ´ç 󸮰¡ ¿Ï·áµÉ ¶§ ±îÁö ºí·°´çÇÑ´Ù.
µ¿±âÈµÈ ¾²·¹µù ¹æ½ÄÀÇ ÁÖµÈ ÀÕÁ¡Àº ¾îÇø®ÄÉÀÌ¼Ç ÄÚµåÀÇ ´Ü¼øÇÔÀÌ´Ù. Ưº°ÇÑ °æ¿ì, Ŭ¶óÀÌ¾ðÆ® AÀÇ ¿äûÀ» ¼ºñ½ºÇϱâÀ§ÇØ À¥¼¹ö¿¡ ÀÇÇØ ½ÇÇàµÇ´Â 󸮵éÀº Ŭ¶óÀÌ¾ðÆ® BÀÇ ¿äûÀ» ¼ºñ½ºÇϱâ À§ÇÑ Ã³¸®¿Í´Â ´ëºÎºÐ µ¶¸³ÀûÀÌ´Ù. µû¶ó¼, ¾²·¹µå°£¿¡ °øÀ¯µÇ´Â »óŵéÀÇ ¾çÀÌ Àû±â ¶§¹®¿¡ º°µµÀÇ ¾²·¹µå»ó¿¡¼ ¼·Î ´Ù¸¥ ¿äûµéÀ» ¼ºñ½ºÇÏ´Â °ÍÀÌ ½¬¿î °ÍÀÌ´Ù. (À̰ÍÀº µ¿±âÈÀÇ Çʿ伺À» ÃÖ¼ÒÈÇÏ´Â ¿äÀÎÀÌ´Ù) °Ô´Ù°¡, º°µµÀÇ ¾²·¹µå»ó¿¡¼ ¾îÇø®ÄÉÀÌ¼Ç ·ÎÁ÷À» ½ÇÇàÇÏ´Â °ÍÀº °³¹ßÀÚ·Î ÇÏ¿©±Ý ¼øÂ÷ÀûÀÎ ¸í·Éµé°ú ºí·ÏÅ· 󸮸¦ ´Ù·ç´Â °ÍÀ» Çã¿ëÇÑ´Ù.
![]() "Figure 2"´Â ¾î¶»°Ô µ¿±âÀûÀÎ ¾²·¹µå¸¦ »ç¿ëÇÏ¿© µðÀÚÀÎµÈ À¥¼¹ö°¡ ¿©·¯°³ÀÇ Å¬¶óÀÌ¾ðÆ®µéÀ» µ¿½Ã½ÇÇà¹æ½ÄÀ¸·Î ó¸®ÇÒ ¼ö ÀÖ´ÂÁö º¸¿©ÁØ´Ù. ÀÌ figure´Â Sync Acceptor°¡ µ¿±âÀûÀ¸·Î ³×Æ®¿öÅ© Á¢¼ÓÀ» accept ó¸®ÇϱâÀ§ÇÑ ¼¹öÃø ±¸Á¶¸¦ ÀºÆóÇϰí(encapsulate)ÀÖ´Ù´Â °ÍÀ» º¸¿©ÁØ´Ù. "¿¬°á 1°³´ç ¾²·¹µå 1°³" ¹æ½ÄÀ» »ç¿ëÇØ¼ HTTP GET ¿äûÀ» ¼ºñ½ºÇϱâÀ§ÇÑ °¢°¢ÀÇ ¾²·¹µåµéÀÇ ½ÇÇà´Ü°è´Â ´ÙÀ½°ú °°ÀÌ ¿ä¾àµÉ ¼ö ÀÖ´Ù:
[edit]
3.2.2 ¹ÝÀÀÀû(reactive) À̺¥Æ® µð½ºÆÐĪÀ» ÅëÇÑ µ¿½Ãó¸® #¶Ç´Ù¸¥ µ¿±âÀû¹æ½ÄÀÇ À¥¼¹ö¸¦ ±¸ÇöÇÏ´Â ÀϹÝÀûÀÎ ¹æ¹ýÀº ¹ÝÀÀÀû(reactive) À̺¥Æ® µð½ºÆÐĪ ¸ðµ¨À» »ç¿ëÇÏ´Â °ÍÀÌ´Ù. reactor ÆÐÅÏÀº ¾î¶»°Ô ¾îÇø®ÄÉÀ̼ÇÀÌ Initiation Dispatcher¸¦ »ç¿ëÇÏ¿© À̺¥Æ® Çڵ鷯¸¦ µî·ÏÇÒ ¼ö ÀÖ´ÂÁö º¸¿©ÁØ´Ù. Initiation Dispatcher´Â ºí·ÏÅ· ¾øÀÌ ¸í·ÉÀÌ ÀÔȸ(initiate)°¡´ÉÇÒ °æ¿ì ±×¿¡ ¾Ë¸Â´Â À̺¥Æ® Çڵ鷯¸¦ ¾Ë·ÁÁØ´Ù.
The Initiation Dispatcher notifies the Event Handler when it is possible to initiate an operation without blocking.
½Ì±Û¾²·¹µå ±â¹ÝÀÇ µ¿½Ãó¸® ¹æ½Ä À¥¼¹ö´Â reactive À̺¥Æ® µð½ºÆÐĪ ¸ðµ¨À» »ç¿ëÇÒ ¼ö ÀÖ´Ù. (ÀÌ ¸ðµ¨Àº reactor°¡ ¾Ë¸ÂÀº ¸í·ÉÀÌ µé¾î¿ÔÀ½À» ¾Ë·ÁÁÙ ¶§ ±îÁö À̺¥Æ® ·çÇÁ»ó¿¡¼ ±â´Ù¸®´Â ±¸Á¶¸¦ °¡Áø´Ù.)
À¥¼¹ö»óÀÇ reactive ¸í·É¿¡ ´ëÇÑ ¿¹´Â Initiation DispatcherÀ» »ç¿ëÇÑ acceptorÀÇ µî·ÏÀÛ¾÷ÀÌ´Ù. µ¥ÀÌŸ°¡ ³×Æ®¿öÅ© ¿¬°áÀ» ÅëÇØ¼ µµÂøÇϸé, µð½ºÆÐÃÄ´Â acceptor¸¦ ÄݹéÇÑ´Ù. acceptor´Â ³×Æ®¿öÅ© ¿¬°áÀ» ¼ö¶ôÇϰí HTTP Çڵ鷯¸¦ »ý¼ºÇÑ´Ù. ±×·± ´ÙÀ½ ÀÌ HTTP Çڵ鷯´Â À¥¼¹öÀÇ ½Ì±Û¾²·¹µå Á¦¾îÇÏ¿¡¼ ¹æ±Ý ÁøÇàµÇ´Â ¿¬°á·Î Àü¼ÛµÇ¾î¿Â URL ¿äûÀ» ó¸®Çϱâ À§ÇØ reactor¿¡ µî·ÏµÈ´Ù.
figure 3°ú 4´Â ¹ÝÀÀÀû À̺¥Æ® µð½ºÆäĪÀ» »ç¿ëÇÏ¿© µðÀÚÀÎµÈ À¥¼¹ö°¡ ¿©·¯°³ÀÇ Å¬¶óÀÌ¾ðÆ®¸¦ ¾î¶»°Ô ´Ù·ç´ÂÁö¸¦ º¸¿©ÁØ´Ù. figure 3´Â À¥¼¹ö·Î Ŭ¶óÀÌ¾ðÆ®°¡ Á¢¼ÓÇÒ¶§ ¹â°ÔµÇ´Â ´Ü°è¸¦ º¸¿©ÁÖ¸ç, figure 4´Â À¥¼¹ö°¡ ¾î¶»°Ô Ŭ¶óÀÌ¾ðÆ® ¿äûÀ» ó¸®ÇÏ´ÂÁö º¸¿©ÁØ´Ù.
![]() figure 3ÀÇ ´Ü°è´Â ´ÙÀ½°ú °°ÀÌ ¿ä¾àµÉ ¼ö ÀÖ´Ù:
![]() ±×¸² 4´Â reactive À¥¼¹ö°¡ HTTP GET ¿äûÀ» ¼ºñ½ºÇÏ´Â ´Ü°è¸¦ º¸¿©ÁØ´Ù. ±× °úÁ¤Àº ´ÙÀ½°ú °°´Ù:
reactive ¸ðµ¨ÀÇ ÁÖµÈ ÀåÁ¡Àº À̽ļº, coarse-grained µ¿½Ãó¸® Á¦¾î¿¡ µû¸¥ ³·Àº °úºÎÇÏ (´Ù½Ã¸»Çϸé, ½Ì±Û½º·¹µå ¹æ½ÄÀº µ¿±âȳª ÄÁÅØ½ºÆ® ½ºÀ§ÄªÀÌ ¿ä±¸µÇÁö ¾Ê´Â´Ù.), µð½ºÆäĪ ü°è·Î ºÎÅÍ ¾îÇø®ÄÉÀÌ¼Ç ·ÎÁ÷À» ºÐ¸®ÇÔÀ¸·Î¼ ¾òÀ»¼ö ÀÖ´Â ¸ðµâÈÀÇ ÀÕÁ¡µéÀ» µé ¼ö ÀÖ´Ù. ±×·³¿¡µµ ºÒ±¸Çϰí, ÀÌ ¹æ½ÄÀº ´ÙÀ½°ú °°Àº ´ÜÁ¡µéÀ» °¡Áö°í ÀÖ´Ù:
[edit]
3.3 ÇØ°áÃ¥ : proactive 󸮸¦ ÅëÇÑ µ¿½Ãó¸® #OS Ç÷¿ÆûÀÌ ºñµ¿±â ¸í·ÉµéÀ» Áö¿øÇÒ °æ¿ì, °í¼º´ÉÀÇ À¥¼¹ö¸¦ ±¸ÇöÇÏ´Â È¿À²ÀûÀÌ°í Æí¸®ÇÑ ¹æ¹ýÀº proactive À̺¥Æ® µð½ºÆäĪÀ» »ç¿ëÇÏ´Â °ÍÀÌ´Ù. proactive À̺¥Æ® µð½ºÆäĪÀ» »ç¿ëÇÏ¿©µðÀÚÀÎµÈ À¥ ¼¹ö´Â ÇѰ³ÀÌ»óÀÇ ¾²·¹µå¸¦ Á¦¾îÇÏ¿© ºñµ¿±â¸í·ÉÀÇ ¿Ï·á¿©ºÎ¸¦ ´Ù·ç´Â °ÍÀÌ °¡´ÉÇÏ´Ù. µû¶ó¼, proactor ÆÐÅÏÀº ¿Ï·á À̺¥Æ® µð¸ÖƼÇ÷º½Ì°ú À̺¥Æ® Çڵ鷯 µð½ºÆäĪÀ» ÅëÇÕÇÔÀ¸·Î½á ºñµ¿±â¹æ½ÄÀÇ À¥¼¹ö ±¸Á¶¸¦ ´Ü¼øÈ½ÃŲ´Ù.
ºñµ¿±â À¥¼¹ö´Â ¿î¿µÃ¼°è¿¡ óÀ½¿¡ ºñµ¿±â¸í·ÉÀ» ½Ãµ¿ÇÒ ¶§¿Í ¸í·ÉÀÌ ¿Ï·áÇßÀ» ¶§¸¦ ¾Ë·ÁÁÖ±â À§ÇÑ ¿Ï·á ¹ß¼ÛÀÚ(completion dispatcher)¿¡ ÄݹéÇÔ¼ö¸¦ µî·ÏÇϱâ À§ÇØ proactor ÆÐÅÏÀ» »ç¿ëÇÒ ¼ö ÀÖ´Ù. ¿î¿µÃ¼°è´Â À̶§ À¥¼¹öÀÔÀå¿¡¼ ¸í·ÉÀ» ¼öÇàÇÏ¸ç ¼øÂ÷ÀûÀ¸·Î ¿î¿µÃ¼°è ³»ÀÇ Àß ¾Ë·ÁÁø °÷¿¡ °á°ú¸¦ ÀûÀç(queue)ÇÑ´Ù. ¿Ï·á ¹ß¼ÛÀÚ(Completion Dispatcher)´Â ¿Ï·á ¾Ë¸²¸Þ¼¼ÁöµéÀ» »Ì¾Æ³»°í(dequeue), ¾îÇø®ÄÉÀÌ¼Ç µ¿ÀÛÀ§ÁÖÀÇ À¥¼¹ö Äڵ带 ´ãÀº ¾Ë¸ÂÀº ÄݹéÇÔ¼ö¸¦ ½ÇÇàÇÏ´Â ¿ªÇÒÀ» ´ã´çÇÑ´Ù.
±×¸² 5¿Í 6Àº proactor ÆÐÅϹæ½ÄÀÇ À̺¥Æ® µð½ºÆÐĪÀ» »ç¿ëÇÏ¿© µðÀÚÀÎµÈ À¥¼¹ö°¡ ÇѰ³ ÀÌ»óÀÇ ¾²·¹µå³»¿¡¼ ¿©·¯ Ŭ¶óÀÌ¾ðÆ®µéÀ» ¾î¶»°Ô µ¿½Ãó¸®ÇÏ´ÂÁö¸¦ º¸¿©ÁØ´Ù.
![]() ±×¸² 5´Â Ŭ¶óÀÌ¾ðÆ®°¡ À¥¼¹ö·Î Á¢¼ÓÇßÀ»¶§ ½ÇÇàµÇ´Â ´Ü°èÀÇ ¼ø¼¸¦ º¸¿©ÁØ´Ù.
![]() ±×¸² 6Àº proactor ÆÐÅÏÀ» Àû¿ëÇÑ À¥¼¹ö°¡ HTTP GET ¿äûÀ» ¼ºñ½ºÇϱâÀ§ÇÑ ´Ü°è¸¦ º¸¿©ÁØ´Ù. ÀÌ ´Ü°è´Â ¾Æ·¡¿Í °°´Ù.
proactor ¸ðµ¨ÀÇ ÁÖ¿ä ´ÜÁ¡Àº reactor ¸ðµ¨º¸´Ù ÇÁ·Î±×·¡¹Ö ·ÎÁ÷ÀÌ º¸´Ù ´õ º¹ÀâÇØÁú ¼ö ÀÖ´Ù´Â °Í ÀÌ´Ù. °Ô´Ù°¡, ºñµ¿±â ¸í·ÉµéÀº °¡²û ¿¹ÃøÇϱâ Èûµé°í ¹Ýº¹µÇÁö¾Ê´Â ½ÇÇà¼ø¼¸¦ °¡Áö´Â ±î´ß¿¡ proactor ÆÐÅÏÀº ½ÇÇà ºÐ¼®°ú µð¹ö±×ÇϱⰡ ´Ù¼Ò ¾î·Æ´Ù. 7ÀåÀº ºñµ¿±â ¾îÇø®ÄÉÀ̼ÇÀ» ´Ü¼øÈ½ÃÄÑÁÖ´Â (ºñµ¿±â ¿Ï·á ÅäÅ«[8]°ú °°Àº) ´Ù¸¥ ÆÐÅϵéÀ» Àû¿ë½ÃŰ´Â ¹æ¹ý¿¡ ´ëÇØ ¼³¸íÇϰí ÀÖ´Ù.
[edit]
4 Àû¿ëÇØ¾ß ÇÒ °æ¿ì #proactor ÆÐÅÏÀº ´ÙÀ½°ú °°Àº Á¶°ÇÀ» ÇѰ³ ÀÌ»ó ¸¸Á·ÇÒ ¶§ »ç¿ëÇϱ⸦ ±ÇÀåÇÑ´Ù.
[edit]
5 ±¸Á¶¿Í ±¸¼º¿ä¼Òµé #proactor ÆÐÅÏÀÇ ±¸Á¶´Â figure 7¿¡ OMT Ç¥±â¹ýÀ¸·Î ±×·ÁÁ®ÀÖ´Ù.
![]() proactor ÆÐÅÏÀÇ ÇÙ½É ±¸¼º¿ä¼Ò´Â ´ÙÀ½°ú °°´Ù:
[edit]
6 Collaborations #![]() There are several well-defined steps that occur for all Asynchronous Operations. At a high level of abstraction, applications initiate operations asynchronously and are notified when the operations complete. Figure 8 shows the following interactions that must occur between the pattern participants:
[edit]
7.1 ÀåÁ¡ #proactor ÆÐÅÏÀº ´ÙÀ½°ú °°Àº ÀåÁ¡µéÀ» °¡Áö°í ÀÖ´Ù:
[edit]
7.2 ´ÜÁ¡ #proactor ÆÐÅÏÀº ´ÙÀ½°ú °°Àº ´ÜÁ¡µéÀ» °¡Áö°í ÀÖ´Ù:
[edit]
8 ±¸Çö #Proactor ÆÐÅÏÀº ´Ù¾çÇÑ ¹æ¹ýÀ¸·Î ±¸ÇöµÉ ¼ö ÀÖ´Ù. ÀÌ ÀåÀº Proactor ÆÐÅÏÀ» ±¸ÇöÇϴµ¥ ÇÊ¿äÇÑ ´Ü°è¸¦ ¾Ë¾Æº»´Ù.
[edit]
8.1 ºñµ¿±â ¸í·É ÇÁ·Î¼¼¼ÀÇ ±¸Çö #The first step in implementing the Proactor pattern is build-ing the Asynchronous Operation Processor. The Asynchronous Operation Processor is responsible for executing operations asynchronously on behalf of applications. As a result, its two primary responsibilities are exporting Asynchronous Operation APIs and implementing an Asynchronous Operation Engine to do the work.
[edit]
8.1.1 ºñµ¿±â ¸í·É API¸¦ Á¤ÀÇÇϱâ #The Asynchronous Operation Processor must provide an API that allows applications to request Asynchronous Operations. There are several forces to be considered when designing these APIs:
class Asynch_Stream
// = TITLE
// A Factory for initiating reads
// and writes asynchronously.
{
// Initializes the factory with information
// which will be used with each asynchronous
// call. <handler> is notified when the
// operation completes. The asynchronous
// operations are performed on the <handle>
// and the results of the operations are
// sent to the <Completion_Dispatcher>.
Asynch_Stream (Completion_Handler &handler, HANDLE handle, Completion_Dispatcher *);
// This starts off an asynchronous read.
// Upto <bytes_to_read> will be read and
// stored in the <message_block>.
int read (Message_Block &message_block, u_long bytes_to_read, const void *act = 0);
// This starts off an asynchronous write.
// Upto <bytes_to_write> will be written
// from the <message_block>.
int write (Message_Block &message_block, u_long bytes_to_write, const void *act = 0);
...
};
[edit]
8.1.2 ºñµ¿±â ó¸® ¿£ÁøÀÇ ±¸Çö #The Asynchronous Operation Processor must contain a mechanism that performs the operations asynchronously. In other words, when an application thread invokes an Asynchronous Operation, the operation must be performed without borrowing the application's thread of control. Fortunately, modern operating systems provide mechanisms for Asynchronous Operations (for example, POSIX asynchronous I/O and WinNT overlapped I/O). When this is the case, implementing this part of the pattern simply requires mapping the platform APIs to the Asynchronous Operation APIs described above. If the OS platform does not provide support for Asynchronous Operations, there are several implementation techniques that can be used to build an Asynchronous Operation Engine. Perhaps the most intuitive solution is to use dedicated threads to perform the Asynchronous Operations for applications.
To implement a threaded Asynchronous Operation Engine, there are three primary steps:
[edit]
8.2 Implementing the Completion Dispatcher #The Completion Dispatcher calls back to the Completion Handler that is associated with the application objects when it receives operation completions from the Asynchronous Operation Processor. There are two issues involved with implementing the Completion Dispatcher: (1) implementing callbacks and (2) defining the concurrency strategy used to perform the callbacks.
[edit]
8.2.1 Implementing Callbacks #The Completion Dispatcher must implement a mechanism through which Completion Handlers are invoked. This requires Proactive Initiators to specify a callback when initiating operations. The following are common callback alternatives:
[edit]
8.2.2 Defining Completion Dispatcher Concurrency Strategies #A Completion Dispatcher will be notified by the Asynchronous Operation Processor when operations completes. At this point, the Completion Dispatcher can utilize one of the following concurrency strategies to perform the application callback:
![]() If your OS only supports synchronous I/O, then refer to the Reactor pattern [5]. However, most modern operating systems support some form of asynchronous I/O. In combination A and B from Table 1, the Post-reactive approach to asynchronous I/O is probably the best, assuming you are not waiting on any semaphores or mutexes. If you are, a Call-through implementation may be more responsive. In combination C, use a Call-through approach. In combination D, use a Thread Pool approach. In practice, systematic empirical measurements are necessary to select the most appropriate alternative.
[edit]
8.3 Implementing Completion Handlers #The implementation of Completion Handlers raises the following concerns.
[edit]
8.3.1 State Integrity #A Completion Handler may need to maintain state information concerning a specific request. For instance, the OS may notify the Web Server that only part of a file was written to the network communication port. As a result, a Completion Handler may need to reissue the request until the file is fully written or the connection becomes invalid. Therefore, it must know the file that was originally specified, how many bytes are left to write, and what was the file pointer position at the start of the previous request. There is no implicit limitation that prevents Proactive Initiators from assigning multiple Asynchronous Operation requests to a single Completion Handler.
As a result, the Completion Handler must tie request-specific state information throughout the chain of completion notifications. To do this, Completion Handlers can utilize the Asynchronous Completion Token pattern [8].
[edit]
8.3.2 Resource Management #As with any multi-threaded environment, the Proactor pattern does not alleviate Completion Handlers from ensuring that access to shared resources is thread-safe. However,
a Completion Handler must not hold onto a shared resource across multiple completion notifications. If it does, it risks invoking the dining philosopher's problem 11.
This problem is the deadlock that results when a logical thread of control waits forever for a semaphore to become signaled. This is illustrated by imagining a dinner party attended by a group of philosophers. The diners are seated around a circular table with exactly one chop stick between each philosopher. When a philosopher becomes hungry, he
must obtained the chop stick to his left and to his right in order to eat. Once philosophers obtain a chop stick, they will not release it until their hunger is satisfied. If all philosophers pick up the chop stick on their right, a deadlock occurs because the chop stick on the left will never become available.
[edit]
8.3.3 Preemptive Policy #The Completion Dispatcher type determines if a Completion Handler can be preemptive while executing. When attached to Dynamic-thread and Thread Pool dispatchers, Completion Handlers are naturally preemptive. However, when tied to a Post-reactive Completion Dispatcher, Completion Handlers are not preemptive with respect to each other. When driven by a Call-through dispatcher, the Completion Handlers are not preemptive with respect to the thread-of-control that is in the alertable wait state. In general, a handler should not perform long-duration synchronous operations unless multiple completion threads are used since this will significantly decrease the overall responsiveness of the application. This risk can be alleviated by increased programming discipline. For instance, all Completion Handlers are required to act as Proactive Initiators instead of executing synchronous operations.
[edit]
9 Sample Code #This section shows howto use the Proactor pattern to develop a Web server. The example is based on the Proactor pattern implementation in the ACE framework [4]. When a client connects to the Web server, the HTTP Handler¡¯s open method is called. The server then initializes the asynchronous I/O object with the object to call-back when the Asynchronous Operation completes (which in this case is this), the network connection for transferring the data, and the Completion Dispatcher to be used once the operation completes (proactor ). The read operation is then started asynchronously and the server returns to the event loop. The HTTP Handler::handle read stream is called back by the dispatcher when the Async read operation completes. If there is enough data, the client request is then parsed. If the entire client request has not arrived yet, another read operation is initiated asynchronously. In response to a GET request, the server memory-maps the requested file and writes the file data asynchronously to the client. The dispatcher calls back on HTTP Handler::handle write stream when the write operation completes, which frees up dynamically allocated resources. The Appendix contains two other code examples for implementing the Web server using a synchronous threaded model and a synchronous (non-blocking) reactive model.
class HTTP_Handler : public Proactor::Event_Handler
// = TITLE
// Implements the HTTP protocol
// (asynchronous version).
//
// = PATTERN PARTICIPANTS
// Proactive Initiator = HTTP_Handler
// Asynch Op = Network I/O
// Asynch Op Processor = OS
// Completion Dispatcher = Proactor
// Completion Handler = HTPP_Handler
{
public:
void open (Socket_Stream *client)
{
// Initialize state for request
request_.state_ = INCOMPLETE;
// Store reference to client.
client_ = client;
// Initialize asynch read stream
stream_.open (*this, client_->handle(), proactor_);
// Start read asynchronously.
stream_.read (request_.buffer(), request_.buffer_size());
}
// This is called by the Proactor
// when the asynch read completes
void handle_read_stream(u_long bytes_transferred)
{
if (request_.enough_data(bytes_transferred))
parse_request();
else
// Start reading asynchronously.
stream_.read(request_.buffer(), request_.buffer_size());
}
void parse_request()
{
// Switch on the HTTP command type.
switch (request_.command ()) {
// Client is requesting a file.
case HTTP_Request::GET:
// Memory map the requested file.
file_.map (request_.filename ());
// Start writing asynchronously.
stream_.write (file_.buffer (),
file_.buffer_size ());
break;
// Client is storing a file
// at the server.
case HTTP_Request::PUT:
// ...
}
}
void handle_write_stream(u_long bytes_transferred)
{
if (file_.enough_data(bytes_transferred))
// Success....
else
// Start another asynchronous write
stream_.write(file_.buffer(), file_.buffer_size());
}
private:
// Set at initialization.
Proactor *proactor_;
// Memory-mapped file_;
Mem_Map file_;
// Socket endpoint.
Socket_Stream *client_;
// HTTP Request holder
HTTP_Request request_;
// Used for Asynch I/O
Asynch_Stream stream_;
};
[edit]
10 Known Uses #The following are some widely documented uses of the Proctor pattern:
[edit]
11 Related Patterns #Figure 9 illustrates patterns that are related to the Proactor.
![]() The Asynchronous Completion Token (ACT) pattern [8] is generally used in conjunction with the Proactor pattern. When Asynchronous Operations complete, applications may need more information than simply the notification itself to properly handle the event. The Asynchronous Completion Token pattern allows applications to efficiently associate state with the completion of Asynchronous Operations. The Proactor pattern is related to the Observer pattern 12 (where dependents are updated automatically when a single
subject changes). In the Proactor pattern, handlers are informed automatically when events from multiple sources occur. In general, the Proactor pattern is used to asynchronously demultiplex multiple sources of input to their associated event handlers, whereas an Observer is usually associated with only a single source of events.
The Proactor pattern can be considered an asynchronous variant of the synchronous Reactor pattern [5]. The Reactor pattern is responsible for demultiplexing and dispatching of multiple event handlers that are triggered when it is possible to initiate an operation synchronously without blocking. In contrast, the Proactor supports the demultiplexing and dispatching of multiple event handlers that are triggered by the completion of asynchronous events. The Active Object pattern 13 decouples method execution from method invocation. The Proactor pattern is similar because Asynchronous Operation Processors perform operations on behalf of application Proactive Initiators. That is, both patterns can be used to implement Asynchronous Operations. The Proactor pattern is often used in place of the Active Object pattern to decouple the systems concurrency policy from the threading model. A Proactor may be implemented as a Singleton 12. This is useful for centralizing event demultiplexing and completion
dispatching into a single location within an asynchronous application. The Chain of Responsibility (COR) pattern 12 decouples event handlers from event sources. The Proactor pattern is similar in its segregation of Proactive Initiators and Completion Handlers. However, in COR, the event source has no prior knowledge of which handler will be executed, if any. In Proactor, Proactive Initiators have full disclosure of the target handler. However, the two patterns can be combined by establishing a Completion
Handler that is the entry pont into a responsibility chain dynamically configured by an external factory.
[edit]
12 Concluding Remarks #The Proactor pattern embodies a powerful design paradigm that supports efficient and flexible event dispatching strategies for high-performance concurrent applications. The Proactor pattern provides the performance benefits of executing operations concurrently, without constraining the developer to synchronous multi-threaded or reactive programming.
[edit]
13 References #[1] J. Hu, I. Pyarali, and D. C. Schmidt, ¡°Measuring the Impact of Event Dispatching and Concurrency Models on Web Server Performance Over High-speed Networks,¡± in Proceedings of the 2nd Global Internet Conference, IEEE, November 1997.
[2] J. Hu, I. Pyarali, and D. C. Schmidt, ¡°Applying the Proactor Pattern to High-Performance Web Servers,¡± in Proceedings of the 10th International Conference on Parallel and Distributed Computing and Systems, IASTED, Oct. 1998.
[3] J. C. Mogul, ¡°The Case for Persistent-connection HTTP,¡± in Proceedings of ACMSIGCOMM ¡¯95 Conference in Computer
Communication Review, (Boston, MA, USA), pp. 299–314,
ACM Press, August 1995.
[4] D. C. Schmidt, ¡°ACE: an Object-Oriented Framework for Developing Distributed Applications,¡± in Proceedings of the 6 th USENIX C++ Technical Conference, (Cambridge, Mas-sachusetts), USENIX Association, April 1994.
[5] D. C. Schmidt, ¡°Reactor: An Object Behavioral Pattern for Concurrent Event Demultiplexing and Event Handler Dis-patching,¡± in Pattern Languages of Program Design (J. O.
Coplien and D. C. Schmidt, eds.), pp. 529–545, Reading, MA: Addison-Wesley, 1995.
[6] D. C. Schmidt, ¡°Acceptor and Connector: Design Patterns for Initializing Communication Services,¡± in Pattern Languages of Program Design (R. Martin, F. Buschmann, and D. Riehle, eds.), Reading, MA: Addison-Wesley, 1997.
[7] M. K. McKusick, K. Bostic, M. J. Karels, and J. S. Quarter-man, The Design and Implementation of the 4.4BSD Operating System. Addison Wesley, 1996.
[8] I. Pyarali, T. H. Harrison, and D. C. Schmidt, ¡°Asynchronous Completion Token: an Object Behavioral Pattern for Efficient Asynchronous Event Handling,¡± in Pattern Languages of Program Design (R. Martin, F. Buschmann, and D. Riehle, eds.), Reading, MA: Addison-Wesley, 1997.
[9] ¡°Information Technology – Portable Operating System Interface (POSIX) – Part 1: System Application: Program Interface (API) C Language,¡± 1995. 10 Microsoft Developers Studio, Version 4.2 - Software Development Kit, 1996.
11 E. W. Dijkstra, ¡°Hierarchical Ordering of Sequential Pro-cesses,¡± Acta Informatica, vol. 1, no. 2, pp. 115–138, 1971.
12 E. Gamma, R. Helm, R. Johnson, and J. Vlissides, Design Pat-terns: Elements of Reusable Object-Oriented Software. Reading, MA: Addison-Wesley, 1995.
13 R. G. Lavender and D. C. Schmidt, ¡°Active Object: an Object Behavioral Pattern for Concurrent Programming,¡± in Proceedings of the 2nd Annual Conference on the Pattern Languages of Programs, (Monticello, Illinois), pp. 1–7, September 1995.
[edit]
13.1 A. Alternative Implementations #This Appendix outlines the code used to develop alternatives to the Proactor pattern. Below, we examine both synchronous I/O using multi-threading and reactive I/O using
single-threading.
[edit]
13.1.1 A.1 Multiple Synchronous Threads #The following code shows how to use synchronous I/O with a pool of threads to develop a Web server. When a client con-nects to the server a thread in the pool accepts the connection and calls the open method in class HTTP Handler. The server then synchronously reads the request from the network connection. When the read operation completes,
the client request is then parsed. In response to a GET request, the server memory-maps the requested file and writes the file data synchronously to the client. Note how blocking I/O allows the Web server to follow the steps outlined in Section 2.2.1.
class HTTP_Handler
// = TITLE
// Implements the HTTP protocol
// (synchronous threaded version).
//
// = DESCRIPTION
// This class is called by a
// thread in the Thread Pool.
{
public:
void open (Socket_Stream *client)
{
HTTP_Request request;
// Store reference to client.
client_ = client;
// Synchronously read the HTTP request
// from the network connection and
// parse it.
client_->recv (request);
parse_request (request);
}
void parse_request (HTTP_Request &request)
{
// Switch on the HTTP command type.
switch (request.command ())
{
// Client is requesting a file.
case HTTP_Request::GET:
// Memory map the requested file.
Mem_Map input_file;
input_file.map (request.filename());
// Synchronously send the file
// to the client. Block until the
// file is transferred.
client_->send (input_file.data (),
input_file.size ());
break;
// Client is storing a file at
// the server.
case HTTP_Request::PUT:
// ...
}
}
private:
// Socket endpoint.
Socket_Stream *client_;
// ...
};
[edit]
13.1.2 A.2 Single-threaded Reactive Event Dispatching #The following code shows the use of the Reactor pattern to develop a Web server. When a client connects to the server, the HTTP Handler::open method is called. The server
registers the I/O handle and the object to callback (which in this case is this) when the network handle is ¡°ready for reading.¡± The server returns to the event loop.
When the request data arrives at the server, the reactor calls back the HTTP Handler::handle input method. The client data is read in a non-blocking manner. If there is
enough data, the client request is parsed. If the entire client request has not yet arrived, the application returns to the re-actor event loop.
In response to a GET request, the server memory maps the requested file and registers with the reactor to be notified when the network connection becomes ¡°ready for writing.¡± The reactor then calls back on HTTP Handler::handle output method when writing data to the connection would not blocking the calling thread. When all the data has been sent to the client, the network connection is closed.
class HTTP_Handler :
public Reactor::Event_Handler
// = TITLE
// Implements the HTTP protocol
// (synchronous reactive version).
//
// = DESCRIPTION
// The Event_Handler base class
// defines the hooks for
// handle_input()/handle_output().
//
// = PATTERN PARTICIPANTS
// Reactor = Reactor
// Event Handler = HTTP_Handler
{
public:
void open (Socket_Stream *client)
{
// Initialize state for request
request_.state_ = INCOMPLETE;
// Store reference to client.
client_ = client;
// Register with the reactor for reading.
reactor_->register_handler
(client_->handle (),
this,
Reactor::READ_MASK);
}
// This is called by the Reactor when
// we can read from the client handle.
void handle_input (void)
{
int result = 0;
// Non-blocking read from the network
// connection.
do
result = request_.recv (client_->handle ());
while (result != SOCKET_ERROR
&& request_.state_ == INCOMPLETE);
// No more progress possible,
// blocking will occur
if (request_.state_ == INCOMPLETE
&& errno == EWOULDBLOCK)
reactor_->register_handler
(client_->handle (),
this,
Reactor::READ_MASK);
else
// We now have the entire request
parse_request ();
}
void parse_request (void)
{
// Switch on the HTTP command type.
switch (request_.command ()) {
// Client is requesting a file.
case HTTP_Request::GET:
// Memory map the requested file.
file_.map (request_.filename ());
// Transfer the file using Reactive I/O.
handle_output ();
break;
// Client is storing a file at
// the server.
case HTTP_Request::PUT:
// ...
}
}
void handle_output (void)
{
// Asynchronously send the file
// to the client.
if (client_->send (file_.data (),
file_.size ())
== SOCKET_ERROR
&& errno == EWOULDBLOCK)
// Register with reactor...
else
// Close down and release resources.
handle_close ();
}
private:
// Set at initialization.
Reactor *reactor_;
// Memory-mapped file_;
Mem_Map file_;
// Socket endpoint.
Socket_Stream *client_;
// HTTP Request holder.
HTTP_Request request_;
};
?¹ø¿ªÁß
|
|
|||||||||