<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Arkadiusz Kuryłowicz, blog prywatny &#187; Php</title>
	<atom:link href="http://www.kurylowicz.info/category/php/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.kurylowicz.info</link>
	<description>Blog prywatny, projekty, PHP, Linux, MySQL</description>
	<lastBuildDate>Sun, 11 Apr 2010 12:11:31 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.6</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Proof of concept: demon fastcgi w PHP tudzież serwlet PHP</title>
		<link>http://www.kurylowicz.info/2009/11/01/proof-of-concept-demon-fastcgi-w-php/</link>
		<comments>http://www.kurylowicz.info/2009/11/01/proof-of-concept-demon-fastcgi-w-php/#comments</comments>
		<pubDate>Sun, 01 Nov 2009 00:28:44 +0000</pubDate>
		<dc:creator>Arek Kuryłowicz</dc:creator>
				<category><![CDATA[Developer]]></category>
		<category><![CDATA[Php]]></category>

		<guid isPermaLink="false">http://www.pink.art.pl/?p=133</guid>
		<description><![CDATA[Z racji, iż w firmie dla której pracuję, zajmuję się sporo tematem optymalizacji kodu chodzą mi po głowie kuriozalne niekiedy pomysły. W ostatnim wpisie objaśniałem jak działa mechanizm FastCgi. Pośrednikiem pomiędzy skryptem PHP a klientem FastCGI jest SAPI php.cgi. Implementuje to główne założenie jak i niestety wadę PHP - konieczność uruchomienia skryptu dla każdego żądania [...]]]></description>
			<content:encoded><![CDATA[<p>Z racji, iż w firmie dla której pracuję, zajmuję się sporo tematem optymalizacji kodu chodzą mi po głowie kuriozalne niekiedy pomysły. W ostatnim wpisie objaśniałem jak działa mechanizm FastCgi. Pośrednikiem pomiędzy skryptem PHP a klientem FastCGI jest SAPI php.cgi. Implementuje to główne założenie jak i niestety wadę PHP - konieczność uruchomienia skryptu dla każdego żądania HTTP.</p>
<p>A co gdyby napisać demona w PHP, który potrafiłby przyjmować żądania z klienta FastCgi, przekazać je do zainicjalizowanego już kontrolera (patrz MVC) odebrać wynik i odesłać? Pomysł fajny - odpada konieczność wielokrotnej uruchamiania, kompilacji kodu, inkludowania plików, bo przecież aplikacja cały czas działa. Odpada konieczność konstruowania klas, populowania w nich danych dla każdego żądania - wystarczy zrobić to raz, podczas uruchomienia.</p>
<p>Tak mi się ten pomysł spodobał, że postanowiłem się przekonać, czy to ma szanse zadziałać. Google? Cóż, nikt chyba wcześniej nie wpadł na taki pomysł, ponieważ nie znalazłem niczego konkretnego. Zacząłem zatem od <a href="http://www.fastcgi.com/devkit/doc/fcgi-spec.html">specyfikacji protokołu FastCgi</a>. Sam protokół do najprostszych nie należy, nie mniej jednak w implementacji serwera pomogła mi implementacja klienta stworzona na potrzeby aplikacji <a href="http://nanoweb.si.kz/">Nanoweb - The PHP Web Server</a>. Dodatkowo posłużyłem się funkcjonalnością <a href="http://pl.php.net/manual/en/book.pcntl.php">PCNTL</a> w celu implementacji wielowątkowości.</p>
<p>Testowa aplikacja działała na zasadzie utworzenia nasłuchującego na porcie TCP socketa, w momencie nadejścia połączenia wersja 1) odsyłała odpowiedź, zamykała połaczenie z klientem i była gotowa do obsługi następnych wersja 2) dla każdego żądania tworzyła nowy wątek. Obie wersje, oraz skrypt referencyjny odpowiadały zawartością phpinfo().</p>
<p>Problemy jakie napotkałem po drodze:</p>
<ul>
<li>problem z parsowaniem ramek FCGI_PARAMS - one są osadzane w standardowej ramce FastCgi, ale mają chyba krótszy niż 8 bajtów nagłówek, postanowiłem całkowicie je zignorować, nie mniej jednak od ich poprawnego sparsowania zależy czy w aplikacji pojawi się tablica $_SERVER, $_GET i $_POST</li>
<li>trzeba samodzielnie zaimplementować cały mechanizm dekodowania formularzy jak i plików z żądania POST</li>
<li>zmienne pomiędzy procesami utworzonymi via pcntl_fork() kopiują się, nie są referencjami, dlatego w wątku potomnym możemy odczytać wartość zmiennej, ale zmiana nie jest widoczna dla innych wątków. Nie dotyczy to oczywiście zmiennych typu resource.</li>
</ul>
<p>Kiedy udało mi się już zaimplementować podstawowe minimum: demon potrafi odesłać prawidłową odpowiedź: przystąpiłem do benchmarkowania.</p>
<p><span id="more-133"></span></p>
<p>Wyniki mnie rozwaliły. Pierwsze referencyjny benchmark dla standardowej konfiguracji: apacz + php.fcgi -b 127.0.0.1:80800 + skrypt</p>
<pre class="bash"><span style="color: #7a0874; font-weight: bold;">&#91;</span>pink@poema ~<span style="color: #7a0874; font-weight: bold;">&#93;</span>$ ab -n <span style="color: #000000;">100</span> -c <span style="color: #000000;">5</span> http://stateless.projekt.kurylowicz.info/
This is ApacheBench, Version <span style="color: #000000;">2.3</span> &amp;lt;<span style="color: #007800;">$Revision</span>: <span style="color: #000000;">655654</span> $&amp;gt;
Copyright <span style="color: #000000;">1996</span> Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
&nbsp;
Benchmarking stateless.projekt.kurylowicz.info <span style="color: #7a0874; font-weight: bold;">&#40;</span>be patient<span style="color: #7a0874; font-weight: bold;">&#41;</span>.....<span style="color: #000000; font-weight: bold;">done</span>
&nbsp;
Server Software:        Apache/<span style="color: #000000;">2.2</span><span style="color: #000000;">.14</span>
Server Hostname:        stateless.projekt.kurylowicz.info
Server Port:            <span style="color: #000000;">80</span>
&nbsp;
Document Path:          /
Document Length:        <span style="color: #000000;">57346</span> bytes
&nbsp;
Concurrency Level:      <span style="color: #000000;">5</span>
Time taken <span style="color: #000000; font-weight: bold;">for</span> tests:   <span style="color: #000000;">0.859</span> seconds
Complete requests:      <span style="color: #000000;">100</span>
Failed requests:        <span style="color: #000000;">0</span>
Write errors:           <span style="color: #000000;">0</span>
Total transferred:      <span style="color: #000000;">5750300</span> bytes
HTML transferred:       <span style="color: #000000;">5734600</span> bytes
Requests per second:    <span style="color: #000000;">116.39</span> <span style="color: #7a0874; font-weight: bold;">&#91;</span><span style="color: #808080; font-style: italic;">#/sec<span style="color: #7a0874; font-weight: bold;">&#93;</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>mean<span style="color: #7a0874; font-weight: bold;">&#41;</span></span>
Time per request:       <span style="color: #000000;">42.960</span> <span style="color: #7a0874; font-weight: bold;">&#91;</span>ms<span style="color: #7a0874; font-weight: bold;">&#93;</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>mean<span style="color: #7a0874; font-weight: bold;">&#41;</span>
Time per request:       <span style="color: #000000;">8.592</span> <span style="color: #7a0874; font-weight: bold;">&#91;</span>ms<span style="color: #7a0874; font-weight: bold;">&#93;</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>mean, across all concurrent requests<span style="color: #7a0874; font-weight: bold;">&#41;</span>
Transfer rate:          <span style="color: #000000;">6535.76</span> <span style="color: #7a0874; font-weight: bold;">&#91;</span>Kbytes/sec<span style="color: #7a0874; font-weight: bold;">&#93;</span> received
&nbsp;
Connection Times <span style="color: #7a0874; font-weight: bold;">&#40;</span>ms<span style="color: #7a0874; font-weight: bold;">&#41;</span>
              min  mean<span style="color: #7a0874; font-weight: bold;">&#91;</span>+/-sd<span style="color: #7a0874; font-weight: bold;">&#93;</span> median   max
