Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

 
Reply to this topicStart new topic
> Moja implementacja "keszu"
q.michal
post 23.02.2016, 10:13:40
Post #1





Grupa: Zarejestrowani
Postów: 111
Pomógł: 1
Dołączył: 24.12.2013

Ostrzeżenie: (0%)
-----


Witam,

Chcialbym dzis pdozielic sie swoim kolejnym wynalazkiem - obsluga keszu. Jest to co prawda dopiero zalazek tego co chce zrobic, albowiem brakuje metod odpowiedzialnych za obsluge danych, niemniej sposob 'komunikacji' ze sterownikiem widac na przykladzie metody Cache::dumpCache(); Docelowo zamierzam napisac takze wiecej sterownikow, zapewniajac dostep do: APC, eAccelerator, Dummy, File, MemCache, SQLite, WinCache, XCache. Zastanawiam sie takze nad mozliwoscia przechowywania keszu w cookies.

Jednoczesnie prosze o ocene oraz komentarze - czyli ogolnie mowiac code review wink.gif
Jestem otwarty na wszelkie sugestie i uwagi poparte jakimkolwiek argumentem.

Pozdrawiam serdecznie.


  1. abstract class CacheAbstract {
  2.  
  3. /**
  4. * Disables object cloning
  5. *
  6. * @return void does not return any value
  7. */
  8. protected function __clone() {
  9. throw new Exception('Cloning Cache objects is not allowed!');
  10. }
  11.  
  12. /**
  13. * Class constructor
  14. *
  15. * @return void does not return any value
  16. */
  17. protected function __construct() {
  18. }
  19.  
  20. } /* class */
  21.  
  22.  
  23. interface CacheDriver {
  24.  
  25. /**
  26. * Adds an object to the cache
  27. *
  28. * @param string stores the variable using this name, should be a cache-unique name
  29. * @param mixed the variable to store in cache
  30. * @param integer time-to-live of cached data
  31. * @return boolean TRUE on success, FALSE otherwise
  32. */
  33. public function addValue($key, $var, $ttl = 300);
  34.  
  35. /**
  36. * Returns a dump of all files and user variables from the cache
  37. *
  38. * @return string dump of the user variables, or NULL if an unknown error encountered
  39. */
  40. public function dumpCache();
  41.  
  42. /**
  43. * Flushes (clears) the user and system cache
  44. *
  45. * @return void does not return any value
  46. */
  47. public function flushCache();
  48.  
  49. /**
  50. * Provides a statistics and meta-data of caching subsystem
  51. *
  52. * @return array array containing cache statistics
  53. */
  54. public function getStatistics();
  55.  
  56. /**
  57. * Fetches a stored variable from the cache
  58. *
  59. * @param string key name used to store the value
  60. * @return mixed stored variable on success, FALSE otherwise
  61. */
  62. public function getValue($key);
  63.  
  64. /**
  65. * Checks if container contains an item stored under this key name
  66. *
  67. * @param string key name used to store the value
  68. * @return boolean TRUE if the key exists, FALSE otherwise
  69. */
  70. public function isCached($key);
  71.  
  72. /**
  73. * Checks whether the chosen driver is supported by PHP installation
  74. *
  75. * @return boolean TRUE if driver can be used, FALSE otherwise
  76. */
  77. public function isSupported();
  78.  
  79. /**
  80. * Removes a stored variable from the cache
  81. * @param string key name used to store the value
  82. * @return boolean TRUE on success, FALSE otherwise
  83. */
  84. public function removeValue($key);
  85.  
  86. } /* class */
  87.  
  88.  
  89. final class CacheDriverApc extends CacheAbstract implements CacheDriver {
  90.  
  91. /**
  92. * Adds an object to the cache
  93. *
  94. * @param string stores the variable using this name, should be a cache-unique name
  95. * @param mixed the variable to store in cache
  96. * @param integer time-to-live of cached data
  97. * @return boolean TRUE on success, FALSE otherwise
  98. */
  99. public function addValue($key, $var, $ttl = 300) {
  100. return apc_store($key, $var, $ttl);
  101. }
  102.  
  103. /**
  104. * Returns a dump of all files and user variables from the cache
  105. *
  106. * @return string dump of the user variables, or NULL if an unknown error encountered
  107. */
  108. public function dumpCache() {
  109. return apc_bin_dump();
  110. }
  111.  
  112. /**
  113. * Flushes (clears) the user and system cache
  114. *
  115. * @return void does not return any value
  116. */
  117. public function flushCache() {
  118. apc_clear_cache();
  119. apc_clear_cache('user');
  120. }
  121.  
  122. /**
  123. * Provides a statistics and meta-data of caching subsystem
  124. *
  125. * @return array array containing cache statistics
  126. */
  127. public function getStatistics() {
  128. return array('data' => apc_cache_info('user'), 'info' => '', 'size' => '');
  129. }
  130.  
  131. /**
  132. * Fetches a stored variable from the cache
  133. *
  134. * @param string key name used to store the value
  135. * @return mixed stored variable on success, FALSE otherwise
  136. */
  137. public function getValue($key) {
  138. $var = apc_fetch($key, $result);
  139. if(!$result) {
  140. return false;
  141. }
  142. return $var;
  143. }
  144.  
  145. /**
  146. * Checks if container contains an item stored under this key name
  147. *
  148. * @param string key name used to store the value
  149. * @return boolean TRUE if the key exists, FALSE otherwise
  150. */
  151. public function isCached($key) {
  152. return (bool) apc_exists($key);
  153. }
  154.  
  155. /**
  156. * Checks whether the chosen driver is supported by PHP installation
  157. *
  158. * @return boolean TRUE if driver can be used, FALSE otherwise
  159. */
  160. public function isSupported() {
  161. if(!extension_loaded('apc') || !ini_get('apc.enabled')) {
  162. return false;
  163. }
  164. return true;
  165. }
  166.  
  167. /**
  168. * Removes a stored variable from the cache
  169. * @param string key name used to store the value
  170. * @return boolean TRUE on success, FALSE otherwise
  171. */
  172. public function removeValue($key) {
  173. return apc_delete($key);
  174. }
  175.  
  176. } /* class */
  177.  
  178.  
  179. final class Cache extends CacheAbstract {
  180. private $driver;
  181. private $storage;
  182.  
  183. /**
  184. * Class constructor.
  185. *
  186. * @param string specifies primary cache driver
  187. * @param string specifies secondary (failover) cache driver
  188. * @return object the Cache object
  189. */
  190. protected function __construct($driver, $failover) {
  191. if(!$this->loadDriver($driver)) {
  192. if(!$this->loadDriver($failover)) {
  193. $this->loadDriver('dummy');
  194. }
  195. }
  196. }
  197.  
  198. /**
  199. * Returns a dump of all files and user variables from the cache
  200. *
  201. * @return string dump of the user variables, or NULL if an unknown error encountered
  202. */
  203. public function dumpCache() {
  204. return $this->storage->dumpCache();
  205. }
  206.  
  207. /**
  208. * Returns a name of currently used cache storage driver
  209. *
  210. * @return string name of used storage driver
  211. */
  212. public function getDriver() {
  213. return $this->driver;
  214. }
  215.  
  216. /**
  217. * Returns the global Cache object, creating it only if it does not exist already
  218. *
  219. * @param string specifies primary cache driver
  220. * @param string specifies secondary (failover) cache driver
  221. * @return object the Cache object
  222. */
  223. public static function getInstance($driver = 'file', $failover = 'dummy') {
  224. static $instance;
  225. if(!isset($instance)) {
  226. $instance = new Cache($driver, $failover);
  227. }
  228. return $instance;
  229. }
  230.  
  231. /**
  232. * Returns a list of available cache storage drivers
  233. *
  234. * @return array an array containing a list of available cache drivers
  235. */
  236. public static function getStorageList() {
  237. $list = array();
  238. $directory = System::getSystemDir() . '/cache/driver';
  239. $dh = opendir($directory);
  240. while(($filename = readdir($dh)) !== false) {
  241. $driver = substr($filename, 0, strrpos($filename, '.'));
  242. if($driver != '' && $driver != '.') {
  243. $list[] = $driver;
  244. }
  245. }
  246. return $list;
  247. }
  248.  
  249. /**
  250. * Loads the specified cache driver
  251. *
  252. * @param string cache driver to load
  253. * @return boolean TRUE on success, FALSE otherwise
  254. */
  255. private function loadDriver($driver) {
  256. if(in_array($driver, $this->getStorageList())) {
  257. $class = 'CacheDriver' . ucfirst($driver);
  258. $this->storage = new $class();
  259. if($this->storage->isSupported()) {
  260. $this->driver = $driver;
  261. return true;
  262. }
  263. }
  264. return false;
  265. }
  266.  
  267. } /* class */

Go to the top of the page
+Quote Post
Pyton_000
post 23.02.2016, 11:14:06
Post #2





Grupa: Zarejestrowani
Postów: 8 068
Pomógł: 1414
Dołączył: 26.10.2005

Ostrzeżenie: (0%)
-----


Robisz to dla treningu czy do użytku?
Jest tyle gotowych klas do cache że głowa mała.
Go to the top of the page
+Quote Post
q.michal
post 23.02.2016, 11:19:34
Post #3





Grupa: Zarejestrowani
Postów: 111
Pomógł: 1
Dołączył: 24.12.2013

Ostrzeżenie: (0%)
-----


Oba powody sa poprawne. Przede wszystkim chce sie czegos nauczyc, pocwiczyc, nabrac wprawy.
Przy okazji chcialem takze stworzyc wlasny framework, ktory pozniej moglbym wykorzystac.
Nie zalezy mi na poznawaniu gotowych rozwiazan, mimo ze sa sprawdzone i powszechnie stosowane.
Nie planuje bowiem wykorzystywac nabytych umiejetnosci w celach zawodowych. PHP traktuje raczej jako hobby.
Gdyby bylo inaczej, to mialbym certyfikat Zenda a nie RHCE ;-)

Ten post edytował q.michal 23.02.2016, 11:31:18
Go to the top of the page
+Quote Post
daro0
post 27.02.2016, 08:12:05
Post #4