Connect:        <span style="color: #000000;">0</span>    <span style="color: #000000;">0</span>   <span style="color: #000000;">0.3</span>      <span style="color: #000000;">0</span>       <span style="color: #000000;">3</span>
Processing:    <span style="color: #000000;">13</span>   <span style="color: #000000;">42</span>   <span style="color: #000000;">4.2</span>     <span style="color: #000000;">43</span>      <span style="color: #000000;">48</span>
Waiting:        <span style="color: #000000;">7</span>   <span style="color: #000000;">36</span>   <span style="color: #000000;">4.3</span>     <span style="color: #000000;">38</span>      <span style="color: #000000;">42</span>
Total:         <span style="color: #000000;">13</span>   <span style="color: #000000;">42</span>   <span style="color: #000000;">4.1</span>     <span style="color: #000000;">43</span>      <span style="color: #000000;">48</span>
&nbsp;
Percentage of the requests served within a certain <span style="color: #000000; font-weight: bold;">time</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>ms<span style="color: #7a0874; font-weight: bold;">&#41;</span>
  <span style="color: #000000;">50</span>%     <span style="color: #000000;">43</span>
  <span style="color: #000000;">66</span>%     <span style="color: #000000;">43</span>
  <span style="color: #000000;">75</span>%     <span style="color: #000000;">43</span>
  <span style="color: #000000;">80</span>%     <span style="color: #000000;">43</span>
  <span style="color: #000000;">90</span>%     <span style="color: #000000;">44</span>
  <span style="color: #000000;">95</span>%     <span style="color: #000000;">44</span>
  <span style="color: #000000;">98</span>%     <span style="color: #000000;">44</span>
  <span style="color: #000000;">99</span>%     <span style="color: #000000;">48</span>
 <span style="color: #000000;">100</span>%     <span style="color: #000000;">48</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>longest request<span style="color: #7a0874; font-weight: bold;">&#41;</span></pre>
<p>Drugi wynik apacz + przygotowanego servera FastCGI bez forkowania, co oznacza, że każde żądanie przetwarzane jest pojedynczo:</p>
<pre class="bash"><span style="color: #7a0874; font-weight: bold;">&#91;</span>pink@poema ~<span style="color: #7a0874; font-weight: bold;">&#93;</span>$ ab -n <span style="color: #000000;">100</span> -c <span style="color: #000000;">5</span> http://stateless.projekt.kurylowicz.info/
This is ApacheBench, Version <span style="color: #000000;">2.3</span> &amp;lt;<span style="color: #007800;">$Revision</span>: <span style="color: #000000;">655654</span> $&amp;gt;
Copyright <span style="color: #000000;">1996</span> Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
&nbsp;
Benchmarking stateless.projekt.kurylowicz.info <span style="color: #7a0874; font-weight: bold;">&#40;</span>be patient<span style="color: #7a0874; font-weight: bold;">&#41;</span>.....<span style="color: #000000; font-weight: bold;">done</span>
&nbsp;
Server Software:        Apache/<span style="color: #000000;">2.2</span><span style="color: #000000;">.14</span>
Server Hostname:        stateless.projekt.kurylowicz.info
Server Port:            <span style="color: #000000;">80</span>
&nbsp;
Document Path:          /
Document Length:        <span style="color: #000000;">23082</span> bytes
&nbsp;
Concurrency Level:      <span style="color: #000000;">5</span>
Time taken <span style="color: #000000; font-weight: bold;">for</span> tests:   <span style="color: #000000;">0.622</span> seconds
Complete requests:      <span style="color: #000000;">100</span>
Failed requests:        <span style="color: #000000;">0</span>
Write errors:           <span style="color: #000000;">0</span>
Total transferred:      <span style="color: #000000;">2323900</span> bytes
HTML transferred:       <span style="color: #000000;">2308200</span> bytes
Requests per second:    <span style="color: #000000;">160.84</span> <span style="color: #7a0874; font-weight: bold;">&#91;</span><span style="color: #808080; font-style: italic;">#/sec<span style="color: #7a0874; font-weight: bold;">&#93;</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>mean<span style="color: #7a0874; font-weight: bold;">&#41;</span></span>
Time per request:       <span style="color: #000000;">31.088</span> <span style="color: #7a0874; font-weight: bold;">&#91;</span>ms<span style="color: #7a0874; font-weight: bold;">&#93;</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>mean<span style="color: #7a0874; font-weight: bold;">&#41;</span>
Time per request:       <span style="color: #000000;">6.218</span> <span style="color: #7a0874; font-weight: bold;">&#91;</span>ms<span style="color: #7a0874; font-weight: bold;">&#93;</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>mean, across all concurrent requests<span style="color: #7a0874; font-weight: bold;">&#41;</span>
Transfer rate:          <span style="color: #000000;">3650.07</span> <span style="color: #7a0874; font-weight: bold;">&#91;</span>Kbytes/sec<span style="color: #7a0874; font-weight: bold;">&#93;</span> received
&nbsp;
Connection Times <span style="color: #7a0874; font-weight: bold;">&#40;</span>ms<span style="color: #7a0874; font-weight: bold;">&#41;</span>
              min  mean<span style="color: #7a0874; font-weight: bold;">&#91;</span>+/-sd<span style="color: #7a0874; font-weight: bold;">&#93;</span> median   max
Connect:        <span style="color: #000000;">0</span>    <span style="color: #000000;">0</span>   <span style="color: #000000;">0.6</span>      <span style="color: #000000;">0</span>       <span style="color: #000000;">5</span>
Processing:     <span style="color: #000000;">9</span>   <span style="color: #000000;">30</span>   <span style="color: #000000;">8.3</span>     <span style="color: #000000;">28</span>      <span style="color: #000000;">60</span>
Waiting:        <span style="color: #000000;">9</span>   <span style="color: #000000;">29</span>   <span style="color: #000000;">8.5</span>     <span style="color: #000000;">26</span>      <span style="color: #000000;">59</span>
Total:          <span style="color: #000000;">9</span>   <span style="color: #000000;">30</span>   <span style="color: #000000;">8.2</span>     <span style="color: #000000;">28</span>      <span style="color: #000000;">60</span>
&nbsp;
Percentage of the requests served within a certain <span style="color: #000000; font-weight: bold;">time</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>ms<span style="color: #7a0874; font-weight: bold;">&#41;</span>
  <span style="color: #000000;">50</span>%     <span style="color: #000000;">28</span>
  <span style="color: #000000;">66</span>%     <span style="color: #000000;">30</span>
  <span style="color: #000000;">75</span>%     <span style="color: #000000;">33</span>
  <span style="color: #000000;">80</span>%     <span style="color: #000000;">35</span>
  <span style="color: #000000;">90</span>%     <span style="color: #000000;">38</span>
  <span style="color: #000000;">95</span>%     <span style="color: #000000;">56</span>
  <span style="color: #000000;">98</span>%     <span style="color: #000000;">58</span>
  <span style="color: #000000;">99</span>%     <span style="color: #000000;">60</span>
 <span style="color: #000000;">100</span>%     <span style="color: #000000;">60</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>longest request<span style="color: #7a0874; font-weight: bold;">&#41;</span></pre>