Grupa: Zarejestrowani
Postów: 88
Pomógł: 12
Dołączył: 17.09.2014
Skąd: Krasnystaw

Ostrzeżenie: (0%)
-----


Cache w cookies? To ma jakiekolwiek praktyczne zastosowanie? Rozumiem że sesje, Kohana ma np. taki driver ale żeby cache? File driver jest pewnie w każdym frameworku domyślny ale jest to jedna z najwolniejszych metod. Oprócz tych driverów co je tu wymieniłeś są jeszcze Redis, SSDB, MongoDB i jeszcze nie wiadomo jakie bazy Nosql.

Ale co do FileDriver, to jest tak rozwiązane, że nazwy plików są jako md5 albo SHA1 na podstawie klucza, natomiast jest jeszcze ważna sprawa, pliki o nazwach md5 albo SHA1 są zapisywane nie tylko w tym katalogu gdzie przechowujesz cache ale jeszcze w odpowiednich podkatalogach dwuliterowych, gdzie ich nazwy to dwa pierwsze znaki w nazwach md5 albo SHA1 a nie tak żeby wszystkie pliki były w tym samym katalogu.

Stworzenie własnego frameworka wymaga dużej wiedzy. Prędzej się nauczysz jak przejrzysz dobrze kod Kohany (bo ten FW ma też takie rozwiązanie i różne drivery do cache, tworzone jako singletony) i zobacz jak to tam jest rozwiązane. Będziesz pewnie to tworzył przez kilka mies. albo i więcej, pytanie co takiego osiągniesz? No chyba że Twój FW powali na kolana Symfony2 albo Laravel smile.gif
Go to the top of the page
+Quote Post
q.michal
post 27.02.2016, 12:32:15
Post #5





Grupa: Zarejestrowani
Postów: 111
Pomógł: 1
Dołączył: 24.12.2013

Ostrzeżenie: (0%)
-----


Jak to mowia, takie hobby ;-)

Przyznam szczerze ze nie rozumiem idei wrzucania plikow cache do podkatalogow, jak to opisales daro0. Jaki jest sens takiego segregowania?
Na szybko napisalem driver do obslugi cache bazujacego na plikach. Poniewaz wielokrotnie trzeba wyliczyc sumy MD5 zdecydowalem sie przechowywac je dodatkowo w tablicy:

  1. <?php
  2. final class CacheDriverFile extends CacheAbstract implements CacheDriver {
  3. private $cachedir;
  4. private $keys;
  5.  
  6. /**
  7. * Adds an object to the cache
  8. *
  9. * @param string stores the variable using this name, should be a cache-unique name
  10. * @param mixed the variable to store in cache
  11. * @param integer time-to-live of cached data
  12. * @return boolean TRUE on success, FALSE otherwise
  13. */
  14. public function addValue($key, $var, $ttl = 300) {
  15. if(!$fp = fopen($this->getCacheFilePath($key), 'wb')) {
  16. return false;
  17. }
  18. $data = '<?php $var = ' . var_export($var, true) . '; $expires = ' . (time() + $ttl) . '; ?>';
  19. flock($fp, LOCK_EX);
  20. fwrite($fp, $data);
  21. flock($fp, LOCK_UN);
  22. return true;
  23. }
  24.  
  25. /**
  26. * Opens a connection to remote cache server
  27. *
  28. * @param array data necessary to connect to cache server (eg. host, port, credentials)
  29. * @return boolean TRUE on success, FALSE otherwise
  30. */
  31. public function connect($config) {
  32. if(!is_writable($config['path'])) {
  33. return false;
  34. }
  35. $this->cachedir = $config['path'];
  36. return true;
  37. }
  38.  
  39. /**
  40. * Closes a connection to remote cache server
  41. *
  42. * @return boolean TRUE on success, false otherwise
  43. */
  44. public function disconnect() {
  45. return true;
  46. }
  47.  
  48. /**
  49. * Returns a path to QCF file containing cached data
  50. *
  51. * @param string key name used to store the value
  52. * @return string absolute path to file containing variable saved under key name
  53. */
  54. private function getCacheFilePath($key) {
  55. if(!isset($this->keys[$key])) {
  56. $this->keys[$key] = $this->cachedir . '/' . md5($key) . '.qcf';
  57. }
  58. return $this->keys[$key];
  59. }
  60.  
  61. /**
  62. * Fetches a stored variable from the cache
  63. *
  64. * @param string key name used to store the value
  65. * @return mixed stored variable on success, FALSE otherwise
  66. */
  67. public function getValue($key) {
  68. include($this->getCacheFilePath($key));
  69. if(!$var || !$expires || $expires < time()) {
  70. $this->removeValue($key);
  71. return false;
  72. }
  73. return $var;
  74. }
  75.  
  76. /**
  77. * Checks if container contains an item stored under this key name
  78. *
  79. * @param string key name used to store the value
  80. * @return boolean TRUE if the key exists, FALSE otherwise
  81. */
  82. public function isCached($key) {
  83. if(!file_exists($this->getCacheFilePath($key))) {
  84. return false;
  85. }
  86. return true;
  87. }
  88.  
  89. /**
  90. * Checks whether the chosen driver is supported by PHP installation
  91. *
  92. * @return boolean TRUE if driver can be used, FALSE otherwise
  93. */
  94. public function isSupported() {
  95. return true;
  96. }
  97.  
  98. /**
  99. * Removes a stored variable from the cache
  100. * @param string key name used to store the value
  101. * @return boolean TRUE on success, FALSE otherwise
  102. */
  103. public function removeValue($key) {
  104. unlink($this->getCacheFilePath($key));
  105. }
  106.  
  107. } /* class */


Wszelkie sugestie mile widziane wink.gif

Ten post edytował q.michal 27.02.2016, 12:32:30
Go to the top of the page
+Quote Post
daro0
post 27.02.2016, 12:48:23
Post #6





Grupa: Zarejestrowani
Postów: 88
Pomógł: 12
Dołączył: 17.09.2014
Skąd: Krasnystaw

Ostrzeżenie: (0%)
-----


Też na początku się zastanawiałem po co te podkatalogi ale jak przejrzałem to co jest w źródłach Kohana Core, tutaj chodzi mi o statyczną funkcję Kohana::cache to tam jest wyjaśnione, że chodzi o zapobieganie przeciążeniu systemu plików. No bo co sie stanie gdy masz gdzieś koło miliona plików w jednym katalogu?

https://kohanaframework.org/3.1/guide/api/Kohana_Core#cache