<p>Czas o parę procent krótszy dla zwykłego phpinfo(). W przypadku inicjalizacji połączeń z bazami, memcache, odczytywania identycznych zestawów danych (ustawień) ta różnica może być o wiele większa. <span style="text-decoration: line-through;">Przy czym pojawia się tu jeden problem: im więcej równoczesnych żądań (parametr -c dla ab) tym wynik jest gorszy i zbliża się do tego poniżej. Oba benchmarki, referencyjny jak i testowy obsługiwane były przez pojedynczy backend FastCgi, przy czym ten w referencyjnej konfiguracji radzi sobie lepiej ze współbieżnymi żądaniami.</span> Należy zadbać tutaj o odpowiednie parametry dla <a href="http://pl.php.net/manual/pl/function.socket-listen.php">socket_listen()</a> ponieważ zbyt mały backlog powoduje, iż połączenia współbieżne się kolejkują. Przy odpowiednio dużej wartości $backlog problem nie występuje.</p>
<pre class="bash"><span style="color: #7a0874; font-weight: bold;">&#91;</span>pink@poema ~<span style="color: #7a0874; font-weight: bold;">&#93;</span>$ ab -n <span style="color: #000000;">100</span> -c <span style="color: #000000;">5</span> http://stateless.projekt.kurylowicz.info/
This is ApacheBench, Version <span style="color: #000000;">2.3</span> &amp;lt;<span style="color: #007800;">$Revision</span>: <span style="color: #000000;">655654</span> $&amp;gt;
Copyright <span style="color: #000000;">1996</span> Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
&nbsp;
Benchmarking stateless.projekt.kurylowicz.info <span style="color: #7a0874; font-weight: bold;">&#40;</span>be patient<span style="color: #7a0874; font-weight: bold;">&#41;</span>.....<span style="color: #000000; font-weight: bold;">done</span>
&nbsp;
Server Software:        Apache/<span style="color: #000000;">2.2</span><span style="color: #000000;">.14</span>
Server Hostname:        stateless.projekt.kurylowicz.info
Server Port:            <span style="color: #000000;">80</span>
&nbsp;
Document Path:          /
Document Length:        <span style="color: #000000;">23047</span> bytes
&nbsp;
Concurrency Level:      <span style="color: #000000;">5</span>
Time taken <span style="color: #000000; font-weight: bold;">for</span> tests:   <span style="color: #000000;">3.327</span> seconds
Complete requests:      <span style="color: #000000;">100</span>
Failed requests:        <span style="color: #000000;">0</span>
Write errors:           <span style="color: #000000;">0</span>
Total transferred:      <span style="color: #000000;">2320400</span> bytes
HTML transferred:       <span style="color: #000000;">2304700</span> bytes
Requests per second:    <span style="color: #000000;">30.05</span> <span style="color: #7a0874; font-weight: bold;">&#91;</span><span style="color: #808080; font-style: italic;">#/sec<span style="color: #7a0874; font-weight: bold;">&#93;</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>mean<span style="color: #7a0874; font-weight: bold;">&#41;</span></span>
Time per request:       <span style="color: #000000;">166.372</span> <span style="color: #7a0874; font-weight: bold;">&#91;</span>ms<span style="color: #7a0874; font-weight: bold;">&#93;</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>mean<span style="color: #7a0874; font-weight: bold;">&#41;</span>
Time per request:       <span style="color: #000000;">33.274</span> <span style="color: #7a0874; font-weight: bold;">&#91;</span>ms<span style="color: #7a0874; font-weight: bold;">&#93;</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>mean, across all concurrent requests<span style="color: #7a0874; font-weight: bold;">&#41;</span>
Transfer rate:          <span style="color: #000000;">681.01</span> <span style="color: #7a0874; font-weight: bold;">&#91;</span>Kbytes/sec<span style="color: #7a0874; font-weight: bold;">&#93;</span> received
&nbsp;
Connection Times <span style="color: #7a0874; font-weight: bold;">&#40;</span>ms<span style="color: #7a0874; font-weight: bold;">&#41;</span>
              min  mean<span style="color: #7a0874; font-weight: bold;">&#91;</span>+/-sd<span style="color: #7a0874; font-weight: bold;">&#93;</span> median   max
Connect:        <span style="color: #000000;">0</span>    <span style="color: #000000;">0</span>   <span style="color: #000000;">0.9</span>      <span style="color: #000000;">0</span>       <span style="color: #000000;">7</span>
Processing:    <span style="color: #000000;">11</span>  <span style="color: #000000;">165</span>  <span style="color: #000000;">62.5</span>    <span style="color: #000000;">166</span>     <span style="color: #000000;">391</span>
Waiting:        <span style="color: #000000;">8</span>  <span style="color: #000000;">132</span>  <span style="color: #000000;">60.7</span>    <span style="color: #000000;">129</span>     <span style="color: #000000;">288</span>
Total:         <span style="color: #000000;">11</span>  <span style="color: #000000;">165</span>  <span style="color: #000000;">62.3</span>    <span style="color: #000000;">166</span>     <span style="color: #000000;">391</span>
&nbsp;
Percentage of the requests served within a certain <span style="color: #000000; font-weight: bold;">time</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>ms<span style="color: #7a0874; font-weight: bold;">&#41;</span>
  <span style="color: #000000;">50</span>%    <span style="color: #000000;">166</span>
  <span style="color: #000000;">66</span>%    <span style="color: #000000;">189</span>
  <span style="color: #000000;">75</span>%    <span style="color: #000000;">204</span>
  <span style="color: #000000;">80</span>%    <span style="color: #000000;">209</span>
  <span style="color: #000000;">90</span>%    <span style="color: #000000;">236</span>
  <span style="color: #000000;">95</span>%    <span style="color: #000000;">270</span>
  <span style="color: #000000;">98</span>%    <span style="color: #000000;">337</span>
  <span style="color: #000000;">99</span>%    <span style="color: #000000;">391</span>
 <span style="color: #000000;">100</span>%    <span style="color: #000000;">391</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>longest request<span style="color: #7a0874; font-weight: bold;">&#41;</span></pre>
<p>Jak łatwo zauważyć w tym przypadku wyniki są kilkanaście razy gorsze. Przypuszczam, że wynika to z niewydolności forkowania w PHP.</p>
<p><strong>Konkludując:</strong> idea wygląda wielce obiecująco i wydaje mi się, że <span style="text-decoration: line-through;">jeśli by rozwiązać jakoś problem spadku wydajności przy konkurencyjnych żądaniach (forkowanie skreślam całkowicie, wyniki mówią same za siebie) poprzez uruchomienie większej ilości instancji demona i rozproszoną dystrybucję żądań CGI</span> można by osiągnąć całkiem przyzwoite wyniki. Dalszą poprawę wydajności jak i redundancję systemu można osiągnąć poprzez uruchomienie klastra serwletów i balansowanie ruchu apacz, klient fCGI -&gt;serwlet z użyciem <a href="http://haproxy.1wt.eu/">HAProxy</a> (przetestowałem, z metodą balansowania round-robin, wyniki niżej)</p>
<p>Niestety inną sprawą jest stabilność takiego rozwiązania. PHP nie jest zaprojektowany do pracy w trybie demona i prawdopodobnie mogą pojawić się problemy z garbage collectorem lub wyciekami pamięci. Dodatkowo pisanie aplikacji serwletowych wymaga nieco innego podejścia do samej architektury - nie można sobie pozwolić na radosne używanie miliona zmiennych bez zawracania sobie głowy zwalnianiem pamięci, ponieważ trzeba mieć na uwadze, iż po zakończeniu żądania zaalokowana pamięć nie zostanie zwolniona samodzielnie. Dodatkowo zmienia się także koncepcja deployowania aplikacji, ponieważ każda zmiana kodu wymaga restartu całego klastra serwletów.</p>
<p><strong>Update:</strong> wyniki z użyciem 3 wątków php.fcgi -b 127.0.0.1:8061, php.fcgi -b 127.0.0.1:8062, php.fcgi -b 127.0.0.1:8063 + haproxy z balansowaniem round-robin:</p>
<pre class="bash"><span style="color: #7a0874; font-weight: bold;">&#91;</span>pink@poema php cgi server<span style="color: #7a0874; font-weight: bold;">&#93;</span>$ ab -n <span style="color: #000000;">100</span> -c <span style="color: #000000;">15</span> http://stateless.projekt.kurylowicz.info/
This is ApacheBench, Version <span style="color: #000000;">2.3</span> &amp;lt;<span style="color: #007800;">$Revision</span>: <span style="color: #000000;">655654</span> $&amp;gt;
Copyright <span style="color: #000000;">1996</span> Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
&nbsp;
Benchmarking stateless.projekt.kurylowicz.info <span style="color: #7a0874; font-weight: bold;">&#40;</span>be patient<span style="color: #7a0874; font-weight: bold;">&#41;</span>.....<span style="color: #000000; font-weight: bold;">done</span>
&nbsp;
Server Software:        Apache/<span style="color: #000000;">2.2</span><span style="color: #000000;">.14</span>
Server Hostname:        stateless.projekt.kurylowicz.info
Server Port:            <span style="color: #000000;">80</span>
&nbsp;
Document Path:          /
Document Length:        <span style="color: #000000;">57373</span> bytes
&nbsp;
Concurrency Level:      <span style="color: #000000;">15</span>
Time taken <span style="color: #000000; font-weight: bold;">for</span> tests:   <span style="color: #000000;">0.535</span> seconds
Complete requests:      <span style="color: #000000;">100</span>
Failed requests:        <span style="color: #000000;">33</span>
   <span style="color: #7a0874; font-weight: bold;">&#40;</span>Connect: <span style="color: #000000;">0</span>, Receive: <span style="color: #000000;">0</span>, Length: <span style="color: #000000;">33</span>, Exceptions: <span style="color: #000000;">0</span><span style="color: #7a0874; font-weight: bold;">&#41;</span>
Write errors:           <span style="color: #000000;">0</span>
Total transferred:      <span style="color: #000000;">5754584</span> bytes
HTML transferred:       <span style="color: #000000;">5738884</span> bytes
Requests per second:    <span style="color: #000000;">186.75</span> <span style="color: #7a0874; font-weight: bold;">&#91;</span><span style="color: #808080; font-style: italic;">#/sec<span style="color: #7a0874; font-weight: bold;">&#93;</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>mean<span style="color: #7a0874; font-weight: bold;">&#41;</span></span>
Time per request:       <span style="color: #000000;">80.323</span> <span style="color: #7a0874; font-weight: bold;">&#91;</span>ms<span style="color: #7a0874; font-weight: bold;">&#93;</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>mean<span style="color: #7a0874; font-weight: bold;">&#41;</span>
Time per request:       <span style="color: #000000;">5.355</span> <span style="color: #7a0874; font-weight: bold;">&#91;</span>ms<span style="color: #7a0874; font-weight: bold;">&#93;</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>mean, across all concurrent requests<span style="color: #7a0874; font-weight: bold;">&#41;</span>
Transfer rate:          <span style="color: #000000;">10494.54</span> <span style="color: #7a0874; font-weight: bold;">&#91;</span>Kbytes/sec<span style="color: #7a0874; font-weight: bold;">&#93;</span> received
&nbsp;
Connection Times <span style="color: #7a0874; font-weight: bold;">&#40;</span>ms<span style="color: #7a0874; font-weight: bold;">&#41;</span>
              min  mean<span style="color: #7a0874; font-weight: bold;">&#91;</span>+/-sd<span style="color: #7a0874; font-weight: bold;">&#93;</span> median   max
Connect:        <span style="color: #000000;">0</span>    <span style="color: #000000;">5</span>   <span style="color: #000000;">9.6</span>      <span style="color: #000000;">0</span>      <span style="color: #000000;">31</span>
Processing:    <span style="color: #000000;">24</span>   <span style="color: #000000;">70</span>  <span style="color: #000000;">24.0</span>     <span style="color: #000000;">68</span>     <span style="color: #000000;">117</span>
Waiting:        <span style="color: #000000;">6</span>   <span style="color: #000000;">57</span>  <span style="color: #000000;">25.4</span>     <span style="color: #000000;">55</span>     <span style="color: #000000;">106</span>
Total:         <span style="color: #000000;">30</span>   <span style="color: #000000;">75</span>  <span style="color: #000000;">22.6</span>     <span style="color: #000000;">77</span>     <span style="color: #000000;">117</span>
&nbsp;
Percentage of the requests served within a certain <span style="color: #000000; font-weight: bold;">time</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>ms<span style="color: #7a0874; font-weight: bold;">&#41;</span>
  <span style="color: #000000;">50</span>%     <span style="color: #000000;">77</span>
  <span style="color: #000000;">66</span>%     <span style="color: #000000;">85</span>
  <span style="color: #000000;">75</span>%     <span style="color: #000000;">90</span>
  <span style="color: #000000;">80</span>%     <span style="color: #000000;">95</span>
  <span style="color: #000000;">90</span>%    <span style="color: #000000;">110</span>
  <span style="color: #000000;">95</span>%    <span style="color: #000000;">112</span>
  <span style="color: #000000;">98</span>%    <span style="color: #000000;">115</span>
  <span style="color: #000000;">99</span>%    <span style="color: #000000;">117</span>
 <span style="color: #000000;">100</span>%    <span style="color: #000000;">117</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>longest request<span style="color: #7a0874; font-weight: bold;">&#41;</span></pre>
<p>Wyniki z użyciem 3 instancji serwletu + haproxy z balansowaniem round-robin:</p>
<pre class="bash"><span style="color: #7a0874; font-weight: bold;">&#91;</span>pink@poema php cgi server<span style="color: #7a0874; font-weight: bold;">&#93;</span>$ ab -n <span style="color: #000000;">100</span> -c <span style="color: #000000;">15</span> http://stateless.projekt.kurylowicz.info/
This is ApacheBench, Version <span style="color: #000000;">2.3</span> &amp;lt;<span style="color: #007800;">$Revision</span>: <span style="color: #000000;">655654</span> $&amp;gt;
Copyright <span style="color: #000000;">1996</span> Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
&nbsp;
Benchmarking stateless.projekt.kurylowicz.info <span style="color: #7a0874; font-weight: bold;">&#40;</span>be patient<span style="color: #7a0874; font-weight: bold;">&#41;</span>.....<span style="color: #000000; font-weight: bold;">done</span>
&nbsp;
Server Software:        Apache/<span style="color: #000000;">2.2</span><span style="color: #000000;">.14</span>
Server Hostname:        stateless.projekt.kurylowicz.info
Server Port:            <span style="color: #000000;">80</span>
&nbsp;
Document Path:          /
Document Length:        <span style="color: #000000;">23173</span> bytes
&nbsp;
Concurrency Level:      <span style="color: #000000;">15</span>
Time taken <span style="color: #000000; font-weight: bold;">for</span> tests:   <span style="color: #000000;">0.479</span> seconds
Complete requests:      <span style="color: #000000;">100</span>
Failed requests:        <span style="color: #000000;">67</span>
   <span style="color: #7a0874; font-weight: bold;">&#40;</span>Connect: <span style="color: #000000;">0</span>, Receive: <span style="color: #000000;">0</span>, Length: <span style="color: #000000;">67</span>, Exceptions: <span style="color: #000000;">0</span><span style="color: #7a0874; font-weight: bold;">&#41;</span>
Write errors:           <span style="color: #000000;">0</span>
Total transferred:      <span style="color: #000000;">2329784</span> bytes
HTML transferred:       <span style="color: #000000;">2314084</span> bytes
Requests per second:    <span style="color: #000000;">208.65</span> <span style="color: #7a0874; font-weight: bold;">&#91;</span><span style="color: #808080; font-style: italic;">#/sec<span style="color: #7a0874; font-weight: bold;">&#93;</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>mean<span style="color: #7a0874; font-weight: bold;">&#41;</span></span>
Time per request:       <span style="color: #000000;">71.891</span> <span style="color: #7a0874; font-weight: bold;">&#91;</span>ms<span style="color: #7a0874; font-weight: bold;">&#93;</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>mean<span style="color: #7a0874; font-weight: bold;">&#41;</span>
Time per request:       <span style="color: #000000;">4.793</span> <span style="color: #7a0874; font-weight: bold;">&#91;</span>ms<span style="color: #7a0874; font-weight: bold;">&#93;</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>mean, across all concurrent requests<span style="color: #7a0874; font-weight: bold;">&#41;</span>
Transfer rate:          <span style="color: #000000;">4747.12</span> <span style="color: #7a0874; font-weight: bold;">&#91;</span>Kbytes/sec<span style="color: #7a0874; font-weight: bold;">&#93;</span> received
&nbsp;
Connection Times <span style="color: #7a0874; font-weight: bold;">&#40;</span>ms<span style="color: #7a0874; font-weight: bold;">&#41;</span>
              min  mean<span style="color: #7a0874; font-weight: bold;">&#91;</span>+/-sd<span style="color: #7a0874; font-weight: bold;">&#93;</span> median   max