ten fragment:
  1. // Cache file is a hash of the name
  2. $file = sha1($name).'.txt';
  3.  
  4. // Cache directories are split by keys to prevent filesystem overload
  5. $dir = Kohana::$cache_dir.DIRECTORY_SEPARATOR.$file[0].$file[1].DIRECTORY_SEPARATOR;
  6.  


Laravel ma jeszcze bardziej podzielone, tj. podkatalogi of 00 do ff czyli 2 pierwsze litery z nazw plików cache (md5) a w nich jeszcze znowu podział na podkatalogi w ten sam sposób czyli kolejne dwa znaki na bazie nazwy plików cache.

Ten post edytował daro0 27.02.2016, 12:50:00
Go to the top of the page
+Quote Post
q.michal
post 27.02.2016, 12:57:35
Post #7





Grupa: Zarejestrowani
Postów: 111
Pomógł: 1
Dołączył: 24.12.2013

Ostrzeżenie: (0%)
-----


Brzmi sensownie, ale tez we wszystko bym bezgranicznie nie wierzyl. Nie mowie teraz konkretnie o tych podkatalogach, jednak jak widze pare linijek nizej chmod 777, to od razu na mysl przychodzi mi potencjalna luka w bezpieczenstwie wink.gif
Niemniej jednak przekonales mnie do idei stosowania podkatalogow. W wolnej chwili naniose poprawki i rozbuduje kod.

BTW: Co sadzisz o globalnej serializacji? Tj. zastanawiam sie czy warto wszystko serializowac, bez wzgledu na wartosc oraz typ zastosowanego cache?
Go to the top of the page
+Quote Post
daro0
post 27.02.2016, 13:11:00
Post #8





Grupa: Zarejestrowani
Postów: 88
Pomógł: 12
Dołączył: 17.09.2014
Skąd: Krasnystaw

Ostrzeżenie: (0%)
-----


Co do serializacji to też chyba nie jest takie proste:
http://techblog.procurios.nl/k/news/view/3...var_export.html

Kolejna sprawa, nie za bardzo rozumiem to w Kohanie np:

  1. return (bool) file_put_contents($dir.$file, $data, LOCK_EX);


Natomiast jest też moduł cache zamiast standowego Kohana:cache
https://kohanaframework.org/3.1/guide/api/Cache_File#set

I nie mogę załapać dlaczego w Kohana Core jest blokada LOCK_EX a w tym module File_Cache nie ma tzn. jest tak:

  1. try
  2. {
  3. $file->fwrite($data, $size);
  4. return (bool) $file->fflush();
  5. }
  6. catch (Exception $e)
  7. {
  8. throw $e;
  9. }


Ta blokada jest w ogóle pod Linux wymagana czy nie?

Co do Twojego przykładu, pomyśl jeszcze o odśmiecaniu (GarbageCollector), tak żeby co jakiś czas usuwać niepotrzebne i wygasłe pliki np. w taki sposób:

  1. // 10% prawdopodobieństwa
  2. $gc = 10;
  3.  
  4. // jeśli trafione
  5. if (rand(0, 99) < $gc)
  6. {
  7. $cache->garbageCollect();
  8. }
  9.  


To co jakiś 10 request wywali niepotrzebne już pliki.
Go to the top of the page
+Quote Post
q.michal
post 27.02.2016, 13:21:49
Post #9





Grupa: Zarejestrowani
Postów: 111
Pomógł: 1
Dołączył: 24.12.2013

Ostrzeżenie: (0%)
-----


Ciekawe zestawienie. Watpie jednak, aby ktos pakowal tak duze porcje danych do cache. Sporo interpreterow ma limit pamieci ustawiony na 64-128MB AFAIK. Wynika wiec z tego ze musialbys wsadzic (niemal) caly skrypt do cache.
Pytajac o serializacja bardziej oabwialem sie narzutu pamieci/CPU niezbednego na wykonanie serializacji przy zapisie i deserializacji przy odczycie. Z tego benchmarku wynika jednak, ze to nie powinno byc problemem.