Connect:        <span style="color: #000000;">0</span>    <span style="color: #000000;">2</span>   <span style="color: #000000;">5.1</span>      <span style="color: #000000;">0</span>      <span style="color: #000000;">21</span>
Processing:     <span style="color: #000000;">9</span>   <span style="color: #000000;">64</span>  <span style="color: #000000;">45.3</span>     <span style="color: #000000;">49</span>     <span style="color: #000000;">180</span>
Waiting:        <span style="color: #000000;">2</span>   <span style="color: #000000;">61</span>  <span style="color: #000000;">45.0</span>     <span style="color: #000000;">46</span>     <span style="color: #000000;">179</span>
Total:          <span style="color: #000000;">9</span>   <span style="color: #000000;">66</span>  <span style="color: #000000;">44.8</span>     <span style="color: #000000;">52</span>     <span style="color: #000000;">180</span>
&nbsp;
Percentage of the requests served within a certain <span style="color: #000000; font-weight: bold;">time</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>ms<span style="color: #7a0874; font-weight: bold;">&#41;</span>
  <span style="color: #000000;">50</span>%     <span style="color: #000000;">52</span>
  <span style="color: #000000;">66</span>%     <span style="color: #000000;">60</span>
  <span style="color: #000000;">75</span>%     <span style="color: #000000;">85</span>
  <span style="color: #000000;">80</span>%    <span style="color: #000000;">105</span>
  <span style="color: #000000;">90</span>%    <span style="color: #000000;">148</span>
  <span style="color: #000000;">95</span>%    <span style="color: #000000;">166</span>
  <span style="color: #000000;">98</span>%    <span style="color: #000000;">180</span>
  <span style="color: #000000;">99</span>%    <span style="color: #000000;">180</span>
 <span style="color: #000000;">100</span>%    <span style="color: #000000;">180</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>longest request<span style="color: #7a0874; font-weight: bold;">&#41;</span></pre>
<p>Jak widać wyniki serwletu nadal lepsze i zapewne jak w poprzednim przypadku róznica może powiększyć się parokrotnie przy kodzie wymagającym inicjalizacji stałych elementów (konfig, includy klas).</p>
<p><strong>Update:</strong> wyniki z jednym watkiem serwletu, ale za to z nieco bardziej złożoną aplikacją: klasa, która w konstruktorze tworzy jedno połaczenie do memcache, jedno połaczenie z bazą danych i robi prepare zapytania SQL, i dalej we właściwej metodzie run() podbija wartość klucza memcache i wykonuje przygotowane wcześniej zapytanie.</p>
<p>Test php.cli -b 127.0.0.1:8060, klasa inicjalizowana i uruchamiana w pojedynczym pliku index.php</p>
<pre class="bash"><span style="color: #7a0874; font-weight: bold;">&#91;</span>pink@poema var<span style="color: #7a0874; font-weight: bold;">&#93;</span>$ ab -n <span style="color: #000000;">100</span> -c <span style="color: #000000;">10</span> http://stateless.projekt.kurylowicz.info/
This is ApacheBench, Version <span style="color: #000000;">2.3</span> &amp;lt;<span style="color: #007800;">$Revision</span>: <span style="color: #000000;">655654</span> $&amp;gt;
Copyright <span style="color: #000000;">1996</span> Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
&nbsp;
Benchmarking stateless.projekt.kurylowicz.info <span style="color: #7a0874; font-weight: bold;">&#40;</span>be patient<span style="color: #7a0874; font-weight: bold;">&#41;</span>.....<span style="color: #000000; font-weight: bold;">done</span>
&nbsp;
Server Software:        Apache/<span style="color: #000000;">2.2</span><span style="color: #000000;">.14</span>
Server Hostname:        stateless.projekt.kurylowicz.info
Server Port:            <span style="color: #000000;">80</span>
&nbsp;
Document Path:          /
Document Length:        <span style="color: #000000;">1990</span> bytes
&nbsp;
Concurrency Level:      <span style="color: #000000;">10</span>
Time taken <span style="color: #000000; font-weight: bold;">for</span> tests:   <span style="color: #000000;">0.483</span> seconds
Complete requests:      <span style="color: #000000;">100</span>
Failed requests:        <span style="color: #000000;">0</span>
Write errors:           <span style="color: #000000;">0</span>
Total transferred:      <span style="color: #000000;">214700</span> bytes
HTML transferred:       <span style="color: #000000;">199000</span> bytes
Requests per second:    <span style="color: #000000;">207.17</span> <span style="color: #7a0874; font-weight: bold;">&#91;</span><span style="color: #808080; font-style: italic;">#/sec<span style="color: #7a0874; font-weight: bold;">&#93;</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>mean<span style="color: #7a0874; font-weight: bold;">&#41;</span></span>
Time per request:       <span style="color: #000000;">48.270</span> <span style="color: #7a0874; font-weight: bold;">&#91;</span>ms<span style="color: #7a0874; font-weight: bold;">&#93;</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>mean<span style="color: #7a0874; font-weight: bold;">&#41;</span>
Time per request:       <span style="color: #000000;">4.827</span> <span style="color: #7a0874; font-weight: bold;">&#91;</span>ms<span style="color: #7a0874; font-weight: bold;">&#93;</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>mean, across all concurrent requests<span style="color: #7a0874; font-weight: bold;">&#41;</span>
Transfer rate:          <span style="color: #000000;">434.36</span> <span style="color: #7a0874; font-weight: bold;">&#91;</span>Kbytes/sec<span style="color: #7a0874; font-weight: bold;">&#93;</span> received
&nbsp;
Connection Times <span style="color: #7a0874; font-weight: bold;">&#40;</span>ms<span style="color: #7a0874; font-weight: bold;">&#41;</span>
              min  mean<span style="color: #7a0874; font-weight: bold;">&#91;</span>+/-sd<span style="color: #7a0874; font-weight: bold;">&#93;</span> median   max
Connect:        <span style="color: #000000;">0</span>    <span style="color: #000000;">0</span>   <span style="color: #000000;">1.2</span>      <span style="color: #000000;">0</span>       <span style="color: #000000;">5</span>
Processing:    <span style="color: #000000;">12</span>   <span style="color: #000000;">46</span>   <span style="color: #000000;">6.6</span>     <span style="color: #000000;">48</span>      <span style="color: #000000;">60</span>
Waiting:       <span style="color: #000000;">11</span>   <span style="color: #000000;">45</span>   <span style="color: #000000;">6.6</span>     <span style="color: #000000;">47</span>      <span style="color: #000000;">60</span>
Total:         <span style="color: #000000;">13</span>   <span style="color: #000000;">46</span>   <span style="color: #000000;">6.4</span>     <span style="color: #000000;">48</span>      <span style="color: #000000;">65</span>
&nbsp;
Percentage of the requests served within a certain <span style="color: #000000; font-weight: bold;">time</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>ms<span style="color: #7a0874; font-weight: bold;">&#41;</span>
  <span style="color: #000000;">50</span>%     <span style="color: #000000;">48</span>
  <span style="color: #000000;">66</span>%     <span style="color: #000000;">48</span>
  <span style="color: #000000;">75</span>%     <span style="color: #000000;">49</span>
  <span style="color: #000000;">80</span>%     <span style="color: #000000;">49</span>
  <span style="color: #000000;">90</span>%     <span style="color: #000000;">50</span>
  <span style="color: #000000;">95</span>%     <span style="color: #000000;">50</span>
  <span style="color: #000000;">98</span>%     <span style="color: #000000;">59</span>
  <span style="color: #000000;">99</span>%     <span style="color: #000000;">65</span>
 <span style="color: #000000;">100</span>%     <span style="color: #000000;">65</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>longest request<span style="color: #7a0874; font-weight: bold;">&#41;</span></pre>