Lockowanie ma ta zalete, ze inny watek (request) nie nadpisze Ci tych danych. Teoretycznie istnieje ryzyko, ze 2 requesty odpalone w tym samym czasie beda chcialy cos zapisac do tego pliku i wtedy jego zawartosc moze byc inna niz sie tego spodziewasz. Exclusive lock sprawia, ze inny watek nie bedzie w stanie sie w miedzyczasie dokleic do pliku i uwazam ze warto to dopisac. Sam zreszta stosuje tego locka wink.gif

Przemysle odsmiecanie, choc nie jestem teraz pewien czy we wszystkich magazynach da sie to osiagnac i jaki bylby narzut.
Jezeli chodzi o pliki, to trzeba byloby sie przeiterowac po wszystkich istniejacych plikach, zaladowac je, sprawdzic czy nie wygasly i ew usunac. Musialbym jeszcze sprawdzic czy np. APC oferuje taka mozliwosc.
Go to the top of the page
+Quote Post
Pyton_000
post 27.02.2016, 14:53:23
Post #10





Grupa: Zarejestrowani
Postów: 8 068
Pomógł: 1414
Dołączył: 26.10.2005

Ostrzeżenie: (0%)
-----


Zawsze możesz stworzyć "tablicę" file cache, która będzie trzymała hash i czas pliku, a potem tylko przefiltrować tablicę i usunąć pliki.
Go to the top of the page
+Quote Post
q.michal
post 27.02.2016, 14:59:36
Post #11





Grupa: Zarejestrowani
Postów: 111
Pomógł: 1
Dołączył: 24.12.2013

Ostrzeżenie: (0%)
-----


Cytat(Pyton_000 @ 27.02.2016, 14:53:23 ) *
Zawsze możesz stworzyć "tablicę" file cache, która będzie trzymała hash i czas pliku, a potem tylko przefiltrować tablicę i usunąć pliki.


W nowym requescie tablica bedzie pusta.
Go to the top of the page
+Quote Post
Pyton_000
post 27.02.2016, 15:20:44
Post #12





Grupa: Zarejestrowani
Postów: 8 068
Pomógł: 1414
Dołączył: 26.10.2005

Ostrzeżenie: (0%)
-----


Musisz ją zapisać. Wydało mi się to logiczne.

Ten post edytował Pyton_000 27.02.2016, 15:20:58
Go to the top of the page
+Quote Post
daro0
post 27.02.2016, 17:19:17
Post #13





Grupa: Zarejestrowani
Postów: 88
Pomógł: 12
Dołączył: 17.09.2014
Skąd: Krasnystaw

Ostrzeżenie: (0%)
-----


O jakich tablicach Wy w ogóle piszecie? Nic takiego się w praktyce nie wykorzystuje smile.gif W praktyce najprostszy cache polega na zapisaniu serializowanych danych (tutaj można zapisywać do cache cokolwiek) do pliku w okreslonym katalogu cache z nazwą jako md5 dla tego co zapisujecie do cache (klucz), natomiast przy odczycie ważność cache sprawdzana jest przez porównanie czasu ostatniej modyfikacji pliku za pomocą filemtime() z aktualnym czasem pobranym za pomocą time(), czas ten jest liczony w sekundach, po prostu odejmuje się time() od filemtime() i jesli to jest mniejsze niż ustalony czas życia ttl, cache jest jeszcze ważna, po upływie tego czasu już należy pobrać dane na nowo i zapisac do cache.

Oczywiście należy też uwzględnić obsługę wyjątków, jak się wywali (jakiś błąd deserializacji) to też można przyjąć że cache jest nieprawidłowe i się znowu pobiera coś z bazy albo jakieś inne dane i znowu zapisuje. Oczywiście te pliki które fizycznie zostaną a upłynął czas życia wynikający z tego porównania należałoby usunąć, robi się to albo za pomocą cron-a, albo probalilistycznie, przy założeniu że wykona się to biorąc pod uwagę ilość odwiedzin. Prawdopodobieństwo można sobie ustalić np. na 1%.

A te Wasze tablice to też należałoby zapisywać do cache, żeby Wam nie zniknęły te dane, więc jak Wy sobie to wyobrażacie? I tak File Store jest jednym z najwolniejszych sposobów a Wy jeszcze macie zamiar to spowalniać zapisując serializowane tablice asocjacyjne o nie wiadomo jakiej ilości elementów? A to trzeba za każdym razem i deserializować i odczytywać a potem zapisywać. Pytanie po co?
Go to the top of the page
+Quote Post
Pyton_000
post 27.02.2016, 19:10:06
Post #14