<p>Test serwletu, klasa inicjalizowana przed otwarciem portu TCP, połączenie klienckie wykonuje metodę run() na zainicjalizowanej już klasie:</p>
<pre class="bash"><span style="color: #7a0874; font-weight: bold;">&#91;</span>pink@poema var<span style="color: #7a0874; font-weight: bold;">&#93;</span>$ ab -n <span style="color: #000000;">100</span> -c <span style="color: #000000;">10</span> http://stateless.projekt.kurylowicz.info/
This is ApacheBench, Version <span style="color: #000000;">2.3</span> &amp;lt;<span style="color: #007800;">$Revision</span>: <span style="color: #000000;">655654</span> $&amp;gt;
Copyright <span style="color: #000000;">1996</span> Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
&nbsp;
Benchmarking stateless.projekt.kurylowicz.info <span style="color: #7a0874; font-weight: bold;">&#40;</span>be patient<span style="color: #7a0874; font-weight: bold;">&#41;</span>.....<span style="color: #000000; font-weight: bold;">done</span>
&nbsp;
Server Software:        Apache/<span style="color: #000000;">2.2</span><span style="color: #000000;">.14</span>
Server Hostname:        stateless.projekt.kurylowicz.info
Server Port:            <span style="color: #000000;">80</span>
&nbsp;
Document Path:          /
Document Length:        <span style="color: #000000;">1994</span> bytes
&nbsp;
Concurrency Level:      <span style="color: #000000;">10</span>
Time taken <span style="color: #000000; font-weight: bold;">for</span> tests:   <span style="color: #000000;">0.245</span> seconds
Complete requests:      <span style="color: #000000;">100</span>
Failed requests:        <span style="color: #000000;">0</span>
Write errors:           <span style="color: #000000;">0</span>
Total transferred:      <span style="color: #000000;">215100</span> bytes
HTML transferred:       <span style="color: #000000;">199400</span> bytes
Requests per second:    <span style="color: #000000;">407.70</span> <span style="color: #7a0874; font-weight: bold;">&#91;</span><span style="color: #808080; font-style: italic;">#/sec<span style="color: #7a0874; font-weight: bold;">&#93;</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>mean<span style="color: #7a0874; font-weight: bold;">&#41;</span></span>
Time per request:       <span style="color: #000000;">24.528</span> <span style="color: #7a0874; font-weight: bold;">&#91;</span>ms<span style="color: #7a0874; font-weight: bold;">&#93;</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>mean<span style="color: #7a0874; font-weight: bold;">&#41;</span>
Time per request:       <span style="color: #000000;">2.453</span> <span style="color: #7a0874; font-weight: bold;">&#91;</span>ms<span style="color: #7a0874; font-weight: bold;">&#93;</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>mean, across all concurrent requests<span style="color: #7a0874; font-weight: bold;">&#41;</span>
Transfer rate:          <span style="color: #000000;">856.41</span> <span style="color: #7a0874; font-weight: bold;">&#91;</span>Kbytes/sec<span style="color: #7a0874; font-weight: bold;">&#93;</span> received
&nbsp;
Connection Times <span style="color: #7a0874; font-weight: bold;">&#40;</span>ms<span style="color: #7a0874; font-weight: bold;">&#41;</span>
              min  mean<span style="color: #7a0874; font-weight: bold;">&#91;</span>+/-sd<span style="color: #7a0874; font-weight: bold;">&#93;</span> median   max
Connect:        <span style="color: #000000;">0</span>    <span style="color: #000000;">1</span>   <span style="color: #000000;">2.5</span>      <span style="color: #000000;">0</span>      <span style="color: #000000;">13</span>
Processing:     <span style="color: #000000;">8</span>   <span style="color: #000000;">22</span>   <span style="color: #000000;">7.0</span>     <span style="color: #000000;">22</span>      <span style="color: #000000;">42</span>
Waiting:        <span style="color: #000000;">5</span>   <span style="color: #000000;">21</span>   <span style="color: #000000;">7.3</span>     <span style="color: #000000;">20</span>      <span style="color: #000000;">41</span>
Total:          <span style="color: #000000;">8</span>   <span style="color: #000000;">24</span>   <span style="color: #000000;">7.0</span>     <span style="color: #000000;">23</span>      <span style="color: #000000;">43</span>
&nbsp;
Percentage of the requests served within a certain <span style="color: #000000; font-weight: bold;">time</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>ms<span style="color: #7a0874; font-weight: bold;">&#41;</span>
  <span style="color: #000000;">50</span>%     <span style="color: #000000;">23</span>
  <span style="color: #000000;">66</span>%     <span style="color: #000000;">25</span>
  <span style="color: #000000;">75</span>%     <span style="color: #000000;">28</span>
  <span style="color: #000000;">80</span>%     <span style="color: #000000;">31</span>
  <span style="color: #000000;">90</span>%     <span style="color: #000000;">33</span>
  <span style="color: #000000;">95</span>%     <span style="color: #000000;">37</span>
  <span style="color: #000000;">98</span>%     <span style="color: #000000;">41</span>
  <span style="color: #000000;">99</span>%     <span style="color: #000000;">43</span>
 <span style="color: #000000;">100</span>%     <span style="color: #000000;">43</span> <span style="color: #7a0874; font-weight: bold;">&#40;</span>longest request<span style="color: #7a0874; font-weight: bold;">&#41;</span></pre>