Grupa: Zarejestrowani
Postów: 8 068
Pomógł: 1414
Dołączył: 26.10.2005

Ostrzeżenie: (0%)
-----


@daro0 To oświeć mnie jak zapiszesz obiekt do Cache? Bo ja ostatnio walczyłem z FileStorage i co nie co musiałem się pogłowić żeby to sprawnie działało.

Co do tablicy to owszem, można zapuścić CRON który będzie czyścił pliki nie ważne, ale i tak musisz napisać taki mechanizm. A jeśli będziesz miał 1mln plików w podkatalogach to przelecenie wszystkich plików i sprawdzanie który przypadkiem jest przeterminowany to bzdura totalna. Dla tego moja propozycja z tablicą cache. 1 funkcja i masz listę plików które trzeba wywalić, i nie trzeba latać po katalogach i mordować I/O dysku.

Tak FileStorage to najwolniejszy z silników (cache nawet nie wspominam bo to się nie nadaje).
Go to the top of the page
+Quote Post
daro0
post 27.02.2016, 19:48:46
Post #15





Grupa: Zarejestrowani
Postów: 88
Pomógł: 12
Dołączył: 17.09.2014
Skąd: Krasnystaw

Ostrzeżenie: (0%)
-----


Tak w dużym uproszczeniu:

  1. <?php
  2.  
  3. class Person
  4. {
  5. public $firstname;
  6. public $lastname;
  7. public $age;
  8.  
  9. public function __construct($firstname, $lastname, $age)
  10. {
  11. $this->firstname = $firstname;
  12. $this->lastname = $lastname;
  13. $this->age = $age;
  14. }
  15. }
  16.  
  17. $person = new Person('Foo', 'Boo', 30);
  18.  
  19. var_dump($person);
  20.  
  21. $cache_file = md5('person') . '.txt';
  22. file_put_contents($cache_file, serialize($person));
  23.  
  24. $person_from_cache = unserialize(file_get_contents($cache_file));
  25. var_dump($person_from_cache);
  26.  
  27. ?>


W czym tu jest problem? Zserializuje całą klasę jako string a po deserializacji zwróci te dane jako obiekt.

Co do odśmiecania, dlatego wydaje mi się że ten typ cache jest do małej ilości ruchu na stronie. Tak przelecieć podkatalogi trzeba by rekurencyjnie i wszystko sprawdzać. CRON podałem tylko jako przykład, natomiast czemu nie użyć odśmiecania w oparciu o random numbers? Któryś z użytkowników wejdzie na stronę i GC zadziała smile.gif

A sesje to niby jaki mają mechanizm? Przecież też działa probalilistyczny garbage collector. I też przecież można by teoretycznie tworzyć miliony sesji bo jest kilka milionów użytkowników i co wtedy? Chyba podobnie.
Go to the top of the page
+Quote Post
Pyton_000
post 27.02.2016, 19:53:35
Post #16





Grupa: Zarejestrowani
Postów: 8 068
Pomógł: 1414
Dołączył: 26.10.2005

Ostrzeżenie: (0%)
-----


Ja wiem że trzeba obiekt zserializować.

Ale... Chcesz to robić po stronie użytkownika? Bez sensu, dodasz serializację wszystkich danych w cachce? Też bez sensu.
Dasz serializację obiektów a potem unserialize dla wszystkich danych? Też bez sensu (wywalla błędy).

Rozwiązanie które użyłem to serializowanie tylko obiektów, i przy odczycie sprawdzanie czy string jest ciągiem zserializowanym (by regex) i jeśli tak to zrób unserialize.

Co do random number, to tak, 1% i odpala się GC. Ale żeby nie zabijać tym usera właśnie po to jest tablica cache.
Go to the top of the page
+Quote Post
q.michal
post 27.02.2016, 21:32:28
Post #17





Grupa: Zarejestrowani
Postów: 111
Pomógł: 1
Dołączył: 24.12.2013

Ostrzeżenie: (0%)
-----


Odnoszac sie do benchmarku zalaczonego przez daro0, mysle ze serializacja wszystkich danych wcale nie jest takim zlym pomyslem. Narzut wydaje sie byc znikomy, a raczej malo kto keszuje kilkudziesiecio megabajtowe dane. Poza tym to wiele upraszcza i nie trzeba pozniej sprawdzac czy potrzebna jest deserializacja czy nie. Poprostu wszystkie dane z gory traktujemy tak samo. Potencjalnie rozwiazuje to takze bugi, np. moze zwrocic zserializowanego NULLa zamiast NULLa, tak wiec konstrukcja if(!$zmienna) zadziala inaczej.

Osobiscie myslalem nad zrobieniem GC bardziej przewidywalnym i np keszowaniem ostatniego czasu wykonania GC. Po czym ustalic np. czas jego wykonania co godzine. Tak, wiec GC wykonywaloby sie nie czesciej niz raz na godzine, ew rzadziej w zaleznosci od ruchu na stronie. Jest to bardziej przewidywalne, niz probabilistyka. Random moze wygenerowac kilka razy z rzedu ta sama liczbe, albo przez tydzien w ogole jej nie wygenerowac.
Go to the top of the page
+Quote Post
daro0
post 28.02.2016, 07:37:29
Post #18





Grupa: Zarejestrowani
Postów: 88
Pomógł: 12
Dołączył: 17.09.2014
Skąd: Krasnystaw

Ostrzeżenie: (0%)
-----


To rand(0,99) jest chyba niezbyt dokładne. Można jeszcze użyć mt_rand, natomiast jakie jest prawdopodobieństwo że się trafi kilka razy pod rząd ta sama liczba? Tu zakładam że generator jest dość dobry a nie jakiś pseudo generator. Poza tym na ile jest prawdopodobne że się w katalogu cache utworzy ileś tam milionów plików a jeżeli nawet to ile one zajmują?

Weźmy np. jakieś API i pobieranie kursów walut jako JSON. Pobieranie ostatnich notowań kursów walut, oczywiście bez sensu to wywoływać za każdym razem, zwłaszcza że tu notowania są co dzień więc cache można sobie ustawić nawet na 24h. Nawet jak ktoś zechce szukać z różnymi opcjami to ile tu się tych plików utworzy? Jakiś użytkownik poczeka sobie może z 2 sekundy, reszta użytkowników będzie miała dane z cache i tu będzie ułamek sekundy, więc w czym tu jest problem?

Ten post edytował daro0 28.02.2016, 07:39:46
Go to the top of the page
+Quote Post
q.michal
post 29.02.2016, 16:13:24
Post #19





Grupa: Zarejestrowani
Postów: 111
Pomógł: 1
Dołączył: 24.12.2013

Ostrzeżenie: (0%)
-----


Raczej malo trafiony przyklad wink.gif
Dane pobierzesz, przeparsujesz i zapiszesz raz, obliczasz sobie pozniej dowoli.


Funkcja touch() pozwala ustawic modified time w przyszlosci. Co prawda w przyszlosc mozna wybiec jedynie o ok 11 dni, ale zawsze cos. Tak wiec po utworzeniu pliku zawierajacego dane, mozna mu zmienic mtime na time() + $ttl.
Nie ma pozniej potrzeby zapisywania czegokolwiek do keszu. Trzeba poprostu przeleciec sie po plikach, sprawdzic ich mtime i jezeli sa starsze niz aktualny timestamp, to wywalic do kosza ;-) Moze to generowac I/O, ale z cala pewnoscia mniejsze niz ladowania calego pliku do pamieci, deserializacja obiektu i sprawdzanie jego TTL. Poza tym, jezeli ktos buduje duzy serwis, to nie postawi go raczej na hostingu wspoldzielonym, tylko VPS czy wrecz dedyk. A tak bedzie mozliwosc skorzystania z bardziej zaawansowanych metod keszowania - np. Redis.


Najnowsza wersja z plikowym sterownikiem:
http://wklej.org/hash/443ec12d749/

Ten post edytował q.michal 29.02.2016, 16:17:44
Go to the top of the page
+Quote Post

Reply to this topicStart new topic
1 Użytkowników czyta ten temat (1 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Wersja Lo-Fi Aktualny czas: 10.06.2024 - 14:56