<p>Jak widac z dwóch powyższych testów średni czas przetwarzania pojedynczego żadania przez serwlet jest <strong>50%</strong> krótszy.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kurylowicz.info/2009/11/01/proof-of-concept-demon-fastcgi-w-php/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Apache + PHP+ FastCGI + Spawn FastCGI? Wydajność, migracja.</title>
		<link>http://www.kurylowicz.info/2009/09/20/apache-php-fastcgi-spawn-fastcgi-wydajnosc-migracja/</link>
		<comments>http://www.kurylowicz.info/2009/09/20/apache-php-fastcgi-spawn-fastcgi-wydajnosc-migracja/#comments</comments>
		<pubDate>Sun, 20 Sep 2009 21:32:54 +0000</pubDate>
		<dc:creator>Arek Kuryłowicz</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Php]]></category>

		<guid isPermaLink="false">http://www.pink.art.pl/?p=119</guid>
		<description><![CDATA[
Mijający już weekend upłynął mi pod znakiem migracji serwera www.poema.art.pl na FastCgi. Sporo alternatyw, sporo, ale niepełnych informacji - trudno się w tym połapać komuś, kto nie jest zaznajomiony z tą technologią.  Ale od początku. Dlaczego warto się przerzucić  na FastCgi? Otóż powody są co najmniej dwa:

wydajność
bezpieczeństwo

Rozwinięcie wątku wydajności należałoby zacząć od wyjaśnienia w [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify">
<p>Mijający już weekend upłynął mi pod znakiem migracji serwera www.poema.art.pl na FastCgi. Sporo alternatyw, sporo, ale niepełnych informacji - trudno się w tym połapać komuś, kto nie jest zaznajomiony z tą technologią.  Ale od początku. Dlaczego warto się przerzucić  na FastCgi? Otóż powody są co najmniej dwa:</p>
<ol>
<li>wydajność</li>
<li>bezpieczeństwo</li>
</ol>
<p style="text-align: justify">Rozwinięcie wątku wydajności należałoby zacząć od wyjaśnienia w jaki sposób FastCgi działa. Otóż dzięki FastCgi separujemy wykonanie kodu PHP od wątków Apacza. Apacz w modelu FastCgi służy wyłącznie do komunikacji z przeglądarką klienta. W momencie kiedy klient zażąda uruchomienia skryptu PHP - żądanie to przekazywane jest do 'serwera FastCGI' który używając interpretera PHP uruchamia skrypt i odsyła wynik do Apacza. Wygląda to mniej więcej tak:</p>
<pre>+----------+  TCP 80  +----------+ Unix socket
|  Klient  | &lt;-----&gt;  |  Apache  | &lt;-----+
+----------+          +----------+       |
                       user: http        |
+----------+          +----------+       |      +-------------+        +--------------+
|  Klient  | &lt;-----&gt;  |  Apache  | &lt;-----+----&gt; | PHP FastCgi | &lt;----&gt; | Server MySQL |
+----------+          +----------+       |      +-------------+        +--------------+
                       user: http        |        user: john
+----------+          +----------+       |
|  Klient  | &lt;-----&gt;  |  Apache  | &lt;-----+
+----------+          +----------+
                       user: http</pre>
<p style="text-align: justify">Przy użyciu spawn-fastcgi uruchamiamy dla każdego z użytkowników własny serwer PHP FastCgi który nasłuchuje na Unixowym sockecie. Apache komunikuje się po sockecie z wybranym zgodnie z ustawieniami (per vhost) serwerem żądając wykonania skryptu. Dzięki temu otrzymujemy izolację wątków PHP od wątków Apache. Dodatkowo PHP FastCgi działając z uprawnieniami użytkownika, nie zaś serwera Apache ma możliwość zapisu do katalogów użytkownika, a także ograniczenia odczytu plików do których posiada uprawnienia.</p>
<h2>Ok, ale co z wydajnością?</h2>
<p style="text-align: justify">Po pierwsze Apache, dzięki temu że nie parsuje w każdym swoim wątku PHP zabiera o wiele mniej pamięci. Przy dużej ilości żądań jest  to sprawa kluczowa. Dzięki modelowi FastCgi można w prawidłowy sposób wykorzystać mechanizm database persistent connections (eg. w MySQL). W przypadku wielu wątków Apacza parsujących PHP nawiązywanych jest tyle połączeń ile wątków. Nowe wątki nie potrafią użyć ponownie połączeń utworzonych przez zabite wątki. Dzięki temu w parę minut osiągamy maksymalny limit połączeń z bazą.  W przypadku FastCgi nawiązanych zostaje tyle połączeń ile wątków i połączenia te są cały czas utrzymywane. To jest także sprawa kluczowa dla serwisów o wysokim obciążeniu.</p>
<h2>Bezpieczeństwo?</h2>
<p style="text-align: justify">Wspomniałem wcześniej - wykonanie kodu PHP z uprawnieniami użytkownika. Ale nie tylko - separacja cache APC to kolejny element bezpieczeństwa. Dalej: możliwość pozbycia się safe mode w PHP - aktualnie to mechanizm z którego PHP się wycofuje. W wersji 5 ma już go nie być.</p>
<h2>fCgi, FastCgi, fCgid?</h2>
<p style="text-align: justify;">Jest sporo zamieszania jeśli chodzi o mechanizm FastCgi. Istnieje mnóstwo alternatyw. Osoba zaznajamiająca się z tematem za pomocą poldek search *cgi*   może czyć się nieco zagubiona. Dlatego opiszę co potrzebujemy aby to wszystko właściwie uruchomić.</p>
<p style="text-align: justify;">Zacznijmy od końca, czyli od PHP. Potrzebna będzie paczka zawierająca interpreter fcgi dla PHP - jest to dodatkowy moduł SAPI (oprócz eg. zwyczajnego cli, lub modułu do Apacha) które można skompilować, tudzież zainstalować. Pod PLD jest to pakiet "php-fcgi". Dalej, przyda się coś co będzie potrafiło utworzyć nam serwery FastCgi dla użytkowników. Pakiet "spawn-fcgi" jest całkiem dobry do tego. I wreszcie musimy nauczyć Apache komunikować się z naszymi serwerami. Doinstalujmy moduł "apache-mod_fastcgi".</p>
<p style="text-align: justify;">Wszystkie moduły można bez obaw zainstalować na serwerze który używa modułu "apache-mod_php"  do parsowania kodu PHP. Generalnie można używać obu modeli parsowania jednocześnie - część vhostów i aplikacji parsować modułem Apacza, najbardziej obciążone vhosty puścić przez FastCgi.</p>
<p><span id="more-119"></span></p>
<h2>Konfiguracja PHP</h2>
<p style="text-align: justify;">Nieco problematyczną sprawą w modelu FastCgi jest  konfiguracja PHP per vhost. Nie możemy do tego użyć ani plików .htacess ani dyrektyw php_flag w konfiguracji vhosta. Należy dla każdego z vhostów wymagającego niestandardowego konfigu utworzyć osobny php.ini. Ma to jedną zasadniczą wadę. /etc/php/php.ini który zawiera nasze istawienia PHP nie będzie czytany. Zatem dla każdego z vhostów należy mieć kopię php.ini zawierającą <strong>wsztstkie opcje konfiguracyjne które chcemy zmienić</strong>. Jeśli którejś z opcji nie ujmiemy w indywidualnym .ini będzie ona miała wartość domyślną, taką, jaka jest wkompilowana w php. Czytany jest za to katalog /etc/php/conf.d, przy czym każda zdefiniowana tam opcja konfiguracyjna <strong>nadpisuje</strong> tę zdefiniowaną w indywidualnym .ini.</p>
<p style="text-align: justify;">W PHP 5 ma być możliwość używania w globalnym php.ini wpisów [myhost.com], dzięki czemu będzie można nadpisać globalne ustawienia per vhost. Niestety póki co, trzeba robić kopie globalnego php.ini i zmieniać w niej wybrane opcje. Minimalna postać indywidualnego php.ini, tak, aby zachować najważniejsze spersonalizowane dla dystrybucji ustawienia to:</p>
<pre class="ini"><span style="color: #666666; font-style: italic;">; Safe mode w binarce PHP domyślnie jest on, więc jeśli serwis</span>
<span style="color: #666666; font-style: italic;">; pod nim nie działa trzeba wyłączyć</span>
<span style="color: #000099;">safe_mode </span>=<span style="color: #660066;"> Off</span>
<span style="color: #666666; font-style: italic;">; To warto podać, ograniczymy możliwość otwierania cudzych katalogów</span>
<span style="color: #000099;">open_basedir </span>= <span style="color: #933;">&quot;/home/webhosts/www.mychost.pl:/home/services/httpd:/tmp&quot;</span>
<span style="color: #000099;">include_path </span>=<span style="color: #660066;"> $<span style="">&#123;</span>open_basedir<span style="">&#125;</span></span>
<span style="color: #000099;">error_reporting  </span>=<span style="color: #660066;">  E_ALL &amp; ~E_NOTICE &amp; ~E_STRICT</span>
<span style="color: #000099;">display_errors </span>=<span style="color: #660066;"> Off</span>
<span style="color: #000099;">log_errors </span>=<span style="color: #660066;"> On</span>
<span style="color: #666666; font-style: italic;">; Błedy do sysloga, to php będzie działało z prawami usera, więc nie</span>
<span style="color: #666666; font-style: italic;">; można zapisywać błędów do wspólnego pliku</span>
<span style="color: #000099;">error_log </span>=<span style="color: #660066;"> syslog</span>
<span style="color: #000099;">ignore_repeated_errors </span>=<span style="color: #660066;"> On</span>
<span style="color: #000099;">html_errors </span>=<span style="color: #660066;"> Off</span>
<span style="color: #000099;">upload_tmp_dir </span>= <span style="color: #933;">&quot;/tmp&quot;</span>
<span style="color: #000099;">expose_php </span>=<span style="color: #660066;"> Off</span>
<span style="color: #000099;">memory_limit </span>=<span style="color: #660066;"> 32M</span>
date.<span style="color: #000099;">timezone </span>= <span style="color: #933;">&quot;Europe/Warsaw&quot;</span>
<span style="color: #000099;">browscap </span>=<span style="color: #660066;"> /etc/php/browscap.ini</span></pre>
<h2>Konfiguracja spawn-fastcgi</h2>
<p style="text-align: justify;">Tu sprawa jest dość prosta, otóż spawn-fastcgi nie ma żadych konfigów, a wszystkie parametry przekazujemy w linii poleceń.</p>
<pre class="bash">spawn-fcgi -s /var/run/fastcgi/fcgi.myhost.sock -M <span style="color: #000000;">0660</span> \
  -P /var/run/fastcgi/fcgi.myhost.pid -U http -G http \
  -u <span style="color: #000000;">10012</span> -g <span style="color: #000000;">10012</span> -C <span style="color: #000000;">2</span> -- /usr/sbin/php.fcgi -c /etc/php/webapps.d/myhost.ini</pre>
<p style="text-align: justify;">Po kolei: -s  /var/run/fastcgi/fcgi.myhost.sock mówi w którym miejscu tworzyć sockety serwerów FastCgi. Ja wybrałem sobie katalog /var/run/fastcgi. Należy go utworzyć nadając prawo do zapisu użytkownikowi, z którego prawami będzie działał serwer oraz prawa do odczytu dla serwera Apache (user http). -M 0660 każe zmienić uprawnienia do utworzonego socketa na 660, -P /var/run/fastcgi/fcgi.myhost.pid mówi gdzie zapisujemy plik z PIDem serwera. Parametry -U http -G http są ważne, i mówią że prawa dostępu do socketa mają być nadane dla użytkownika http. Jako że z socketa będzie korzystał serwer Apache zmiana uprawnień jest konieczna. Dalej -u 10012 -g 10012 - tutaj definiujemy id (bądź nazwę) użytkownika i grupy z uprawnieniami którego ma działać FastCgi. Z uprawnieniami tego użytkownika będzie wykonany kod PHP.  Można tu użyć ID'ów użytkowników wirtualnych. Nie muszą być dodani do /etc/passwd. Idealnie to się wpisuje w model hostingu gdzie użytkowników trzymamy w bazie danych bazie danych. Ważną sprawą jest jedynie, aby wirtualny użytkownik miał prawa do zapisu do katalogu na który wskazuje parametr -s. Można mu nadać prawa 777 lub trzymać sockety w katalogach domowych użytkowników.  -C 2 określa ile instancji FastCgi ma zostać uruchomionych. Wartość trzeba dobrać doświadczalnie. W przypadku zbyt małej ilości Apache będzie podawał klientom bład 500. I wreszcie pora na interpreter: -- /usr/sbin/php.fcgi -c /etc/php/webapps.d/myhost.ini - używamy zainstalowanego wcześniej php-fgi - podając mu parametr -c z indywidualnym plikiem konfiguracyjnym. Jeśli nie podamy parametru -c zostanie użyty domyślny /etc/php/php.ini.</p>
<p style="text-align: justify;">Jeden serwer FastCgi może obsługiwać wiele hostów wirtualnych. W najprostszym przypadku  można uruchomić jedną instancję serwera na prawach użytkownika mającego prawa do odczyty dla plików PHP (może to być http) i uruchomić na nim wszystkie nasze vhosty. Przy czym w oczywisty sposób pozbywamy się w takim przypadku korzyści płynących z separacji uprawnień. Przy hostingu wirtualnym lepiej przyjąć strategię: jedna instancja per 1 użytkownik na której odpalamy wszystkie jego vhosty.</p>
<p style="text-align: justify;">W ogóle fajnie jest napisać sobie jakiś rc.skrypt, który będzie uruchamiał wszystkie zdefiniowane instancje podczas startu, pozwoli nam je zresetować, zatrzymać, etc.</p>
<h2>Konfiguracja Apache</h2>
<p style="text-align: justify;">Ok, i teraz Apacz. Wszystko co zostało zrobione do tej pory można bez obaw robić na serwerze który obsługuje PHP via moduł Apacza. Operacje poniżej wymagają już zmiany w konfiguracji serwera WWW, a co za tym idzie są ryzykowne. Główne ryzyko, przy pomyłce w konfiguracji to możliwość zaserwowania niesparsowanych skryptów PHP, tak więc na działających serwerach polecam blokadę dostępu dla virtualki via Order deny, allow Allow from my.ad.re.ss Deny from all. Dodatkowo jak wspomniałem wcześnej model parsowania PHP via moduł jak i FastCgi może współistnieć jednocześnie, tak więc możemy przerzucać na FastCgi pojedyncze virtualki.</p>
<p style="text-align: justify;">Ok, zaczynamy od pliku "/etc/httpd/conf.d/90_mod_fastcgi.conf" . Domyślnie jest tam jedynie linijka ładująca moduł fastcgi. Dopiszmy do pliku:</p>
<pre class="apache">&lt;IfModule mod_fastcgi.c&gt;
  <span style="color: #adadad; font-style: italic;"># Domyślne ustawienia serwera FastCgi, -pass-header Authorization jest bardzo ważne</span>
  FastCgiConfig -idle-<span style="color: #00007f;">timeout</span> <span style="color: #ff0000;">20</span> -maxClassProcesses <span style="color: #ff0000;">1</span> -pass-<span style="color: #00007f;">header</span> Authorization
  <span style="color: #adadad; font-style: italic;"># Zarejestrujmy dwie akcje</span>
  <span style="color: #00007f;">Action</span> php5-fcgi /php-cgi
  <span style="color: #00007f;">Action</span> application/x-fcgi-php /php-cgi
  <span style="color: #adadad; font-style: italic;"># Załóżmy że nasz serwer FastCgi będzie znajdował pod wirtualnym adresem /php-cgi</span>
  &lt;Location <span style="color: #7f007f;">&quot;/php-cgi&quot;</span>&gt;
    php_flag engine <span style="color: #0000ff;">off</span>
    <span style="color: #00007f;">Order</span> <span style="color: #00007f;">Deny</span>,<span style="color: #00007f;">Allow</span>
    <span style="color: #00007f;">Deny</span> <span style="color: #00007f;">from</span> <span style="color: #00007f;">All</span>
    <span style="color: #00007f;">Allow</span> <span style="color: #00007f;">from</span> env=REDIRECT_STATUS
    <span style="color: #00007f;">Options</span> ExecCGI <span style="color: #0000ff;">FollowSymLinks</span>
    <span style="color: #00007f;">SetHandler</span> fastcgi-<span style="color: #00007f;">script</span>
  &lt;/Location&gt;
&lt;/IfModule&gt;</pre>
<p style="text-align: justify;">Ok, to wystarczy, żeby móc przystąpić do konfigurowania vhostów. Do vhosta, dla którego uruchomiona została już instancja spawn-fcgi i mamy utworzony socket w katalogu /var/run/fastcgi/fcgi.myhost.sock dopiszmy:</pre>
<pre class="apache">&lt;VirtualHost www.myhost.pl:<span style="color: #ff0000;">80</span>&gt;
<span style="color: #66cc66;">&#91;</span>...<span style="color: #66cc66;">&#93;</span>
&lt;IfModule mod_fastcgi.c&gt;
<span style="color: #adadad; font-style: italic;"># Tak! wyłączamy engine PHP, dzięki temu moduł PHP, który</span>
<span style="color: #adadad; font-style: italic;"># cały czas działa zostawi pliki PHP tej wirtualki w spokoju</span>
php_flag engine <span style="color: #0000ff;">off</span>
<span style="color: #adadad; font-style: italic;"># Magic! Nadpisujemy handler application/x-httpd-php każąc wszystkim</span>
<span style="color: #adadad; font-style: italic;"># plikom php zdefiniowany wcześniej dla modułu zostać sparsowanym via FastCgi</span>
<span style="color: #00007f;">Action</span> application/x-httpd-php /php-cgi
<span style="color: #adadad; font-style: italic;"># Definicja serwera, ścieżka musi być taka sama jak doc_root vhosta + /php-cgi</span>
<span style="color: #adadad; font-style: italic;"># Nie należy się przejmować faktem, iż /php-cgi nie istnieje w tej ścieżce, tak ma być!</span>
FastCgiExternalServer /home/webhosts/myhost/php-cgi -socket /var/run/fastcgi/fcgi.myhost.sock
&lt;/IfModule&gt;
&lt;/VirtualHost&gt;</pre>
<p>Jeśli mamy jakiekolwiek opcje konfiguracyjne dla php (php_flag, php_admin_value) to należy je usunąć.</p>
<h2>Uruchamiamy!</h2>
<p>Checklist:</p>
<ul>
<li>Sprawdzamy czy  instancja serwera FastCgi uruchomiona przez spawn-cgi działa: ps aux | grep cgi:
<pre>10008   7832  0.0  1.6  71324 34212 ? SN   14:19   0:18 /usr/bin/php.fcgi -c /etc/php/webapps.d/myhost.ini
10008   7833  0.0  1.4  67408 29276 ? SN   14:19   0:19 /usr/bin/php.fcgi -c /etc/php/webapps.d/myhost.ini
http   15808  0.0  0.5  72288 11548 ? SN   20:11   0:00 /usr/sbin/fcgi-pm</pre>
</li>
<li>Sprawdzamy czy jest socket i czy http ma możliwośc odczytu: ls -la /var/run/fastcgi:
<pre>-rw-r--r--  1 root root    4 09-20 14:19 fcgi.myhost.pid
srw-rw----  1 http http    0 09-20 14:19 fcgi.myhost.sock</pre>
</li>
<li>Sprawdzamy, czy w naszym konfigu apacza nie ma błedów: service httpd configtest</li>
</ul>
<p style="text-align: justify;">Pora na restart Apache. Po tym kod PHP dla konfigurowanego vhosta powinien być parsowany via FastCgi, co będzie sygnalozowane w wyniku funkcji phpini() wpisem Server API CGI/FastCGI.</p>
<p style="text-align: justify;">Dla kodu parsowanego via moduł Apache Server API  to Apache Handler.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kurylowicz.info/2009/09/20/apache-php-fastcgi-spawn-fastcgi-wydajnosc-migracja/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
